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'
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;
}
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
}
@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);
}
}
-
@Test
public void testSubscribeWebRTC_P() {
try {
}
@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);
--- /dev/null
+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'
+}
--- /dev/null
+# 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
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.samsung.android.modules.ipc">
+
+</manifest>
--- /dev/null
+/*
+ * 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();
+ }
+}
include ':android:modules:tcp'
include ':android:modules:webrtc'
include ':android:aitt-native'
+include ':android:modules:ipc'