All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ve.bluecove.2.0.3.source-code.BlueSoleilStack.cpp Maven / Gradle / Ivy

/**
 *  BlueCove - Java library for Bluetooth
 *  Copyright (C) 2006-2008 Vlad Skarzhevskyy
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  @version $Id: BlueSoleilStack.cpp 2301 2008-06-26 16:46:31Z skarzhevskyy $
 */

#include "BlueSoleilStack.h"

#ifdef VC6
#define CPP_FILE "BlueSoleilStack.cpp"
#endif

#ifndef _WIN32_WCE
BOOL isBlueSoleilBluetoothStackPresent(JNIEnv *env) {
    HMODULE h = LoadLibrary(BLUESOLEIL_DLL);
    if (h == NULL) {
        return FALSE;
    }
    FreeLibrary(h);
    return TRUE;
}
#endif

#ifdef _BLUESOLEIL

static BOOL BlueSoleilStarted = FALSE;

//static BYTE localDeviceAddress[DEVICE_ADDRESS_LENGTH];
// This is to avoid errors in BlueSoleil when the BLUETOOTH_DEVICE_INFO_EX is in the stack.
int dummyData1[1024];
BLUETOOTH_DEVICE_INFO_EX localDeviceInfo = {0};
int dummyData2[1024];

void BsAddrToString(wchar_t* addressString, BYTE* address) {
    swprintf_s(addressString, 14, L"%02x%02x%02x%02x%02x%02x",
             address[5],
             address[4],
             address[3],
             address[2],
             address[1],
             address[0]);
}

jlong BsAddrToLong(BYTE* address) {
    jlong l = 0;
    for (int i = 5; i >= 0; i--) {
        l = (l << 8) + address[i];
    }
    return l;
}

void LongToBsAddr(jlong addr, BYTE* address) {
    for (int i = 0; i < 6 ; i++) {
        address[i] = (BYTE)(addr & 0xFF);
        addr >>= 8;
    }
}

jint BsDeviceClassToInt(BYTE* devClass) {
    return (((devClass[0] << 8) + devClass[1]) << 8) + devClass[2];
}

#define MULTI_BYTE_HACK

#ifndef MULTI_BYTE_HACK
// For some reson devInfo.szName can't be used in call to JNI NewStringUTF
jstring BsNewDeviceNameString(JNIEnv* env, char* str) {
    char n[MAX_DEVICE_NAME_LENGTH + 1];
    sprintf_s(n, (size_t)MAX_DEVICE_NAME_LENGTH, "%s", str);
	return env->NewStringUTF(n);
}
#else
jstring BsNewDeviceNameString(JNIEnv* env, char* str) {
	jstring value = NULL;
	int length =  (UINT32)strlen(str);
	int length_mb = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, length, NULL, 0);
    unsigned short* buffer = (unsigned short*)malloc(length_mb * 2 + 1);
	if (buffer != NULL) {
		if (MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, length, (LPWSTR)buffer, length_mb) > 0 ) {
			value = env->NewString((jchar*)buffer, length_mb );
		}
		free(buffer);
	}
	return value;
}
#endif

//API calling status code to String
char * getBsAPIStatusString(DWORD dwResult) {
    switch (dwResult) {
        case BTSTATUS_FAIL: return "General fail";
        case BTSTATUS_SUCCESS: return "Success";
        case BTSTATUS_SYSTEM_ERROR: return "System error";
        case BTSTATUS_BT_NOT_READY: return "BT_NOT_READY";
        case BTSTATUS_ALREADY_PAIRED: return "BlueSoleil is already paired with the device";
        case BTSTATUS_AUTHENTICATE_FAILED: return "Authentication fails";
        case BTSTATUS_BT_BUSY: return "Bluetooth is busy with browsing services or connecting to a device";
        case BTSTATUS_CONNECTION_EXIST: return "The connection to the service is already established";
        case BTSTATUS_CONNECTION_NOT_EXIST: return "The connection does not exist or is released";
        case BTSTATUS_PARAMETER_ERROR: return "PARAMETER_ERROR";
        case BTSTATUS_SERVICE_NOT_EXIST: return "SERVICE_NOT_EXIST";
        case BTSTATUS_DEVICE_NOT_EXIST: return "DEVICE_NOT_EXIST";
        default:
            return "Unknown BlueSoleil error";
    }
}

JNIEXPORT jboolean JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_isNativeCodeLoaded
  (JNIEnv *env, jobject peer) {
    return JNI_TRUE;
}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_getLibraryVersion
(JNIEnv *, jobject) {
    return blueCoveVersion();
}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_detectBluetoothStack
(JNIEnv *env, jobject) {
    return detectBluetoothStack(env);
}

JNIEXPORT void JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_enableNativeDebug
  (JNIEnv *env, jobject, jclass loggerClass, jboolean on) {
    enableNativeDebug(env, loggerClass, on);
}

JNIEXPORT jboolean JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_initializeImpl
(JNIEnv *env, jobject) {
    if (BT_InitializeLibrary()) {
        if (BT_IsBluetoothReady(10)) {
            BlueSoleilStarted = TRUE;
            stack = new BlueSoleilStack();
            return JNI_TRUE;
        } else {
            debug(("Error in BlueSoleil BT_IsBluetoothReady"));
        }
    } else {
        debug(("Error in BlueSoleil InitializeLibrary"));
    }
    return JNI_FALSE;
}

JNIEXPORT void JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_uninitialize
(JNIEnv *env, jobject) {
    if (stack != NULL) {
        debug(("uninitialize BlueSoleil"));
        BlueSoleilStack* stackTmp = stack;
        stack = NULL;
        delete stackTmp;
    }
    if (BlueSoleilStarted) {
        BlueSoleilStarted = FALSE;
        BT_UninitializeLibrary();
    }
}

BOOL BsGetLocalDeviceInfo(JNIEnv *env, DWORD dwMask, PBLUETOOTH_DEVICE_INFO_EX pDevInfo) {
    //memset(pDevInfo, 0, sizeof(BLUETOOTH_DEVICE_INFO_EX));
    pDevInfo->dwSize = sizeof(BLUETOOTH_DEVICE_INFO_EX);
	//if ((dwMask & MASK_DEVICE_ADDRESS) == 0) {
		//memcpy(pDevInfo->address, localDeviceAddress, DEVICE_ADDRESS_LENGTH);
	//}
    DWORD dwResult = BT_GetLocalDeviceInfo(dwMask, pDevInfo);
    if (dwResult != BTSTATUS_SUCCESS) {
        debug(("BT_GetLocalDeviceInfo return  [%s]", getBsAPIStatusString(dwResult)));
        return FALSE;
    } else {
        return TRUE;
    }
}

JNIEXPORT jstring JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_getLocalDeviceBluetoothAddress
(JNIEnv *env, jobject) {
    if (stack == NULL) {
        throwRuntimeException(env, cSTACK_CLOSED);
        return NULL;
    }
    BsGetLocalDeviceInfo(env, MASK_DEVICE_ADDRESS, &localDeviceInfo);
    //memcpy(localDeviceAddress, localDeviceInfo.address, DEVICE_ADDRESS_LENGTH);
    wchar_t addressString[14];
    BsAddrToString(addressString, localDeviceInfo.address);
    return env->NewString((jchar*)addressString, (jsize)wcslen(addressString));
}

JNIEXPORT jstring JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_getLocalDeviceName
(JNIEnv *env, jobject) {
    if (stack == NULL) {
        throwRuntimeException(env, cSTACK_CLOSED);
        return NULL;
    }
    if (!BsGetLocalDeviceInfo(env, MASK_DEVICE_NAME, &localDeviceInfo)) {
        return NULL;
    }
    return BsNewDeviceNameString(env, localDeviceInfo.szName);
}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_getDeviceVersion
(JNIEnv *env, jobject) {
    if (stack == NULL) {
        throwRuntimeException(env, cSTACK_CLOSED);
        return 0;
    }
    BsGetLocalDeviceInfo(env, MASK_LMP_VERSION, &localDeviceInfo);
    return localDeviceInfo.wLmpSubversion;
}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_getDeviceManufacturer
(JNIEnv *env, jobject) {
    if (stack == NULL) {
        throwRuntimeException(env, cSTACK_CLOSED);
        return 0;
    }
    BsGetLocalDeviceInfo(env, MASK_LMP_VERSION, &localDeviceInfo);
    return localDeviceInfo.wManuName;
}


JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_getStackVersionInfo
(JNIEnv *env, jobject) {
    if (stack == NULL) {
        throwRuntimeException(env, cSTACK_CLOSED);
        return 0;
    }
    return BT_GetVersion();
}

JNIEXPORT jboolean JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_isBlueSoleilStarted
(JNIEnv *, jobject, jint seconds) {
    return BT_IsBlueSoleilStarted(seconds);
}

JNIEXPORT jboolean JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_isBluetoothReady
(JNIEnv *, jobject, jint seconds) {
    return BT_IsBluetoothReady(seconds);
}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_getDeviceClassImpl
(JNIEnv *env, jobject) {
    return getDeviceClassByOS(env);
}

// --- Device Inquiry

//void BsOnDeviceResponded(PBLUETOOTH_DEVICE_INFO pDevInfo) {
//}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_runDeviceInquiryImpl
(JNIEnv * env, jobject peer, jobject startedNotify, jint accessCode, jobject listener) {
    if (stack == NULL) {
        throwIOException(env, cSTACK_CLOSED);
        return 0;
    }
    // We do Asynchronous call and there are no way to see when inquiry is started.

//  DWORD dwResult = BT_RegisterCallback(EVENT_INQUIRY_DEVICE_REPORT, &BsOnDeviceResponded);
//  if (dwResult != BTSTATUS_SUCCESS) {
//      //throwException(env, "javax/bluetooth/BluetoothStateException", "Can't RegisterCallback");
//      return INQUIRY_ERROR;
//  }

    DeviceInquiryCallback callback;
    if (!callback.builDeviceInquiryCallbacks(env, peer, startedNotify)) {
        return INQUIRY_ERROR;
    }

    UCHAR  ucInqMode = INQUIRY_GENERAL_MODE;
    if (accessCode == LIAC) {
        ucInqMode = INQUIRY_LIMITED_MODE;
    }
    UCHAR ucInqLen = 0x0A; //~~ 15 sec
    BLUETOOTH_DEVICE_INFO   lpDevsList[DEVICE_RESPONDED_MAX] = {0};
    DWORD devsListLen = sizeof(BLUETOOTH_DEVICE_INFO) * DEVICE_RESPONDED_MAX;

    stack->inquiringDevice = TRUE;
    DWORD dwResult = BT_InquireDevices(ucInqMode, ucInqLen, &devsListLen, lpDevsList);
    if (stack == NULL) {
		return INQUIRY_ERROR;
	}
    stack->inquiringDevice = FALSE;
    if (dwResult != BTSTATUS_SUCCESS) {
        debug(("BT_InquireDevices return  [%s]", getBsAPIStatusString(dwResult)));
        return INQUIRY_ERROR;
    }

    for (DWORD i=0; i < ((devsListLen)/sizeof(BLUETOOTH_DEVICE_INFO)); i++) {
        BLUETOOTH_DEVICE_INFO *pDevice = (BLUETOOTH_DEVICE_INFO*)((UCHAR*)lpDevsList + i * sizeof(BLUETOOTH_DEVICE_INFO));
        jlong deviceAddr = BsAddrToLong(pDevice->address);


        BLUETOOTH_DEVICE_INFO_EX devInfo = {0};
        memcpy(&devInfo.address, pDevice->address, DEVICE_ADDRESS_LENGTH);
        devInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO_EX);
        devInfo.szName[0] = '\0';
        BT_GetRemoteDeviceInfo(MASK_DEVICE_NAME | MASK_DEVICE_CLASS, &devInfo);
        jboolean paired = pDevice->bPaired;
        jint deviceClass = BsDeviceClassToInt(devInfo.classOfDevice);
        if (!callback.callDeviceDiscovered(env, listener, deviceAddr, deviceClass, BsNewDeviceNameString(env, (char*)(devInfo.szName)), paired)) {
		    return INQUIRY_ERROR;
		}
    }

    return INQUIRY_COMPLETED;
}

JNIEXPORT jboolean JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_cancelInquirympl
(JNIEnv *env, jobject){
    if (stack == NULL) {
        return FALSE;
    }
    if (!stack->inquiringDevice) {
        return FALSE;
    }
    DWORD dwResult = BT_CancelInquiry();
    if (dwResult != BTSTATUS_SUCCESS) {
        debug(("BT_CancelInquiry return  [%s]", getBsAPIStatusString(dwResult)));
        return FALSE;
    } else {
        return TRUE;
    }
}

// --- Service search

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_runSearchServicesImpl
(JNIEnv *env, jobject peer, jobject startedNotify, jobject listener, jbyteArray uuidValue, jlong address, jobject device)  {
    if (stack == NULL) {
        throwIOException(env, cSTACK_CLOSED);
        return 0;
    }
    BLUETOOTH_DEVICE_INFO devInfo={0};
    devInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
    LongToBsAddr(address, devInfo.address);

    SPPEX_SERVICE_INFO sppex_svc_info[5];
    memset(&sppex_svc_info, 0, 5 * sizeof(SPPEX_SERVICE_INFO));
    DWORD dwLength = 5 * sizeof(SPPEX_SERVICE_INFO);
    sppex_svc_info[0].dwSize = sizeof(SPPEX_SERVICE_INFO);

    GUID service_guid;

    // pin array
    jbyte *bytes = env->GetByteArrayElements(uuidValue, 0);

    // build UUID
    convertUUIDBytesToGUID(bytes, &service_guid);

    // unpin array
    env->ReleaseByteArrayElements(uuidValue, bytes, 0);

    memcpy(&(sppex_svc_info[0].serviceClassUuid128), &service_guid, sizeof(UUID));

    DWORD dwResult;
    dwResult = BT_SearchSPPExServices(&devInfo, &dwLength, sppex_svc_info);
    if (dwResult != BTSTATUS_SUCCESS)   {
        debug(("BT_SearchSPPExServices return  [%s]", getBsAPIStatusString(dwResult)));
        if (dwResult == BTSTATUS_SERVICE_NOT_EXIST) {
            return SERVICE_SEARCH_NO_RECORDS;
        } else {
            return SERVICE_SEARCH_ERROR;
        }
    }

    jclass peerClass = env->GetObjectClass(peer);
    if (peerClass == NULL) {
        throwRuntimeException(env, "Fail to get Object Class");
        return SERVICE_SEARCH_ERROR;
    }

    jmethodID servicesFoundCallbackMethod = env->GetMethodID(peerClass, "servicesFoundCallback", "(Lcom/intel/bluetooth/SearchServicesThread;Ljavax/bluetooth/DiscoveryListener;Ljavax/bluetooth/RemoteDevice;Ljava/lang/String;[BIJ)V");
    if (servicesFoundCallbackMethod == NULL) {
        throwRuntimeException(env, "Fail to get MethodID servicesFoundCallback");
        return SERVICE_SEARCH_ERROR;
    }
    DWORD foundCount = dwLength / sizeof(SPPEX_SERVICE_INFO);
    debug(("services found: %i", foundCount));
    for(DWORD i = 0; i < foundCount; i++) {
        SPPEX_SERVICE_INFO* sr = &(sppex_svc_info[i]);
        if ((sr == NULL) || (sr->dwSDAPRecordHanlde == 0)) {
            debug((" ignore null sppex_svc_info"));
            continue;
        }

        debug(("SDAP Record Handle: %u", sr->dwSDAPRecordHanlde));
        if (sr->szServiceName != NULL) {
        debug(("      Service Name: %s", sr->szServiceName));
        }
        debug(("   Service Channel: %02X", sr->ucServiceChannel));
        debug(("         Com Index: %u", (unsigned int)(sr->ucComIndex)));

        jbyteArray uuidValueFound = env->NewByteArray(16);
        jbyte *bytes = env->GetByteArrayElements(uuidValueFound, 0);

        GUID found_service_guid;
        memcpy(&found_service_guid, &(sr->serviceClassUuid128), sizeof(UUID));
        convertGUIDToUUIDBytes(&found_service_guid, bytes);

        env->ReleaseByteArrayElements(uuidValueFound, bytes, 0);


        //DiscoveryListener listener, RemoteDevice device, String serviceName, byte[] uuidValue, int channel
        env->CallVoidMethod(peer, servicesFoundCallbackMethod,
            startedNotify, listener, device, env->NewStringUTF((char*)(sr->szServiceName)), uuidValueFound, (jint)sr->ucServiceChannel, (jlong)sr->dwSDAPRecordHanlde);

        if (ExceptionCheckCompatible(env)) {
           return SERVICE_SEARCH_ERROR;
        }
    }

    return SERVICE_SEARCH_COMPLETED;
}

