CAResult_t CAManagerConnectGatt(JNIEnv *env, jstring remote_le_address)
{
    VERIFY_NON_NULL(env, TAG, "env");
    VERIFY_NON_NULL(remote_le_address, TAG, "remote_le_address");

    OIC_LOG(DEBUG, TAG, "IN - CAManagerConnectGatt");

    jobject jni_bluetooth = CAManagerGetRemoteDevice(env, remote_le_address);
    if (!jni_bluetooth)
    {
        OIC_LOG(ERROR, TAG, "jni_bluetooth is null");
        return CA_STATUS_FAILED;
    }

    if (!CAManagerIsDeviceBonded(env, jni_bluetooth))
    {
        OIC_LOG(INFO, TAG, "device is BONDED_NONE");
    }

    // request to connection with AutoConnection Flag
    OIC_LOG(INFO, TAG, "request to gatt connection for auto connection");
    CAResult_t res = CALEClientDirectConnect(env, jni_bluetooth, JNI_TRUE);
    if (CA_STATUS_OK != res)
    {
        OIC_LOG(INFO, TAG, "re-connection will be started");
        return res;
    }

    // set flag auto connection is requested.
    CAManagerSetAutoConnectionFlag(env, remote_le_address, true);

    OIC_LOG(DEBUG, TAG, "OUT - CAManagerConnectGatt");
    return CA_STATUS_OK;
}
JNIEXPORT void JNICALL
Java_org_iotivity_ca_CaLeClientInterface_caManagerLeGattConnectionStateChangeCB(
        JNIEnv *env, jobject obj, jobject gatt, jint status, jint newState)
{
    OIC_LOG_V(DEBUG, TAG, "caManagerLeGattConnectionStateChangeCB-status(%d), newState(%d)",
              status, newState);

    VERIFY_NON_NULL_VOID(env, TAG, "env");
    VERIFY_NON_NULL_VOID(obj, TAG, "obj");
    VERIFY_NON_NULL_VOID(gatt, TAG, "gatt");

    jint state_connected = CALEGetConstantsValue(env, CLASSPATH_BT_PROFILE, "STATE_CONNECTED");
    jint state_disconnected = CALEGetConstantsValue(env, CLASSPATH_BT_PROFILE, "STATE_DISCONNECTED");
    jint gatt_success = CALEGetConstantsValue(env, CLASSPATH_BT_GATT, "GATT_SUCCESS");

    jstring jni_address = CAManagerGetAddressFromGatt(env, gatt);
    if (!jni_address)
    {
        OIC_LOG(ERROR, TAG, "CAManagerGetAddressFromGatt is null");
        return;
    }

    char* address = (char*)(*env)->GetStringUTFChars(env, jni_address, NULL);
    if (!address)
    {
        OIC_LOG(ERROR, TAG, "address is null");
        return;
    }

    OIC_LOG_V(INFO, TAG, "connection state : status(%d), addr:(%s), newState(%d)",
              status, address, newState);

    if (gatt_success == status && state_connected == newState) // le connected
    {
        OIC_LOG(DEBUG, TAG, "LE is connected");
        CAResult_t res = CAManagerReadRemoteRssi(env, gatt);
        if (CA_STATUS_OK != res)
        {
            OIC_LOG(ERROR, TAG, "CAManagerReadRemoteRssi has failed");
            (*env)->ReleaseStringUTFChars(env, jni_address, address);
            return;
        }
    }
    else if (state_disconnected == newState)// le disconnected
    {
        if (LINK_LOSS == status || REMOTE_DISCONNECT == status)
        {
            OIC_LOG(DEBUG, TAG, "LE is disconnected");

            if (g_connStateCB)
            {
                OIC_LOG_V(DEBUG, TAG, "LE Disconnected state is %d, %s", newState, address);
                g_connStateCB(CA_ADAPTER_GATT_BTLE, address, false);
                OIC_LOG(DEBUG, TAG, "LE Disconnected state callback is called");
            }

            if (!CAManagerIsMatchedACData(env, jni_address))
            {
                OIC_LOG_V(DEBUG, TAG, "this[%s] is not target address for Auto Connection",
                          address);
                (*env)->ReleaseStringUTFChars(env, jni_address, address);
                return;
            }

            CAManagerSetAutoConnectionFlag(env, jni_address, false);

            CAResult_t res = CAManagerStartAutoConnection(env, jni_address);
            if (CA_STATUS_OK != res)
            {
                (*env)->ReleaseStringUTFChars(env, jni_address, address);
                OIC_LOG(ERROR, TAG, "CAManagerStartAutoConnection has failed");
                return;
            }
        }
        else if (ACCEPT_TIMEOUT_EXCEPTION == status)
        {
            CAManagerProcessRecovery(env, START_RECOVERY);
        }
    }
    (*env)->ReleaseStringUTFChars(env, jni_address, address);
    (*env)->DeleteLocalRef(env, jni_address);
}
/*
 * Class:     org_iotivity_ca_jar_caleinterface
 * Method:    caManagerLeServicesDiscoveredCallback
 * Signature: (Landroid/bluetooth/BluetoothGatt;I)V
 */
