void populateServiceRecord(JNIEnv *env, jobject serviceRecord, sdp_record_t* sdpRecord, sdp_list_t* attributeList) { jclass serviceRecordImplClass = (*env)->GetObjectClass(env, serviceRecord); debug("populateServiceRecord"); jmethodID populateAttributeValueID = getGetMethodID(env, serviceRecordImplClass, "populateAttributeValue", "(ILjavax/bluetooth/DataElement;)V"); if (populateAttributeValueID == NULL) { return; } int attrCount = 0; for(; attributeList; attributeList = attributeList->next) { jint attributeID=*(uint16_t*)attributeList->data; sdp_data_t *data = sdp_data_get(sdpRecord, (uint16_t)attributeID); if (data) { jobject dataElement = createDataElement(env, data); if ((*env)->ExceptionCheck(env)) { break; } if (dataElement == NULL) { break; } (*env)->CallVoidMethod(env, serviceRecord, populateAttributeValueID, attributeID, dataElement); if ((*env)->ExceptionCheck(env)) { break; } attrCount ++; } } Edebug("attrCount %i", attrCount); }
JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueZDBus_connectionRfRead (JNIEnv* env, jobject peer, jlong handle, jbyteArray b, jint off, jint len ) { if (b == NULL) { throwRuntimeException(env, "Invalid argument"); return 0; } jbyte *bytes = (*env)->GetByteArrayElements(env, b, 0); if (bytes == NULL) { throwRuntimeException(env, "Invalid argument"); return 0; } int done = 0; while (done == 0) { int flags = MSG_DONTWAIT; int count = recv(handle, (char *)(bytes + off + done), len - done, flags); if (count < 0) { if (errno == EAGAIN) { // Try again for non-blocking operation count = 0; Edebug("no data available for read"); } else if (errno == ECONNRESET) { //104 Connection reset by peer debug("Connection closed, Connection reset by peer"); // See InputStream.read(); done = -1; goto rfReadEnd; } else { throwIOException(env, "Failed to read. [%d] %s", errno, strerror(errno)); done = 0; goto rfReadEnd; } } else if (count == 0) { debug("Connection closed"); if (done == 0) { // See InputStream.read(); done = -1; } goto rfReadEnd; } done += count; if (isCurrentThreadInterrupted(env, peer)) { done = 0; goto rfReadEnd; } if (done == 0) { // Sleep while not avalable bool available = false; do { struct pollfd fds; int timeout = 500; // milliseconds memset(&fds, 0, sizeof(fds)); fds.fd = handle; fds.events = POLLIN | POLLHUP | POLLERR;// | POLLRDHUP; fds.revents = 0; //Edebug("poll: wait"); int poll_rc = poll(&fds, 1, timeout); if (poll_rc > 0) { if (fds.revents & (POLLHUP | POLLERR /* | POLLRDHUP */)) { debug("Stream socket peer closed connection"); done = -1; goto rfReadEnd; } else if (fds.revents & POLLNVAL) { // socket closed... done = -1; goto rfReadEnd; } else if (fds.revents & POLLIN) { //Edebug("poll: data to read available"); available = true; } else { Edebug("poll: revents %i", fds.revents); } } else if (poll_rc == -1) { //Edebug("poll: call error %i", errno); throwIOException(env, "Failed to poll. [%d] %s", errno, strerror(errno)); done = 0; goto rfReadEnd; } else { //Edebug("poll: call timed out"); } if (isCurrentThreadInterrupted(env, peer)) { done = -1; goto rfReadEnd; } } while (!available); } } rfReadEnd: (*env)->ReleaseByteArrayElements(env, b, bytes, 0); return done; }
JNIEXPORT jlong JNICALL Java_com_intel_bluetooth_BluetoothStackBlueZ_l2OpenClientConnectionImpl (JNIEnv* env, jobject peer, jlong localDeviceBTAddress, jlong address, jint channel, jboolean authenticate, jboolean encrypt, jint receiveMTU, jint transmitMTU, jint timeout) { debug("CONNECT connect, psm %d", channel); // allocate socket int handle = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (handle < 0) { throwIOException(env, "Failed to create socket. [%d] %s", errno, strerror(errno)); return 0; } struct sockaddr_l2 localAddr; //bind local address localAddr.l2_family = AF_BLUETOOTH; localAddr.l2_psm = 0; //bacpy(&localAddr.l2_bdaddr, BDADDR_ANY); longToDeviceAddr(localDeviceBTAddress, &localAddr.l2_bdaddr); if (bind(handle, (struct sockaddr *)&localAddr, sizeof(localAddr)) < 0) { throwIOException(env, "Failed to bind socket. [%d] %s", errno, strerror(errno)); close(handle); return 0; } // Set link mtu and security options struct l2cap_options opt; socklen_t opt_len = sizeof(opt); memset(&opt, 0, opt_len); opt.imtu = receiveMTU; opt.omtu = (transmitMTU > 0)?transmitMTU:L2CAP_DEFAULT_MTU; opt.flush_to = L2CAP_DEFAULT_FLUSH_TO; Edebug("L2CAP set imtu %i, omtu %i", opt.imtu, opt.omtu); if (setsockopt(handle, SOL_L2CAP, L2CAP_OPTIONS, &opt, opt_len) < 0) { throwIOException(env, "Failed to set L2CAP mtu options. [%d] %s", errno, strerror(errno)); close(handle); return 0; } if (encrypt || authenticate) { int socket_opt = 0; socklen_t len = sizeof(socket_opt); if (getsockopt(handle, SOL_L2CAP, L2CAP_LM, &socket_opt, &len) < 0) { throwIOException(env, "Failed to read L2CAP link mode. [%d] %s", errno, strerror(errno)); close(handle); return 0; } //if (master) { // socket_opt |= L2CAP_LM_MASTER; //} if (authenticate) { socket_opt |= L2CAP_LM_AUTH; Edebug("L2CAP set authenticate"); } if (encrypt) { socket_opt |= L2CAP_LM_ENCRYPT; } if ((socket_opt != 0) && setsockopt(handle, SOL_L2CAP, L2CAP_LM, &socket_opt, sizeof(socket_opt)) < 0) { throwIOException(env, "Failed to set L2CAP link mode. [%d] %s", errno, strerror(errno)); close(handle); return 0; } } struct sockaddr_l2 remoteAddr; remoteAddr.l2_family = AF_BLUETOOTH; longToDeviceAddr(address, &remoteAddr.l2_bdaddr); remoteAddr.l2_psm = channel; // connect to server if (connect(handle, (struct sockaddr*)&remoteAddr, sizeof(remoteAddr)) != 0) { throwIOException(env, "Failed to connect. [%d] %s", errno, strerror(errno)); close(handle); return 0; } debug("L2CAP connected, handle %li", handle); struct l2cap_options copt; if (!l2Get_options(env, handle, &copt)) { close(handle); return 0; } debug("L2CAP imtu %i, omtu %i", copt.imtu, copt.omtu); return handle; }
JNIEXPORT jlong JNICALL Java_com_intel_bluetooth_BluetoothStackBlueZ_l2ServerOpenImpl (JNIEnv* env, jobject peer, jlong localDeviceBTAddress, jboolean authorize, jboolean authenticate, jboolean encrypt, jboolean master, jboolean timeouts, jint backlog, jint receiveMTU, jint transmitMTU) { // allocate socket int handle = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (handle < 0) { throwIOException(env, "Failed to create socket. [%d] %s", errno, strerror(errno)); return 0; } struct sockaddr_l2 localAddr; //bind local address localAddr.l2_family = AF_BLUETOOTH; localAddr.l2_psm = 0; //bacpy(&localAddr.l2_bdaddr, BDADDR_ANY); longToDeviceAddr(localDeviceBTAddress, &localAddr.l2_bdaddr); if (bind(handle, (struct sockaddr *)&localAddr, sizeof(localAddr)) < 0) { throwIOException(env, "Failed to bind socket. [%d] %s", errno, strerror(errno)); close(handle); return 0; } // Set link mtu and security options struct l2cap_options opt; socklen_t opt_len = sizeof(opt); memset(&opt, 0, opt_len); opt.imtu = receiveMTU; opt.omtu = (transmitMTU > 0)?transmitMTU:L2CAP_DEFAULT_MTU; opt.flush_to = L2CAP_DEFAULT_FLUSH_TO; Edebug("L2CAP set imtu %i, omtu %i", opt.imtu, opt.omtu); if (setsockopt(handle, SOL_L2CAP, L2CAP_OPTIONS, &opt, opt_len) < 0) { throwIOException(env, "Failed to set L2CAP mtu options. [%d] %s", errno, strerror(errno)); close(handle); return 0; } // Set link security options if (encrypt || authenticate || authorize || master) { int socket_opt = 0; socklen_t len = sizeof(socket_opt); if (getsockopt(handle, SOL_L2CAP, L2CAP_LM, &socket_opt, &len) < 0) { throwIOException(env, "Failed to read L2CAP server mode. [%d] %s", errno, strerror(errno)); close(handle); return 0; } if (master) { socket_opt |= L2CAP_LM_MASTER; } if (authenticate) { socket_opt |= L2CAP_LM_AUTH; debug("L2CAP set authenticate"); } if (encrypt) { socket_opt |= L2CAP_LM_ENCRYPT; } if (authorize) { socket_opt |= L2CAP_LM_SECURE; } if ((socket_opt != 0) && setsockopt(handle, SOL_L2CAP, L2CAP_LM, &socket_opt, sizeof(socket_opt)) < 0) { throwIOException(env, "Failed to set L2CAP server mode. [%d] %s", errno, strerror(errno)); close(handle); return 0; } } // put socket into listening mode if (listen(handle, backlog) < 0) { throwIOException(env, "Failed to listen for L2CAP connections. [%d] %s", errno, strerror(errno)); close(handle); return 0; } return handle; }
JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueZ_runSearchServicesImpl (JNIEnv *env, jobject peer, jobject searchServicesThread, jlong localDeviceBTAddress, jobjectArray uuidValues, jlong remoteDeviceAddressLong) { // Prepare serviceDiscoveredCallback jclass peerClass = (*env)->GetObjectClass(env, peer); if (peerClass == NULL) { throwRuntimeException(env, "Fail to get Object Class"); return SERVICE_SEARCH_ERROR; } jmethodID serviceDiscoveredCallback = getGetMethodID(env, peerClass, "serviceDiscoveredCallback", "(Lcom/intel/bluetooth/SearchServicesThread;JJ)Z"); if (serviceDiscoveredCallback == NULL) { return SERVICE_SEARCH_ERROR; } sdp_list_t *uuidList = NULL; sdp_list_t *rsp_list = NULL; sdp_session_t *session = NULL; jint rc = SERVICE_SEARCH_ERROR; const uint16_t max_rec_num = 256; int serviceCount = 0; int error; // convert uuid set from java array to bluez sdp_list_t jsize uuidSetSize = (*env)->GetArrayLength(env, uuidValues); jsize i; debug("runSearchServicesImpl uuidSetSize %i", uuidSetSize); for(i = 0; i < uuidSetSize; i++) { jbyteArray byteArray = (jbyteArray)(*env)->GetObjectArrayElement(env, uuidValues, i); uuid_t* uuid = (uuid_t*)malloc(sizeof(uuid_t)); convertUUIDByteArrayToUUID(env, byteArray, uuid); uuidList = sdp_list_append(uuidList, uuid); } // convert remote device address from jlong to bluez bdaddr_t bdaddr_t remoteAddress; longToDeviceAddr(remoteDeviceAddressLong, &remoteAddress); bdaddr_t localAddr; longToDeviceAddr(localDeviceBTAddress, &localAddr); // connect to the device to retrieve services session = sdp_connect(&localAddr, &remoteAddress, SDP_RETRY_IF_BUSY); // if connection is not established throw an exception if (session == NULL) { rc = SERVICE_SEARCH_DEVICE_NOT_REACHABLE; goto searchServicesImplEnd; } // then ask the device for service record handles error = sdp_service_search_req(session, uuidList, max_rec_num, &(rsp_list)); if (error) { debug("sdp_service_search_req error %i", error); rc = SERVICE_SEARCH_ERROR; goto searchServicesImplEnd; } Edebug("runSearchServicesImpl session %p %li", session, ptr2jlong(session)); // Notify java about found services sdp_list_t* handle; for(handle = rsp_list; handle; handle = handle->next) { uint32_t record = *(uint32_t*)handle->data; jlong recordHandle = record; Edebug("runSearchServicesImpl serviceRecordHandle %li", recordHandle); jboolean isTerminated = (*env)->CallBooleanMethod(env, peer, serviceDiscoveredCallback, searchServicesThread, ptr2jlong(session), recordHandle); if ((*env)->ExceptionCheck(env)) { rc = SERVICE_SEARCH_ERROR; goto searchServicesImplEnd; } else if (isTerminated) { rc = SERVICE_SEARCH_TERMINATED; goto searchServicesImplEnd; } serviceCount ++; } debug("runSearchServicesImpl found %i", serviceCount); rc = SERVICE_SEARCH_COMPLETED; searchServicesImplEnd: sdp_list_free(uuidList, free); sdp_list_free(rsp_list, free); if (session != NULL) { sdp_close(session); } return rc; }
jobject createDataElement(JNIEnv *env, sdp_data_t *data) { Edebug("createDataElement 0x%x", data->dtd); jclass dataElementClass = (*env)->FindClass(env, "javax/bluetooth/DataElement"); jmethodID constructorID; jobject dataElement = NULL; switch (data->dtd) { case SDP_DATA_NIL: { constructorID = getGetMethodID(env, dataElementClass, "<init>", "(I)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_NULL); break; } case SDP_BOOL: { jboolean boolean = data->val.uint8; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(Z)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, boolean); break; } case SDP_UINT8: { jlong value = (jlong)data->val.uint8; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(IJ)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_U_INT_1, value); break; } case SDP_UINT16: { jlong value = (jlong)data->val.uint16; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(IJ)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_U_INT_2, value); break; } case SDP_UINT32: { jlong value = (jlong)data->val.uint32; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(IJ)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_U_INT_4, value); break; } case SDP_INT8: { jlong value = (jlong)data->val.int8; constructorID = getGetMethodID(env, dataElementClass,"<init>","(IJ)V"); dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_INT_1, value); break; } case SDP_INT16: { jlong value = (jlong)data->val.int16; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(IJ)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_INT_2, value); break; } case SDP_INT32: { jlong value = (jlong)data->val.int32; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(IJ)V"); dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_INT_4, value); break; } case SDP_INT64: { jlong value = (jlong)data->val.int64; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(IJ)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_INT_8, value); break; } case SDP_UINT64: { Edebug("SDP_UINT64"); uint64_t value = data->val.uint64; jbyte* bytes = (jbyte*)&value; reverseArray(bytes, sizeof(value)); jbyteArray byteArray = (*env)->NewByteArray(env, sizeof(value)); (*env)->SetByteArrayRegion(env, byteArray, 0, sizeof(value), bytes); constructorID = getGetMethodID(env, dataElementClass, "<init>", "(ILjava/lang/Object;)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_U_INT_8, byteArray); break; } case SDP_UINT128: { Edebug("SDP_UINT128"); uint128_t value = data->val.uint128; jbyte* bytes = (jbyte*)&value; reverseArray(bytes, sizeof(value)); jbyteArray byteArray = (*env)->NewByteArray(env, sizeof(value)); (*env)->SetByteArrayRegion(env, byteArray, 0, sizeof(value), bytes); constructorID = getGetMethodID(env, dataElementClass, "<init>", "(ILjava/lang/Object;)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_U_INT_16, byteArray); break; } case SDP_INT128: { Edebug("SDP_INT128"); uint128_t value = data->val.int128; jbyte* bytes = (jbyte*)&value; reverseArray(bytes, sizeof(value)); jbyteArray byteArray = (*env)->NewByteArray(env, sizeof(value)); (*env)->SetByteArrayRegion(env, byteArray, 0, sizeof(value), bytes); constructorID = getGetMethodID(env, dataElementClass, "<init>", "(ILjava/lang/Object;)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_INT_16, byteArray); break; } case SDP_URL_STR_UNSPEC: case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: { Edebug("SDP_URL"); char* str = data->val.str; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(ILjava/lang/Object;)V"); if (constructorID == NULL) { break; } jstring string = (*env)->NewStringUTF(env, str); dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_URL, string); break; } case SDP_TEXT_STR_UNSPEC: case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_TEXT_STR32: { Edebug("SDP_TEXT"); char* str = data->val.str; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(ILjava/lang/Object;)V"); if (constructorID == NULL) { break; } jstring string = (*env)->NewStringUTF(env, str); dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_STRING, string); break; } case SDP_UUID_UNSPEC: case SDP_UUID16: case SDP_UUID32: case SDP_UUID128: { Edebug("SDP_UUID"); jobject javaUUID = createJavaUUID(env, data->val.uuid); if (javaUUID == NULL) { debug("fail to create UUID"); break; } constructorID = getGetMethodID(env, dataElementClass, "<init>", "(ILjava/lang/Object;)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_UUID, javaUUID); break; } case SDP_SEQ_UNSPEC: case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: { Edebug("SDP_SEQ"); sdp_data_t *newData = data->val.dataseq; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(I)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_DATSEQ); jmethodID addElementID = getGetMethodID(env, dataElementClass, "addElement", "(Ljavax/bluetooth/DataElement;)V"); for(; newData; newData = newData->next) { jobject newDataElement = createDataElement(env, newData); if (newDataElement != NULL) { (*env)->CallVoidMethod(env, dataElement, addElementID, newDataElement); } if ((*env)->ExceptionCheck(env)) { break; } } break; } case SDP_ALT_UNSPEC: case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: { Edebug("SDP_ALT"); sdp_data_t *newData = data->val.dataseq; constructorID = getGetMethodID(env, dataElementClass, "<init>", "(I)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_DATALT); jmethodID addElementID = getGetMethodID(env, dataElementClass, "addElement", "(Ljavax/bluetooth/DataElement;)V"); for(; newData; newData = newData->next) { jobject newDataElement = createDataElement(env, newData); if (newDataElement == NULL) { break; } (*env)->CallVoidMethod(env, dataElement, addElementID, newDataElement); if ((*env)->ExceptionCheck(env)) { break; } } break; } default: { debug("strange data type 0x%x", data->dtd); constructorID = getGetMethodID(env, dataElementClass, "<init>", "(I)V"); if (constructorID == NULL) { break; } dataElement = (*env)->NewObject(env, dataElementClass, constructorID, DATA_ELEMENT_TYPE_NULL); break; } } if (dataElement != NULL) { Edebug("dataElement created 0x%x", data->dtd); } if ((*env)->ExceptionCheck(env)) { ndebug("Exception in data element creation 0x%x", data->dtd); } return dataElement; }