From 4ecaeba1075a92fc910ecaa917fdcc9100defc48 Mon Sep 17 00:00:00 2001 From: CHANDRASHEKHAR S BYADGI Date: Thu, 25 May 2023 19:23:18 +0530 Subject: [PATCH] Add setWillInfo API support for Android AITT --- .../android/aittnative/JniInterface.java | 16 +++++ android/aitt-native/src/main/jni/aitt_jni.cc | 65 ++++++++++++++++--- android/aitt-native/src/main/jni/aitt_jni.h | 3 + .../java/com/samsung/android/aitt/Aitt.java | 19 ++++++ .../samsung/android/aitt/AittUnitTest.java | 46 +++++++++++++ 5 files changed, 139 insertions(+), 10 deletions(-) diff --git a/android/aitt-native/src/main/java/com/samsung/android/aittnative/JniInterface.java b/android/aitt-native/src/main/java/com/samsung/android/aittnative/JniInterface.java index f7783d5..138f771 100644 --- a/android/aitt-native/src/main/java/com/samsung/android/aittnative/JniInterface.java +++ b/android/aitt-native/src/main/java/com/samsung/android/aittnative/JniInterface.java @@ -123,6 +123,19 @@ public class JniInterface { publishJNI(instance, topic, data, dataLen, protocol, qos, retain); } + /** + * 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); diff --git a/android/aitt-native/src/main/jni/aitt_jni.cc b/android/aitt-native/src/main/jni/aitt_jni.cc index 955fa74..8cc1a34 100644 --- a/android/aitt-native/src/main/jni/aitt_jni.cc +++ b/android/aitt-native/src/main/jni/aitt_jni.cc @@ -178,6 +178,50 @@ void AittNativeInterface::Publish(JNIEnv *env, jobject jni_interface_object, jlo env->ReleaseByteArrayElements(data, reinterpret_cast((char *) cdata), 0); } +/** + * 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(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(cdata); + + auto _qos = static_cast(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((char *) cdata), 0); +} + /** * JNI API to disconnect from MQTT broker * @param env JNI interface pointer @@ -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(AittNativeInterface::Init)}, - {"connectJNI", "(JLjava/lang/String;I)V", reinterpret_cast(AittNativeInterface::Connect)}, - {"subscribeJNI", "(JLjava/lang/String;II)J", reinterpret_cast(AittNativeInterface::Subscribe)}, - {"publishJNI", "(JLjava/lang/String;[BJIIZ)V", reinterpret_cast(AittNativeInterface::Publish)}, - {"unsubscribeJNI", "(JJ)V", reinterpret_cast(AittNativeInterface::Unsubscribe)}, - {"disconnectJNI", "(J)V", reinterpret_cast(AittNativeInterface::Disconnect)}, - {"setConnectionCallbackJNI", "(J)V", reinterpret_cast(AittNativeInterface::SetConnectionCallback)}, - {"setDiscoveryCallbackJNI", "(JLjava/lang/String;)I", reinterpret_cast(AittNativeInterface::SetDiscoveryCallback)}, - {"removeDiscoveryCallbackJNI", "(JI)V", reinterpret_cast(AittNativeInterface::RemoveDiscoveryCallback)}, - {"updateDiscoveryMessageJNI", "(JLjava/lang/String;[BJ)V", reinterpret_cast(AittNativeInterface::UpdateDiscoveryMessage)}}; + {"initJNI", "(Ljava/lang/String;Ljava/lang/String;Z)J", reinterpret_cast(AittNativeInterface::Init)}, + {"connectJNI", "(JLjava/lang/String;I)V", reinterpret_cast(AittNativeInterface::Connect)}, + {"subscribeJNI", "(JLjava/lang/String;II)J", reinterpret_cast(AittNativeInterface::Subscribe)}, + {"publishJNI", "(JLjava/lang/String;[BJIIZ)V", reinterpret_cast(AittNativeInterface::Publish)}, + {"setWillInfoJNI", "(JLjava/lang/String;[BJIZ)V", reinterpret_cast(AittNativeInterface::SetWillInfo)}, + {"unsubscribeJNI", "(JJ)V", reinterpret_cast(AittNativeInterface::Unsubscribe)}, + {"disconnectJNI", "(J)V", reinterpret_cast(AittNativeInterface::Disconnect)}, + {"setConnectionCallbackJNI", "(J)V", reinterpret_cast(AittNativeInterface::SetConnectionCallback)}, + {"setDiscoveryCallbackJNI", "(JLjava/lang/String;)I", reinterpret_cast(AittNativeInterface::SetDiscoveryCallback)}, + {"removeDiscoveryCallbackJNI", "(JI)V", reinterpret_cast(AittNativeInterface::RemoveDiscoveryCallback)}, + {"updateDiscoveryMessageJNI", "(JLjava/lang/String;[BJ)V", reinterpret_cast(AittNativeInterface::UpdateDiscoveryMessage)}}; if (env->RegisterNatives(klass, aitt_jni_methods, sizeof(aitt_jni_methods) / sizeof(aitt_jni_methods[0]))) { env->DeleteLocalRef(klass); diff --git a/android/aitt-native/src/main/jni/aitt_jni.h b/android/aitt-native/src/main/jni/aitt_jni.h index 2ec80e8..2c12cc2 100644 --- a/android/aitt-native/src/main/jni/aitt_jni.h +++ b/android/aitt-native/src/main/jni/aitt_jni.h @@ -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); diff --git a/android/aitt/src/main/java/com/samsung/android/aitt/Aitt.java b/android/aitt/src/main/java/com/samsung/android/aitt/Aitt.java index 539f434..d244c75 100644 --- a/android/aitt/src/main/java/com/samsung/android/aitt/Aitt.java +++ b/android/aitt/src/main/java/com/samsung/android/aitt/Aitt.java @@ -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 publishTable = new HashMap<>(); @@ -298,6 +299,24 @@ public class Aitt { publishTransportProtocols(topic, message, protocols); } + /** + * 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 * diff --git a/android/aitt/src/test/java/com/samsung/android/aitt/AittUnitTest.java b/android/aitt/src/test/java/com/samsung/android/aitt/AittUnitTest.java index 45fbc10..2c4586d 100644 --- a/android/aitt/src/test/java/com/samsung/android/aitt/AittUnitTest.java +++ b/android/aitt/src/test/java/com/samsung/android/aitt/AittUnitTest.java @@ -259,6 +259,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 { -- 2.34.1