Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 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" |
Doug Kwan | 75d086e | 2011-07-16 19:14:18 -0700 | [diff] [blame] | 20 | #include "android_bluetooth_c.h" |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 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> |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 35 | #include <bluetooth/l2cap.h> |
| 36 | #include <bluetooth/sco.h> |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 37 | #endif |
| 38 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 39 | #define TYPE_AS_STR(t) \ |
| 40 | ((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : "L2CAP")) |
| 41 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 42 | namespace android { |
| 43 | |
| 44 | static jfieldID field_mAuth; /* read-only */ |
| 45 | static jfieldID field_mEncrypt; /* read-only */ |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 46 | static jfieldID field_mType; /* read-only */ |
| 47 | static jfieldID field_mAddress; /* read-only */ |
| 48 | static jfieldID field_mPort; /* read-only */ |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 49 | static jfieldID field_mSocketData; |
| 50 | static jmethodID method_BluetoothSocket_ctor; |
| 51 | static jclass class_BluetoothSocket; |
| 52 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 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 | |
Nick Pelly | 41a0a4a | 2009-08-31 13:33:06 -0700 | [diff] [blame] | 58 | static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer |
| 59 | |
Nick Pelly | fbbea2e | 2011-07-28 17:59:27 -0700 | [diff] [blame] | 60 | static void abortNative(JNIEnv *env, jobject obj); |
| 61 | static void destroyNative(JNIEnv *env, jobject obj); |
| 62 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 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 | |
| 71 | static void initSocketFromFdNative(JNIEnv *env, jobject obj, jint fd) { |
| 72 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 73 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 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 | |
| 90 | static void initSocketNative(JNIEnv *env, jobject obj) { |
| 91 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 92 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 93 | |
| 94 | int fd; |
| 95 | int lm = 0; |
Nick Pelly | 41a0a4a | 2009-08-31 13:33:06 -0700 | [diff] [blame] | 96 | int sndbuf; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 97 | jboolean auth; |
| 98 | jboolean encrypt; |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 99 | jint type; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 100 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 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 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 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 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 127 | /* kernel does not yet support LM for SCO */ |
| 128 | switch (type) { |
| 129 | case TYPE_RFCOMM: |
| 130 | lm |= auth ? RFCOMM_LM_AUTH : 0; |
Nick Pelly | 1a42cfa | 2009-09-24 18:03:42 -0700 | [diff] [blame] | 131 | lm |= encrypt ? RFCOMM_LM_ENCRYPT : 0; |
| 132 | lm |= (auth && encrypt) ? RFCOMM_LM_SECURE : 0; |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 133 | break; |
| 134 | case TYPE_L2CAP: |
| 135 | lm |= auth ? L2CAP_LM_AUTH : 0; |
Nick Pelly | 1a42cfa | 2009-09-24 18:03:42 -0700 | [diff] [blame] | 136 | lm |= encrypt ? L2CAP_LM_ENCRYPT : 0; |
| 137 | lm |= (auth && encrypt) ? L2CAP_LM_SECURE : 0; |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 138 | break; |
| 139 | } |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 140 | |
| 141 | if (lm) { |
| 142 | if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) { |
Nick Pelly | 41a0a4a | 2009-08-31 13:33:06 -0700 | [diff] [blame] | 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"); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 153 | jniThrowIOException(env, errno); |
| 154 | return; |
| 155 | } |
| 156 | } |
| 157 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 158 | LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm); |
| 159 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 160 | initSocketFromFdNative(env, obj, fd); |
| 161 | return; |
| 162 | #endif |
| 163 | jniThrowIOException(env, ENOSYS); |
| 164 | } |
| 165 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 166 | static void connectNative(JNIEnv *env, jobject obj) { |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 167 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 168 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 169 | |
| 170 | int ret; |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 171 | jint type; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 172 | const char *c_address; |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 173 | jstring address; |
| 174 | bdaddr_t bdaddress; |
| 175 | socklen_t addr_sz; |
| 176 | struct sockaddr *addr; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 177 | struct asocket *s = get_socketData(env, obj); |
Nick Pelly | fbbea2e | 2011-07-28 17:59:27 -0700 | [diff] [blame] | 178 | int retry = 0; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 179 | |
| 180 | if (!s) |
| 181 | return; |
| 182 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 183 | type = env->GetIntField(obj, field_mType); |
| 184 | |
| 185 | /* parse address into bdaddress */ |
| 186 | address = (jstring) env->GetObjectField(obj, field_mAddress); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 187 | c_address = env->GetStringUTFChars(address, NULL); |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 188 | if (get_bdaddr(c_address, &bdaddress)) { |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 189 | env->ReleaseStringUTFChars(address, c_address); |
| 190 | jniThrowIOException(env, EINVAL); |
| 191 | return; |
| 192 | } |
| 193 | env->ReleaseStringUTFChars(address, c_address); |
| 194 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 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 | |
Nick Pelly | fbbea2e | 2011-07-28 17:59:27 -0700 | [diff] [blame] | 233 | connect: |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 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); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 237 | |
Nick Pelly | fbbea2e | 2011-07-28 17:59:27 -0700 | [diff] [blame] | 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 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 257 | if (ret) |
| 258 | jniThrowIOException(env, errno); |
| 259 | |
| 260 | return; |
| 261 | #endif |
| 262 | jniThrowIOException(env, ENOSYS); |
| 263 | } |
| 264 | |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 265 | /* Returns errno instead of throwing, so java can check errno */ |
| 266 | static int bindListenNative(JNIEnv *env, jobject obj) { |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 267 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 268 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 269 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 270 | jint type; |
| 271 | socklen_t addr_sz; |
| 272 | struct sockaddr *addr; |
Doug Kwan | 75d086e | 2011-07-16 19:14:18 -0700 | [diff] [blame] | 273 | bdaddr_t bdaddr = android_bluetooth_bdaddr_any(); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 274 | struct asocket *s = get_socketData(env, obj); |
| 275 | |
| 276 | if (!s) |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 277 | return EINVAL; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 278 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 279 | type = env->GetIntField(obj, field_mType); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 280 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 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: |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 312 | return ENOSYS; |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 313 | } |
| 314 | |
| 315 | if (bind(s->fd, addr, addr_sz)) { |
Nick Pelly | 41a0a4a | 2009-08-31 13:33:06 -0700 | [diff] [blame] | 316 | LOGV("...bind(%d) gave errno %d", s->fd, errno); |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 317 | return errno; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 318 | } |
| 319 | |
| 320 | if (listen(s->fd, 1)) { |
Nick Pelly | 41a0a4a | 2009-08-31 13:33:06 -0700 | [diff] [blame] | 321 | LOGV("...listen(%d) gave errno %d", s->fd, errno); |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 322 | return errno; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 323 | } |
| 324 | |
Nick Pelly | 41a0a4a | 2009-08-31 13:33:06 -0700 | [diff] [blame] | 325 | LOGV("...bindListenNative(%d) success", s->fd); |
| 326 | |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 327 | return 0; |
Nick Pelly | 41a0a4a | 2009-08-31 13:33:06 -0700 | [diff] [blame] | 328 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 329 | #endif |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 330 | return ENOSYS; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 331 | } |
| 332 | |
| 333 | static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) { |
| 334 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 335 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 336 | |
| 337 | int fd; |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 338 | jint type; |
| 339 | struct sockaddr *addr; |
| 340 | socklen_t addr_sz; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 341 | jstring addr_jstr; |
| 342 | char addr_cstr[BTADDR_SIZE]; |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 343 | bdaddr_t *bdaddr; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 344 | jboolean auth; |
| 345 | jboolean encrypt; |
| 346 | |
| 347 | struct asocket *s = get_socketData(env, obj); |
| 348 | |
| 349 | if (!s) |
| 350 | return NULL; |
| 351 | |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 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); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 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); |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 394 | |
| 395 | get_bdaddr_as_string(bdaddr, addr_cstr); |
| 396 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 397 | addr_jstr = env->NewStringUTF(addr_cstr); |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 398 | return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor, |
| 399 | type, fd, auth, encrypt, addr_jstr, -1); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 400 | |
| 401 | #endif |
| 402 | jniThrowIOException(env, ENOSYS); |
| 403 | return NULL; |
| 404 | } |
| 405 | |
| 406 | static jint availableNative(JNIEnv *env, jobject obj) { |
| 407 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 408 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 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 | |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 428 | static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset, |
| 429 | jint length) { |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 430 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 431 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 432 | |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 433 | int ret; |
| 434 | jbyte *b; |
Nick Pelly | 57a2292 | 2009-09-25 14:13:49 -0700 | [diff] [blame] | 435 | int sz; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 436 | struct asocket *s = get_socketData(env, obj); |
| 437 | |
| 438 | if (!s) |
| 439 | return -1; |
Nick Pelly | 57a2292 | 2009-09-25 14:13:49 -0700 | [diff] [blame] | 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 | } |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 449 | |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 450 | b = env->GetByteArrayElements(jb, NULL); |
| 451 | if (b == NULL) { |
| 452 | jniThrowIOException(env, EINVAL); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 453 | return -1; |
| 454 | } |
| 455 | |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 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; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 465 | |
| 466 | #endif |
| 467 | jniThrowIOException(env, ENOSYS); |
| 468 | return -1; |
| 469 | } |
| 470 | |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 471 | static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset, |
| 472 | jint length) { |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 473 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 474 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 475 | |
Mike Playle | 36aa883 | 2010-10-22 13:58:17 +0100 | [diff] [blame] | 476 | int ret, total; |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 477 | jbyte *b; |
Nick Pelly | 57a2292 | 2009-09-25 14:13:49 -0700 | [diff] [blame] | 478 | int sz; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 479 | struct asocket *s = get_socketData(env, obj); |
| 480 | |
| 481 | if (!s) |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 482 | return -1; |
Nick Pelly | 57a2292 | 2009-09-25 14:13:49 -0700 | [diff] [blame] | 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 | } |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 492 | |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 493 | b = env->GetByteArrayElements(jb, NULL); |
| 494 | if (b == NULL) { |
| 495 | jniThrowIOException(env, EINVAL); |
| 496 | return -1; |
| 497 | } |
| 498 | |
Mike Playle | 36aa883 | 2010-10-22 13:58:17 +0100 | [diff] [blame] | 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; |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 510 | } |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 511 | |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 512 | env->ReleaseByteArrayElements(jb, b, JNI_ABORT); // no need to commit |
Mike Playle | 36aa883 | 2010-10-22 13:58:17 +0100 | [diff] [blame] | 513 | return (jint)total; |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 514 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 515 | #endif |
| 516 | jniThrowIOException(env, ENOSYS); |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 517 | return -1; |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 518 | } |
| 519 | |
Nick Pelly | 71c3c78 | 2009-09-02 11:51:35 -0700 | [diff] [blame] | 520 | static void abortNative(JNIEnv *env, jobject obj) { |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 521 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 522 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 523 | struct asocket *s = get_socketData(env, obj); |
| 524 | |
| 525 | if (!s) |
| 526 | return; |
| 527 | |
| 528 | asocket_abort(s); |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 529 | |
| 530 | LOGV("...asocket_abort(%d) complete", s->fd); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 531 | return; |
| 532 | #endif |
| 533 | jniThrowIOException(env, ENOSYS); |
| 534 | } |
| 535 | |
| 536 | static void destroyNative(JNIEnv *env, jobject obj) { |
| 537 | #ifdef HAVE_BLUETOOTH |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 538 | LOGV("%s", __FUNCTION__); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 539 | struct asocket *s = get_socketData(env, obj); |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 540 | int fd = s->fd; |
| 541 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 542 | if (!s) |
| 543 | return; |
| 544 | |
| 545 | asocket_destroy(s); |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 546 | |
| 547 | LOGV("...asocket_destroy(%d) complete", fd); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 548 | return; |
| 549 | #endif |
| 550 | jniThrowIOException(env, ENOSYS); |
| 551 | } |
| 552 | |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 553 | static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) { |
| 554 | jniThrowIOException(env, err); |
| 555 | } |
| 556 | |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 557 | static JNINativeMethod sMethods[] = { |
| 558 | {"initSocketNative", "()V", (void*) initSocketNative}, |
| 559 | {"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative}, |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 560 | {"connectNative", "()V", (void *) connectNative}, |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 561 | {"bindListenNative", "()I", (void *) bindListenNative}, |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 562 | {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative}, |
| 563 | {"availableNative", "()I", (void *) availableNative}, |
Nick Pelly | 47e82de | 2009-06-01 19:09:37 -0700 | [diff] [blame] | 564 | {"readNative", "([BII)I", (void *) readNative}, |
| 565 | {"writeNative", "([BII)I", (void *) writeNative}, |
Nick Pelly | 71c3c78 | 2009-09-02 11:51:35 -0700 | [diff] [blame] | 566 | {"abortNative", "()V", (void *) abortNative}, |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 567 | {"destroyNative", "()V", (void *) destroyNative}, |
Nick Pelly | 24bb9b8 | 2009-10-02 20:34:18 -0700 | [diff] [blame] | 568 | {"throwErrnoNative", "(I)V", (void *) throwErrnoNative}, |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 569 | }; |
| 570 | |
| 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); |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 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"); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 579 | field_mAuth = env->GetFieldID(clazz, "mAuth", "Z"); |
| 580 | field_mEncrypt = env->GetFieldID(clazz, "mEncrypt", "Z"); |
| 581 | field_mSocketData = env->GetFieldID(clazz, "mSocketData", "I"); |
Nick Pelly | 6a669fa | 2009-06-02 15:57:18 -0700 | [diff] [blame] | 582 | method_BluetoothSocket_ctor = env->GetMethodID(clazz, "<init>", "(IIZZLjava/lang/String;I)V"); |
Nick Pelly | 0b6955a | 2009-05-26 19:13:43 -0700 | [diff] [blame] | 583 | return AndroidRuntime::registerNativeMethods(env, |
| 584 | "android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods)); |
| 585 | } |
| 586 | |
| 587 | } /* namespace android */ |
| 588 | |