//   --- Client RFCOMM connections

void BS_SPPEXConnectionCallback(DWORD dwServerHandle, BYTE* lpBdAddr, UCHAR ucStatus, DWORD dwConnetionHandle) {
    if (stack != NULL) {
        stack->SPPEXConnectionCallback(dwServerHandle, lpBdAddr, ucStatus, dwConnetionHandle);
    }
}

BlueSoleilStack::BlueSoleilStack() {
    inquiringDevice = FALSE;
    InitializeCriticalSection(&openingPortLock);

    commPortsPool = new ObjectPool(COMMPORTS_POOL_MAX, 1, FALSE);
    servicesPool = new ObjectPool(SERVERS_POOL_MAX, 1000, FALSE);

    BT_RegisterCallback(EVENT_SPPEX_CONNECTION_STATUS, BS_SPPEXConnectionCallback);
}

BlueSoleilStack::~BlueSoleilStack() {
    BT_UnregisterCallback(EVENT_SPPEX_CONNECTION_STATUS);
    delete servicesPool;
    delete commPortsPool;

    DeleteCriticalSection(&openingPortLock);
    if (inquiringDevice) {
        BT_CancelInquiry();
        inquiringDevice = FALSE;
    }
}

BlueSoleilCOMPort* BlueSoleilStack::createCommPort() {
    BlueSoleilCOMPort* rf = new BlueSoleilCOMPort();
    if (!commPortsPool->addObject(rf)) {
        delete rf;
        return NULL;
    }
    return rf;
}

BlueSoleilCOMPort* validRfCommHandle(JNIEnv *env, jlong handle) {
    if (stack == NULL) {
        throwIOException(env, cSTACK_CLOSED);
        return NULL;
    }
    return stack->getCommPort(env, handle);
}

BlueSoleilCOMPort* BlueSoleilStack::getCommPort(JNIEnv *env, jlong handle) {
    return (BlueSoleilCOMPort*)commPortsPool->getObject(env, handle);
}

void BlueSoleilStack::deleteCommPort(BlueSoleilCOMPort* commPort) {
    if (commPort != NULL) {
        commPortsPool->removeObject(commPort);
        delete commPort;
    }
}

BlueSoleilSPPExService* BlueSoleilStack::createService() {
    BlueSoleilSPPExService* o = new BlueSoleilSPPExService();
    if (!servicesPool->addObject(o)) {
        delete o;
        return NULL;
    }
    return o;
}

BlueSoleilSPPExService* validServiceHandle(JNIEnv *env, jlong handle) {
    if (stack == NULL) {
        throwIOException(env, cSTACK_CLOSED);
        return NULL;
    }
    return stack->getService(env, handle);
}

BlueSoleilSPPExService* BlueSoleilStack::getService(JNIEnv *env, jlong handle) {
    return (BlueSoleilSPPExService*)servicesPool->getObject(env, handle);
}

void BlueSoleilStack::deleteService(BlueSoleilSPPExService* service) {
    if (service != NULL) {
        servicesPool->removeObject(service);
        delete service;
    }
}


void BlueSoleilStack::SPPEXConnectionCallback(DWORD dwServerHandle, BYTE* lpBdAddr, UCHAR ucStatus, DWORD dwConnetionHandle) {
    BlueSoleilSPPExService* service = (BlueSoleilSPPExService*)servicesPool->getObjectByExternalHandle(dwServerHandle);
    if (service != NULL) {
        service->SPPEXConnectionCallback(lpBdAddr, ucStatus, dwConnetionHandle);
    }
}

BlueSoleilCOMPort::BlueSoleilCOMPort() {
    portMagic1 = MAGIC_1;
    hComPort = INVALID_HANDLE_VALUE;
    dwConnectionHandle = 0;
    isClosing = FALSE;
    receivedEOF = FALSE;
    memset(&ovlRead, 0, sizeof(OVERLAPPED));
    memset(&ovlWrite, 0, sizeof(OVERLAPPED));
    memset(&ovlComState, 0, sizeof(OVERLAPPED));
}

BlueSoleilCOMPort::~BlueSoleilCOMPort() {
    portMagic1 = 0;
    close(NULL);
}

BOOL BlueSoleilCOMPort::isValidObject() {
    return (portMagic1 == MAGIC_1);
}

BOOL BlueSoleilCOMPort::openComPort(JNIEnv *env, int portN) {
    char portString[20];
    sprintf_s(portString, 20, "\\\\.\\COM%i", portN);
    debug(("open COM port [%s]", portString));
    hComPort = CreateFileA(portString, GENERIC_READ | GENERIC_WRITE,
        0, /* exclusive access */
        NULL, /* no security attrs */
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, /* overlapped I/O */
        NULL);
    if (hComPort == INVALID_HANDLE_VALUE) {
        DWORD last_error = GetLastError();
        char message[80];
        sprintf_s(message, 80, "Can't open COM port [%s]", portString);
        debug((message));
        throwIOExceptionWinErrorMessage(env, message, last_error);
        return FALSE;
    }
    return TRUE;
}

