Add setWillInfo API support for Android AITT
authorCHANDRASHEKHAR S BYADGI <chandru.b@samsung.com>
Thu, 25 May 2023 13:53:18 +0000 (19:23 +0530)
committerCHANDRASHEKHAR S BYADGI <chandru.b@samsung.com>
Wed, 14 Jun 2023 11:55:18 +0000 (20:55 +0900)
android/aitt-native/src/main/java/com/samsung/android/aittnative/JniInterface.java
android/aitt-native/src/main/jni/aitt_jni.cc
android/aitt-native/src/main/jni/aitt_jni.h
android/aitt/src/main/java/com/samsung/android/aitt/Aitt.java
android/aitt/src/test/java/com/samsung/android/aitt/AittUnitTest.java

index f7783d5..138f771 100644 (file)
@@ -124,6 +124,19 @@ public class JniInterface {
     }
 
     /**
+     * JNI Interface API to set will info data to specified MQTT topic
+     *
+     * @param topic   String to which message needs to be published
+     * @param data    Byte message to be published
+     * @param dataLen Size/length of the message to be published
+     * @param qos     QoS at which the message should be delivered
+     * @param retain  Boolean to decide whether or not the message should be retained by the broker
+     */
+    public void setWillInfo(final String topic, final byte[] data, long dataLen, int qos, boolean retain) {
+        setWillInfoJNI(instance, topic, data, dataLen, qos, retain);
+    }
+
+    /**
      * JNI Interface API to unsubscribe the given topic
      * @param aittSubId Subscribe ID of the topics to be unsubscribed
      */
