[IOT-931] Added send logic to check write/notify in BLE
authorjihwan.seo <jihwan.seo@samsung.com>
Thu, 21 Jan 2016 06:13:22 +0000 (15:13 +0900)
committerJon A. Cruz <jonc@osg.samsung.com>
Wed, 3 Feb 2016 22:22:26 +0000 (22:22 +0000)
according to android BLE guide,
I have modified transmission logic for BLE.
when there is a big message to sent,
next segmentation data has to wait for
callback write/notify Characteristic for previous sent data.

Change-Id: Idf543504d634ab4dfbc2fbd4bb90aa7e2ad1831e
Signed-off-by: jihwan.seo <jihwan.seo@samsung.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/4841
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Jaehong Jo <jaehong.jo@samsung.com>
Reviewed-by: Hyuna Jo <hyuna0213.jo@samsung.com>
Reviewed-by: Jon A. Cruz <jonc@osg.samsung.com>
resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c
resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.h
resource/csdk/connectivity/src/bt_le_adapter/android/calenwmonitor.c
resource/csdk/connectivity/src/bt_le_adapter/android/caleserver.c

index 41133cc..767cbce 100644 (file)
 #define TAG PCF("OIC_CA_LE_CLIENT")
 
 #define MICROSECS_PER_SEC 1000000
+#define WAIT_TIME_WRITE_CHARACTERISTIC 10 * MICROSECS_PER_SEC
 
 static const char METHODID_OBJECTNONPARAM[] = "()Landroid/bluetooth/BluetoothAdapter;";
 static const char CLASSPATH_BT_ADAPTER[] = "android/bluetooth/BluetoothAdapter";
 static const char CLASSPATH_BT_UUID[] = "java/util/UUID";
 static const char CLASSPATH_BT_GATT[] = "android/bluetooth/BluetoothGatt";
 
+static ca_thread_pool_t g_threadPoolHandle = NULL;
+
 JavaVM *g_jvm;
 static u_arraylist_t *g_deviceList = NULL; // device list to have same UUID
 static u_arraylist_t *g_gattObjectList = NULL;
@@ -73,6 +76,9 @@ static ca_cond g_threadCond = NULL;
 static ca_cond g_deviceDescCond = NULL;
 
 static ca_mutex g_threadSendMutex = NULL;
+static ca_mutex g_threadWriteCharacteristicMutex = NULL;
+static ca_cond g_threadWriteCharacteristicCond = NULL;
+static bool g_isSignalSetFlag = false;
 
 static ca_mutex g_bleReqRespClientCbMutex = NULL;
 static ca_mutex g_bleServerBDAddressMutex = NULL;
@@ -224,6 +230,7 @@ CAResult_t CALEClientInitialize()
 
     // init mutex for send logic
     g_threadCond = ca_cond_new();
+    g_threadWriteCharacteristicCond = ca_cond_new();
 
     CALEClientCreateDeviceList();
     CALEClientJNISetContext();
@@ -336,9 +343,12 @@ void CALEClientTerminate()
 
     ca_cond_free(g_deviceDescCond);
     ca_cond_free(g_threadCond);
+    ca_cond_free(g_threadWriteCharacteristicCond);
 
     g_deviceDescCond = NULL;
     g_threadCond = NULL;