char* BlueSoleilCOMPort::configureComPort(JNIEnv *env) {

    /* get any early notifications */
    //DWORD dwEvtMask = EV_RXCHAR | EV_ERR | EV_BREAK | EV_RLSD | EV_TXEMPTY | EV_DSR | EV_CTS;
    DWORD dwEvtMask = EV_RXCHAR | EV_ERR | EV_BREAK | EV_RLSD;
    if (!SetCommMask(hComPort, dwEvtMask)) {
        return "SetCommMask error";
    }

    /* setup device buffers */
    if (!SetupComm(hComPort, 0x1000, 0)) {
        return "SetupComm error";
    }

    /* purge any information in the buffer */
    if (!PurgeComm(hComPort, PURGE_TXABORT | PURGE_RXABORT |  PURGE_TXCLEAR | PURGE_RXCLEAR)) {
        return "PurgeComm error";
    }

    COMMTIMEOUTS commTimeouts;

    if (!GetCommTimeouts(hComPort, &commTimeouts)) {
        return "GetCommTimeouts error";
    }
    Edebug(("commTimeouts.ReadIntervalTimeout         [%u]", commTimeouts.ReadIntervalTimeout));
    Edebug(("commTimeouts.ReadTotalTimeoutConstant    [%u]", commTimeouts.ReadTotalTimeoutConstant));
    Edebug(("commTimeouts.ReadTotalTimeoutMultiplier  [%u]", commTimeouts.ReadTotalTimeoutMultiplier));
    Edebug(("commTimeouts.WriteTotalTimeoutConstant   [%u]", commTimeouts.WriteTotalTimeoutConstant));
    Edebug(("commTimeouts.WriteTotalTimeoutMultiplier [%u]", commTimeouts.WriteTotalTimeoutMultiplier));

    /* set up for overlapped I/O */
    commTimeouts.ReadIntervalTimeout = 0xFFFFFFFF;
    commTimeouts.ReadTotalTimeoutConstant = 1000;
    commTimeouts.ReadTotalTimeoutMultiplier = 0;
    commTimeouts.WriteTotalTimeoutConstant = 0;
    commTimeouts.WriteTotalTimeoutMultiplier = 10;
    if (!SetCommTimeouts (hComPort, &commTimeouts)) {
        return "SetCommTimeouts error";
    }

    DCB dcb;
    memset(&dcb, 0, sizeof(DCB));
    dcb.DCBlength = sizeof(DCB);
    if (!GetCommState(hComPort, &dcb)) {
        return "GetCommState error";
    }
    // Fill in DCB: 115,200 bps, 8 data bits, no parity, and 1 stop bit.
    dcb.BaudRate = CBR_115200;    // set the baud rate
    dcb.ByteSize = 8;             // data size, xmit, and rcv
    dcb.Parity = NOPARITY;        // no parity bit
    dcb.StopBits = ONESTOPBIT;    // one stop bit
    dcb.fAbortOnError = TRUE;

    dcb.fBinary = TRUE;

    /*
    dcb.fOutxDsrFlow = 0;
    BOOL hardwareHandshake = TRUE;
    if (hardwareHandshake) {
        dcb.fOutxCtsFlow = TRUE;
        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
    } else {
        dcb.fOutxCtsFlow = FALSE;
        dcb.fRtsControl = RTS_CONTROL_ENABLE;
    }
    */

    if (!SetCommState(hComPort, &dcb)) {
        return "SetCommState error";
    }

    /*
    if (!hardwareHandshake) {
        if (!EscapeCommFunction(hComPort, SETRTS)) { //Sends the DTR (data-terminal-ready) signal.
            return "EscapeCommFunction error";
        }
    if (!EscapeCommFunction(hComPort, SETDTR)) { //Sends the RTS (request-to-send) signal.
            return "EscapeCommFunction error";
        }
    }
    */

    ovlRead.hEvent = CreateEvent(
            NULL,    // no security attributes
            TRUE,    // (bManualReset) auto-reset event
            FALSE,   // initial state is NOT signaled
            NULL);   // object not named
    if (ovlRead.hEvent == NULL) {
        return "Error creating overlapped event";
    }

    ovlWrite.hEvent = CreateEvent(
            NULL,    // no security attributes
            TRUE,    // (bManualReset) auto-reset event
            FALSE,   // initial state is NOT signaled
            NULL);   // object not named
    if (ovlWrite.hEvent == NULL) {
        return "Error creating overlapped event";
    }

    hCloseEvent = CreateEvent(
            NULL,     // no security attributes
            FALSE,    // (bManualReset) auto-reset event
            FALSE,    // initial state is NOT signaled
            NULL);    // object not named
    if (hCloseEvent == NULL) {
        return "Error creating event";
    }
    ovlComState.hEvent = CreateEvent(
            NULL,     // no security attributes
            TRUE,     // (bManualReset) auto-reset event
            FALSE,    // initial state is NOT signaled
            NULL);    // object not named
    if (ovlComState.hEvent == NULL) {
        return "Error creating event";
    }

    return NULL;
}

JNIEXPORT jlong JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_connectionRfOpenImpl
(JNIEnv *env, jobject, jlong address, jbyteArray uuidValue) {
    if (stack == NULL) {
        throwIOException(env, cSTACK_CLOSED);
        return 0;
    }

    BlueSoleilCOMPort* rf = stack->createCommPort();
    if (rf == NULL) {
        throwIOException(env, "No free connections Objects in Pool");
        return 0;
    }
    rf->remoteAddress = address;

    BLUETOOTH_DEVICE_INFO devInfo={0};
    devInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
    LongToBsAddr(address, devInfo.address);

    SPPEX_SERVICE_INFO svcInfoSPPEx;
    memset(&svcInfoSPPEx, 0, sizeof(SPPEX_SERVICE_INFO));
    svcInfoSPPEx.dwSize = sizeof(SPPEX_SERVICE_INFO);

    GUID service_guid;
    jbyte *bytes = env->GetByteArrayElements(uuidValue, 0);
    convertUUIDBytesToGUID(bytes, &service_guid);
    env->ReleaseByteArrayElements(uuidValue, bytes, 0);
    memcpy(&(svcInfoSPPEx.serviceClassUuid128), &service_guid, sizeof(UUID));

    DWORD dwConnectionHandle;
    EnterCriticalSection(&stack->openingPortLock);

    DWORD dwResult;
    dwResult = BT_ConnectSPPExService(&devInfo, &svcInfoSPPEx, &dwConnectionHandle);
    if (stack == NULL) {
		throwIOException(env, cSTACK_CLOSED);
		return 0;
	}
    if (dwResult != BTSTATUS_SUCCESS)   {
        debug(("BT_ConnectSPPExService return  [%s]", getBsAPIStatusString(dwResult)));
        stack->deleteCommPort(rf);
        LeaveCriticalSection(&stack->openingPortLock);
        throwIOException(env, "Can't connect [%s]", getBsAPIStatusString(dwResult));
        return 0;
    }
    if (dwConnectionHandle == 0) {
        stack->deleteCommPort(rf);
        BT_DisconnectSPPExService(dwConnectionHandle);
        LeaveCriticalSection(&stack->openingPortLock);
        throwIOException(env, "Can't use 0 Handle");
        return 0;
    }

    BOOL bIsOutGoing;
    SPP_CONNECT_INFO sppConnInfo ={0};
    sppConnInfo.dwSize = sizeof(SPP_CONNECT_INFO);
    DWORD dwLen = sizeof(SPP_CONNECT_INFO);
    BYTE bdAddr[6]={0};
    WORD wClass;

    dwResult = BT_GetConnectInfo(dwConnectionHandle, &bIsOutGoing, &wClass, bdAddr, &dwLen, (BYTE*)&sppConnInfo);
    if (stack == NULL) {
		throwIOException(env, cSTACK_CLOSED);
		return 0;
	}
    if (dwResult != BTSTATUS_SUCCESS)   {
        debug(("BT_GetConnectInfo return  [%s]", getBsAPIStatusString(dwResult)));
        stack->deleteCommPort(rf);
        LeaveCriticalSection(&stack->openingPortLock);
        throwIOException(env, "Can't get SPP info [%s]", getBsAPIStatusString(dwResult));
        return 0;
    }

    if (sppConnInfo.ucComPort != svcInfoSPPEx.ucComIndex) {
        debug(("Port# mismatch [%u] and [%u]", (unsigned int)(sppConnInfo.ucComPort), (unsigned int)(svcInfoSPPEx.ucComIndex)));
        if (sppConnInfo.ucComPort == 0) {
            // Try to connect anyway to fix bluesoleil 5.0.5
            sppConnInfo.ucComPort = svcInfoSPPEx.ucComIndex;
        } else {
            stack->deleteCommPort(rf);
            LeaveCriticalSection(&stack->openingPortLock);
            throwIOException(env, "Port# mismatch [%u] and [%u]", (unsigned int)(sppConnInfo.ucComPort), (unsigned int)(svcInfoSPPEx.ucComIndex));
            return 0;
        }
    }

    // To solve concurrent connections problem
    //Sleep(5000);

    int portN;
    //portN = svcInfoSPPEx.ucComIndex;
    portN = sppConnInfo.ucComPort;

    debug(("open COM port [%i] for [%li]", portN, address));
    if (!rf->openComPort(env, portN)) {
        if (stack == NULL) {
		    throwIOException(env, cSTACK_CLOSED);
		    return 0;
	    }
        stack->deleteCommPort(rf);
        BT_DisconnectSPPExService(dwConnectionHandle);
        LeaveCriticalSection(&stack->openingPortLock);
        return 0;
    }
    rf->dwConnectionHandle = dwConnectionHandle;

    char* errorMessage = rf->configureComPort(env);
    if (errorMessage != NULL) {
        DWORD last_error = GetLastError();
        stack->deleteCommPort(rf);
        LeaveCriticalSection(&stack->openingPortLock);
        throwIOExceptionWinErrorMessage(env, errorMessage, last_error);
        return 0;
    }
    LeaveCriticalSection(&stack->openingPortLock);
    debug(("Connected [%i] [%p]-[%lu]", rf->internalHandle, rf->hComPort, rf->dwConnectionHandle));
    return rf->internalHandle;
}

