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