• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
42 namespace android {
43 
44 static jfieldID  field_mAuth;     /* read-only */
45 static jfieldID  field_mEncrypt;  /* read-only */
46 static jfieldID  field_mType;     /* read-only */
47 static jfieldID  field_mAddress;  /* read-only */
48 static jfieldID  field_mPort;     /* read-only */
49 static jfieldID  field_mSocketData;
50 static jmethodID method_BluetoothSocket_ctor;
51 static jclass    class_BluetoothSocket;
52 
53 /* Keep TYPE_RFCOMM etc in sync with BluetoothSocket.java */
54 static const int TYPE_RFCOMM = 1;
55 static const int TYPE_SCO = 2;
56 static const int TYPE_L2CAP = 3;  // TODO: Test l2cap code paths
57 
58 static const int RFCOMM_SO_SNDBUF = 70 * 1024;  // 70 KB send buffer
59 
60 static void abortNative(JNIEnv *env, jobject obj);
61 static void destroyNative(JNIEnv *env, jobject obj);
62 
get_socketData(JNIEnv * env,jobject obj)63 static 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 
initSocketFromFdNative(JNIEnv * env,jobject obj,jint fd)71 static 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 
initSocketNative(JNIEnv * env,jobject obj)90 static 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 
connectNative(JNIEnv * env,jobject obj)166 static 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 
233 connect:
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 */
bindListenNative(JNIEnv * env,jobject obj)266 static 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 
acceptNative(JNIEnv * env,jobject obj,int timeout)333 static 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 
availableNative(JNIEnv * env,jobject obj)406 static 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 
readNative(JNIEnv * env,jobject obj,jbyteArray jb,jint offset,jint length)428 static 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 
writeNative(JNIEnv * env,jobject obj,jbyteArray jb,jint offset,jint length)471 static 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 
abortNative(JNIEnv * env,jobject obj)520 static 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 
destroyNative(JNIEnv * env,jobject obj)536 static 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 
throwErrnoNative(JNIEnv * env,jobject obj,jint err)553 static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) {
554     jniThrowIOException(env, err);
555 }
556 
557 static 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 
register_android_bluetooth_BluetoothSocket(JNIEnv * env)571 int 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