void BlueSoleilCOMPort::clearCommError() {
    if (isClosing) {
        return;
    }
    dwErrorFlags = 0;
    ClearCommError(hComPort, &dwErrorFlags, &comStat);
    if (dwErrorFlags != 0) {
        comStat.fEof = TRUE;
    }
}

void BlueSoleilCOMPort::close(JNIEnv *env) {
    BOOL error = FALSE;
    DWORD last_error = 0;

    debug(("close [%i] [%p]-[%lu]", internalHandle, hComPort, dwConnectionHandle));

    if (hCloseEvent != NULL) {
        isClosing = TRUE;
        SetEvent(hCloseEvent);
    }

    if (ovlWrite.hEvent != NULL) {
        CloseHandle(ovlWrite.hEvent);
        ovlWrite.hEvent = NULL;
    }
    if (ovlRead.hEvent != NULL) {
        CloseHandle(ovlRead.hEvent);
        ovlRead.hEvent = NULL;
    }
    if (ovlComState.hEvent != NULL) {
        CloseHandle(ovlComState.hEvent);
        ovlComState.hEvent = NULL;
    }

    if (hComPort != INVALID_HANDLE_VALUE) {
        /* disable event notification and wait for thread to halt */
        //SetCommMask(hComPort, 0);

        /* drop DTR */
        //EscapeCommFunction(hComPort, CLRDTR);

        /* purge any outstanding reads/writes and close device handle */
        //PurgeComm(hComPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

        if (!CloseHandle(hComPort)) {
            last_error = GetLastError();
            debug(("close ComPort error [%lu] %S", last_error, getWinErrorMessage(last_error)));
            error = TRUE;
        }
        hComPort = INVALID_HANDLE_VALUE;
    }

    if (hCloseEvent != NULL) {
        CloseHandle(hCloseEvent);
        hCloseEvent = NULL;
    }

    DWORD dwResult = BTSTATUS_SUCCESS;
    if (dwConnectionHandle != 0) {
        //Sleep(5500);
        if (stack != NULL) {
            EnterCriticalSection(&stack->openingPortLock);
        }
        dwResult = BT_DisconnectSPPExService(dwConnectionHandle);
        if (stack != NULL) {
            LeaveCriticalSection(&stack->openingPortLock);
        }
        dwConnectionHandle = 0;
        if ((dwResult != BTSTATUS_SUCCESS) /*&& (dwResult != BTSTATUS_CONNECTION_NOT_EXIST)*/ && (env != NULL)) {
            debug(("BT_DisconnectSPPExService return  [%s]", getBsAPIStatusString(dwResult)));
        }
    }

    if (env != NULL) {
        if (error) {
            throwIOExceptionWinErrorMessage(env, "close ComPort error", last_error);
            return;
        }
        if ((dwResult != BTSTATUS_SUCCESS) && (dwResult != BTSTATUS_CONNECTION_NOT_EXIST))  {
            throwIOException(env, "Can't disconnect SPP [%s]", getBsAPIStatusString(dwResult));
            return;
        }
    }
}

JNIEXPORT void JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_connectionRfCloseClientConnection
(JNIEnv *env, jobject, jlong handle) {
    debug(("close connection [%li]", handle));
    BlueSoleilCOMPort* rf = validRfCommHandle(env, handle);
    if (rf == NULL) {
        return;
    }
    rf->close(env);
    if (stack != NULL) {
        stack->deleteCommPort(rf);
    }
}

JNIEXPORT jlong JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_getConnectionRfRemoteAddress
(JNIEnv *env, jobject, jlong handle) {
    BlueSoleilCOMPort* rf = validRfCommHandle(env, handle);
    if (rf == NULL) {
        return 0;
    }
    if (rf->isClosing) {
        throwIOException(env, cCONNECTION_IS_CLOSED);
        return 0;
    }
    return rf->remoteAddress;
}

void printCOMSTAT(JNIEnv *env, COMSTAT* comStat) {
    Edebug(("COMSTAT.fEof      %i", comStat->fEof));
    Edebug(("COMSTAT.cbInQue   %i", comStat->cbInQue));
    Edebug(("COMSTAT.cbOutQue  %i", comStat->cbOutQue));
    Edebug(("COMSTAT.fCtsHold  %i", comStat->fCtsHold));
    Edebug(("COMSTAT.fDsrHold  %i", comStat->fDsrHold));
    Edebug(("COMSTAT.fRlsdHold %i", comStat->fRlsdHold));
    Edebug(("COMSTAT.fXoffHold %i", comStat->fXoffHold));
    Edebug(("COMSTAT.fXoffSent %i", comStat->fXoffSent));
}

void printCOMEvtMask(JNIEnv *env, DWORD dwEvtMask) {
    if (dwEvtMask & EV_BREAK) {
        debug(("EV_BREAK"));
    }
    if (dwEvtMask & EV_ERR) {
        debug(("EV_ERR"));
    }
    if (dwEvtMask & EV_RXCHAR) {
        debug(("EV_RXCHAR"));
    }
    if (dwEvtMask & EV_RLSD) {
        debug(("EV_RLSD"));
    }
    if (dwEvtMask & EV_DSR) {
        debug(("EV_DSR"));
    }
    if (dwEvtMask & EV_CTS) {
        debug(("EV_CTS"));
    }
}

