diff --git a/Android.bp b/Android.bp
new file mode 100755
index 0000000..9157286
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+// Copyright (C) 2021 Intel Corporation.
+//
+// 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.
+//
+
+// This makefile shows how to build a shared library and an activity that
+// bundles the shared library and calls it using JNI.
+
+android_app {
+ name: "ClipboardAgent",
+ srcs: ["**/*.java"],
+ // JNI library built from C++ source code
+ jni_libs: ["libVsockMsgDispatch", "libVsocketClientImpl"],
+ optimize: {
+ enabled: false,
+ },
+ sdk_version: "system_current",
+ dex_preopt: {
+ enabled: false,
+ },
+ privileged: true,
+ // To match the signature
+ certificate: "platform",
+}
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100755
index 0000000..4d7c85a
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jni/Android.bp b/jni/Android.bp
new file mode 100755
index 0000000..2440d09
--- /dev/null
+++ b/jni/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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.
+//
+
+// This makefile supplies the rules for building a library of JNI code for
+// use by our example of how to bundle a shared library with an APK.
+
+cc_library_shared {
+ name: "libVsocketClientImpl",
+ // All of the source files that we will compile.
+ srcs: ["VsockClientImpl.cpp"],
+ // All of the shared libraries we link against.
+ // liblog is used to print trace log in C plus plus source code.
+ shared_libs: ["liblog"],
+ // No static libraries.
+ header_libs: ["jni_headers"],
+ static_libs: [],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ // We cannot use stl:"none" here to link libc++ dynamically because
+ // it caused "'iostream' file not found" build issue.
+ stl: "c++_static",
+ sdk_version: "current",
+}
+
+cc_library_shared {
+ name: "libVsockMsgDispatch",
+ srcs: [
+ "VsockMsgDispatcher.cpp",
+ "DispatchHelper.cpp",
+ ],
+ header_libs: ["jni_headers"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-unused-label",
+ ],
+ shared_libs: ["libbase", "liblog"],
+ sdk_version: "current",
+}
+
diff --git a/jni/DispatchHelper.cpp b/jni/DispatchHelper.cpp
new file mode 100755
index 0000000..f2aba1a
--- /dev/null
+++ b/jni/DispatchHelper.cpp
@@ -0,0 +1,139 @@
+#include "VsockMsgDispatcher.h"
+#include "DispatchHelper.h"
+#include
+#include
+
+using namespace vsock;
+std::map< std::string, std::vector > comp_msg_map {
+ {"ClipboardComponent", {MSG_TYPE_CLIPBOARD}},
+ {"AppstatusComponent", {MSG_TYPE_APPSTATUS}}
+};
+std::map< std::string, jclass > jclass_map;
+
+static JavaVM* gVm = nullptr;
+JNIEnv* getenv() {
+ JNIEnv *env = nullptr;
+ int getEnvStat = gVm->GetEnv((void **)&env, JNI_VERSION_1_6);
+ if (getEnvStat == JNI_EDETACHED) {
+ if (gVm->AttachCurrentThread(&env, NULL) != 0) {
+ LOGIT("Failed to attach");
+ }
+ } else if (getEnvStat == JNI_OK) {
+ //
+ } else if (getEnvStat == JNI_EVERSION) {
+ LOGIT("GetEnv: version not supported");
+ }
+ return env;
+}
+
+class JavaComponent:public Component {
+ public:
+ std::string java_class_name;
+ std::vector msg_list;
+ JavaComponent(std::string name) {
+ std::map< std::string, std::vector >::iterator it;
+ java_class_name = name;
+ it = comp_msg_map.find(name);
+ if (it != comp_msg_map.end()) {
+ msg_list = it->second;
+ }
+ }
+ virtual ~JavaComponent(){
+ JNIEnv* env = getenv();
+ jclass reqClass = GetJClass();
+ jobject singleInstance = GetSingletonInstance(reqClass);
+ jmethodID reqMethod = env->GetMethodID(reqClass, "stop", "()V");
+ env->CallVoidMethod(singleInstance, reqMethod);
+
+ }
+ virtual void init() {
+ JNIEnv* env = getenv();
+ jclass reqClass = GetJClass();
+ jobject singleInstance = GetSingletonInstance(reqClass);
+ jmethodID reqMethod = env->GetMethodID(reqClass, "init", "()V");
+ env->CallVoidMethod(singleInstance, reqMethod);
+ }
+
+ virtual void ProcessMsg(Message& msg, uint64_t hndl) {
+ //LOGIT("Process msg - %s\n", msg.payload);
+ JNIEnv *env = getenv();
+ jclass reqClass = GetJClass();
+ jobject singleInstance = GetSingletonInstance(reqClass);
+ jmethodID reqMethod = env->GetMethodID(reqClass, "processMsg", "(Ljava/lang/String;J)V");
+ jstring str = env->NewStringUTF(msg.payload);
+ env->CallVoidMethod(singleInstance, reqMethod, str, static_cast(hndl));
+ }
+ private:
+ jclass GetJClass() {
+ std::map< std::string, jclass >::iterator it;
+ jclass reqClass = nullptr;
+ it = jclass_map.find(java_class_name.c_str());
+ if (it != jclass_map.end()) {
+ reqClass = it->second;
+ }
+ return reqClass;
+ }
+
+ jobject GetSingletonInstance(jclass reqClass) {
+ JNIEnv *env = getenv();
+ std::string sig = "()Lcom/intel/clipboardagent/"+java_class_name+";";
+ jmethodID instMethod = env->GetStaticMethodID(reqClass, "getInstance", sig.c_str());
+ jobject singleInstance = env->CallStaticObjectMethod(reqClass, instMethod);
+ return singleInstance;
+ }
+};
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) {
+ return JNI_ERR;
+ }
+ jclass tmp = nullptr;
+ tmp = env->FindClass("com/intel/clipboardagent/ClipboardComponent");
+ if (tmp!= nullptr) {
+ jclass_map.insert({"ClipboardComponent", (jclass)env->NewGlobalRef(tmp)});
+ }
+ tmp = env->FindClass("com/intel/clipboardagent/AppstatusComponent");
+ if (tmp!= nullptr) {
+ jclass_map.insert({"AppstatusComponent", (jclass)env->NewGlobalRef(tmp)});
+ }
+ return JNI_VERSION_1_6;
+}
+
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_registerComponent(JNIEnv *env, jobject thisObject, jstring className) {
+ MsgDispatcher* dispatcher = MsgDispatcher::getInstance();
+ env->GetJavaVM(&gVm);
+ std::string name = env->GetStringUTFChars(className, 0);
+ JavaComponent* javaComponent = new JavaComponent(name);
+ dispatcher->RegisterComponent(javaComponent->msg_list, javaComponent);
+}
+
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_sendMsg(JNIEnv *env, jobject thisObject, jstring className, jstring msg, jlong handle) {
+ MsgDispatcher* dispatcher = MsgDispatcher::getInstance();
+ std::string payload = env->GetStringUTFChars(msg, 0);
+ int size = env->GetStringUTFLength(msg);
+ std::vector msg_list;
+ std::map< std::string, std::vector >::iterator it;
+ std::string name = env->GetStringUTFChars(className, 0);
+ it = comp_msg_map.find(name);
+ if (it != comp_msg_map.end()) {
+ msg_list = it->second;
+ }
+ if (handle == 0) {
+ handle = dispatcher->GetHandleForMsgType(msg_list.front());
+ }
+ dispatcher->SendMsg(handle, msg_list.front(), payload.c_str(), size);
+}
+
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_start(JNIEnv *env, jobject thisObject) {
+ MsgDispatcher* dispatcher = MsgDispatcher::getInstance();
+ dispatcher->Start();
+}
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_stop(JNIEnv *env, jobject thisObject) {
+ MsgDispatcher* dispatcher = MsgDispatcher::getInstance();
+ dispatcher->Stop();
+}
diff --git a/jni/DispatchHelper.h b/jni/DispatchHelper.h
new file mode 100755
index 0000000..3b67be4
--- /dev/null
+++ b/jni/DispatchHelper.h
@@ -0,0 +1,45 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+/* Header for class com_intel_clipboardagent_DispatchHelper */
+
+#ifndef _Included_com_intel_clipboardagent_DispatchHelper
+#define _Included_com_intel_clipboardagent_DispatchHelper
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_intel_clipboardagent_DispatchHelper
+ * Method: registerComponent
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_registerComponent
+ (JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_intel_clipboardagent_DispatchHelper
+ * Method: sendMsg
+ * Signature: (Ljava/lang/String;Ljava/lang/String;J)V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_sendMsg
+ (JNIEnv *, jobject, jstring, jstring, jlong);
+
+/*
+ * Class: com_intel_clipboardagent_DispatchHelper
+ * Method: start
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_start
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_intel_clipboardagent_DispatchHelper
+ * Method: stop
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_stop
+ (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/jni/VsockClientImpl.cpp b/jni/VsockClientImpl.cpp
new file mode 100755
index 0000000..11c0ce7
--- /dev/null
+++ b/jni/VsockClientImpl.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021 Intel Corporation
+ *
+ * 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.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#define JVM_IO_INTR (-2)
+#ifndef bufferFER_LEN
+#define bufferFER_LEN 65536
+#endif
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define LOG_TAG "vsock"
+#include
+
+#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+#define DATA_SIZE_LENGTH 4
+#define MAX_CHUNK_LENGTH 8192
+#define MAX_DATA_LENGTH 512*1024
+
+static const char *vsockClientImplPath = "com/intel/clipboardagent/VsockClientImpl";
+static const char *vsockAddressPath = "com/intel/clipboardagent/VsockAddress";
+static const char *javaConnException = "java/net/ConnectException";
+static const char *javaIntrIOException = "java/io/InterruptedIOException";
+static const char *sunConnResetException = "sun/net/ConnectionResetException";
+
+int read_from_vsock(JNIEnv* env, int sockfd, uint8_t* bytes, uint32_t size) {
+ int nread = (jint) recv(sockfd, bytes, size, 0);
+ if (nread <= 0) {
+ if (nread < 0 && errno != ENOTCONN) {
+ env->ThrowNew(env->FindClass(javaConnException),
+ ("vsock read: Read failed with error no: " + std::to_string(errno)).c_str());
+ } else {
+ env->ThrowNew(env->FindClass(javaConnException),
+ ("vsock read: Connection is closed by peer."));
+ }
+ return nread;
+ }
+ return nread;
+}
+
+bool write_to_vsock(JNIEnv* env, int sockfd, uint8_t* bytes, uint32_t size) {
+ int n = (int)send(sockfd, bytes, size, 0);
+ if (n == JVM_IO_INTR) {
+ env->ThrowNew(env->FindClass(javaIntrIOException), 0);
+ } else if (n <= 0){
+ if (errno == ECONNRESET) {
+ env->ThrowNew(env->FindClass(sunConnResetException), "vsock write: Connection reset");
+ } else {
+ env->ThrowNew(env->FindClass(javaConnException), "vsock write: Write failed");
+ }
+ return false;
+ } else if (n != size) {
+ env->ThrowNew(env->FindClass(javaConnException), "vsock write: Failed to write complete msg");
+ return false;
+ }
+ return true;
+}
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_socketCreate
+ (JNIEnv *env, jobject thisObject) {
+ int sock = socket(AF_VSOCK, SOCK_STREAM, 0);
+
+ jclass implement = env->FindClass(vsockClientImplPath);
+ jfieldID fdField = env->GetFieldID(implement, "fd", "I");
+ env->SetIntField(thisObject, fdField, sock);
+}
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_connect
+ (JNIEnv *env, jobject thisObject, jobject addr) {
+ jclass implement = env->FindClass(vsockClientImplPath);
+ jfieldID fdField = env->GetFieldID(implement, "fd", "I");
+ int sock = (int)env->GetIntField(thisObject, fdField);
+
+ if (sock == -1) {
+ env->ThrowNew(env->FindClass(javaConnException), "vsock: Socket is closed");
+ return;
+ }
+
+ jclass vsockAddress = env->FindClass(vsockAddressPath);
+ jfieldID cidField = env->GetFieldID(vsockAddress, "cid", "I");
+ jfieldID portField = env->GetFieldID(vsockAddress, "port", "I");
+
+
+ struct sockaddr_vm sock_addr;
+ std::memset(&sock_addr, 0, sizeof(struct sockaddr_vm));
+ sock_addr.svm_family = AF_VSOCK;
+ sock_addr.svm_port = (int)env->GetIntField(addr, portField);
+ sock_addr.svm_cid = (int)env->GetIntField(addr, cidField);
+ int status = connect(sock, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_vm));
+ if (status != 0) {
+ if (errno == EALREADY || errno == EISCONN ) {
+ env->ThrowNew(env->FindClass(javaConnException),
+ ("Connect failed: " + std::to_string(errno)).c_str());
+ }
+ }
+}
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_close
+ (JNIEnv *env, jobject thisObject) {
+ jclass implement = env->FindClass(vsockClientImplPath);
+ jfieldID fdField = env->GetFieldID(implement, "fd", "I");
+ int s = (int)env->GetIntField(thisObject, fdField);
+
+ if (s == -1) {
+ env->ThrowNew(env->FindClass(javaConnException), "vsock close: Socket is already closed.");
+ return;
+ }
+
+ int status = close(s);
+
+ env->SetIntField(thisObject, fdField, -1);
+ if (status != 0) {
+ env->ThrowNew(env->FindClass(javaConnException),
+ ("Close failed: " + std::to_string(errno)).c_str());
+ }
+}
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_write
+ (JNIEnv * env, jobject thisObject, jbyteArray b, jint offset, jint len) {
+ jclass implement = env->FindClass(vsockClientImplPath);
+ jfieldID fdField = env->GetFieldID(implement, "fd", "I");
+ int s = (int)env->GetIntField(thisObject, fdField);
+
+ if (s == -1) {
+ env->ThrowNew(env->FindClass(javaConnException), "vsock write: Socket is already closed.");
+ return;
+ }
+
+
+ // Send the actual data
+ char buffer[MAX_CHUNK_LENGTH];
+ while(len > 0) {
+ int chunkLen = min(MAX_CHUNK_LENGTH, len);
+
+ env->GetByteArrayRegion(b, offset, chunkLen, (jbyte *)buffer);
+ if(!write_to_vsock(env, s, (uint8_t*)buffer, chunkLen)) {
+ return;
+ }
+ len -= chunkLen;
+ offset += chunkLen;
+ }
+ return;
+}
+
+JNIEXPORT jint JNICALL Java_com_intel_clipboardagent_VsockClientImpl_read
+ (JNIEnv * env, jobject thisObject, jbyteArray b, jint off, jint len) {
+ jclass implement = env->FindClass(vsockClientImplPath);
+ jfieldID fdField = env->GetFieldID(implement, "fd", "I");
+ int s = (int)env->GetIntField(thisObject, fdField);
+
+ if (s == -1) {
+ env->ThrowNew(env->FindClass(javaConnException), "vsock read: Socket is already closed");
+ return -1;
+ }
+ uint8_t buffer[MAX_CHUNK_LENGTH];
+ uint32_t remaining = len;
+ while (remaining > 0) {
+ int nread = 0;
+ uint32_t chunkLen = min(remaining, MAX_CHUNK_LENGTH);
+ if ((nread = read_from_vsock(env, s, buffer, chunkLen)) <= 0) {
+ ALOGE("vsock read: Failed to read complete msg");
+ }
+ env->SetByteArrayRegion(b, off, nread, (jbyte *)buffer);
+ remaining -= nread;
+ off += nread;
+ }
+
+ return (jint)len;
+}
+
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_writeInt
+ (JNIEnv *env, jobject thisObject, jint length) {
+ jclass implement = env->FindClass(vsockClientImplPath);
+ jfieldID fdField = env->GetFieldID(implement, "fd", "I");
+ int s = (int)env->GetIntField(thisObject, fdField);
+
+ if (s == -1) {
+ env->ThrowNew(env->FindClass(javaConnException), "vsock read: Socket is already closed");
+ return;
+ }
+
+ {
+ uint32_t size = length;
+ size = htonl(size);
+ uint8_t* buffer = (uint8_t*)&size;
+ if (!write_to_vsock(env, s, buffer, DATA_SIZE_LENGTH)) {
+ return;
+ }
+ }
+}
+
+
+JNIEXPORT jint JNICALL Java_com_intel_clipboardagent_VsockClientImpl_readInt
+ (JNIEnv *env, jobject thisObject) {
+ jclass implement = env->FindClass(vsockClientImplPath);
+ jfieldID fdField = env->GetFieldID(implement, "fd", "I");
+ int s = (int)env->GetIntField(thisObject, fdField);
+
+ if (s == -1) {
+ env->ThrowNew(env->FindClass(javaConnException), "vsock read: Socket is already closed");
+ return -1;
+ }
+
+ uint32_t size = 0;
+ {
+ uint8_t buffer[DATA_SIZE_LENGTH + 1] = {0};
+ if (read_from_vsock(env, s, buffer, DATA_SIZE_LENGTH) != DATA_SIZE_LENGTH) {
+ ALOGE("vsock read: Failed to read data size.");
+ return -1;
+ }
+ size = *(uint32_t*)buffer;
+ size = ntohl(size);
+ }
+ return (jint)size;
+}
diff --git a/jni/VsockClientImpl.h b/jni/VsockClientImpl.h
new file mode 100755
index 0000000..efab088
--- /dev/null
+++ b/jni/VsockClientImpl.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 Intel Corporation
+ *
+ * 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.
+ */
+
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+/* Header for class com_intel_clipboardagent_VsockClientImpl */
+
+#ifndef _Included_com_intel_clipboardagent_VsockClientImpl
+#define _Included_com_intel_clipboardagent_VsockClientImpl
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_intel_clipboardagent_VsockClientImpl
+ * Method: socketCreate
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_socketCreate
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_intel_clipboardagent_VsockClientImpl
+ * Method: connect
+ * Signature: (Lcom/intel/clipboardagent/VsockAddress;)V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_connect
+ (JNIEnv *, jobject, jobject);
+
+/*
+ * Class: com_intel_clipboardagent_VsockClientImpl
+ * Method: close
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_close
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_intel_clipboardagent_VsockClientImpl
+ * Method: write
+ * Signature: ([BII)V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_write
+ (JNIEnv *, jobject, jbyteArray, jint, jint);
+
+/*
+ * Class: com_intel_clipboardagent_VsockClientImpl
+ * Method: read
+ * Signature: ([BII)I
+ */
+JNIEXPORT jint JNICALL Java_com_intel_clipboardagent_VsockClientImpl_read
+ (JNIEnv *, jobject, jbyteArray, jint, jint);
+
+/*
+ * Class: com_intel_clipboardagent_VsockClientImpl
+ * Method: writeInt
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_writeInt
+ (JNIEnv *, jobject, jint);
+
+/*
+ * Class: com_intel_clipboardagent_VsockClientImpl
+ * Method: readInt
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_com_intel_clipboardagent_VsockClientImpl_readInt
+ (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/jni/VsockMsgDispatcher.cpp b/jni/VsockMsgDispatcher.cpp
new file mode 100755
index 0000000..daee35f
--- /dev/null
+++ b/jni/VsockMsgDispatcher.cpp
@@ -0,0 +1,352 @@
+#include "VsockMsgDispatcher.h"
+#include
+#include
+#include
+#include
+#include
+
+namespace vsock {
+#ifdef MSG_SERVER
+static const bool gServer = true;
+#else
+static const bool gServer = false;
+#endif
+
+#define VMADDR_CID_HOST 2
+#define VSOCK_PORT 77788
+#define CHUNK_SIZE 8192
+
+MsgDispatcher *MsgDispatcher::instance = nullptr;
+MsgDispatcher::MsgDispatcher() {
+ std::vector empty_vector;
+ fd_ = socket(AF_VSOCK, SOCK_STREAM, 0);
+ if (fd_ <= 0) {
+ ERR("Socket Create:");
+ }
+ nclients_ = 0;
+ m_bStop = false;
+ msg_type_map[MSG_TYPE_INVALID] = empty_vector;
+}
+
+bool MsgDispatcher::Start() {
+ struct sockaddr_vm sock_addr;
+ memset(&sock_addr, 0, sizeof(struct sockaddr_vm));
+ sock_addr.svm_family = AF_VSOCK;
+ sock_addr.svm_port = VSOCK_PORT;
+ sock_addr.svm_cid = VMADDR_CID_HOST;
+
+ if (gServer) {
+ int ret;
+ if ((ret = bind(fd_, (struct sockaddr*)&sock_addr, sizeof(sock_addr))) < 0) {
+ ERR("Socket bind:");
+ return ret;
+ }
+
+ //Bind succesful, so start accepting connections on a separate thread
+ Channel* channel = new Channel(this, fd_, 0);
+ channels_.emplace_back(channel);
+ LOGIT("Start Listen thread\n");
+ CHECK_PTHREAD_CALL(pthread_create, (&t_main_, nullptr, &Listen, channel), "Failed to create server listener thread");
+
+ } else {
+ bool bConnected = true;
+ bConnected = connect(fd_, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_vm));
+ while(!bConnected) {
+ LOGIT("Failed to connect to server. Waiting to try again...");
+ sleep(1);
+ ERR("Socket connect:");
+ bConnected = connect(fd_, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_vm));
+ }
+
+ //Connected to server, start msg xchange
+ Channel* channel = new Channel(this, fd_, 0);
+ channels_.emplace_back(channel);
+ LOGIT("Start Run thread\n");
+ CHECK_PTHREAD_CALL(pthread_create, (&t_main_, nullptr, &Run, channel), "Failed to create leader thread");
+ }
+
+ //Create workers
+ for (int i = 0; i < WORKER_POOL; ++i) {
+ LOGIT("Start worker thread-%d\n", i);
+ CHECK_PTHREAD_CALL(pthread_create, (&t_workers_[i], nullptr, &WorkerStart, this), "Failed to create worker thread");
+ }
+
+ return true;
+}
+
+void* MsgDispatcher::Listen(void* arg) {
+ Channel* channel = reinterpret_cast(arg);
+ MsgDispatcher* dispatcher = channel->dispatcher_;
+
+ struct sockaddr_vm sock_addr;
+ memset(&sock_addr, 0, sizeof(struct sockaddr_vm));
+ sock_addr.svm_family = AF_VSOCK;
+ sock_addr.svm_port = VSOCK_PORT;
+ sock_addr.svm_cid = VMADDR_CID_HOST;
+ size_t sock_addr_len = sizeof(struct sockaddr_vm);
+
+ if (!gServer) {
+ return (void*)-1;
+ }
+
+ int client_fd = -1;
+
+ while (!dispatcher->m_bStop) {
+ if (listen(channel->fd_, 2) < 0) {
+ ERR("Socket listen:");
+ }
+ //TODO: Make accept non-blocking
+ if((client_fd = accept(channel->fd_, (struct sockaddr*)&sock_addr, (socklen_t*)&sock_addr_len)) < 0) {
+ // if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // sleep(1);
+ // } else {
+ ERR("Socket accept:");
+ // }
+ return (void *)-1;
+ }
+ if (dispatcher->t_clients_.size() == MAX_CLIENT_CONNECTIONS) {
+ close(client_fd);
+ LOGIT("Max connections exceeded. Closing client connection.");
+ continue;
+ }
+ // start msg xchange
+ pthread_t thread;
+ dispatcher->nclients_++;
+ Channel* channel = new Channel(dispatcher, client_fd, dispatcher->nclients_);
+ dispatcher->channels_.emplace_back(channel);
+ CHECK_PTHREAD_CALL(pthread_create, (&thread, nullptr, &Run, channel), "Failed to create client handler thread");
+ dispatcher->t_clients_[dispatcher->nclients_] = thread;
+ }
+ pthread_exit(nullptr);
+ return (void*)0;
+}
+
+void* MsgDispatcher::Run(void* arg) {
+ Channel* channel = reinterpret_cast(arg);
+ MsgDispatcher* dispatcher = channel->dispatcher_;
+ if (gServer) {
+ SendCapabilities(channel);
+ }
+ dispatcher->InitComponentsIfReq();
+ MessageHeader hdr;
+ //TODO: Make recv non-blocking
+ do {
+ int nread = (int) recv(channel->fd_, &hdr, sizeof(MessageHeader), 0);
+ if (nread != sizeof(MessageHeader)) {
+ LOGIT("Failed to recv header:%d", nread);
+ ERR("Socket recv header:");
+ break;
+ }
+ int remaining = ntohl(hdr._size);
+ //LOGIT("Reading payload - %d", remaining);
+ // allocate a buffer to read the payload
+ char* payload = new char[remaining + 1];
+ payload[remaining] = 0;
+ Message msg = {hdr, payload};
+ while (nread > 0 && remaining != 0) {
+ nread = (int) recv(channel->fd_, payload, (remaining > CHUNK_SIZE) ? CHUNK_SIZE : remaining, 0);
+ payload += nread;
+ remaining -= nread;
+ }
+
+ if (nread < 0) {
+ LOGIT("Failed to recv... will reconnect...\n");
+ ERR("Socket recv:");
+ break;
+ }
+ //LOGIT("Recvd payload - %s\n", payload);
+ switch(hdr._type) {
+ MSG_TYPE_CAPABILITIES: SaveCapabilities(channel);
+ if (!gServer) {
+ SendCapabilities(channel);
+ }
+ break;
+ default: if (ValidateMsg(channel, hdr)) {
+ //LOGIT("Enqueue message");
+ Enqueue(channel, msg);
+ } else {
+ // Msg dropped
+ }
+ break;
+ }
+
+ } while (!dispatcher->m_bStop);
+
+ // 0 is reserved for the main channel
+ if (channel->id_ > 0) {
+ //Mark the end of this thread
+ dispatcher->t_clients_.erase(channel->id_);
+ //Close the channel
+ std::lock_guard lock(dispatcher->q_lock_);
+ for (auto it = dispatcher->channels_.begin(); it != dispatcher->channels_.end(); ++it) {
+ if (*it == channel) {
+ dispatcher->channels_.erase(it);
+ delete channel;
+ break;
+ }
+ }
+ } else if (!dispatcher->m_bStop) {
+ //We may want to reconnect
+ close(channel->fd_);
+ dispatcher->fd_ = socket(AF_VSOCK, SOCK_STREAM, 0);
+ sleep(1);
+ bool bConnected = true;
+ struct sockaddr_vm sock_addr;
+ memset(&sock_addr, 0, sizeof(struct sockaddr_vm));
+ sock_addr.svm_family = AF_VSOCK;
+ sock_addr.svm_port = VSOCK_PORT;
+ sock_addr.svm_cid = VMADDR_CID_HOST;
+ bConnected = connect(dispatcher->fd_, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_vm));
+ while(!bConnected) {
+ LOGIT("Failed to connect to server. Waiting to try again...");
+ ERR("Socket connect:");
+ sleep(1);
+ bConnected = connect(dispatcher->fd_, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_vm));
+ }
+ channel->fd_ = dispatcher->fd_;
+ return Run(arg);
+ }
+ pthread_exit(nullptr);
+ return (void*)0;
+}
+
+void* MsgDispatcher::WorkerStart(void* arg) {
+ MsgDispatcher* dispatcher = reinterpret_cast(arg);
+ while (!dispatcher->m_bStop) {
+ Message msg;
+ Channel* channel = nullptr;
+ bool bMsg = false;
+ {
+ std::lock_guard lock(dispatcher->q_lock_);
+ for (auto& chnl : dispatcher->channels_) {
+ if (chnl->msg_queue.size() > 0) {
+ msg = chnl->msg_queue.front();
+ chnl->msg_queue.pop();
+ channel = chnl;
+ bMsg = true;
+ }
+ }
+ }
+ if (bMsg) {
+ for (auto& component : dispatcher->GetComponentsForMsgType(msg.header._type)) {
+ //LOGIT("Worker Dispatch msg - %s\n", msg.payload);
+ component->ProcessMsg(msg, reinterpret_cast(channel));
+ }
+ delete msg.payload;
+ msg.payload = nullptr;
+ channel = nullptr;
+ } else {
+ sleep(1);
+ }
+ }
+ pthread_exit(nullptr);
+ return (void*)0;
+}
+
+void MsgDispatcher::Enqueue(Channel* channel, Message& msg) {
+ std::queue& mq = channel->msg_queue;
+ // No need of locks, only a single thread does the queing
+ while (mq.size() >= Q_SIZE) {
+ // Sleep for the queue to free up
+ sleep(1);
+ }
+
+ mq.push(msg);
+}
+
+void MsgDispatcher::Stop() {
+ m_bStop = true;
+ for (auto it = t_clients_.begin(); it != t_clients_.end(); ++it) {
+ CHECK_PTHREAD_CALL(pthread_join, (it->second, nullptr), "Failed to join on client thread");
+ }
+ for (int i = 0; i < WORKER_POOL; ++i) {
+ CHECK_PTHREAD_CALL(pthread_join, (t_workers_[i], nullptr), "Failed to join on worker thread");
+ }
+ CHECK_PTHREAD_CALL(pthread_join, (t_main_, nullptr), "Failed to join on the main thread");
+
+ //Clear all the channels
+ std::lock_guard lock(q_lock_);
+ for(auto& channel : channels_) {
+ delete channel;
+ }
+ channels_.clear();
+ for(auto& comp : components_) {
+ delete comp;
+ }
+ components_.clear();
+
+}
+
+bool MsgDispatcher::SendMsg(uint64_t hndl, MSG_TYPE msg_type, const char* buffer, int size) {
+ MessageHeader header;
+ Channel* channel = reinterpret_cast(hndl);
+ bool bValidChannel = false;
+ for (auto it = channels_.begin(); it != channels_.end(); ++it) {
+ if (*it == channel) {
+ bValidChannel = true;
+ break;
+ }
+ }
+ if (!bValidChannel) {
+ return false;
+ }
+
+ header._ver = MSG_VERSION;
+ header._type = msg_type;
+ header._id = 0;
+ header._size = htonl(size);
+ int nsent = (int)send(channel->fd_, &header, sizeof(MessageHeader), 0);
+ if (nsent != sizeof(MessageHeader)) {
+ LOGIT("Failed to send header:%d", nsent);
+ ERR("Socket send hdr:");
+ return false;
+ }
+ while (nsent > 0 && size > 0) {
+ nsent = (int)send(channel->fd_, buffer, (size > CHUNK_SIZE) ? CHUNK_SIZE : size, 0);
+ size -= nsent;
+ buffer += nsent;
+ }
+ if (nsent < 0) {
+ ERR("Socket send:");
+ }
+ return (nsent >= 0);
+}
+
+std::vector& MsgDispatcher::GetComponentsForMsgType(MSG_TYPE msg_type) {
+ auto search = msg_type_map.find(msg_type);
+ if (search != msg_type_map.end()) {
+ return search->second;
+ } else {
+ return msg_type_map.find(MSG_TYPE_INVALID)->second;
+ }
+}
+
+void MsgDispatcher::RegisterComponent(std::vector& msg_list, Component* component) {
+ components_.emplace_back(component);
+ for (auto type : msg_list) {
+ if (type == MSG_TYPE_INVALID) {
+ continue;
+ }
+ auto search = msg_type_map.find(type);
+ if (search != msg_type_map.end()) {
+ search->second.emplace_back(component);
+ } else {
+ std::vector comp_vector;
+ comp_vector.emplace_back(component);
+ msg_type_map[type] = comp_vector;
+ }
+ }
+}
+
+uint64_t MsgDispatcher::GetHandleForMsgType(MSG_TYPE msg_type) {
+ return reinterpret_cast(channels_.back());
+}
+
+void MsgDispatcher::InitComponentsIfReq() {
+ for (auto component : components_) {
+ component->setDispatcher(this);
+ component->Initialize();
+ }
+}
+
+}
diff --git a/jni/VsockMsgDispatcher.h b/jni/VsockMsgDispatcher.h
new file mode 100755
index 0000000..4b9071f
--- /dev/null
+++ b/jni/VsockMsgDispatcher.h
@@ -0,0 +1,173 @@
+#ifndef VSOCK_MSG_DISPATCHER_H
+#define VSOCK_MSG_DISPATCHER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include