@@ -270,6 +283,9 @@ public class JniInterface {
     /* Native API for publishing to a topic */
     private native void publishJNI(long instance, final String topic, final byte[] data, long dataLen, int protocol, int qos, boolean retain);
 
+    /* Native API for set will info to a topic */
+    private native void setWillInfoJNI(long instance, final String topic, final byte[] data, long dataLen, int qos, boolean retain);
+
     /* Native API for subscribing to a topic */
     private native long subscribeJNI(long instance, final String topic, int protocol, int qos);
 
index 955fa74..8cc1a34 100644 (file)
@@ -179,6 +179,50 @@ void AittNativeInterface::Publish(JNIEnv *env, jobject jni_interface_object, jlo
 }
 
 /**
+ * JNI API to set will info data to a given MQTT topic
+ * @param env JNI interface pointer
+ * @param jni_interface_object JNI interface object
+ * @param handle AittNativeInterface object
+ * @param topic subscribe topic
+ * @param data data to be published
+ * @param data_len data length of a publishing data
+ * @param protocol publishing protocol
+ * @param qos publishing qos
+ * @param retain Currently used in MQTT to inform broker to retain data or not
+ */
+void AittNativeInterface::SetWillInfo(JNIEnv *env, jobject jni_interface_object, jlong handle,
+                                  jstring topic, jbyteArray data, jlong data_len, jint qos, jboolean retain)
+{
+    if (!CheckParams(env, jni_interface_object)) {
+        return;
+    }
+
+    auto *instance = reinterpret_cast<AittNativeInterface *>(handle);
+    std::string mqttTopic = GetStringUTF(env, topic);
+    if (mqttTopic.empty()) {
+        return;
+    }
+
+    const char *cdata = (char *)env->GetByteArrayElements(data, nullptr);
+    if (cdata == nullptr) {
+        JNI_LOG(ANDROID_LOG_ERROR, TAG, "Failed to get byte array elements");
+        return;
+    }
+    const void *_data = reinterpret_cast<const void *>(cdata);
+
+    auto _qos = static_cast<AittQoS>(qos);
+    bool _retain = (bool) retain;
+
+    try {
+        instance->aitt.SetWillInfo(mqttTopic, _data, (int) data_len, _qos, _retain);
+    } catch (std::exception &e) {
+        JNI_LOG(ANDROID_LOG_ERROR, TAG, "Failed to SetWillInfo");
+        JNI_LOG(ANDROID_LOG_ERROR, TAG, e.what());
+    }
+    env->ReleaseByteArrayElements(data, reinterpret_cast<jbyte *>((char *) cdata), 0);
+}
+
+/**
  * JNI API to disconnect from MQTT broker
  * @param env JNI interface pointer
  * @param jni_interface_object JNI interface object
@@ -577,16 +621,17 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
         return JNI_ERR;
     }
     static JNINativeMethod aitt_jni_methods[] = {
-            {"initJNI",                     "(Ljava/lang/String;Ljava/lang/String;Z)J", reinterpret_cast<void *>(AittNativeInterface::Init)},
-            {"connectJNI",                  "(JLjava/lang/String;I)V",                  reinterpret_cast<void *>(AittNativeInterface::Connect)},
-            {"subscribeJNI",                "(JLjava/lang/String;II)J",                 reinterpret_cast<void *>(AittNativeInterface::Subscribe)},
-            {"publishJNI",                  "(JLjava/lang/String;[BJIIZ)V",             reinterpret_cast<void *>(AittNativeInterface::Publish)},
-            {"unsubscribeJNI",              "(JJ)V",                                    reinterpret_cast<void *>(AittNativeInterface::Unsubscribe)},
-            {"disconnectJNI",               "(J)V",                                     reinterpret_cast<void *>(AittNativeInterface::Disconnect)},
-            {"setConnectionCallbackJNI",    "(J)V",                                     reinterpret_cast<void *>(AittNativeInterface::SetConnectionCallback)},
-            {"setDiscoveryCallbackJNI",     "(JLjava/lang/String;)I",                   reinterpret_cast<void *>(AittNativeInterface::SetDiscoveryCallback)},
-            {"removeDiscoveryCallbackJNI",  "(JI)V",                                    reinterpret_cast<void *>(AittNativeInterface::RemoveDiscoveryCallback)},
-            {"updateDiscoveryMessageJNI",   "(JLjava/lang/String;[BJ)V",                reinterpret_cast<void *>(AittNativeInterface::UpdateDiscoveryMessage)}};
+            {"initJNI",                    "(Ljava/lang/String;Ljava/lang/String;Z)J", reinterpret_cast<void *>(AittNativeInterface::Init)},
+            {"connectJNI",                 "(JLjava/lang/String;I)V",                  reinterpret_cast<void *>(AittNativeInterface::Connect)},
+            {"subscribeJNI",               "(JLjava/lang/String;II)J",                 reinterpret_cast<void *>(AittNativeInterface::Subscribe)},
+            {"publishJNI",                 "(JLjava/lang/String;[BJIIZ)V",             reinterpret_cast<void *>(AittNativeInterface::Publish)},
+            {"setWillInfoJNI",             "(JLjava/lang/String;[BJIZ)V",              reinterpret_cast<void *>(AittNativeInterface::SetWillInfo)},
+            {"unsubscribeJNI",             "(JJ)V",                                    reinterpret_cast<void *>(AittNativeInterface::Unsubscribe)},
+            {"disconnectJNI",              "(J)V",                                     reinterpret_cast<void *>(AittNativeInterface::Disconnect)},
+            {"setConnectionCallbackJNI",   "(J)V",                                     reinterpret_cast<void *>(AittNativeInterface::SetConnectionCallback)},
+            {"setDiscoveryCallbackJNI",    "(JLjava/lang/String;)I",                   reinterpret_cast<void *>(AittNativeInterface::SetDiscoveryCallback)},
+            {"removeDiscoveryCallbackJNI", "(JI)V",                                    reinterpret_cast<void *>(AittNativeInterface::RemoveDiscoveryCallback)},
+            {"updateDiscoveryMessageJNI",  "(JLjava/lang/String;[BJ)V",                reinterpret_cast<void *>(AittNativeInterface::UpdateDiscoveryMessage)}};
     if (env->RegisterNatives(klass, aitt_jni_methods,
                              sizeof(aitt_jni_methods) / sizeof(aitt_jni_methods[0]))) {
         env->DeleteLocalRef(klass);
index 2ec80e8..2c12cc2 100644 (file)
@@ -65,6 +65,9 @@ public:
                         jstring topic, jbyteArray data, jlong datalen, jint protocol,
                         jint qos, jboolean retain);
 
+    static void SetWillInfo(JNIEnv *env, jobject jniInterfaceObject, jlong handle,
+                            jstring topic, jbyteArray data, jlong datalen, jint qos, jboolean retain);
+
     static void Unsubscribe(JNIEnv *env, jobject jniInterfaceObject, jlong handle,
                             jlong aittSubId);
 
index 539f434..d244c75 100644 (file)
@@ -51,6 +51,7 @@ public class Aitt {
 
     private static final String TAG = "AITT_ANDROID";
     private static final String INVALID_TOPIC = "Invalid topic";
+    private static final String INVALID_WILL_INFO = "Invalid will info";
     private static final String HOST_STRING = "host";
 
     private final Map<String, HostTable> publishTable = new HashMap<>();
@@ -299,6 +300,24 @@ public class Aitt {
     }
 
     /**
+     * Method to set will info message to a specific mqtt topic with qos, and retain
+     *
+     * @param topic  String to which message needs to be published
+     * @param data   Byte message that needs to be published
+     * @param qos    QoS at which the message should be delivered
+     * @param retain Boolean to decide whether or not the message should be retained by the broker
+     */
+    public void setWillInfo(String topic, byte[] data, QoS qos, boolean retain) {
+        if (topic == null || topic.isEmpty()) {
+            throw new IllegalArgumentException(INVALID_TOPIC);
+        }
+        if (data == null || data.length <= 0) {
+            throw new IllegalArgumentException(INVALID_WILL_INFO);
+        }
+        mJniInterface.setWillInfo(topic, data, data.length, qos.ordinal(), retain);
+    }
+
+    /**
      * Method to publish message to a specific topic with transport protocols
      *
      * @param topic     String to which message needs to be published
index 45fbc10..2c4586d 100644 (file)
@@ -260,6 +260,52 @@ public class AittUnitTest {
     }
 
     @Test
+    public void testSetWillInfoMqtt_P() {
+        try {
+            String willInfo = "Test Will info data";
+            shadowJniInterface.setInitReturn(true);
+            Aitt aitt = new Aitt(appContext, aittId);
+            byte[] data = willInfo.getBytes();
+            aitt.setWillInfo(topic, data, Aitt.QoS.AT_MOST_ONCE, false);
+            aitt.connect(brokerIp, port);
+            aitt.disconnect();
+        } catch (Exception e) {
+            fail("Failed testSetWillInfoMqtt_P " + e);
+        }
+    }
+
+    @Test
+    public void testWithoutWillInfo_N() {
+        try {
+            String willInfo = "";
+            shadowJniInterface.setInitReturn(true);
+            Aitt aitt = new Aitt(appContext, aittId);
+            byte[] data = willInfo.getBytes();
+            assertThrows(IllegalArgumentException.class, () -> aitt.setWillInfo(topic, data, Aitt.QoS.AT_MOST_ONCE, false));
+            aitt.connect(brokerIp, port);
+            aitt.disconnect();
+        } catch (Exception e) {
+            fail("Failed testWithoutWillInfo_N " + e);
+        }
+    }
+
+    @Test
+    public void testWillInfoWithoutTopic_N() {
+        try {
+            String _topic = "";
+            String willInfo = "Test Will info data";
+            shadowJniInterface.setInitReturn(true);
+            Aitt aitt = new Aitt(appContext, aittId);
+            byte[] data = willInfo.getBytes();
+            assertThrows(IllegalArgumentException.class, () -> aitt.setWillInfo(_topic, data, Aitt.QoS.AT_MOST_ONCE, false));
+            aitt.connect(brokerIp, port);
+            aitt.disconnect();
+        } catch (Exception e) {
+            fail("Failed testWillInfoWithoutTopic_N " + e);
+        }
+    }
+
+    @Test
     public void testConnectWithoutIP_P() {
         try {
             shadowJniInterface.setInitReturn(true);