int waitBytesAvailable(JNIEnv *env, jobject peer, BlueSoleilCOMPort* rf) {

    HANDLE hEvents[2];
    hEvents[0] = rf->hCloseEvent;
    hEvents[1] = rf->ovlComState.hEvent;

    // In fact we make asynchronous IO synchronous Just to be able to Close it any time!
    while ((rf->comStat.cbInQue == 0) && (!rf->isClosing) && (!rf->comStat.fEof) && (!rf->receivedEOF)) {
        DWORD dwEvtMask = 0;
        debug(("read WaitCommEvent"));
        if (!WaitCommEvent(rf->hComPort, &dwEvtMask, &(rf->ovlComState))) {
            DWORD last_error = GetLastError();
            if ((last_error == ERROR_SUCCESS) && (last_error != ERROR_IO_PENDING)) {
                debug(("connection handle [%i] [%p]", rf->internalHandle, rf->hComPort));
                throwIOExceptionWinErrorMessage(env, "Failed to read", last_error);
                return -1;
            }
            if (last_error != ERROR_SUCCESS)  {
                debug(("read WaitForMultipleObjects"));
                DWORD rc = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
                if (rc == WAIT_FAILED) {
                    throwRuntimeException(env, "WaitForMultipleObjects");
                    return -1;
                }
            }
        }
        printCOMEvtMask(env, dwEvtMask);
        if (dwEvtMask & EV_RLSD) {
            DWORD dwModemStat = 0;
            if (GetCommModemStatus(rf->hComPort, &dwModemStat)) {
                BOOL RDLS_ON = dwModemStat & MS_RLSD_ON;
                if (!RDLS_ON) {
                    rf->receivedEOF = TRUE;
                    debug(("read receivedEOF"));
                }
            }
        }
        rf->clearCommError();
        printCOMSTAT(env, &(rf->comStat));
        if (isCurrentThreadInterrupted(env, peer)) {
            debug(("Interrupted while reading"));
            return -1;
        }
    }
    if (rf->isClosing) {
        throwIOException(env, cCONNECTION_IS_CLOSED);
        return -1;
    }
    if ((rf->comStat.fEof) || (rf->receivedEOF)) {
        return 0;
    }
    return rf->comStat.cbInQue;
}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_connectionRfRead__J
(JNIEnv *env, jobject peer, jlong handle) {
    BlueSoleilCOMPort* rf = validRfCommHandle(env, handle);
    if (rf == NULL) {
        return -1;
    }
    debug(("->read()"));
    if (rf->isClosing) {
        return -1;
    }
    rf->clearCommError();
    if ((rf->comStat.fEof) || (rf->receivedEOF)) {
        return -1;
    }
    rf->tInc();
    //printCOMSTAT(env, &(rf->comStat));
    int avl = waitBytesAvailable(env, peer, rf);
    if ((avl == -1) || (avl == 0)) {
        rf->tDec();
        return -1;
    }

    HANDLE hEvents[2];
    hEvents[0] = rf->hCloseEvent;
    hEvents[1] = rf->ovlRead.hEvent;

    unsigned char c;
    DWORD numberOfBytesRead = 0;
    while ((!rf->isClosing) && (!rf->receivedEOF) && (numberOfBytesRead == 0)) {
        if (!ReadFile(rf->hComPort, &c, 1, &numberOfBytesRead, &(rf->ovlRead))) {
            DWORD last_error = GetLastError();
            if (last_error != ERROR_IO_PENDING) {
                throwIOExceptionWinErrorMessage(env, "Failed to read", last_error);
                rf->tDec();
                return -1;
            }
            while ((!rf->isClosing) && (!rf->receivedEOF) && (!GetOverlappedResult(rf->hComPort, &(rf->ovlRead), &numberOfBytesRead, FALSE))) {
                last_error = GetLastError();
                if (last_error == ERROR_SUCCESS) {
                    break;
                }
                if (last_error != ERROR_IO_INCOMPLETE) {
                    throwIOExceptionWinErrorMessage(env, "Failed to read overlapped", last_error);
                    return -1;
                }
                debug(("read WaitForMultipleObjects"));
                DWORD rc = WaitForMultipleObjects(2, hEvents, FALSE, 500);
                if (rc == WAIT_FAILED) {
                    throwRuntimeException(env, "WaitForMultipleObjects");
                    rf->tDec();
                    return -1;
                }
                if (isCurrentThreadInterrupted(env, peer)) {
                    debug(("Interrupted while reading"));
                    return -1;
                }
                rf->clearCommError();
            }
        }
        rf->clearCommError();
    }
    if (rf->isClosing) {
        rf->tDec();
        return -1;
    }
    if (numberOfBytesRead == 0) {
        rf->receivedEOF = TRUE;
        rf->tDec();
        return -1;
    }
    rf->tDec();
    return (int)c;
}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_connectionRfRead__J_3BII
(JNIEnv *env, jobject peer, jlong handle, jbyteArray b, jint off, jint len) {
    BlueSoleilCOMPort* rf = validRfCommHandle(env, handle);
    if (rf == NULL) {
        return -1;
    }
    debug(("->read(byte[%i])", len));
    if (rf->isClosing) {
        return -1;
    }
    rf->clearCommError();
    if ((rf->comStat.fEof) || (rf->receivedEOF)) {
        return -1;
    }
    rf->tInc();
    jbyte *bytes = env->GetByteArrayElements(b, 0);
    int done = 0;

    HANDLE hEvents[2];
    hEvents[0] = rf->hCloseEvent;
    hEvents[1] = rf->ovlRead.hEvent;

    while (!rf->isClosing && (!rf->receivedEOF) && (done < len)) {
        int avl = waitBytesAvailable(env, peer, rf);
        if (avl == -1) {
            rf->tDec();
            return -1;
        }
        if (avl == 0) {
            break;
        }
        DWORD numberOfBytesRead = 0;
        if (!ReadFile(rf->hComPort, (void*)(bytes + off + done), len - done, &numberOfBytesRead, &(rf->ovlRead))) {
            if (GetLastError() != ERROR_IO_PENDING) {
                env->ReleaseByteArrayElements(b, bytes, 0);
                throwIOExceptionWinGetLastError(env, "Failed to read array");
                rf->tDec();
                return -1;
            }
            while ((!rf->isClosing) && (!rf->receivedEOF) && (!GetOverlappedResult(rf->hComPort, &(rf->ovlRead), &numberOfBytesRead, FALSE))) {
                DWORD last_error = GetLastError();
                if (last_error == ERROR_SUCCESS) {
                    break;
                }
                if (last_error != ERROR_IO_INCOMPLETE) {
                    env->ReleaseByteArrayElements(b, bytes, 0);
                    throwIOExceptionWinErrorMessage(env, "Failed to read array overlapped", last_error);
                    rf->tDec();
                    return -1;
                }
                DWORD rc = WaitForMultipleObjects(2, hEvents, FALSE, 500);
                if (rc == WAIT_FAILED) {
                    throwRuntimeException(env, "WaitForMultipleObjects");
                    rf->tDec();
                    return -1;
                }
                rf->clearCommError();
                if (isCurrentThreadInterrupted(env, peer)) {
                    debug(("Interrupted while reading"));
                    return -1;
                }
            }
        }
        rf->clearCommError();
        debug(("numberOfBytesRead [%i]", numberOfBytesRead));
        done += numberOfBytesRead;
        if (done != 0) {
            // Don't do readFully!
            break;
        }
    }

    env->ReleaseByteArrayElements(b, bytes, 0);

    if (done == 0) {
        rf->receivedEOF = TRUE;
        done = -1;
    }
    rf->tDec();
    return done;

}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_connectionRfReadAvailable
(JNIEnv *env, jobject peer, jlong handle) {
    BlueSoleilCOMPort* rf = validRfCommHandle(env, handle);
    if (rf == NULL || rf->isClosing) {
        return 0;
    }
    rf->clearCommError();
    return rf->comStat.cbInQue;
}

JNIEXPORT void JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_connectionRfFlush
(JNIEnv *env, jobject peer, jlong handle) {
    BlueSoleilCOMPort* rf = validRfCommHandle(env, handle);
    if (rf == NULL) {
        return;
    }
    debug(("->flush"));
    if (rf->isClosing || rf->receivedEOF) {
        throwIOException(env, cCONNECTION_IS_CLOSED);
        return;
    }
    /* TODO This does not work. "output buffer is empty" all the time. We use rf->receivedEOF hack for now.
    rf->clearCommError();
    if (rf->comStat.fEof) {
        // EOF character received
        throwIOException(env, "Failed to flush to closed connection");
        return;
    }
    if (rf->comStat.cbOutQue == 0) {
        debug(("output buffer is empty"));
        return;
    }

    HANDLE hEvents[2];
    hEvents[0] = rf->hCloseEvent;
    hEvents[1] = rf->ovlComState.hEvent;

    while ((!rf->isClosing) && (!rf->comStat.fEof)) {
        DWORD dwEvtMask = 0;
        if (!WaitCommEvent(rf->hComPort, &dwEvtMask, &(rf->ovlComState))) {
            DWORD last_error = GetLastError();
            if ((last_error == ERROR_SUCCESS) && (last_error != ERROR_IO_PENDING)) {
                debug2("connection handle [%i] [%p]", rf->internalHandle, rf->hComPort);
                throwIOExceptionWinErrorMessage(env, "Failed to flush", last_error);
                return;
            }
            DWORD rc = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
            if (rc == WAIT_FAILED) {
                throwRuntimeException(env, "WaitForMultipleObjects");
                return;
            }
        }

        if (dwEvtMask & EV_TXEMPTY) {
            debug(("output buffer was sent"));
            break;
        }

        if ((dwEvtMask & EV_BREAK) || (dwEvtMask & EV_ERR)) {
            throwIOException(env, "Connection flush error");
            return;
        }

    }

    if (rf->isClosing) {
        throwIOException(env, cCONNECTION_IS_CLOSED);
        return;
    }
    */
}