+    g_threadWriteCharacteristicCond = NULL;
+    g_isSignalSetFlag = false;
 
     if (isAttached)
     {
@@ -642,6 +652,7 @@ CAResult_t CALEClientSendUnicastMessageImpl(const char* address, const uint8_t*
                 if (g_sendBuffer)
                 {
                     (*env)->DeleteGlobalRef(env, g_sendBuffer);
+                    g_sendBuffer = NULL;
                 }
                 jbyteArray jni_arr = (*env)->NewByteArray(env, dataLen);
                 (*env)->SetByteArrayRegion(env, jni_arr, 0, dataLen, (jbyte*) data);
@@ -729,6 +740,7 @@ CAResult_t CALEClientSendMulticastMessageImpl(JNIEnv *env, const uint8_t* data,
     if (g_sendBuffer)
     {
         (*env)->DeleteGlobalRef(env, g_sendBuffer);
+        g_sendBuffer = NULL;
     }
 
     if (0 == u_arraylist_length(g_deviceList))
@@ -915,10 +927,10 @@ CAResult_t CALEClientSendData(JNIEnv *env, jobject device)
                 return CA_STATUS_FAILED;
             }
 
-            CAResult_t ret = CALEClientWriteCharacteristic(env, gatt);
+            CAResult_t ret = CALESetValueAndWriteCharacteristic(env, gatt);
             if (CA_STATUS_OK != ret)
             {
-                OIC_LOG(ERROR, TAG, "CALEClientWriteCharacteristic has failed");
+                OIC_LOG(ERROR, TAG, "CALESetValueAndWriteCharacteristic has failed");
                 (*env)->ReleaseStringUTFChars(env, jni_address, address);
                 return ret;
             }
@@ -1549,10 +1561,43 @@ CAResult_t CALEClientDiscoverServices(JNIEnv *env, jobject bluetoothGatt)
     return CA_STATUS_OK;
 }
 
-CAResult_t CALEClientWriteCharacteristic(JNIEnv *env, jobject gatt)
+static void CALEWriteCharacteristicThread(void* object)
+{
+    VERIFY_NON_NULL(object, TAG, "object is null");
+
+    bool isAttached = false;
+    JNIEnv* env;
+    jint res = (*g_jvm)->GetEnv(g_jvm, (void**) &env, JNI_VERSION_1_6);
+    if (JNI_OK != res)
+    {
+        OIC_LOG(INFO, TAG, "Could not get JNIEnv pointer");
+        res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+
+        if (JNI_OK != res)
+        {
+            OIC_LOG(ERROR, TAG, "AttachCurrentThread has failed");
+            return;
+        }
+        isAttached = true;
+    }
+
+    jobject gatt = (jobject)object;
+    CAResult_t ret = CALESetValueAndWriteCharacteristic(env, gatt);
+    if (CA_STATUS_OK != ret)
+    {
+        OIC_LOG(ERROR, TAG, "CALESetValueAndWriteCharacteristic has failed");
+    }
+
+    if (isAttached)
+    {
+        (*g_jvm)->DetachCurrentThread(g_jvm);
+    }
+}
+
+CAResult_t CALESetValueAndWriteCharacteristic(JNIEnv* env, jobject gatt)
 {
-    VERIFY_NON_NULL(env, TAG, "env is null");
     VERIFY_NON_NULL(gatt, TAG, "gatt is null");
+    VERIFY_NON_NULL(env, TAG, "env is null");
 
     // send data
     jobject jni_obj_character = CALEClientCreateGattCharacteristic(env, gatt, g_sendBuffer);
@@ -1566,14 +1611,53 @@ CAResult_t CALEClientWriteCharacteristic(JNIEnv *env, jobject gatt)
     if (CA_STATUS_OK != ret)
     {
         CALEClientSendFinish(env, gatt);
-        return ret;
+        return CA_STATUS_FAILED;
+    }
+
+    // wait for callback for write Characteristic with success to sent data
+    OIC_LOG_V(DEBUG, TAG, "callback flag is %d", g_isSignalSetFlag);
+    ca_mutex_lock(g_threadWriteCharacteristicMutex);
+    if (!g_isSignalSetFlag)
+    {
+        OIC_LOG(DEBUG, TAG, "wait for callback to notify writeCharacteristic is success");
+        if (CA_WAIT_SUCCESS != ca_cond_wait_for(g_threadWriteCharacteristicCond,
+                                  g_threadWriteCharacteristicMutex,
+                                  WAIT_TIME_WRITE_CHARACTERISTIC))
+        {
+            OIC_LOG(ERROR, TAG, "there is no response. write has failed");
+            g_isSignalSetFlag = false;
+            ca_mutex_unlock(g_threadWriteCharacteristicMutex);
+            return CA_STATUS_FAILED;
+        }
     }
+    // reset flag set by writeCharacteristic Callback
+    g_isSignalSetFlag = false;
+    ca_mutex_unlock(g_threadWriteCharacteristicMutex);
 
+    OIC_LOG(INFO, TAG, "writeCharacteristic success!!");
+    return CA_STATUS_OK;
+}
+
+CAResult_t CALEClientWriteCharacteristic(JNIEnv *env, jobject gatt)
+{
+    OIC_LOG(DEBUG, TAG, "IN - CALEClientWriteCharacteristic");
+    VERIFY_NON_NULL(env, TAG, "env is null");
+    VERIFY_NON_NULL(gatt, TAG, "gatt is null");
+
+    jobject gattParam = (*env)->NewGlobalRef(env, gatt);
+    if (CA_STATUS_OK != ca_thread_pool_add_task(g_threadPoolHandle,
+                                                CALEWriteCharacteristicThread, (void*)gattParam))
+    {
+        OIC_LOG(ERROR, TAG, "Failed to create read thread!");
+        return CA_STATUS_FAILED;
+    }
+
+    OIC_LOG(DEBUG, TAG, "OUT - CALEClientWriteCharacteristic");
     return CA_STATUS_OK;
 }
 
 CAResult_t CALEClientWriteCharacteristicImpl(JNIEnv *env, jobject bluetoothGatt,
-                                           jobject gattCharacteristic)
+                                             jobject gattCharacteristic)
 {
     OIC_LOG(DEBUG, TAG, "WRITE GATT CHARACTERISTIC");
     VERIFY_NON_NULL(env, TAG, "env is null");
@@ -1613,7 +1697,7 @@ CAResult_t CALEClientWriteCharacteristicImpl(JNIEnv *env, jobject bluetoothGatt,
                                                        gattCharacteristic);
     if (ret)
     {
-        OIC_LOG(DEBUG, TAG, "writeCharacteristic success");
+        OIC_LOG(DEBUG, TAG, "writeCharacteristic is called successfully");
     }
     else
     {
@@ -3020,6 +3104,7 @@ void CALEClientUpdateSendCnt(JNIEnv *env)
         }
         // notity the thread
         ca_cond_signal(g_threadCond);
+
         CALEClientSetSendFinishFlag(true);
         OIC_LOG(DEBUG, TAG, "set signal for send data");
     }
