1/*
2** Copyright 2006, 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 DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Adapter"
18#define LOG_TAG "BluetoothDeviceService.cpp"
19
20#include "android_bluetooth_common.h"
21#include "android_runtime/AndroidRuntime.h"
22#include "JNIHelp.h"
23#include "jni.h"
24#include "utils/Log.h"
25#include "utils/misc.h"
26
27#include <ctype.h>
28#include <stdio.h>
29#include <string.h>
30#include <stdlib.h>
31#include <errno.h>
32#include <unistd.h>
33
34#include <sys/socket.h>
35#include <sys/ioctl.h>
36#include <fcntl.h>
37
38#ifdef HAVE_BLUETOOTH
39#include <dbus/dbus.h>
40#include <bluedroid/bluetooth.h>
41#endif
42
43#include <cutils/properties.h>
44
45namespace android {
46
47#define BLUETOOTH_CLASS_ERROR 0xFF000000
48
49#ifdef HAVE_BLUETOOTH
50// We initialize these variables when we load class
51// android.server.BluetoothDeviceService
52static jfieldID field_mNativeData;
53static jfieldID field_mEventLoop;
54
55typedef struct {
56    JNIEnv *env;
57    DBusConnection *conn;
58    const char *adapter;  // dbus object name of the local adapter
59} native_data_t;
60
61extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *,
62                                                           jobject);
63void onCreateBondingResult(DBusMessage *msg, void *user, void *nat);
64void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *nat);
65
66/** Get native data stored in the opaque (Java code maintained) pointer mNativeData
67 *  Perform quick sanity check, if there are any problems return NULL
68 */
69static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
70    native_data_t *nat =
71            (native_data_t *)(env->GetIntField(object, field_mNativeData));
72    if (nat == NULL || nat->conn == NULL) {
73        LOGE("Uninitialized native data\n");
74        return NULL;
75    }
76    return nat;
77}
78#endif
79
80static void classInitNative(JNIEnv* env, jclass clazz) {
81    LOGV(__FUNCTION__);
82#ifdef HAVE_BLUETOOTH
83    field_mNativeData = get_field(env, clazz, "mNativeData", "I");
84    field_mEventLoop = get_field(env, clazz, "mEventLoop",
85            "Landroid/server/BluetoothEventLoop;");
86#endif
87}
88
89/* Returns true on success (even if adapter is present but disabled).
90 * Return false if dbus is down, or another serious error (out of memory)
91*/
92static bool initializeNativeDataNative(JNIEnv* env, jobject object) {
93    LOGV(__FUNCTION__);
94#ifdef HAVE_BLUETOOTH
95    native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
96    if (NULL == nat) {
97        LOGE("%s: out of memory!", __FUNCTION__);
98        return false;
99    }
100    nat->env = env;
101
102    env->SetIntField(object, field_mNativeData, (jint)nat);
103    DBusError err;
104    dbus_error_init(&err);
105    dbus_threads_init_default();
106    nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
107    if (dbus_error_is_set(&err)) {
108        LOGE("Could not get onto the system bus: %s", err.message);
109        dbus_error_free(&err);
110        return false;
111    }
112    dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
113
114    nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME;
115#endif  /*HAVE_BLUETOOTH*/
116    return true;
117}
118
119static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
120    LOGV(__FUNCTION__);
121#ifdef HAVE_BLUETOOTH
122    native_data_t *nat =
123        (native_data_t *)env->GetIntField(object, field_mNativeData);
124    if (nat) {
125        free(nat);
126        nat = NULL;
127    }
128#endif
129}
130
131static jstring getNameNative(JNIEnv *env, jobject object){
132    LOGV(__FUNCTION__);
133#ifdef HAVE_BLUETOOTH
134    native_data_t *nat = get_native_data(env, object);
135    if (nat) {
136        DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter,
137                                            DBUS_CLASS_NAME, "GetName",
138                                            DBUS_TYPE_INVALID);
139        return reply ? dbus_returns_string(env, reply) : NULL;
140    }
141#endif
142    return NULL;
143}
144
145static jstring getAdapterPathNative(JNIEnv *env, jobject object) {
146    LOGV(__FUNCTION__);
147#ifdef HAVE_BLUETOOTH
148    native_data_t *nat = get_native_data(env, object);
149    if (nat) {
150        return (env->NewStringUTF(nat->adapter));
151    }
152#endif
153    return NULL;
154}
155
156
157static jboolean startDiscoveryNative(JNIEnv *env, jobject object) {
158    LOGV(__FUNCTION__);
159#ifdef HAVE_BLUETOOTH
160    DBusMessage *msg = NULL;
161    DBusMessage *reply = NULL;
162    DBusError err;
163    const char *name;
164    jboolean ret = JNI_FALSE;
165
166    native_data_t *nat = get_native_data(env, object);
167    if (nat == NULL) {
168        goto done;
169    }
170
171    dbus_error_init(&err);
172
173    /* Compose the command */
174    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
175                                       DBUS_CLASS_NAME, "DiscoverDevices");
176
177    if (msg == NULL) {
178        LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__);
179        goto done;
180    }
181
182    /* Send the command. */
183    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
184    if (dbus_error_is_set(&err)) {
185        /* We treat the in-progress error code as success. */
186        if(strcmp(err.message, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
187            LOGW("%s: D-Bus error: %s, treating as startDiscoveryNative success\n",
188                 __FUNCTION__, err.message);
189            ret = JNI_TRUE;
190            goto done;
191        } else {
192            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
193            ret = JNI_FALSE;
194            goto done;
195        }
196    }
197
198    ret = JNI_TRUE;
199done:
200    if (reply) dbus_message_unref(reply);
201    if (msg) dbus_message_unref(msg);
202    return ret;
203#else
204    return JNI_FALSE;
205#endif
206}
207
208static void cancelDiscoveryNative(JNIEnv *env, jobject object) {
209    LOGV(__FUNCTION__);
210#ifdef HAVE_BLUETOOTH
211    DBusMessage *msg = NULL;
212    DBusMessage *reply = NULL;
213    DBusError err;
214    const char *name;
215    jstring ret;
216    native_data_t *nat;
217
218    dbus_error_init(&err);
219
220    nat = get_native_data(env, object);
221    if (nat == NULL) {
222        goto done;
223    }
224
225    /* Compose the command */
226    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
227                                       DBUS_CLASS_NAME, "CancelDiscovery");
228
229    if (msg == NULL) {
230        LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__);
231        goto done;
232    }
233
234    /* Send the command. */
235    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
236    if (dbus_error_is_set(&err)) {
237        if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized") == 0) {
238            // hcid sends this if there is no active discovery to cancel
239            LOGV("%s: There was no active discovery to cancel", __FUNCTION__);
240            dbus_error_free(&err);
241        } else {
242            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
243        }
244    }
245
246done:
247    if (msg) dbus_message_unref(msg);
248    if (reply) dbus_message_unref(reply);
249#endif
250}
251
252static jboolean startPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
253    LOGV(__FUNCTION__);
254#ifdef HAVE_BLUETOOTH
255    DBusMessage *msg = NULL;
256    DBusMessage *reply = NULL;
257    DBusError err;
258    jboolean ret = JNI_FALSE;
259
260    native_data_t *nat = get_native_data(env, object);
261    if (nat == NULL) {
262        goto done;
263    }
264
265    dbus_error_init(&err);
266    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
267            DBUS_CLASS_NAME, "StartPeriodicDiscovery");
268    if (msg == NULL) {
269        LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__);
270        goto done;
271    }
272
273    /* Send the command. */
274    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
275    if (dbus_error_is_set(&err)) {
276        /* We treat the in-progress error code as success. */
277        if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
278            LOGW("%s: D-Bus error: %s (%s), treating as "
279                 "startPeriodicDiscoveryNative success\n",
280                 __FUNCTION__, err.name, err.message);
281        } else {
282            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
283            ret = JNI_FALSE;
284            goto done;
285        }
286    }
287
288    ret = JNI_TRUE;
289done:
290    if (reply) dbus_message_unref(reply);
291    if (msg) dbus_message_unref(msg);
292    return ret;
293#else
294    return JNI_FALSE;
295#endif
296}
297
298static jboolean stopPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
299    LOGV(__FUNCTION__);
300#ifdef HAVE_BLUETOOTH
301    DBusMessage *msg = NULL;
302    DBusMessage *reply = NULL;
303    DBusError err;
304    const char *name;
305    jboolean ret = JNI_FALSE;
306
307    native_data_t *nat = get_native_data(env, object);
308    if (nat == NULL) {
309        goto done;
310    }
311
312    dbus_error_init(&err);
313    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
314            DBUS_CLASS_NAME, "StopPeriodicDiscovery");
315    if (msg == NULL) {
316        LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__);
317        goto done;
318    }
319
320    /* Send the command. */
321    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
322    if (dbus_error_is_set(&err)) {
323        /* We treat the in-progress error code as success. */
324        if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
325            LOGW("%s: D-Bus error: %s (%s), treating as "
326                 "stopPeriodicDiscoveryNative success\n",
327                 __FUNCTION__, err.name, err.message);
328        } else {
329            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
330            ret = JNI_FALSE;
331            goto done;
332        }
333    }
334
335    ret = JNI_TRUE;
336done:
337    if (reply) dbus_message_unref(reply);
338    if (msg) dbus_message_unref(msg);
339    return ret;
340#else
341    return JNI_FALSE;
342#endif
343}
344
345static jboolean isPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
346#ifdef HAVE_BLUETOOTH
347    LOGV(__FUNCTION__);
348    native_data_t *nat = get_native_data(env, object);
349    if (nat) {
350        DBusMessage *reply =
351            dbus_func_args(env, nat->conn, nat->adapter,
352                           DBUS_CLASS_NAME, "IsPeriodicDiscovery",
353                           DBUS_TYPE_INVALID);
354        return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
355    }
356#endif
357    return JNI_FALSE;
358}
359
360static jboolean setDiscoverableTimeoutNative(JNIEnv *env, jobject object, jint timeout_s) {
361#ifdef HAVE_BLUETOOTH
362    LOGV(__FUNCTION__);
363
364    if (timeout_s < 0) {
365        return JNI_FALSE;
366    }
367
368    native_data_t *nat = get_native_data(env, object);
369    if (nat) {
370        DBusMessage *reply =
371            dbus_func_args(env, nat->conn, nat->adapter,
372                           DBUS_CLASS_NAME, "SetDiscoverableTimeout",
373                           DBUS_TYPE_UINT32, &timeout_s,
374                           DBUS_TYPE_INVALID);
375        if (reply != NULL) {
376            dbus_message_unref(reply);
377            return JNI_TRUE;
378        }
379    }
380#endif
381    return JNI_FALSE;
382}
383
384static jint getDiscoverableTimeoutNative(JNIEnv *env, jobject object) {
385#ifdef HAVE_BLUETOOTH
386    LOGV(__FUNCTION__);
387    native_data_t *nat = get_native_data(env, object);
388    if (nat) {
389        DBusMessage *reply =
390            dbus_func_args(env, nat->conn, nat->adapter,
391                           DBUS_CLASS_NAME, "GetDiscoverableTimeout",
392                           DBUS_TYPE_INVALID);
393        return reply ? dbus_returns_uint32(env, reply) : -1;
394    }
395#endif
396    return -1;
397}
398
399static jboolean isConnectedNative(JNIEnv *env, jobject object, jstring address) {
400#ifdef HAVE_BLUETOOTH
401    LOGV(__FUNCTION__);
402    native_data_t *nat = get_native_data(env, object);
403    if (nat) {
404        const char *c_address = env->GetStringUTFChars(address, NULL);
405        DBusMessage *reply =
406            dbus_func_args(env, nat->conn, nat->adapter,
407                           DBUS_CLASS_NAME, "IsConnected",
408                           DBUS_TYPE_STRING, &c_address,
409                           DBUS_TYPE_INVALID);
410        env->ReleaseStringUTFChars(address, c_address);
411        return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
412    }
413#endif
414    return JNI_FALSE;
415}
416
417static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring address) {
418#ifdef HAVE_BLUETOOTH
419    LOGV(__FUNCTION__);
420    native_data_t *nat = get_native_data(env, object);
421    if (nat) {
422        const char *c_address = env->GetStringUTFChars(address, NULL);
423        // Set a timeout of 5 seconds.  Specifying the default timeout is
424        // not long enough, as a remote-device disconnect results in
425        // signal RemoteDisconnectRequested being sent, followed by a
426        // delay of 2 seconds, after which the actual disconnect takes
427        // place.
428        DBusMessage *reply =
429            dbus_func_args_timeout(env, nat->conn, 60000, nat->adapter,
430                                   DBUS_CLASS_NAME, "DisconnectRemoteDevice",
431                                   DBUS_TYPE_STRING, &c_address,
432                                   DBUS_TYPE_INVALID);
433        env->ReleaseStringUTFChars(address, c_address);
434        if (reply) dbus_message_unref(reply);
435    }
436#endif
437}
438
439static jstring getModeNative(JNIEnv *env, jobject object) {
440#ifdef HAVE_BLUETOOTH
441    LOGV(__FUNCTION__);
442    native_data_t *nat = get_native_data(env, object);
443    if (nat) {
444        DBusMessage *reply =
445            dbus_func_args(env, nat->conn, nat->adapter,
446                           DBUS_CLASS_NAME, "GetMode",
447                           DBUS_TYPE_INVALID);
448        return reply ? dbus_returns_string(env, reply) : NULL;
449    }
450#endif
451    return NULL;
452}
453
454static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) {
455#ifdef HAVE_BLUETOOTH
456    LOGV(__FUNCTION__);
457    native_data_t *nat = get_native_data(env, object);
458    if (nat) {
459        const char *c_mode = env->GetStringUTFChars(mode, NULL);
460        DBusMessage *reply =
461            dbus_func_args(env, nat->conn, nat->adapter,
462                           DBUS_CLASS_NAME, "SetMode",
463                           DBUS_TYPE_STRING, &c_mode,
464                           DBUS_TYPE_INVALID);
465        env->ReleaseStringUTFChars(mode, c_mode);
466        if (reply) {
467            dbus_message_unref(reply);
468            return JNI_TRUE;
469        }
470        return JNI_FALSE;
471    }
472#endif
473    return JNI_FALSE;
474}
475
476static jboolean createBondingNative(JNIEnv *env, jobject object,
477                                    jstring address, jint timeout_ms) {
478    LOGV(__FUNCTION__);
479#ifdef HAVE_BLUETOOTH
480    native_data_t *nat = get_native_data(env, object);
481    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
482    struct event_loop_native_data_t *eventLoopNat =
483            get_EventLoop_native_data(env, eventLoop);
484
485    if (nat && eventLoopNat) {
486        const char *c_address = env->GetStringUTFChars(address, NULL);
487        LOGV("... address = %s", c_address);
488        char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
489        strlcpy(context_address, c_address, BTADDR_SIZE);  // for callback
490        bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
491                                        onCreateBondingResult, // callback
492                                        context_address,
493                                        eventLoopNat,
494                                        nat->adapter,
495                                        DBUS_CLASS_NAME, "CreateBonding",
496                                        DBUS_TYPE_STRING, &c_address,
497                                        DBUS_TYPE_INVALID);
498        env->ReleaseStringUTFChars(address, c_address);
499        return ret ? JNI_TRUE : JNI_FALSE;
500
501    }
502#endif
503    return JNI_FALSE;
504}
505
506static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object,
507                                       jstring address) {
508    LOGV(__FUNCTION__);
509#ifdef HAVE_BLUETOOTH
510    native_data_t *nat = get_native_data(env, object);
511    if (nat) {
512        const char *c_address = env->GetStringUTFChars(address, NULL);
513        LOGV("... address = %s", c_address);
514        DBusMessage *reply =
515            dbus_func_args_timeout(env, nat->conn, -1, nat->adapter,
516                                   DBUS_CLASS_NAME, "CancelBondingProcess",
517                                   DBUS_TYPE_STRING, &c_address,
518                                   DBUS_TYPE_INVALID);
519        env->ReleaseStringUTFChars(address, c_address);
520        if (reply) {
521            dbus_message_unref(reply);
522        }
523        return JNI_TRUE;
524    }
525#endif
526    return JNI_FALSE;
527}
528
529static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) {
530    LOGV(__FUNCTION__);
531    jboolean result = JNI_FALSE;
532#ifdef HAVE_BLUETOOTH
533    native_data_t *nat = get_native_data(env, object);
534    if (nat) {
535        const char *c_address = env->GetStringUTFChars(address, NULL);
536        LOGV("... address = %s", c_address);
537        DBusError err;
538        dbus_error_init(&err);
539        DBusMessage *reply =
540            dbus_func_args_error(env, nat->conn, &err, nat->adapter,
541                                 DBUS_CLASS_NAME, "RemoveBonding",
542                                 DBUS_TYPE_STRING, &c_address,
543                                 DBUS_TYPE_INVALID);
544        if (dbus_error_is_set(&err)) {
545            if (dbus_error_has_name(&err,
546                    BLUEZ_DBUS_BASE_IFC ".Error.DoesNotExist")) {
547                LOGW("%s: Warning: %s (%s)", __FUNCTION__, err.message,
548                     c_address);
549                result = JNI_TRUE;
550            } else {
551                LOGE("%s: D-Bus error %s (%s)", __FUNCTION__, err.name,
552                        err.message);
553            }
554        } else {
555            result = JNI_TRUE;
556        }
557
558        env->ReleaseStringUTFChars(address, c_address);
559        dbus_error_free(&err);
560        if (reply) dbus_message_unref(reply);
561    }
562#endif
563    return result;
564}
565
566static jobjectArray listBondingsNative(JNIEnv *env, jobject object) {
567#ifdef HAVE_BLUETOOTH
568    LOGV(__FUNCTION__);
569    native_data_t *nat = get_native_data(env, object);
570    if (nat) {
571        DBusMessage *reply =
572            dbus_func_args(env, nat->conn, nat->adapter,
573                           DBUS_CLASS_NAME, "ListBondings",
574                           DBUS_TYPE_INVALID);
575        // return String[]
576        return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
577    }
578#endif
579    return NULL;
580}
581
582static jobjectArray listConnectionsNative(JNIEnv *env, jobject object) {
583#ifdef HAVE_BLUETOOTH
584    LOGV(__FUNCTION__);
585    native_data_t *nat = get_native_data(env, object);
586    if (nat) {
587        DBusMessage *reply =
588            dbus_func_args(env, nat->conn, nat->adapter,
589                           DBUS_CLASS_NAME, "ListConnections",
590                           DBUS_TYPE_INVALID);
591        // return String[]
592        return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
593    }
594#endif
595    return NULL;
596}
597
598static jobjectArray listRemoteDevicesNative(JNIEnv *env, jobject object) {
599#ifdef HAVE_BLUETOOTH
600    LOGV(__FUNCTION__);
601    native_data_t *nat = get_native_data(env, object);
602    if (nat) {
603        DBusMessage *reply =
604            dbus_func_args(env, nat->conn, nat->adapter,
605                           DBUS_CLASS_NAME, "ListRemoteDevices",
606                           DBUS_TYPE_INVALID);
607        return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
608    }
609#endif
610    return NULL;
611}
612
613static jstring common_Get(JNIEnv *env, jobject object, const char *func) {
614    LOGV("%s:%s", __FUNCTION__, func);
615#ifdef HAVE_BLUETOOTH
616    native_data_t *nat = get_native_data(env, object);
617    if (nat) {
618        DBusError err;
619        dbus_error_init(&err);
620        DBusMessage *reply =
621            dbus_func_args_error(env, nat->conn, &err, nat->adapter,
622                                 DBUS_CLASS_NAME, func,
623                                 DBUS_TYPE_INVALID);
624        if (reply) {
625            return dbus_returns_string(env, reply);
626        } else {
627            LOG_AND_FREE_DBUS_ERROR(&err);
628            return NULL;
629        }
630    }
631#endif
632    return NULL;
633}
634
635static jstring getAddressNative(JNIEnv *env, jobject obj) {
636    return common_Get(env, obj, "GetAddress");
637}
638
639static jstring getVersionNative(JNIEnv *env, jobject obj) {
640    return common_Get(env, obj, "GetVersion");
641}
642
643static jstring getRevisionNative(JNIEnv *env, jobject obj) {
644    return common_Get(env, obj, "GetRevision");
645}
646
647static jstring getManufacturerNative(JNIEnv *env, jobject obj) {
648    return common_Get(env, obj, "GetManufacturer");
649}
650
651static jstring getCompanyNative(JNIEnv *env, jobject obj) {
652    return common_Get(env, obj, "GetCompany");
653}
654
655static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) {
656#ifdef HAVE_BLUETOOTH
657    LOGV(__FUNCTION__);
658    native_data_t *nat = get_native_data(env, obj);
659    if (nat) {
660        const char *c_name = env->GetStringUTFChars(name, NULL);
661        DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter,
662                                            DBUS_CLASS_NAME, "SetName",
663                                            DBUS_TYPE_STRING, &c_name,
664                                            DBUS_TYPE_INVALID);
665        env->ReleaseStringUTFChars(name, c_name);
666        if (reply) {
667            dbus_message_unref(reply);
668            return JNI_TRUE;
669        }
670    }
671#endif
672    return JNI_FALSE;
673}
674
675static jstring common_getRemote(JNIEnv *env, jobject object, const char *func,
676                                jstring address) {
677    LOGV("%s:%s", __FUNCTION__, func);
678#ifdef HAVE_BLUETOOTH
679    native_data_t *nat = get_native_data(env, object);
680    if (nat) {
681        const char *c_address = env->GetStringUTFChars(address, NULL);
682        DBusError err;
683        dbus_error_init(&err);
684
685        LOGV("... address = %s", c_address);
686
687        DBusMessage *reply =
688            dbus_func_args_error(env, nat->conn, &err, nat->adapter,
689                                 DBUS_CLASS_NAME, func,
690                                 DBUS_TYPE_STRING, &c_address,
691                                 DBUS_TYPE_INVALID);
692        env->ReleaseStringUTFChars(address, c_address);
693        if (reply) {
694            return dbus_returns_string(env, reply);
695        } else if (!strcmp(func, "GetRemoteName") &&
696                dbus_error_has_name(&err, "org.bluez.Error.RequestDeferred")) {
697            // This error occurs if we request name during device discovery,
698            // its fine
699            LOGV("... %s: %s", func, err.message);
700            dbus_error_free(&err);
701            return NULL;
702        } else {
703            LOG_AND_FREE_DBUS_ERROR(&err);
704            return NULL;
705        }
706    }
707#endif
708    return NULL;
709}
710
711static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) {
712    return common_getRemote(env, obj, "GetRemoteVersion", address);
713}
714
715static jstring getRemoteRevisionNative(JNIEnv *env, jobject obj, jstring address) {
716    return common_getRemote(env, obj, "GetRemoteRevision", address);
717}
718
719static jstring getRemoteManufacturerNative(JNIEnv *env, jobject obj, jstring address) {
720    return common_getRemote(env, obj, "GetRemoteManufacturer", address);
721}
722
723static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) {
724    return common_getRemote(env, obj, "GetRemoteCompany", address);
725}
726
727static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) {
728    return common_getRemote(env, obj, "GetRemoteName", address);
729}
730
731static jstring lastSeenNative(JNIEnv *env, jobject obj, jstring address) {
732    return common_getRemote(env, obj, "LastSeen", address);
733}
734
735static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) {
736    return common_getRemote(env, obj, "LastUsed", address);
737}
738
739static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) {
740    jint result = BLUETOOTH_CLASS_ERROR;
741#ifdef HAVE_BLUETOOTH
742    LOGV(__FUNCTION__);
743    native_data_t *nat = get_native_data(env, object);
744    if (nat) {
745        const char *c_address = env->GetStringUTFChars(address, NULL);
746
747        LOGV("... address = %s", c_address);
748
749        DBusMessage *reply =
750            dbus_func_args(env, nat->conn, nat->adapter,
751                           DBUS_CLASS_NAME, "GetRemoteClass",
752                           DBUS_TYPE_STRING, &c_address,
753                           DBUS_TYPE_INVALID);
754        env->ReleaseStringUTFChars(address, c_address);
755        if (reply)
756        {
757            DBusError err;
758            dbus_error_init(&err);
759            if (!dbus_message_get_args(reply, &err,
760                                      DBUS_TYPE_UINT32, &result,
761                                      DBUS_TYPE_INVALID)) {
762                LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
763            }
764            dbus_message_unref(reply);
765        }
766    }
767#endif
768    return result;
769}
770
771static jbyteArray getRemoteFeaturesNative(JNIEnv *env, jobject object,
772                                          jstring address) {
773#ifdef HAVE_BLUETOOTH
774    LOGV(__FUNCTION__);
775    native_data_t *nat = get_native_data(env, object);
776    if (nat) {
777        const char *c_address = env->GetStringUTFChars(address, NULL);
778
779        LOGV("... address = %s", c_address);
780
781        DBusMessage *reply =
782            dbus_func_args(env, nat->conn, nat->adapter,
783                           DBUS_CLASS_NAME, "GetRemoteFeatures",
784                           DBUS_TYPE_STRING, &c_address,
785                           DBUS_TYPE_INVALID);
786        env->ReleaseStringUTFChars(address, c_address);
787        /* array of DBUS_TYPE_BYTE_AS_STRING */
788        return reply ? dbus_returns_array_of_bytes(env, reply) : NULL;
789    }
790#endif
791    return NULL;
792}
793
794static jintArray getRemoteServiceHandlesNative(JNIEnv *env, jobject object,
795                                               jstring address, jstring match) {
796#ifdef HAVE_BLUETOOTH
797    LOGV(__FUNCTION__);
798    native_data_t *nat = get_native_data(env, object);
799    if (nat) {
800        jintArray intArray = NULL;
801        const char *c_address = env->GetStringUTFChars(address, NULL);
802        const char *c_match = env->GetStringUTFChars(match, NULL);
803
804        LOGV("... address = %s match = %s", c_address, c_match);
805
806        DBusMessage *reply =
807            dbus_func_args(env, nat->conn, nat->adapter,
808                           DBUS_CLASS_NAME, "GetRemoteServiceHandles",
809                           DBUS_TYPE_STRING, &c_address,
810                           DBUS_TYPE_STRING, &c_match,
811                           DBUS_TYPE_INVALID);
812        env->ReleaseStringUTFChars(address, c_address);
813        env->ReleaseStringUTFChars(match, c_match);
814        if (reply)
815        {
816            DBusError err;
817            jint *list;
818            int i, len;
819
820            dbus_error_init(&err);
821            if (dbus_message_get_args (reply, &err,
822                                       DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
823                                       &list, &len,
824                                       DBUS_TYPE_INVALID)) {
825                if (len) {
826                    intArray = env->NewIntArray(len);
827                    if (intArray)
828                        env->SetIntArrayRegion(intArray, 0, len, list);
829                }
830            } else {
831                LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
832            }
833
834            dbus_message_unref(reply);
835        }
836        return intArray;
837    }
838#endif
839    return NULL;
840}
841
842static jbyteArray getRemoteServiceRecordNative(JNIEnv *env, jobject object,
843                                                 jstring address, jint handle) {
844#ifdef HAVE_BLUETOOTH
845    LOGV(__FUNCTION__);
846    native_data_t *nat = get_native_data(env, object);
847    if (nat) {
848        const char *c_address = env->GetStringUTFChars(address, NULL);
849
850        LOGV("... address = %s", c_address);
851
852        DBusMessage *reply =
853            dbus_func_args(env, nat->conn, nat->adapter,
854                           DBUS_CLASS_NAME, "GetRemoteServiceRecord",
855                           DBUS_TYPE_STRING, &c_address,
856                           DBUS_TYPE_UINT32, &handle,
857                           DBUS_TYPE_INVALID);
858        env->ReleaseStringUTFChars(address, c_address);
859        return reply ? dbus_returns_array_of_bytes(env, reply) : NULL;
860    }
861#endif
862    return NULL;
863}
864
865static jboolean getRemoteServiceChannelNative(JNIEnv *env, jobject object,
866                                          jstring address, jshort uuid16) {
867#ifdef HAVE_BLUETOOTH
868    LOGV(__FUNCTION__);
869    native_data_t *nat = get_native_data(env, object);
870    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
871    struct event_loop_native_data_t *eventLoopNat =
872            get_EventLoop_native_data(env, eventLoop);
873    if (nat && eventLoopNat) {
874        const char *c_address = env->GetStringUTFChars(address, NULL);
875        char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
876        strlcpy(context_address, c_address, BTADDR_SIZE);
877
878        LOGV("... address = %s", c_address);
879        LOGV("... uuid16 = %#X", uuid16);
880
881        bool ret = dbus_func_args_async(env, nat->conn, 20000,  // ms
882                           onGetRemoteServiceChannelResult, context_address,
883                           eventLoopNat,
884                           nat->adapter,
885                           DBUS_CLASS_NAME, "GetRemoteServiceChannel",
886                           DBUS_TYPE_STRING, &c_address,
887                           DBUS_TYPE_UINT16, &uuid16,
888                           DBUS_TYPE_INVALID);
889        env->ReleaseStringUTFChars(address, c_address);
890        return ret ? JNI_TRUE : JNI_FALSE;
891    }
892#endif
893    return JNI_FALSE;
894}
895
896static jint enableNative(JNIEnv *env, jobject object) {
897#ifdef HAVE_BLUETOOTH
898    LOGV(__FUNCTION__);
899    return bt_enable();
900#endif
901    return -1;
902}
903
904static jint disableNative(JNIEnv *env, jobject object) {
905#ifdef HAVE_BLUETOOTH
906    LOGV(__FUNCTION__);
907    return bt_disable();
908#endif
909    return -1;
910}
911
912static jint isEnabledNative(JNIEnv *env, jobject object) {
913#ifdef HAVE_BLUETOOTH
914    LOGV(__FUNCTION__);
915    return bt_is_enabled();
916#endif
917    return -1;
918}
919
920static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
921                         jstring pin, int nativeData) {
922#ifdef HAVE_BLUETOOTH
923    LOGV(__FUNCTION__);
924    native_data_t *nat = get_native_data(env, object);
925    if (nat) {
926        DBusMessage *msg = (DBusMessage *)nativeData;
927        DBusMessage *reply = dbus_message_new_method_return(msg);
928        if (!reply) {
929            LOGE("%s: Cannot create message reply to return PIN code to "
930                 "D-Bus\n", __FUNCTION__);
931            dbus_message_unref(msg);
932            return JNI_FALSE;
933        }
934
935        const char *c_pin = env->GetStringUTFChars(pin, NULL);
936
937        dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin,
938                                 DBUS_TYPE_INVALID);
939
940        dbus_connection_send(nat->conn, reply, NULL);
941        dbus_message_unref(msg);
942        dbus_message_unref(reply);
943        env->ReleaseStringUTFChars(pin, c_pin);
944        return JNI_TRUE;
945    }
946#endif
947    return JNI_FALSE;
948}
949
950static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address,
951                            int nativeData) {
952#ifdef HAVE_BLUETOOTH
953    LOGV(__FUNCTION__);
954    native_data_t *nat = get_native_data(env, object);
955    if (nat) {
956        DBusMessage *msg = (DBusMessage *)nativeData;
957        DBusMessage *reply = dbus_message_new_error(msg,
958                "org.bluez.Error.Canceled", "PIN Entry was canceled");
959        if (!reply) {
960            LOGE("%s: Cannot create message reply to return PIN cancel to "
961                 "D-BUS\n", __FUNCTION__);
962            dbus_message_unref(msg);
963            return JNI_FALSE;
964        }
965
966        dbus_connection_send(nat->conn, reply, NULL);
967        dbus_message_unref(msg);
968        dbus_message_unref(reply);
969        return JNI_TRUE;
970    }
971#endif
972    return JNI_FALSE;
973}
974
975static JNINativeMethod sMethods[] = {
976     /* name, signature, funcPtr */
977    {"classInitNative", "()V", (void*)classInitNative},
978    {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
979    {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
980    {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative},
981
982    {"isEnabledNative", "()I", (void *)isEnabledNative},
983    {"enableNative", "()I", (void *)enableNative},
984    {"disableNative", "()I", (void *)disableNative},
985
986    {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative},
987    {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative},
988    {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative},
989    {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative},
990    {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative},
991    {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative},
992    {"getCompanyNative", "()Ljava/lang/String;", (void *)getCompanyNative},
993
994    {"getModeNative", "()Ljava/lang/String;", (void *)getModeNative},
995    {"setModeNative", "(Ljava/lang/String;)Z", (void *)setModeNative},
996
997    {"getDiscoverableTimeoutNative", "()I", (void *)getDiscoverableTimeoutNative},
998    {"setDiscoverableTimeoutNative", "(I)Z", (void *)setDiscoverableTimeoutNative},
999
1000    {"startDiscoveryNative", "(Z)Z", (void*)startDiscoveryNative},
1001    {"cancelDiscoveryNative", "()Z", (void *)cancelDiscoveryNative},
1002    {"startPeriodicDiscoveryNative", "()Z", (void *)startPeriodicDiscoveryNative},
1003    {"stopPeriodicDiscoveryNative", "()Z", (void *)stopPeriodicDiscoveryNative},
1004    {"isPeriodicDiscoveryNative", "()Z", (void *)isPeriodicDiscoveryNative},
1005    {"listRemoteDevicesNative", "()[Ljava/lang/String;", (void *)listRemoteDevicesNative},
1006
1007    {"listConnectionsNative", "()[Ljava/lang/String;", (void *)listConnectionsNative},
1008    {"isConnectedNative", "(Ljava/lang/String;)Z", (void *)isConnectedNative},
1009    {"disconnectRemoteDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectRemoteDeviceNative},
1010
1011    {"createBondingNative", "(Ljava/lang/String;I)Z", (void *)createBondingNative},
1012    {"cancelBondingProcessNative", "(Ljava/lang/String;)Z", (void *)cancelBondingProcessNative},
1013    {"listBondingsNative", "()[Ljava/lang/String;", (void *)listBondingsNative},
1014    {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative},
1015
1016    {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative},
1017    {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative},
1018    {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative},
1019    {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative},
1020    {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative},
1021    {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative},
1022    {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative},
1023    {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative},
1024    {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative},
1025    {"lastUsedNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastUsedNative},
1026    {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
1027    {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative},
1028};
1029
1030int register_android_server_BluetoothDeviceService(JNIEnv *env) {
1031    return AndroidRuntime::registerNativeMethods(env,
1032                "android/server/BluetoothDeviceService", sMethods, NELEM(sMethods));
1033}
1034
1035} /* namespace android */
1036