1/*
2 * Copyright 2009, 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 "BluetoothSocket.cpp"
18
19#include "android_bluetooth_common.h"
20#include "android_bluetooth_c.h"
21#include "android_runtime/AndroidRuntime.h"
22#include "JNIHelp.h"
23#include "utils/Log.h"
24#include "cutils/abort_socket.h"
25
26#include <stdlib.h>
27#include <errno.h>
28#include <unistd.h>
29#include <sys/socket.h>
30#include <sys/ioctl.h>
31
32#ifdef HAVE_BLUETOOTH
33#include <bluetooth/bluetooth.h>
34#include <bluetooth/rfcomm.h>
35#include <bluetooth/l2cap.h>
36#include <bluetooth/sco.h>
37#endif
38
39#define TYPE_AS_STR(t) \
40    ((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : "L2CAP"))
41
42namespace android {
43
44static jfieldID  field_mAuth;     /* read-only */
45static jfieldID  field_mEncrypt;  /* read-only */
46static jfieldID  field_mType;     /* read-only */
47static jfieldID  field_mAddress;  /* read-only */
48static jfieldID  field_mPort;     /* read-only */
49static jfieldID  field_mSocketData;
50static jmethodID method_BluetoothSocket_ctor;
51static jclass    class_BluetoothSocket;
52
53/* Keep TYPE_RFCOMM etc in sync with BluetoothSocket.java */
54static const int TYPE_RFCOMM = 1;
55static const int TYPE_SCO = 2;
56static const int TYPE_L2CAP = 3;  // TODO: Test l2cap code paths
57
58static const int RFCOMM_SO_SNDBUF = 70 * 1024;  // 70 KB send buffer
59
60static void abortNative(JNIEnv *env, jobject obj);
61static void destroyNative(JNIEnv *env, jobject obj);
62
63static struct asocket *get_socketData(JNIEnv *env, jobject obj) {
64    struct asocket *s =
65            (struct asocket *) env->GetIntField(obj, field_mSocketData);
66    if (!s)
67        jniThrowException(env, "java/io/IOException", "null socketData");
68    return s;
69}
70
71static void initSocketFromFdNative(JNIEnv *env, jobject obj, jint fd) {
72#ifdef HAVE_BLUETOOTH
73    LOGV("%s", __FUNCTION__);
74
75    struct asocket *s = asocket_init(fd);
76
77    if (!s) {
78        LOGV("asocket_init() failed, throwing");
79        jniThrowIOException(env, errno);
80        return;
81    }
82
83    env->SetIntField(obj, field_mSocketData, (jint)s);
84
85    return;
86#endif
87    jniThrowIOException(env, ENOSYS);
88}
89
90static void initSocketNative(JNIEnv *env, jobject obj) {
91#ifdef HAVE_BLUETOOTH
92    LOGV("%s", __FUNCTION__);
93
94    int fd;
95    int lm = 0;
96    int sndbuf;
97    jboolean auth;
98    jboolean encrypt;
99    jint type;
100
101    type = env->GetIntField(obj, field_mType);
102
103    switch (type) {
104    case TYPE_RFCOMM:
105        fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
106        break;
107    case TYPE_SCO:
108        fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
109        break;
110    case TYPE_L2CAP:
111        fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
112        break;
113    default:
114        jniThrowIOException(env, ENOSYS);
115        return;
116    }
117
118    if (fd < 0) {
119        LOGV("socket() failed, throwing");
120        jniThrowIOException(env, errno);
121        return;
122    }
123
124    auth = env->GetBooleanField(obj, field_mAuth);
125    encrypt = env->GetBooleanField(obj, field_mEncrypt);
126
127    /* kernel does not yet support LM for SCO */
128    switch (type) {
129    case TYPE_RFCOMM:
130        lm |= auth ? RFCOMM_LM_AUTH : 0;
131        lm |= encrypt ? RFCOMM_LM_ENCRYPT : 0;
132        lm |= (auth && encrypt) ? RFCOMM_LM_SECURE : 0;
133        break;
134    case TYPE_L2CAP:
135        lm |= auth ? L2CAP_LM_AUTH : 0;
136        lm |= encrypt ? L2CAP_LM_ENCRYPT : 0;
137        lm |= (auth && encrypt) ? L2CAP_LM_SECURE : 0;
138        break;
139    }
140
141    if (lm) {
142        if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
143            LOGV("setsockopt(RFCOMM_LM) failed, throwing");
144            jniThrowIOException(env, errno);
145            return;
146        }
147    }
148
149    if (type == TYPE_RFCOMM) {
150        sndbuf = RFCOMM_SO_SNDBUF;
151        if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
152            LOGV("setsockopt(SO_SNDBUF) failed, throwing");
153            jniThrowIOException(env, errno);
154            return;
155        }
156    }
157
158    LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm);
159
160    initSocketFromFdNative(env, obj, fd);
161    return;
162#endif
163    jniThrowIOException(env, ENOSYS);
164}
165
166static void connectNative(JNIEnv *env, jobject obj) {
167#ifdef HAVE_BLUETOOTH
168    LOGV("%s", __FUNCTION__);
169
170    int ret;
171    jint type;
172    const char *c_address;
173    jstring address;
174    bdaddr_t bdaddress;
175    socklen_t addr_sz;
176    struct sockaddr *addr;
177    struct asocket *s = get_socketData(env, obj);
178    int retry = 0;
179
180    if (!s)
181        return;
182
183    type = env->GetIntField(obj, field_mType);
184
185    /* parse address into bdaddress */
186    address = (jstring) env->GetObjectField(obj, field_mAddress);
187    c_address = env->GetStringUTFChars(address, NULL);
188    if (get_bdaddr(c_address, &bdaddress)) {
189        env->ReleaseStringUTFChars(address, c_address);
190        jniThrowIOException(env, EINVAL);
191        return;
192    }
193    env->ReleaseStringUTFChars(address, c_address);
194
195    switch (type) {
196    case TYPE_RFCOMM:
197        struct sockaddr_rc addr_rc;
198        addr = (struct sockaddr *)&addr_rc;
199        addr_sz = sizeof(addr_rc);
200
201        memset(addr, 0, addr_sz);
202        addr_rc.rc_family = AF_BLUETOOTH;
203        addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
204        memcpy(&addr_rc.rc_bdaddr, &bdaddress, sizeof(bdaddr_t));
205
206        break;
207    case TYPE_SCO:
208        struct sockaddr_sco addr_sco;
209        addr = (struct sockaddr *)&addr_sco;
210        addr_sz = sizeof(addr_sco);
211
212        memset(addr, 0, addr_sz);
213        addr_sco.sco_family = AF_BLUETOOTH;
214        memcpy(&addr_sco.sco_bdaddr, &bdaddress, sizeof(bdaddr_t));
215
216        break;
217    case TYPE_L2CAP:
218        struct sockaddr_l2 addr_l2;
219        addr = (struct sockaddr *)&addr_l2;
220        addr_sz = sizeof(addr_l2);
221
222        memset(addr, 0, addr_sz);
223        addr_l2.l2_family = AF_BLUETOOTH;
224        addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
225        memcpy(&addr_l2.l2_bdaddr, &bdaddress, sizeof(bdaddr_t));
226
227        break;
228    default:
229        jniThrowIOException(env, ENOSYS);
230        return;
231    }
232
233connect:
234    ret = asocket_connect(s, addr, addr_sz, -1);
235    LOGV("...connect(%d, %s) = %d (errno %d)",
236            s->fd, TYPE_AS_STR(type), ret, errno);
237
238    if (ret && errno == EALREADY && retry < 2) {
239        /* workaround for bug 5082381 (EALREADY on ACL collision):
240         * retry the connect. Unfortunately we have to create a new fd.
241         * It's not ideal to switch the fd underneath the object, but
242         * is currently safe */
243        LOGD("Hit bug 5082381 (EALREADY on ACL collision), trying workaround");
244        usleep(100000);
245        retry++;
246        abortNative(env, obj);
247        destroyNative(env, obj);
248        initSocketNative(env, obj);
249        if (env->ExceptionOccurred()) {
250            return;
251        }
252        goto connect;
253    }
254    if (!ret && retry > 0)
255        LOGD("...workaround ok");
256
257    if (ret)
258        jniThrowIOException(env, errno);
259
260    return;
261#endif
262    jniThrowIOException(env, ENOSYS);
263}
264
265/* Returns errno instead of throwing, so java can check errno */
266static int bindListenNative(JNIEnv *env, jobject obj) {
267#ifdef HAVE_BLUETOOTH
268    LOGV("%s", __FUNCTION__);
269
270    jint type;
271    socklen_t addr_sz;
272    struct sockaddr *addr;
273    bdaddr_t bdaddr = android_bluetooth_bdaddr_any();
274    struct asocket *s = get_socketData(env, obj);
275
276    if (!s)
277        return EINVAL;
278
279    type = env->GetIntField(obj, field_mType);
280
281    switch (type) {
282    case TYPE_RFCOMM:
283        struct sockaddr_rc addr_rc;
284        addr = (struct sockaddr *)&addr_rc;
285        addr_sz = sizeof(addr_rc);
286
287        memset(addr, 0, addr_sz);
288        addr_rc.rc_family = AF_BLUETOOTH;
289        addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
290        memcpy(&addr_rc.rc_bdaddr, &bdaddr, sizeof(bdaddr_t));
291        break;
292    case TYPE_SCO:
293        struct sockaddr_sco addr_sco;
294        addr = (struct sockaddr *)&addr_sco;
295        addr_sz = sizeof(addr_sco);
296
297        memset(addr, 0, addr_sz);
298        addr_sco.sco_family = AF_BLUETOOTH;
299        memcpy(&addr_sco.sco_bdaddr, &bdaddr, sizeof(bdaddr_t));
300        break;
301    case TYPE_L2CAP:
302        struct sockaddr_l2 addr_l2;
303        addr = (struct sockaddr *)&addr_l2;
304        addr_sz = sizeof(addr_l2);
305
306        memset(addr, 0, addr_sz);
307        addr_l2.l2_family = AF_BLUETOOTH;
308        addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
309        memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t));
310        break;
311    default:
312        return ENOSYS;
313    }
314
315    if (bind(s->fd, addr, addr_sz)) {
316        LOGV("...bind(%d) gave errno %d", s->fd, errno);
317        return errno;
318    }
319
320    if (listen(s->fd, 1)) {
321        LOGV("...listen(%d) gave errno %d", s->fd, errno);
322        return errno;
323    }
324
325    LOGV("...bindListenNative(%d) success", s->fd);
326
327    return 0;
328
329#endif
330    return ENOSYS;
331}
332
333static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
334#ifdef HAVE_BLUETOOTH
335    LOGV("%s", __FUNCTION__);
336
337    int fd;
338    jint type;
339    struct sockaddr *addr;
340    socklen_t addr_sz;
341    jstring addr_jstr;
342    char addr_cstr[BTADDR_SIZE];
343    bdaddr_t *bdaddr;
344    jboolean auth;
345    jboolean encrypt;
346
347    struct asocket *s = get_socketData(env, obj);
348
349    if (!s)
350        return NULL;
351
352    type = env->GetIntField(obj, field_mType);
353
354    switch (type) {
355    case TYPE_RFCOMM:
356        struct sockaddr_rc addr_rc;
357        addr = (struct sockaddr *)&addr_rc;
358        addr_sz = sizeof(addr_rc);
359        bdaddr = &addr_rc.rc_bdaddr;
360        memset(addr, 0, addr_sz);
361        break;
362    case TYPE_SCO:
363        struct sockaddr_sco addr_sco;
364        addr = (struct sockaddr *)&addr_sco;
365        addr_sz = sizeof(addr_sco);
366        bdaddr = &addr_sco.sco_bdaddr;
367        memset(addr, 0, addr_sz);
368        break;
369    case TYPE_L2CAP:
370        struct sockaddr_l2 addr_l2;
371        addr = (struct sockaddr *)&addr_l2;
372        addr_sz = sizeof(addr_l2);
373        bdaddr = &addr_l2.l2_bdaddr;
374        memset(addr, 0, addr_sz);
375        break;
376    default:
377        jniThrowIOException(env, ENOSYS);
378        return NULL;
379    }
380
381    fd = asocket_accept(s, addr, &addr_sz, timeout);
382
383    LOGV("...accept(%d, %s) = %d (errno %d)",
384            s->fd, TYPE_AS_STR(type), fd, errno);
385
386    if (fd < 0) {
387        jniThrowIOException(env, errno);
388        return NULL;
389    }
390
391    /* Connected - return new BluetoothSocket */
392    auth = env->GetBooleanField(obj, field_mAuth);
393    encrypt = env->GetBooleanField(obj, field_mEncrypt);
394
395    get_bdaddr_as_string(bdaddr, addr_cstr);
396
397    addr_jstr = env->NewStringUTF(addr_cstr);
398    return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor,
399            type, fd, auth, encrypt, addr_jstr, -1);
400
401#endif
402    jniThrowIOException(env, ENOSYS);
403    return NULL;
404}
405
406static jint availableNative(JNIEnv *env, jobject obj) {
407#ifdef HAVE_BLUETOOTH
408    LOGV("%s", __FUNCTION__);
409
410    int available;
411    struct asocket *s = get_socketData(env, obj);
412
413    if (!s)
414        return -1;
415
416    if (ioctl(s->fd, FIONREAD, &available) < 0) {
417        jniThrowIOException(env, errno);
418        return -1;
419    }
420
421    return available;
422
423#endif
424    jniThrowIOException(env, ENOSYS);
425    return -1;
426}
427
428static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
429        jint length) {
430#ifdef HAVE_BLUETOOTH
431    LOGV("%s", __FUNCTION__);
432
433    int ret;
434    jbyte *b;
435    int sz;
436    struct asocket *s = get_socketData(env, obj);
437
438    if (!s)
439        return -1;
440    if (jb == NULL) {
441        jniThrowIOException(env, EINVAL);
442        return -1;
443    }
444    sz = env->GetArrayLength(jb);
445    if (offset < 0 || length < 0 || offset + length > sz) {
446        jniThrowIOException(env, EINVAL);
447        return -1;
448    }
449
450    b = env->GetByteArrayElements(jb, NULL);
451    if (b == NULL) {
452        jniThrowIOException(env, EINVAL);
453        return -1;
454    }
455
456    ret = asocket_read(s, &b[offset], length, -1);
457    if (ret < 0) {
458        jniThrowIOException(env, errno);
459        env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
460        return -1;
461    }
462
463    env->ReleaseByteArrayElements(jb, b, 0);
464    return (jint)ret;
465
466#endif
467    jniThrowIOException(env, ENOSYS);
468    return -1;
469}
470
471static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
472        jint length) {
473#ifdef HAVE_BLUETOOTH
474    LOGV("%s", __FUNCTION__);
475
476    int ret, total;
477    jbyte *b;
478    int sz;
479    struct asocket *s = get_socketData(env, obj);
480
481    if (!s)
482        return -1;
483    if (jb == NULL) {
484        jniThrowIOException(env, EINVAL);
485        return -1;
486    }
487    sz = env->GetArrayLength(jb);
488    if (offset < 0 || length < 0 || offset + length > sz) {
489        jniThrowIOException(env, EINVAL);
490        return -1;
491    }
492
493    b = env->GetByteArrayElements(jb, NULL);
494    if (b == NULL) {
495        jniThrowIOException(env, EINVAL);
496        return -1;
497    }
498
499    total = 0;
500    while (length > 0) {
501        ret = asocket_write(s, &b[offset], length, -1);
502        if (ret < 0) {
503            jniThrowIOException(env, errno);
504            env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
505            return -1;
506        }
507        offset += ret;
508        total += ret;
509        length -= ret;
510    }
511
512    env->ReleaseByteArrayElements(jb, b, JNI_ABORT);  // no need to commit
513    return (jint)total;
514
515#endif
516    jniThrowIOException(env, ENOSYS);
517    return -1;
518}
519
520static void abortNative(JNIEnv *env, jobject obj) {
521#ifdef HAVE_BLUETOOTH
522    LOGV("%s", __FUNCTION__);
523    struct asocket *s = get_socketData(env, obj);
524
525    if (!s)
526        return;
527
528    asocket_abort(s);
529
530    LOGV("...asocket_abort(%d) complete", s->fd);
531    return;
532#endif
533    jniThrowIOException(env, ENOSYS);
534}
535
536static void destroyNative(JNIEnv *env, jobject obj) {
537#ifdef HAVE_BLUETOOTH
538    LOGV("%s", __FUNCTION__);
539    struct asocket *s = get_socketData(env, obj);
540    int fd = s->fd;
541
542    if (!s)
543        return;
544
545    asocket_destroy(s);
546
547    LOGV("...asocket_destroy(%d) complete", fd);
548    return;
549#endif
550    jniThrowIOException(env, ENOSYS);
551}
552
553static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) {
554    jniThrowIOException(env, err);
555}
556
557static JNINativeMethod sMethods[] = {
558    {"initSocketNative", "()V",  (void*) initSocketNative},
559    {"initSocketFromFdNative", "(I)V",  (void*) initSocketFromFdNative},
560    {"connectNative", "()V", (void *) connectNative},
561    {"bindListenNative", "()I", (void *) bindListenNative},
562    {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
563    {"availableNative", "()I",    (void *) availableNative},
564    {"readNative", "([BII)I",    (void *) readNative},
565    {"writeNative", "([BII)I",    (void *) writeNative},
566    {"abortNative", "()V",    (void *) abortNative},
567    {"destroyNative", "()V",    (void *) destroyNative},
568    {"throwErrnoNative", "(I)V",    (void *) throwErrnoNative},
569};
570
571int register_android_bluetooth_BluetoothSocket(JNIEnv *env) {
572    jclass clazz = env->FindClass("android/bluetooth/BluetoothSocket");
573    if (clazz == NULL)
574        return -1;
575    class_BluetoothSocket = (jclass) env->NewGlobalRef(clazz);
576    field_mType = env->GetFieldID(clazz, "mType", "I");
577    field_mAddress = env->GetFieldID(clazz, "mAddress", "Ljava/lang/String;");
578    field_mPort = env->GetFieldID(clazz, "mPort", "I");
579    field_mAuth = env->GetFieldID(clazz, "mAuth", "Z");
580    field_mEncrypt = env->GetFieldID(clazz, "mEncrypt", "Z");
581    field_mSocketData = env->GetFieldID(clazz, "mSocketData", "I");
582    method_BluetoothSocket_ctor = env->GetMethodID(clazz, "<init>", "(IIZZLjava/lang/String;I)V");
583    return AndroidRuntime::registerNativeMethods(env,
584        "android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods));
585}
586
587} /* namespace android */
588
589