JNIEXPORT void JNICALL
Java_org_iotivity_ca_CaLeClientInterface_caManagerLeServicesDiscoveredCallback(JNIEnv *env,
                                                                               jobject obj,
                                                                               jobject gatt,
                                                                               jint status)
{
    OIC_LOG_V(DEBUG, TAG, "caManagerLeServicesDiscoveredCallback - status %d: ", status);
    VERIFY_NON_NULL_VOID(env, TAG, "env");
    VERIFY_NON_NULL_VOID(obj, TAG, "obj");
    VERIFY_NON_NULL_VOID(gatt, TAG, "gatt");

    if (GATT_SUCCESS == status)
    {
        jstring jni_address = CAManagerGetAddressFromGatt(env, gatt);
        if (!jni_address)
        {
            OIC_LOG(ERROR, TAG, "CAManagerGetAddressFromGatt is null");
            return;
        }

        char* address = (char*)(*env)->GetStringUTFChars(env, jni_address, NULL);
        if (!address)
        {
            OIC_LOG(ERROR, TAG, "address is null");
            return;
        }
        OIC_LOG_V(DEBUG, TAG, "ServicesDiscovered device : %s", address);

        // target address for auto connection will be set in device list.
        // check set connected address information by user
        jclass jni_cls_set = (*env)->FindClass(env, "java/util/HashSet");
        if (!jni_cls_set)
        {
            OIC_LOG(ERROR, TAG, "jni_cls_set is null");
            return;
        }

        jmethodID jni_mid_iterator = (*env)->GetMethodID(env, jni_cls_set, "iterator", "()Ljava/util/Iterator;");
        if (!jni_mid_iterator)
        {
            OIC_LOG(ERROR, TAG, "jni_mid_iterator is null");
            return;
        }

        jobject jni_obj_iter = (*env)->CallObjectMethod(env, g_connectedDeviceSet, jni_mid_iterator);
        if (!jni_obj_iter)
        {
            OIC_LOG(ERROR, TAG, "jni_obj_iter is null");
            return;
        }

        // Get the Iterator method IDs
        jclass jni_cls_iterator = (*env)->FindClass(env, "java/util/Iterator");
        if (!jni_cls_iterator)
        {
            OIC_LOG(ERROR, TAG, "jni_cls_iterator is null");
            return;
        }

        jmethodID jni_mid_hasNext = (*env)->GetMethodID(env, jni_cls_iterator, "hasNext", "()Z");
        if (!jni_mid_hasNext)
        {
            OIC_LOG(ERROR, TAG, "jni_mid_hasNext is null");
            return;
        }

        jmethodID jni_mid_next = (*env)->GetMethodID(env, jni_cls_iterator, "next", "()Ljava/lang/Object;");
        if (!jni_mid_next)
        {
            OIC_LOG(ERROR, TAG, "jni_mid_next is null");
            return;
        }

        // Iterate over the entry Set
        while ((*env)->CallBooleanMethod(env, jni_obj_iter, jni_mid_hasNext))
        {
            jstring jni_str_entry = (jstring)(*env)->CallObjectMethod(env, jni_obj_iter, jni_mid_next);
            const char* foundAddress = (*env)->GetStringUTFChars(env, jni_str_entry, NULL);
            if (!foundAddress)
            {
                OIC_LOG(ERROR, TAG, "addr is null");
                return;
            }
            OIC_LOG_V(INFO, TAG, "found last connected address [%s] from SharedPreferences",
                      foundAddress);

            if (!strcmp(foundAddress, address))
            {
                // if BLE address is matched each other
                // this address will be added into auto connection list.
                OIC_LOG(INFO, TAG, "AC list - address will be added into ACData list");
                CAManagerAddACData(env, jni_address);
                CAManagerSetAutoConnectionFlag(env, jni_address, false);

                // next connection will be requested as JNI_TRUE flag
                // after first connection
                CALEClientSetAutoConnectFlag(env, jni_str_entry, JNI_TRUE);
            }
            else
            {
                OIC_LOG(INFO, TAG, "AC list - device is not matched");
            }

            (*env)->ReleaseStringUTFChars(env, jni_str_entry, foundAddress);
            (*env)->DeleteLocalRef(env, jni_str_entry);
        }

        if (g_connStateCB)
        {
            g_connStateCB(CA_ADAPTER_GATT_BTLE, address, true);
            OIC_LOG(DEBUG, TAG, "LE Connected callback is called");
        }
        (*env)->ReleaseStringUTFChars(env, jni_address, address);
        (*env)->DeleteLocalRef(env, jni_address);
        (*env)->DeleteLocalRef(env, jni_cls_set);
        (*env)->DeleteLocalRef(env, jni_obj_iter);
        (*env)->DeleteLocalRef(env, jni_cls_iterator);

        OIC_LOG(INFO, TAG, "ServicesDiscovery is successful");
    }
    else
    {
        OIC_LOG(ERROR, TAG, "ServicesDiscovery has failed");
    }
}
JNIEXPORT void JNICALL
Java_org_iotivity_ca_CaLeClientInterface_caManagerAdapterStateChangedCallback(
        JNIEnv *env, jobject obj, jint state)
{
    OIC_LOG(DEBUG, TAG, "caManagerAdapterStateChangedCallback");
    VERIFY_NON_NULL_VOID(env, TAG, "env");
    VERIFY_NON_NULL_VOID(obj, TAG, "obj");

    jint state_on = CALEGetConstantsValue(env, CLASSPATH_BT_ADAPTER, "STATE_ON");
    jint state_off = CALEGetConstantsValue(env, CLASSPATH_BT_ADAPTER, "STATE_OFF");
    jint state_turning_off = CALEGetConstantsValue(env, CLASSPATH_BT_ADAPTER, "STATE_TURNING_OFF");

    if (state_on == state)
    {
        OIC_LOG(DEBUG, TAG, "AdapterStateChangedCallback : state_on");
        if (g_adapterStateCB)
        {
            g_adapterStateCB(CA_ADAPTER_GATT_BTLE, true);
        }

        // when BT state is on. recovery flag has to be reset.
        CAManagerSetBTRecovery(false);

        // find target device for autoconnect
        size_t length = CAManagerGetACDataLength();
        OIC_LOG_V(DEBUG, TAG, "target device : %d", length);
        for (size_t idx = 0; idx < length; idx++)
        {
            jstring leAddress = CAManagerGetLEAddressFromACData(env, idx);
            if (leAddress)
            {
                CAResult_t res = CAManagerStartAutoConnection(env, leAddress);
                if (CA_STATUS_OK != res)
                {
                    OIC_LOG(ERROR, TAG, "CAManagerStartAutoConnection has failed");
                    return;
                }
            }
        }
    }
    else if (state_off == state)
    {
        OIC_LOG(DEBUG, TAG, "AdapterStateChangedCallback : state_off");
        if (g_adapterStateCB)
        {
            g_adapterStateCB(CA_ADAPTER_GATT_BTLE, false);
        }

        // reset autoconnect flag for all target devices
        size_t length = CAManagerGetACDataLength();
        for (size_t idx = 0; idx < length; idx++)
        {
            jstring address = CAManagerGetLEAddressFromACData(env, idx);
            if (address)
            {
                CAManagerSetAutoConnectionFlag(env, address, false);
            }
        }

        // check whether BT recovery is needed or not
        if (CAManagerIsRecoveryFlagSet())
        {
            CAManagerProcessRecovery(env, STATE_OFF);
        }
    }
    else if (state_turning_off == state)
    {
        OIC_LOG(DEBUG, TAG, "AdapterStateChangedCallback : state_turning_off");
        return;
    }
    else
    {
        OIC_LOG(INFO, TAG, "AdapterStateChangedCallback state is not available");
        return;
    }
}