@@ -3119,6 +3204,16 @@ CAResult_t CALEClientInitGattMutexVaraibles()
         }
     }
 
+    if (NULL == g_threadWriteCharacteristicMutex)
+    {
+        g_threadWriteCharacteristicMutex = ca_mutex_new();
+        if (NULL == g_threadWriteCharacteristicMutex)
+        {
+            OIC_LOG(ERROR, TAG, "ca_mutex_new has failed");
+            return CA_STATUS_FAILED;
+        }
+    }
+
     return CA_STATUS_OK;
 }
 
@@ -3144,6 +3239,9 @@ void CALEClientTerminateGattMutexVariables()
 
     ca_mutex_free(g_scanMutex);
     g_scanMutex = NULL;
+
+    ca_mutex_free(g_threadWriteCharacteristicMutex);
+    g_threadWriteCharacteristicMutex = NULL;
 }
 
 void CALEClientSetSendFinishFlag(bool flag)
@@ -3212,7 +3310,13 @@ void CAStopLEGattClient()
         OIC_LOG(ERROR, TAG, "CALEClientStopScan has failed");
     }
 
+    ca_mutex_lock(g_threadMutex);
     ca_cond_signal(g_threadCond);
+    ca_mutex_unlock(g_threadMutex);
+
+    ca_mutex_lock(g_threadWriteCharacteristicMutex);
+    ca_cond_signal(g_threadWriteCharacteristicCond);
+    ca_mutex_unlock(g_threadWriteCharacteristicMutex);
 
     if (isAttached)
     {
@@ -3268,7 +3372,7 @@ void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback)
 
 void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle)
 {
-    OIC_LOG(INFO, TAG, "CASetLEClientThreadPoolHandle is not support");
+    g_threadPoolHandle = handle;
 }
 
 CAResult_t CAGetLEAddress(char **local_address)
@@ -3413,6 +3517,12 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeGattConnectionStateChangeCallback(J
         {
             OIC_LOG(ERROR, TAG, "CALEClientGattClose has failed");
         }
+
+        if (g_sendBuffer)
+        {
+            (*env)->DeleteGlobalRef(env, g_sendBuffer);
+            g_sendBuffer = NULL;
+        }
     }
     return;
 