JNIEXPORT void JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_connectionRfWrite__JI
(JNIEnv *env, jobject peer, jlong handle, jint b) {
    BlueSoleilCOMPort* rf = validRfCommHandle(env, handle);
    if (rf == NULL) {
        return;
    }
    debug(("->write(byte)"));
    if (rf->isClosing) {
        throwIOException(env, cCONNECTION_IS_CLOSED);
        return;
    }
    rf->clearCommError();
    if (rf->comStat.fEof || rf->receivedEOF) {
        // EOF character received
        throwIOException(env, "Failed to write to closed connection");
        return;
    }
    rf->tInc();
    HANDLE hEvents[2];
    hEvents[0] = rf->hCloseEvent;
    hEvents[1] = rf->ovlWrite.hEvent;

    char c = (char)b;
    DWORD numberOfBytesWritten = 0;
    if (!WriteFile(rf->hComPort, &c, 1, &numberOfBytesWritten, &(rf->ovlWrite))) {
        DWORD last_error = GetLastError();
        if (last_error == ERROR_SUCCESS) {
            rf->tDec();
            return;
        }
        if (last_error != ERROR_IO_PENDING) {
            debug(("connection handle [%i] [%p]", rf->internalHandle, rf->hComPort));
            throwIOExceptionWinErrorMessage(env, "Failed to write byte", last_error);
            rf->clearCommError();
            rf->tDec();
            return;
        }
        BOOL wait = TRUE;
        while (wait) {
            Edebug(("write(byte) ovl wait"));
            DWORD rc = WaitForMultipleObjects(2, hEvents, FALSE, 500);
            if (rf->isClosing) {
                throwIOException(env, cCONNECTION_CLOSED);
                return;
            }
            if (rc == WAIT_FAILED) {
                throwRuntimeException(env, "WaitForMultipleObjects write(byte)");
                rf->tDec();
                return;
            }
            if (!GetOverlappedResult(rf->hComPort, &(rf->ovlWrite), &numberOfBytesWritten, FALSE)) {
                last_error = GetLastError();
                if (last_error == ERROR_SUCCESS) {
                    Edebug(("write(byte) GetOverlappedResult return ERROR_SUCCESS"));
                    break;
                }
                if ((last_error != ERROR_IO_PENDING) && (last_error != ERROR_IO_INCOMPLETE)) {
                    debug(("connection handle [%li] [%p]", handle, rf->hComPort));
                    throwIOExceptionWinErrorMessage(env, "Failed to write byte overlapped", last_error);
                    rf->tDec();
                    return;
                }
                Edebug(("write(byte) wait GetOverlappedResult"));
            } else {
                Edebug(("write(byte) GetOverlappedResult returns SUCCESS"));
                break;
            }
            if (isCurrentThreadInterrupted(env, peer)) {
                debug(("Interrupted while writing"));
                return;
            }
            wait = (rc != WAIT_TIMEOUT);
        }
    }
    if (numberOfBytesWritten != 1) {
        throwIOException(env, "Failed to write byte");
    }
    rf->tDec();
}

JNIEXPORT void JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_connectionRfWrite__J_3BII
(JNIEnv *env, jobject peer, jlong handle, jbyteArray b, jint off, jint len) {
    BlueSoleilCOMPort* rf = validRfCommHandle(env, handle);
    if (rf == NULL) {
        return;
    }
    debug(("->write(byte[])"));
    if (rf->isClosing) {
        throwIOException(env, cCONNECTION_IS_CLOSED);
        return;
    }
    rf->clearCommError();
    if (rf->comStat.fEof || rf->receivedEOF) {
        // EOF character received
        throwIOException(env, "Failed to write to closed connection");
        return;
    }

    jbyte *bytes = env->GetByteArrayElements(b, 0);

    rf->tInc();
    HANDLE hEvents[2];
    hEvents[0] = rf->hCloseEvent;
    hEvents[1] = rf->ovlWrite.hEvent;

    int done = 0;

    while ((done < len) && (!rf->isClosing)) {
        DWORD numberOfBytesWritten = 0;
        if (!WriteFile(rf->hComPort, (char *)(bytes + off + done), len - done, &numberOfBytesWritten, &(rf->ovlWrite))) {
            if (GetLastError() != ERROR_IO_PENDING) {
                env->ReleaseByteArrayElements(b, bytes, 0);
                debug(("connection handle [%li] [%p]", handle, rf->hComPort));
                throwIOExceptionWinGetLastError(env, "Failed to write array");
                rf->tDec();
                return;
            }
            BOOL wait = TRUE;
            while (wait) {
                Edebug(("write(byte[]) ovl wait"));
                DWORD rc = WaitForMultipleObjects(2, hEvents, FALSE, 500);
                if (rf->isClosing) {
                    throwIOException(env, cCONNECTION_CLOSED);
                    return;
                }
                if (rc == WAIT_FAILED) {
                    throwRuntimeException(env, "WaitForMultipleObjects write(byte[])");
                    rf->tDec();
                    return;
                }
                if (!GetOverlappedResult(rf->hComPort, &(rf->ovlWrite), &numberOfBytesWritten, FALSE)) {
                    DWORD last_error = GetLastError();
                    if (last_error == ERROR_SUCCESS) {
                        Edebug(("write(byte[]) GetOverlappedResult return ERROR_SUCCESS"));
                        break;
                    }
                    if ((last_error != ERROR_IO_PENDING) && (last_error != ERROR_IO_INCOMPLETE)) {
                        env->ReleaseByteArrayElements(b, bytes, 0);
                        debug(("connection handle [%li] [%p]", handle, rf->hComPort));
                        throwIOExceptionWinErrorMessage(env, "Failed to write array overlapped", last_error);
                        rf->tDec();
                        return;
                    }
                    Edebug(("write(byte[]) wait GetOverlappedResult"));
                } else {
                    Edebug(("write(byte[]) GetOverlappedResult returns SUCCESS"));
                    break;
                }
                if (isCurrentThreadInterrupted(env, peer)) {
                    debug(("Interrupted while writing"));
                    return;
                }
                wait = (rc != WAIT_TIMEOUT);
            }
        }
        if (numberOfBytesWritten < 0) {
            env->ReleaseByteArrayElements(b, bytes, 0);
            throwIOException(env, "Failed to write full array");
            rf->tDec();
            return;
        }
        done += numberOfBytesWritten;
    }

    env->ReleaseByteArrayElements(b, bytes, 0);
    rf->tDec();
}

//   --- Server RFCOMM connections

BlueSoleilSPPExService::BlueSoleilSPPExService() {
    serviceMagic1 = MAGIC_1;
    wdServerHandle = 0;
    isClosing = FALSE;
    hCloseEvent = CreateEvent(
            NULL,     // no security attributes
            FALSE,    // (bManualReset) auto-reset event
            FALSE,    // initial state is NOT signaled
            NULL);    // object not named
    portHandle = 0;
    isConnected = FALSE;
    dwConnectedConnetionHandle = 0;
    hConnectionEvent = CreateEvent(
            NULL,     // no security attributes
            FALSE,    // (bManualReset) auto-reset event
            FALSE,    // initial state is NOT signaled
            NULL);    // object not named

}

BlueSoleilSPPExService::~BlueSoleilSPPExService() {
    serviceMagic1 = 0;
    close(NULL);
}

BOOL BlueSoleilSPPExService::isValidObject() {
    return (serviceMagic1 == MAGIC_1);
}

BOOL BlueSoleilSPPExService::isExternalHandle(jlong handle) {
    return (handle == wdServerHandle);
}

JNIEXPORT jlong JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_rfServerOpenImpl
(JNIEnv *env, jobject, jbyteArray uuidValue, jstring name, jboolean authenticate, jboolean encrypt) {
    if (stack == NULL) {
        throwIOException(env, cSTACK_CLOSED);
        return 0;
    }
    BlueSoleilSPPExService* srv = stack->createService();
    if (srv == NULL) {
        throwIOException(env, "No free connections Objects in Pool");
        return 0;
    }

    memset(&(srv->serviceInfo), 0, sizeof(SPPEX_SERVICE_INFO));
    srv->serviceInfo.dwSize = sizeof(SPPEX_SERVICE_INFO);

    GUID service_guid;
    jbyte *bytes = env->GetByteArrayElements(uuidValue, 0);
    convertUUIDBytesToGUID(bytes, &service_guid);
    env->ReleaseByteArrayElements(uuidValue, bytes, 0);
    memcpy(&(srv->serviceInfo.serviceClassUuid128), &service_guid, sizeof(UUID));

    const char *cname = env->GetStringUTFChars(name, 0);
    sprintf_s(srv->serviceInfo.szServiceName, MAX_PATH, "%s", cname);
    env->ReleaseStringUTFChars(name, cname);

    DWORD dwResult = BT_StartSPPExService(&(srv->serviceInfo), &(srv->wdServerHandle));
    if (dwResult != BTSTATUS_SUCCESS) {
        debug(("BT_StartSPPExService return  [%s]", getBsAPIStatusString(dwResult)));
        throwIOException(env, "Can't create Service [%s]", getBsAPIStatusString(dwResult));
        return 0;
    }


    return srv->internalHandle;
}

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_rfServerSCN
(JNIEnv *env, jobject, jlong handle) {
    BlueSoleilSPPExService* srv = validServiceHandle(env, handle);
    if (srv == NULL) {
        return -1;
    }
    return srv->serviceInfo.ucServiceChannel;
}

