Add IPC protocol support to aitt android
authorPraveen Naik S <praveen.s@samsung.com>
Wed, 19 Oct 2022 13:41:06 +0000 (19:11 +0530)
committerYoungjae Shin <yj99.shin@samsung.com>
Wed, 9 Nov 2022 08:19:17 +0000 (17:19 +0900)
android/aitt/build.gradle
android/aitt/src/main/java/com/samsung/android/aitt/Definitions.java
android/aitt/src/main/java/com/samsung/android/aitt/IpcHandler.java
android/aitt/src/test/java/com/samsung/android/aitt/AittUnitTest.java
android/modules/ipc/.gitignore [new file with mode: 0644]
android/modules/ipc/build.gradle [new file with mode: 0644]
android/modules/ipc/proguard-rules.pro [new file with mode: 0644]
android/modules/ipc/src/main/AndroidManifest.xml [new file with mode: 0644]
android/modules/ipc/src/main/java/com/samsung/android/modules/ipc/Ipc.java [new file with mode: 0644]
settings.gradle

index 348c0d7..de6e262 100644 (file)
@@ -52,6 +52,7 @@ dependencies {
 
     implementation project(path: ':android:modules:tcp')
     implementation project(path: ':android:modules:webrtc')
+    implementation project(path: ':android:modules:ipc')
     implementation project(path: ':android:aitt-native')
 
     testImplementation 'junit:junit:4.13.2'
index a1de3f6..bf4be83 100644 (file)
@@ -23,4 +23,5 @@ class Definitions {
     public static final String JOIN_NETWORK = "connected";
     public static final String RESPONSE_POSTFIX = "_AittRe_";
     public static final String STATUS = "status";
+    public static final int DEFAULT_IPC_PORT = 0;
 }
index 13afc4e..58d6dc2 100644 (file)
 package com.samsung.android.aitt;
 
 import android.content.Context;
+import android.util.Log;
 
-import com.samsung.android.aittnative.JniInterface;
+import com.google.flatbuffers.FlexBuffersBuilder;
+import com.samsung.android.modules.ipc.Ipc;
+
+import java.nio.ByteBuffer;
 
 class IpcHandler implements TransportHandler {
+
+    private static final String TAG = "IpcHandler";
+    private Context context;
+    private String ip;
+    private Ipc ipc;
+    private byte[] publishData;
+
     public IpcHandler() {
         //ToDo : Copy jni interface and use to communicate with JNI
     }
 
     @Override
     public void setAppContext(Context appContext) {
-        //ToDo : Implement setAppContext method
+        context = appContext;
     }
 
     @Override
     public void setSelfIP(String ip) {
-        //ToDo : Implement setSelfIP method
+        this.ip = ip;
     }
 
     @Override
     public byte[] getPublishData() {
-        //ToDo : Implement getPublishData method
-        return null;
+        return publishData;
     }
 
     @Override
     public void subscribe(String topic, HandlerDataCallback handlerDataCallback) {
-        //ToDo : Implement subscribe method
+        publishData = wrapPublishData(topic, Definitions.DEFAULT_IPC_PORT);
+        try {
+            Ipc.RecieveFrameCallback cb = frame -> {
+                handlerDataCallback.pushHandlerData(frame);
+            };
+            ipc = new Ipc(context, cb);
+            ipc.initConsumer();
+
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to subscribe to IPC");
+        }
+    }
+
+    /**
+     * Method to wrap topic, device IP address, webRTC server instance port number for publishing
+     *
+     * @param topic      Topic to which the application has subscribed to
+     * @param serverPort Port number of the WebRTC server instance
+     * @return Byte data wrapped, contains topic, device IP, webRTC server port number
+     */
+    private byte[] wrapPublishData(String topic, int serverPort) {
+        FlexBuffersBuilder fbb = new FlexBuffersBuilder(ByteBuffer.allocate(512));
+        {
+            int smap = fbb.startMap();
+            fbb.putString(Definitions.STATUS, Definitions.JOIN_NETWORK);
+            fbb.putString("host", ip);
+            {
+                int smap1 = fbb.startMap();
+                fbb.putInt("protocol", Aitt.Protocol.IPC.getValue());
+                fbb.putInt("port", serverPort);
+                fbb.endMap(topic, smap1);
+            }
+            fbb.endMap(null, smap);
+        }
+        ByteBuffer buffer = fbb.finish();
+        byte[] data = new byte[buffer.remaining()];
+        buffer.get(data, 0, data.length);
+        return data;
     }
 
     @Override
     public void publish(String topic, String ip, int port, byte[] message) {
-        //ToDo : Implement publish method
+        if (ipc == null)
+        {
+            ipc = new Ipc(context);
+            ipc.initProducer();
+        }
+        ipc.writeToMemory(message);
     }
 
     @Override
     public void unsubscribe() {
-        //ToDo : Implement unsubscribe method
+        if (ipc != null)
+            ipc.close();
     }
 
     @Override
index 0e38924..0871a08 100644 (file)
@@ -352,6 +352,43 @@ public class AittUnitTest {
     }
 
     @Test
+    public void testPublishIpc_P() {
+        try {
+            shadowJniInterface.setInitReturn(true);
+            Aitt aitt = new Aitt(appContext, aittId);
+
+            assertNotNull("Aitt Instance not null", aitt);
+            aitt.connect(brokerIp, port);
+
+            byte[] payload = message.getBytes();
+            aitt.publish(topic, payload, Aitt.Protocol.IPC, Aitt.QoS.AT_MOST_ONCE, false);
+
+            aitt.disconnect();
+        } catch (Exception e) {
+            fail("Failed testPublishIpc " + e);
+        }
+    }
+
+    @Test
+    public void testPublishIpcInvalidTopic_N() {
+        try {
+            shadowJniInterface.setInitReturn(true);
+            Aitt aitt = new Aitt(appContext, aittId);
+            aitt.connect(brokerIp, port);
+            String _topic = "";
+            byte[] payload = message.getBytes();
+
+            assertThrows(IllegalArgumentException.class, () -> {
+                aitt.publish(_topic, payload, Aitt.Protocol.IPC, Aitt.QoS.AT_MOST_ONCE, false);
+            });
+
+            aitt.disconnect();
+        } catch (Exception e) {
+            fail("Failed testPublishIpcInvalidTopic" + e);
+        }
+    }
+
+    @Test
     public void testPublishAnyProtocol_P() {
         try {
             shadowJniInterface.setInitReturn(true);
@@ -512,7 +549,6 @@ public class AittUnitTest {
         }
     }
 
-
     @Test
     public void testSubscribeWebRTC_P() {
         try {
@@ -578,6 +614,52 @@ public class AittUnitTest {
     }
 
     @Test
+    public void testSubscribeIpc_P() {
+        try {
+            shadowJniInterface.setInitReturn(true);
+            Aitt aitt = new Aitt(appContext, aittId);
+
+            assertNotNull("Aitt Instance not null", aitt);
+            aitt.connect(brokerIp, port);
+
+            aitt.subscribe(topic, new Aitt.SubscribeCallback() {
+                @Override
+                public void onMessageReceived(AittMessage message) {
+                    String _topic = message.getTopic();
+                    byte[] payload = message.getPayload();
+                }
+            }, Aitt.Protocol.IPC, Aitt.QoS.AT_MOST_ONCE);
+
+            aitt.disconnect();
+        } catch (Exception e) {
+            fail("Failed testSubscribeIpc " + e);
+        }
+    }
+
+    @Test
+    public void testSubscribeIpcInvalidTopic_N() {
+        try {
+            shadowJniInterface.setInitReturn(true);
+            Aitt aitt = new Aitt(appContext, aittId);
+            aitt.connect(brokerIp, port);
+
+            String _topic = "";
+
+            assertThrows(IllegalArgumentException.class, () -> {
+                aitt.subscribe(_topic, new Aitt.SubscribeCallback() {
+                    @Override
+                    public void onMessageReceived(AittMessage message) {
+                    }
+                }, Aitt.Protocol.IPC, Aitt.QoS.AT_MOST_ONCE);
+            });
+
+            aitt.disconnect();
+        } catch (Exception e) {
+            fail("Failed testSubscribeIpcInvalidTopic " + e);
+        }
+    }
+
+    @Test
     public void testSubscribeAnyProtocol_P() {
         try {
             shadowJniInterface.setInitReturn(true);
diff --git a/android/modules/ipc/.gitignore b/android/modules/ipc/.gitignore
new file mode 100644 (file)
index 0000000..796b96d
--- /dev/null
@@ -0,0 +1 @@
+/build
diff --git a/android/modules/ipc/build.gradle b/android/modules/ipc/build.gradle
new file mode 100644 (file)
index 0000000..ef269ac
--- /dev/null
@@ -0,0 +1,42 @@
+plugins {
+    id 'com.android.library'
+}
+
+android {
+    compileSdkVersion 33
+
+    defaultConfig {
+        minSdkVersion 21
+        targetSdkVersion 33
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+}
+
+repositories {
+    maven { url "https://newtronlabs.jfrog.io/artifactory/libs-release-local" }
+}
+
+subprojects {
+    apply plugin: 'com.newtronlabs.android'
+}
+
+dependencies {
+    implementation 'androidx.appcompat:appcompat:1.5.1'
+    implementation 'com.google.android.material:material:1.6.1'
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+    compileOnly 'com.newtronlabs.sharedmemory:sharedmemory:5.0.0-alpha01'
+}
diff --git a/android/modules/ipc/proguard-rules.pro b/android/modules/ipc/proguard-rules.pro
new file mode 100644 (file)
index 0000000..f1b4245
--- /dev/null
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/android/modules/ipc/src/main/AndroidManifest.xml b/android/modules/ipc/src/main/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..7e01407
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.samsung.android.modules.ipc">
+
+</manifest>
diff --git a/android/modules/ipc/src/main/java/com/samsung/android/modules/ipc/Ipc.java b/android/modules/ipc/src/main/java/com/samsung/android/modules/ipc/Ipc.java
new file mode 100644 (file)
index 0000000..ae13ee5
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.samsung.android.modules.ipc;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.newtronlabs.sharedmemory.IRemoteSharedMemory;
+import com.newtronlabs.sharedmemory.RemoteMemoryAdapter;
+import com.newtronlabs.sharedmemory.SharedMemoryProducer;
+import com.newtronlabs.sharedmemory.prod.memory.ISharedMemory;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class Ipc {
+    private static final String TAG = "IPC_ANDROID";
+    private static final String regionName = "Test-Region";
+    private static final int sizeInBytes = 470000; // 640*480 frame size
+    private Consumer consumer = null;
+    private Thread thread = null;
+    private int frameCount = 1;
+    private static final int frameIndex = 4;
+    private static final int getFrameCountIndex = 460900;
+    private final byte[] byteLargeArray = new byte[470000];
+    private final Context appContext;
+    private IRemoteSharedMemory remoteMemory;
+    private ISharedMemory sharedMemory;
+    private RecieveFrameCallback frameCallback;
+    private boolean closeIpcMemory = false;
+
+    public Ipc(Context context, RecieveFrameCallback frameCallback) {
+        this.frameCallback = frameCallback;
+        this.appContext = context;
+    }
+
+    public Ipc(Context context) {
+        this.appContext = context;
+    }
+
+    public interface RecieveFrameCallback {
+        void pushFrame(byte[] frame);
+    }
+
+    private class Consumer implements Runnable {
+        @Override
+        public void run() {
+            String producerAppId = "com.example.androidclient";
+            byte[] dataBytes;
+            int globalFrameCount = -1 ;
+            int consumerFrameCount;
+
+            while (true) {
+                remoteMemory = RemoteMemoryAdapter.getDefaultAdapter().getSharedMemory(appContext, producerAppId, regionName);
+                if (remoteMemory == null) {
+                    Log.d(TAG, "Failed to access shared memory");
+                } else {
+                    Log.d(TAG, "Shared memory present");
+                    dataBytes = new byte[remoteMemory.getSize()];
+                    break;
+                }
+            }
+
+            byte[] sample = new byte[4];
+
+            while (!closeIpcMemory) {
+                try {
+                    remoteMemory.readBytes(dataBytes, 0, 0, dataBytes.length);
+                    System.arraycopy(dataBytes, getFrameCountIndex, sample, 0, frameIndex);
+                    consumerFrameCount = ByteBuffer.wrap(sample).getInt();
+
+                    if (globalFrameCount != consumerFrameCount) {
+                        Log.d(TAG, "New frame received : " + globalFrameCount + "&" + consumerFrameCount);
+                        frameCallback.pushFrame(dataBytes);
+                        globalFrameCount = consumerFrameCount;
+                        Thread.sleep(20);
+                    } else {
+                        Log.d(TAG, "Repeated frame received : " + globalFrameCount + "&" + consumerFrameCount);
+                    }
+                } catch (Exception e) {
+                    Log.e(TAG, "Exception thrown while reading shared memory");
+                }
+            }
+        }
+    }
+
+    public void initProducer() {
+        try {
+            sharedMemory = SharedMemoryProducer.getInstance().allocate(regionName, sizeInBytes);
+            if (sharedMemory == null) {
+                Log.d(TAG, "No memory is allocated");
+            } else {
+                Log.d(TAG, "Memory allocated : " + sharedMemory.length());
+            }
+        } catch (IOException ex) {
+            Log.e(TAG,"Exception thrown while shared memory creation");
+        }
+    }
+
+    public void writeToMemory(byte[] byteArray) {
+        try {
+            System.arraycopy(byteArray, 0, byteLargeArray, 0, byteArray.length);
+            ByteBuffer buffer = ByteBuffer.allocate(4);
+            buffer.putInt(frameCount);
+            System.arraycopy(buffer.array(), 0, byteLargeArray, getFrameCountIndex, 4);
+            sharedMemory.writeBytes(byteLargeArray, 0, 0, byteLargeArray.length);
+            frameCount++;
+        } catch (Exception e) {
+            Log.e(TAG, "Exception thrown while writing to shared memory");
+        }
+    }
+
+    public void close() {
+        try {
+            closeIpcMemory = true;
+            Log.d(TAG, "Closing IPC memory : " + closeIpcMemory);
+            if (sharedMemory != null) {
+                sharedMemory.close();
+            }
+            if (remoteMemory != null) {
+                remoteMemory.close();
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to close shared memory resources");
+        }
+    }
+
+    public void initConsumer() {
+        consumer = new Consumer();
+        thread = new Thread(consumer);
+        thread.start();
+    }
+}
index 26408cb..c528e38 100644 (file)
@@ -4,3 +4,4 @@ include ':android:mosquitto'
 include ':android:modules:tcp'
 include ':android:modules:webrtc'
 include ':android:aitt-native'
+include ':android:modules:ipc'