@@ -3487,11 +3597,14 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeGattServicesDiscoveredCallback(JNIE
         if (CA_STATUS_OK != res)
         {
             OIC_LOG_V(INFO, TAG, "Descriptor is not found : %d", res);
-            CAResult_t res = CALEClientWriteCharacteristic(env, gatt);
-            if (CA_STATUS_OK != res)
+            if (g_sendBuffer)
             {
-                OIC_LOG(ERROR, TAG, "CALEClientWriteCharacteristic has failed");
-                goto error_exit;
+                CAResult_t res = CALEClientWriteCharacteristic(env, gatt);
+                if (CA_STATUS_OK != res)
+                {
+                    OIC_LOG(ERROR, TAG, "CALEClientWriteCharacteristic has failed");
+                    goto error_exit;
+                }
             }
         }
 
@@ -3558,20 +3671,34 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeGattCharacteristicWriteCallback(
     if (GATT_SUCCESS != status) // error case
     {
         OIC_LOG(ERROR, TAG, "send failure");
-        CAResult_t res = CALEClientUpdateDeviceState(address, STATE_CONNECTED, STATE_CHARACTER_SET,
-                                                     STATE_SEND_FAILED);
+
+        // retry to write
+        CAResult_t res = CALEClientWriteCharacteristic(env, gatt);
         if (CA_STATUS_OK != res)
         {
-            OIC_LOG(ERROR, TAG, "CALEClientUpdateDeviceState has failed");
-        }
+            OIC_LOG(ERROR, TAG, "WriteCharacteristic has failed");
+            ca_mutex_lock(g_threadWriteCharacteristicMutex);
+            g_isSignalSetFlag = true;
+            ca_cond_signal(g_threadWriteCharacteristicCond);
+            ca_mutex_unlock(g_threadWriteCharacteristicMutex);
 
-        if (g_clientErrorCallback)
-        {
-            jint length = (*env)->GetArrayLength(env, data);
-            g_clientErrorCallback(address, data, length, CA_SEND_FAILED);
-        }
+            CAResult_t res = CALEClientUpdateDeviceState(address, STATE_CONNECTED,
+                                                         STATE_CHARACTER_SET,
+                                                         STATE_SEND_FAILED);
+            if (CA_STATUS_OK != res)
+            {
+                OIC_LOG(ERROR, TAG, "CALEClientUpdateDeviceState has failed");
+            }
 
-        CALEClientSendFinish(env, gatt);
+            if (g_clientErrorCallback)
+            {
+                jint length = (*env)->GetArrayLength(env, data);
+                g_clientErrorCallback(address, data, length, CA_SEND_FAILED);
+            }
+
+            CALEClientSendFinish(env, gatt);
+            goto error_exit;
+        }
     }
     else
     {
@@ -3582,6 +3709,13 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeGattCharacteristicWriteCallback(
         {
             OIC_LOG(ERROR, TAG, "CALEClientUpdateDeviceState has failed");
         }
+
+        ca_mutex_lock(g_threadWriteCharacteristicMutex);
+        OIC_LOG(DEBUG, TAG, "g_isSignalSetFlag is set true and signal");
+        g_isSignalSetFlag = true;
+        ca_cond_signal(g_threadWriteCharacteristicCond);
+        ca_mutex_unlock(g_threadWriteCharacteristicMutex);
+
         CALEClientUpdateSendCnt(env);
     }
 
index 6656a0f..1a1d1e9 100644 (file)
@@ -299,13 +299,21 @@ CAResult_t CALEClientDisconnectAll(JNIEnv *env);
 CAResult_t CALEClientDiscoverServices(JNIEnv *env, jobject bluetoothGatt);
 
 /**
+ * call CALESetValueAndWriteCharacteristic when connection is successful.
+ * @param[in]   env                   JNI interface pointer.
+ * @param[in]   gatt                  Gatt profile object.
+ * @return  ::CA_STATUS_OK or ERROR CODES (::CAResult_t error codes in cacommon.h).
+ */
+CAResult_t CALEClientWriteCharacteristic(JNIEnv *env, jobject gatt);
+
+/**
  * create GattCharacteristic and call CALEClientWriteCharacteristicImpl
  * for request to write gatt characteristic.
  * @param[in]   env                   JNI interface pointer.
  * @param[in]   gatt                  Gatt profile object.
  * @return  ::CA_STATUS_OK or ERROR CODES (::CAResult_t error codes in cacommon.h).
  */
-CAResult_t CALEClientWriteCharacteristic(JNIEnv *env, jobject gatt);
+CAResult_t CALESetValueAndWriteCharacteristic(JNIEnv *env, jobject gatt);
 
 /**
  * request to write gatt characteristic.
index 8b3c24f..1b9f836 100644 (file)
@@ -237,22 +237,12 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeStateChangedCallback(JNIEnv *env, j
         CALEClientCreateDeviceList();
         CALEServerCreateCachedDeviceList();
 
-        CAResult_t res = CALEClientStartScan();
-        if (CA_STATUS_OK != res)
-        {
-            OIC_LOG(ERROR, TAG, "CALEClientStartScan has failed");
-        }
-
-        res = CALEStartAdvertise();
-        if (CA_STATUS_OK != res)
-        {
-            OIC_LOG(ERROR, TAG, "CALEStartAdvertise has failed");
-        }
-
         gCALEDeviceStateChangedCallback(newStatus);
     }
     else if (BT_STATE_OFF == status) // STATE_OFF:10
     {
+        CALEClientStopMulticastServer();
+
         // remove obj for client
         CAResult_t res = CALEClientRemoveAllGattObjs(env);
         if (CA_STATUS_OK != res)
index ff7a309..5a04a02 100644 (file)
@@ -37,6 +37,8 @@
 
 #define TAG PCF("OIC_CA_LE_SERVER")
 
+#define WAIT_TIME_WRITE_CHARACTERISTIC 10000000
+
 static JavaVM *g_jvm = NULL;
 static jobject g_context = NULL;
 static jobject g_bluetoothGattServer = NULL;
@@ -51,11 +53,18 @@ static u_arraylist_t *g_connectedDeviceList = NULL;
 static bool g_isStartServer = false;
 static bool g_isInitializedServer = false;
 
+static jbyteArray g_sendBuffer = NULL;
+
 static CABLEDataReceivedCallback g_CABLEServerDataReceivedCallback = NULL;
 static ca_mutex g_bleReqRespCbMutex = NULL;
 static ca_mutex g_bleClientBDAddressMutex = NULL;
 static ca_mutex g_connectedDeviceListMutex = NULL;
 
+static ca_mutex g_threadSendMutex = NULL;
+static ca_mutex g_threadSendNotifyMutex = NULL;
+static ca_cond g_threadSendNotifyCond = NULL;
+static bool g_isSignalSetFlag = false;
+
 void CALEServerJNISetContext()
 {
     OIC_LOG(DEBUG, TAG, "CALEServerJNISetContext");
@@ -254,7 +263,7 @@ jobject CALEServerSetResponseData(JNIEnv *env, jbyteArray responseData)
 
 CAResult_t CALEServerSendResponseData(JNIEnv *env, jobject device, jobject responseData)
 {
-    OIC_LOG(DEBUG, TAG, "IN - CALEServerSendResponseData");
+    OIC_LOG(DEBUG, TAG, "CALEServerSendResponseData");
     VERIFY_NON_NULL(responseData, TAG, "responseData is null");
     VERIFY_NON_NULL(device, TAG, "device is null");
     VERIFY_NON_NULL(env, TAG, "env is null");
@@ -292,7 +301,23 @@ CAResult_t CALEServerSendResponseData(JNIEnv *env, jobject device, jobject respo
         return CA_SEND_FAILED;
     }
 
-    OIC_LOG(DEBUG, TAG, "OUT - CALEServerSendResponseData");
+    OIC_LOG_V(DEBUG, TAG, "callback flag is %d", g_isSignalSetFlag);
+    ca_mutex_lock(g_threadSendNotifyMutex);
+    if (!g_isSignalSetFlag)
+    {
+        OIC_LOG(DEBUG, TAG, "wait for callback to notify notifyCharacteristic is success");
+        if (0 != ca_cond_wait_for(g_threadSendNotifyCond, g_threadSendNotifyMutex,
+                                  WAIT_TIME_WRITE_CHARACTERISTIC))
+        {
+            OIC_LOG(ERROR, TAG, "there is no response. notifyCharacteristic has failed");
+            ca_mutex_unlock(g_threadSendNotifyMutex);
+            return CA_STATUS_FAILED;
+        }
+    }
+    // reset flag set by writeCharacteristic Callback
+    g_isSignalSetFlag = false;
+    ca_mutex_unlock(g_threadSendNotifyMutex);
+    OIC_LOG(INFO, TAG, "notifyCharacteristic success");
     return CA_STATUS_OK;
 }
 
@@ -1427,6 +1452,8 @@ CAResult_t CALEServerInitialize()
         return ret;
     }
 
+    g_threadSendNotifyCond = ca_cond_new();
+
     ret = CALEServerInitMutexVaraibles();
     if (CA_STATUS_OK != ret)
     {
@@ -1468,11 +1495,47 @@ void CALEServerTerminate()
 {
     OIC_LOG(DEBUG, TAG, "IN - CALEServerTerminate");
 
+    if (!g_jvm)
+    {
+        OIC_LOG(ERROR, TAG, "g_jvm is null");
+        return;
+    }
+
+    bool isAttached = false;
+    JNIEnv* env;
+    jint res = (*g_jvm)->GetEnv(g_jvm, (void**) &env, JNI_VERSION_1_6);
+    if (JNI_OK != res)
+    {
+        OIC_LOG(INFO, TAG, "Could not get JNIEnv pointer");
+        res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+
+        if (JNI_OK != res)
+        {
+            OIC_LOG(ERROR, TAG, "AttachCurrentThread has failed");
+            return;
+        }
+        isAttached = true;
+    }
+
+    if (g_sendBuffer)
+    {
+        (*env)->DeleteGlobalRef(env, g_sendBuffer);
+        g_sendBuffer = NULL;
+    }
+
+    ca_cond_free(g_threadSendNotifyCond);
+    g_threadSendNotifyCond = NULL;
+
     CALEServerTerminateMutexVaraibles();
     CALEServerTerminateConditionVaraibles();
 
     g_isInitializedServer = false;
 
+    if (isAttached)
+    {
+        (*g_jvm)->DetachCurrentThread(g_jvm);
+    }
+
     OIC_LOG(DEBUG, TAG, "OUT - CALEServerTerminate");
 }
 
@@ -1693,6 +1756,8 @@ CAResult_t CALEServerSendUnicastMessageImpl(JNIEnv *env, const char* address, co
         return CA_STATUS_FAILED;
     }
 
+    ca_mutex_lock(g_threadSendMutex);
+
     jobject jni_obj_bluetoothDevice = NULL;
     uint32_t length = u_arraylist_length(g_connectedDeviceList);
     for (uint32_t index = 0; index < length; index++)
@@ -1702,20 +1767,20 @@ CAResult_t CALEServerSendUnicastMessageImpl(JNIEnv *env, const char* address, co
         if (!jarrayObj)
         {
             OIC_LOG(ERROR, TAG, "jarrayObj is null");
-            return CA_STATUS_FAILED;
+            goto error_exit;
         }
 
         jstring jni_setAddress = CALEGetAddressFromBTDevice(env, jarrayObj);
         if (!jni_setAddress)
         {
             OIC_LOG(ERROR, TAG, "jni_setAddress is null");
-            return CA_STATUS_FAILED;
+            goto error_exit;
         }
         const char* setAddress = (*env)->GetStringUTFChars(env, jni_setAddress, NULL);
         if (!setAddress)
         {
             OIC_LOG(ERROR, TAG, "setAddress is null");
-            return CA_STATUS_FAILED;
+            goto error_exit;
         }
 
         OIC_LOG_V(DEBUG, TAG, "setAddress : %s", setAddress);
@@ -1733,23 +1798,42 @@ CAResult_t CALEServerSendUnicastMessageImpl(JNIEnv *env, const char* address, co
 
     if (jni_obj_bluetoothDevice)
     {
-        jbyteArray jni_bytearr_data = (*env)->NewByteArray(env, dataLen);
-        (*env)->SetByteArrayRegion(env, jni_bytearr_data, 0, dataLen, (jbyte*) data);
+        jbyteArray jni_arr = (*env)->NewByteArray(env, dataLen);
+        (*env)->SetByteArrayRegion(env, jni_arr, 0, dataLen, (jbyte*) data);
+        g_sendBuffer = (jbyteArray)(*env)->NewGlobalRef(env, jni_arr);
 
-        CAResult_t res = CALEServerSend(env, jni_obj_bluetoothDevice, jni_bytearr_data);
+        CAResult_t res = CALEServerSend(env, jni_obj_bluetoothDevice, g_sendBuffer);
         if (CA_STATUS_OK != res)
         {
             OIC_LOG(ERROR, TAG, "send has failed");
-            return CA_SEND_FAILED;
+            goto error_exit;
         }
     }
     else
     {
         OIC_LOG(ERROR, TAG, "There are no device to send in the list");
-        return CA_STATUS_FAILED;
+        goto error_exit;
     }
 
+    if (g_sendBuffer)
+    {
+        (*env)->DeleteGlobalRef(env, g_sendBuffer);
+        g_sendBuffer = NULL;
+    }
+
+    ca_mutex_unlock(g_threadSendMutex);
+    OIC_LOG(INFO, TAG, "unicast - send request is successful");
     return CA_STATUS_OK;
+
+error_exit:
+    if (g_sendBuffer)
+    {
+        (*env)->DeleteGlobalRef(env, g_sendBuffer);
+        g_sendBuffer = NULL;
+    }
+
+    ca_mutex_unlock(g_threadSendMutex);
+    return CA_SEND_FAILED;
 }
 
 CAResult_t CALEServerSendMulticastMessageImpl(JNIEnv *env, const uint8_t *data, uint32_t dataLen)
@@ -1764,6 +1848,18 @@ CAResult_t CALEServerSendMulticastMessageImpl(JNIEnv *env, const uint8_t *data,
         return CA_STATUS_FAILED;
     }
 
+    ca_mutex_lock(g_threadSendMutex);
+
+    OIC_LOG(DEBUG, TAG, "set data into g_sendBuffer for notify");
+    if (g_sendBuffer)
+    {
+        (*env)->DeleteGlobalRef(env, g_sendBuffer);
+        g_sendBuffer = NULL;
+    }
+    jbyteArray jni_arr = (*env)->NewByteArray(env, dataLen);
+    (*env)->SetByteArrayRegion(env, jni_arr, 0, dataLen, (jbyte*) data);
+    g_sendBuffer = (jbyteArray)(*env)->NewGlobalRef(env, jni_arr);
+
     uint32_t length = u_arraylist_length(g_connectedDeviceList);
     for (uint32_t index = 0; index < length; index++)
     {
@@ -1771,20 +1867,46 @@ CAResult_t CALEServerSendMulticastMessageImpl(JNIEnv *env, const uint8_t *data,
         if (!jarrayObj)
         {
             OIC_LOG(ERROR, TAG, "jarrayObj is null");
-            return CA_STATUS_FAILED;
+            continue;
         }
 
         // send data for all device
         jbyteArray jni_bytearr_data = (*env)->NewByteArray(env, dataLen);
         (*env)->SetByteArrayRegion(env, jni_bytearr_data, 0, dataLen, (jbyte*) data);
+
+        jstring jni_address = CALEGetAddressFromBTDevice(env, jarrayObj);
+        if (!jni_address)
+        {
+            OIC_LOG(ERROR, TAG, "CALEGetAddressFromBTDevice has failed");
+            continue;
+        }
+
+        const char* address = (*env)->GetStringUTFChars(env, jni_address, NULL);
+        if (!address)
+        {
+            OIC_LOG(ERROR, TAG, "address is not available");
+            continue;
+        }
+
         CAResult_t res = CALEServerSend(env, jarrayObj, jni_bytearr_data);
         if (CA_STATUS_OK != res)
         {
-            OIC_LOG(ERROR, TAG, "send has failed");
-            return CA_SEND_FAILED;
+            OIC_LOG_V(ERROR, TAG, "send has failed for the device[%s]", address);
+            (*env)->ReleaseStringUTFChars(env, jni_address, address);
+            continue;
         }
+
+        OIC_LOG_V(INFO, TAG, "unicast - send request is successful for a device[%s]", address);
+        (*env)->ReleaseStringUTFChars(env, jni_address, address);
     }
 
+    if (g_sendBuffer)
+    {
+        (*env)->DeleteGlobalRef(env, g_sendBuffer);
+        g_sendBuffer = NULL;
+    }
+
+    ca_mutex_unlock(g_threadSendMutex);
     return CA_STATUS_OK;
 }
 
@@ -2206,8 +2328,36 @@ Java_org_iotivity_ca_CaLeServerInterface_caLeGattServerNotificationSentCallback(
     VERIFY_NON_NULL_VOID(obj, TAG, "obj");
     VERIFY_NON_NULL_VOID(device, TAG, "device");
 
-    OIC_LOG_V(DEBUG, TAG, "Gatt Server Notification Sent Callback(%d)",
+    OIC_LOG_V(DEBUG, TAG, "Gatt Server Notification Sent Callback (status : %d)",
               status);
+
+    if (GATT_SUCCESS != status) // error case
+    {
+        OIC_LOG(ERROR, TAG, "it will be sent again.");
+
+        CAResult_t res = CALEServerSend(env, device, g_sendBuffer);
+        if (CA_STATUS_OK != res)
+        {
+            OIC_LOG(ERROR, TAG, "send has failed");
+            ca_mutex_lock(g_threadSendNotifyMutex);
+            g_isSignalSetFlag = true;
+            ca_cond_signal(g_threadSendNotifyCond);
+            ca_mutex_unlock(g_threadSendNotifyMutex);
+            return CA_SEND_FAILED;
+        }
+    }
+    else
+    {
+        OIC_LOG(DEBUG, TAG, "notify success");
+
+        // next data can be sent
+        ca_mutex_lock(g_threadSendNotifyMutex);
+        OIC_LOG(DEBUG, TAG, "g_isSignalSetFlag is set true and signal");
+        g_isSignalSetFlag = true;
+        ca_cond_signal(g_threadSendNotifyCond);
+        ca_mutex_unlock(g_threadSendNotifyMutex);
+    }
+
 }
 
 JNIEXPORT void JNICALL
@@ -2314,6 +2464,10 @@ CAResult_t CAStopLEGattServer()
         (*env)->DeleteGlobalRef(env, g_bluetoothGattServerCallback);
     }
 
+    ca_mutex_lock(g_threadSendNotifyMutex);
+    ca_cond_signal(g_threadSendNotifyCond);
+    ca_mutex_unlock(g_threadSendNotifyMutex);
+
     g_isStartServer = false;
 
     if (isAttached)
@@ -2411,6 +2565,26 @@ CAResult_t CALEServerInitMutexVaraibles()
         }
     }
 
+    if (NULL == g_threadSendMutex)
+    {
+        g_threadSendMutex = ca_mutex_new();
+        if (NULL == g_threadSendMutex)
+        {
+            OIC_LOG(ERROR, TAG, "ca_mutex_new has failed");
+            return CA_STATUS_FAILED;
+        }
+    }
+
+    if (NULL == g_threadSendNotifyMutex)
+    {
+        g_threadSendNotifyMutex = ca_mutex_new();
+        if (NULL == g_threadSendNotifyMutex)
+        {
+            OIC_LOG(ERROR, TAG, "ca_mutex_new has failed");
+            return CA_STATUS_FAILED;
+        }
+    }
+
     return CA_STATUS_OK;
 }
 
@@ -2424,6 +2598,12 @@ void CALEServerTerminateMutexVaraibles()
 
     ca_mutex_free(g_connectedDeviceListMutex);
     g_connectedDeviceListMutex = NULL;
+
+    ca_mutex_free(g_threadSendMutex);
+    g_threadSendMutex = NULL;
+
+    ca_mutex_free(g_threadSendNotifyMutex);
+    g_threadSendNotifyMutex = NULL;
 }
 
 void CALEServerTerminateConditionVaraibles()