void BlueSoleilSPPExService::close(JNIEnv *env) {
    debug(("service close [%i] [%lu] port [%i]", internalHandle, wdServerHandle, portHandle));
    if (portHandle != 0) {
        BlueSoleilCOMPort* rf = validRfCommHandle(NULL, portHandle);
        if (rf != NULL) {
            rf->close(env);
            if (stack != NULL) {
                stack->deleteCommPort(rf);
            }
        }
        portHandle = 0;
    }
    isClosing = TRUE;
    if (hCloseEvent != NULL) {
        SetEvent(hCloseEvent);
    }
    DWORD dwResult = BTSTATUS_SUCCESS;
    if (wdServerHandle != 0) {
        dwResult = BT_StopSPPExService(wdServerHandle);
        if ((dwResult != BTSTATUS_SUCCESS) && (env != NULL))    {
            debug(("BT_StopSPPExService return  [%s]", getBsAPIStatusString(dwResult)));
        }
        wdServerHandle = 0;
    }
    if (hConnectionEvent != NULL) {
        CloseHandle(hConnectionEvent);
        hConnectionEvent = NULL;
    }
    if (hCloseEvent != NULL) {
        CloseHandle(hCloseEvent);
        hCloseEvent = NULL;
    }
    if (dwResult != BTSTATUS_SUCCESS){
        throwIOException(env, "Can't Stop SPP service [%s]", getBsAPIStatusString(dwResult));
        return;
    }
}

JNIEXPORT void JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_rfServerClose
(JNIEnv *env, jobject, jlong handle, jobject) {
    BlueSoleilSPPExService* srv = validServiceHandle(env, handle);
    if (srv == NULL) {
        return;
    }
    srv->close(env);
    stack->deleteService(srv);
}

void BlueSoleilSPPExService::SPPEXConnectionCallback(BYTE* lpBdAddr, UCHAR ucStatus, DWORD dwConnetionHandle) {
    switch (ucStatus) {
    case STATUS_INCOMING_CONNECT:
        isConnected = TRUE;
        dwConnectedConnetionHandle = dwConnetionHandle;
        connectedBdAddr = BsAddrToLong(lpBdAddr);
        SetEvent(hConnectionEvent);
        break;
    case STATUS_INCOMING_DISCONNECT:
        isConnected = FALSE;

        // TODO hack for now.
        BlueSoleilCOMPort* rf = validRfCommHandle(NULL, portHandle);
        if (rf != NULL) {
            rf->receivedEOF = TRUE;
        }

        SetEvent(hConnectionEvent);
        break;
    }
}

JNIEXPORT jlong JNICALL Java_com_intel_bluetooth_BluetoothStackBlueSoleil_rfServerAcceptAndOpenRfServerConnection
(JNIEnv *env, jobject peer, jlong handle) {
    BlueSoleilSPPExService* srv = validServiceHandle(env, handle);
    if (srv == NULL) {
        return 0;
    }
    debug(("service accept [%i] [%lu] port [%i]", srv->internalHandle, srv->wdServerHandle, srv->portHandle));

    HANDLE hEvents[2];
    hEvents[0] = srv->hCloseEvent;
    hEvents[1] = srv->hConnectionEvent;

    BOOL debugWaitsOnce = TRUE;
    while ((stack != NULL) &&
        (srv->isConnected || (validRfCommHandle(NULL, srv->portHandle) != NULL))) {
        if (debugWaitsOnce) {
            debug(("server waits for client prev connection to close"));
            debugWaitsOnce = FALSE;
        }
        DWORD rc = WaitForMultipleObjects(1, hEvents, FALSE, 500);
        if (rc == WAIT_FAILED) {
            throwRuntimeException(env, "WaitForMultipleObjects");
            return 0;
        } else if (rc == WAIT_OBJECT_0) {
            debug(("hCloseEvent became signaled, isConnected=%s", bool2str(srv->isConnected)));
        } else if (rc != WAIT_TIMEOUT) {
            debug(("server prev connection close, waits returns %s", waitResultsString(rc)));
        }
        if (stack == NULL) {
            throwIOException(env, cSTACK_CLOSED);
            return 0;
        }
        if (srv->isClosing) {
            throwInterruptedIOException(env, cCONNECTION_CLOSED);
            return 0;
        }
        if (isCurrentThreadInterrupted(env, peer)) {
            debug(("Interrupted while waiting for connections"));
            return 0;
        }
    }

    BOOL debugOnce = TRUE;
    while ((stack != NULL) && (!srv->isConnected)) {
        if (debugOnce) {
            debug(("server waits for connection"));
            debugOnce = FALSE;
        }
        DWORD rc = WaitForMultipleObjects(2, hEvents, FALSE, 500);
        if (rc == WAIT_FAILED) {
            throwRuntimeException(env, "WaitForMultipleObjects");
            return 0;
        } else if (rc == WAIT_OBJECT_0) {
            debug(("hCloseEvent became signaled"));
        } else if (rc == WAIT_OBJECT_0 + 1) {
            debug(("hConnectionEvent became signaled, isConnected=%s", bool2str(srv->isConnected)));
        } else if (rc != WAIT_TIMEOUT) {
            debug(("server waits returns %s", waitResultsString(rc)));
        }
        if (stack == NULL) {
            throwIOException(env, cSTACK_CLOSED);
            return 0;
        }
        if (srv->isClosing) {
            throwInterruptedIOException(env, cCONNECTION_CLOSED);
            return 0;
        }
        if (isCurrentThreadInterrupted(env, peer)) {
            debug(("Interrupted while waiting for connections"));
            return 0;
        }
    }

    debug(("server received connection, %lu", srv->dwConnectedConnetionHandle));

    /*
    BOOL bIsOutGoing;
    SPP_CONNECT_INFO sppConnInfo ={0};
    sppConnInfo.dwSize = sizeof(SPP_CONNECT_INFO);
    DWORD dwLen = sizeof(SPP_CONNECT_INFO);
    BYTE bdAddr[6]={0};
    WORD wClass;

    DWORD dwResult = BT_GetConnectInfo(srv->dwConnectedConnetionHandle, &bIsOutGoing, &wClass, bdAddr, &dwLen, (BYTE*)&sppConnInfo);
    if (dwResult != BTSTATUS_SUCCESS)   {
        debug(("BT_GetConnectInfo return  [%s]", getBsAPIStatusString(dwResult));
        throwIOException(env, "Can't get SPP info [%s]", getBsAPIStatusString(dwResult));
        return 0;
    }
    */

    BlueSoleilCOMPort* rf = stack->createCommPort();
    if (rf == NULL) {
        throwIOException(env, "No free connections Objects in Pool");
        return 0;
    }

    //rf->remoteAddress = BsAddrToLong(bdAddr);
    rf->remoteAddress = srv->connectedBdAddr;
    //rf->dwConnectionHandle = srv->dwConnectedConnetionHandle;

    if (!rf->openComPort(env, srv->serviceInfo.ucComIndex)) {
        stack->deleteCommPort(rf);
        return 0;
    }
    char* errorMessage = rf->configureComPort(env);
    if (errorMessage != NULL) {
        DWORD last_error = GetLastError();
        stack->deleteCommPort(rf);
        throwIOExceptionWinErrorMessage(env, errorMessage, last_error);
        return 0;
    }

    srv->portHandle = rf->internalHandle;

    debug(("service connected [%i] [%lu] port [%i]", srv->internalHandle, srv->wdServerHandle, srv->portHandle));

    return rf->internalHandle;
}

#endif




© 2015 - 2024 Weber Informatics LLC | Privacy Policy