From: Praveen Naik S Date: Wed, 19 Oct 2022 13:41:06 +0000 (+0530) Subject: Add IPC protocol support to aitt android X-Git-Tag: accepted/tizen/unified/20221115.172906~12 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=027eff5397a887e6ad295a9be7f671a75d883450;p=platform%2Fcore%2Fml%2Faitt.git Add IPC protocol support to aitt android --- diff --git a/android/aitt/build.gradle b/android/aitt/build.gradle index 348c0d7..de6e262 100644 --- a/android/aitt/build.gradle +++ b/android/aitt/build.gradle @@ -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' diff --git a/android/aitt/src/main/java/com/samsung/android/aitt/Definitions.java b/android/aitt/src/main/java/com/samsung/android/aitt/Definitions.java index a1de3f6..bf4be83 100644 --- a/android/aitt/src/main/java/com/samsung/android/aitt/Definitions.java +++ b/android/aitt/src/main/java/com/samsung/android/aitt/Definitions.java @@ -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; } diff --git a/android/aitt/src/main/java/com/samsung/android/aitt/IpcHandler.java b/android/aitt/src/main/java/com/samsung/android/aitt/IpcHandler.java index 13afc4e..58d6dc2 100644 --- a/android/aitt/src/main/java/com/samsung/android/aitt/IpcHandler.java +++ b/android/aitt/src/main/java/com/samsung/android/aitt/IpcHandler.java @@ -16,43 +16,96 @@ 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 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 0e38924..0871a08 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 @@ -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 index 0000000..796b96d --- /dev/null +++ b/android/modules/ipc/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android/modules/ipc/build.gradle b/android/modules/ipc/build.gradle new file mode 100644 index 0000000..ef269ac --- /dev/null +++ b/android/modules/ipc/build.gradle @@ -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 index 0000000..f1b4245 --- /dev/null +++ b/android/modules/ipc/proguard-rules.pro @@ -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 index 0000000..7e01407 --- /dev/null +++ b/android/modules/ipc/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + 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 index 0000000..ae13ee5 --- /dev/null +++ b/android/modules/ipc/src/main/java/com/samsung/android/modules/ipc/Ipc.java @@ -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(); + } +} diff --git a/settings.gradle b/settings.gradle index 26408cb..c528e38 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,3 +4,4 @@ include ':android:mosquitto' include ':android:modules:tcp' include ':android:modules:webrtc' include ':android:aitt-native' +include ':android:modules:ipc'