diff --git a/penguinpeak/Android.mk b/penguinpeak/Android.mk new file mode 100644 index 0000000..fcc6a9c --- /dev/null +++ b/penguinpeak/Android.mk @@ -0,0 +1,18 @@ +# +# 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. +# +LOCAL_PATH := $(my-dir) +include $(call all-subdir-makefiles, $(LOCAL_PATH)) diff --git a/penguinpeak/ClipboardAgent/Android.bp b/penguinpeak/ClipboardAgent/Android.bp new file mode 100644 index 0000000..9157286 --- /dev/null +++ b/penguinpeak/ClipboardAgent/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/penguinpeak/ClipboardAgent/AndroidManifest.xml b/penguinpeak/ClipboardAgent/AndroidManifest.xml new file mode 100644 index 0000000..3764a52 --- /dev/null +++ b/penguinpeak/ClipboardAgent/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/penguinpeak/ClipboardAgent/jni/Android.bp b/penguinpeak/ClipboardAgent/jni/Android.bp new file mode 100644 index 0000000..52c086e --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/Android.bp @@ -0,0 +1,139 @@ +// +// 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. + 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", +} + +genrule { + name: "pgp-proto", + tool_files: [ ":TAF-proto-gen", ":TAF-templates", ":TAF-proto-gen-deps",], + srcs: [ + "proto/appstatus-protogen.inp", + "proto/notification-protogen.inp", + ], + out: [ + "proto/appstatus-gen.proto", + "proto/notification-gen.proto", + ], + cmd: "$(location) -I vendor/intel/external/apps/penguinpeak/ClipboardAgent/jni/ -o $(genDir) -i $(in) --proto", +} + +genrule { + name: "pgp-headers", + tool_files: [ ":TAF-proto-gen", ":TAF-templates", ":TAF-proto-gen-deps",], + srcs: [ + "proto/appstatus-protogen.inp", + "proto/notification-protogen.inp", + ], + out: [ + "proto/appstatus.h", + "proto/notification.h", + ], + cmd: "$(location) -I vendor/intel/external/apps/penguinpeak/ClipboardAgent/jni/ -o $(genDir) -i $(in) --header", +} + + +genrule { + name: "pgp-gRPCGenStub_h", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -Iout/soong/.intermediates/vendor/intel/external/apps/penguinpeak/ClipboardAgent/jni/pgp-proto/gen -Ivendor/intel/external/apps/penguinpeak/ClipboardAgent/jni -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ ":pgp-proto", + "proto/appstatus.proto", + "proto/notification.proto", + ], + out: [ + "proto/appstatus.pb.h", + "proto/appstatus-gen.pb.h", + "proto/appstatus-gen.grpc.pb.h", + "proto/notification.pb.h", + "proto/notification-gen.pb.h", + "proto/notification-gen.grpc.pb.h", + ], +} + +genrule { + name: "pgp-gRPCGenStub_cc", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -Iout/soong/.intermediates/vendor/intel/external/apps/penguinpeak/ClipboardAgent/jni/pgp-proto/gen -Ivendor/intel/external/apps/penguinpeak/ClipboardAgent/jni -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + ":pgp-proto", + "proto/appstatus.proto", + "proto/notification.proto", + ], + out: [ + "proto/appstatus.pb.cc", + "proto/appstatus-gen.pb.cc", + "proto/appstatus-gen.grpc.pb.cc", + "proto/notification.pb.cc", + "proto/notification-gen.pb.cc", + "proto/notification-gen.grpc.pb.cc", + ], +} + +cc_library_shared { + name: "libVsockMsgDispatch", + defaults: ["TAF-defaults",], + srcs: [ + "DispatchHelper.cpp", + "adapter.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wno-unused-label", + ], + generated_headers: [ "pgp-headers", "pgp-gRPCGenStub_h", ], + generated_sources: [ "pgp-gRPCGenStub_cc", ], + sdk_version: "current", +} + +cc_binary { + name: "cfc_host_agent", + host_supported: true, + defaults: ["TAF-defaults",], + srcs: [ + "appstatus.cpp", + "notification.cpp", + ], + generated_headers: [ "pgp-headers", "pgp-gRPCGenStub_h", ], + generated_sources: [ "pgp-gRPCGenStub_cc", ], +} diff --git a/penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp b/penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp new file mode 100644 index 0000000..bc41fea --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp @@ -0,0 +1,44 @@ +#include "DispatchHelper.h" +#include +#include +#include "adapter.h" + +#undef LOG_TAG +#define LOG_TAG "DispatchHelper" + +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_registerComponent(JNIEnv *env, jobject thisObject, jstring className) { + env->GetJavaVM(&gVm); + std::string name = env->GetStringUTFChars(className, 0); + LOG_INFO("Attempting to register Service %s\n", name.c_str()); + ServiceAdapter* adapter = AdapterFactory::GetAdapter(name); + if (adapter != nullptr) { + adapter->Register(); + } else { + LOG_ERROR("Service adapter not found for %s\n", name.c_str()); + } +} + +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_sendMsg(JNIEnv *env, jobject thisObject, jstring className, jobject msg, jlong handle) { + JavaObjectHelper jobjHelper(msg); + std::string name = env->GetStringUTFChars(className, 0); + ServiceAdapter* adapter = AdapterFactory::GetAdapter(name); + if (adapter == nullptr) { + LOG_ERROR("Service adapter not found for %s\n", name.c_str()); + return; + } + adapter->SendResponse(&jobjHelper); +} + +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_start(JNIEnv *env, jobject thisObject) { + if (!g_server_->Start()) { + LOG_ERROR("FATAL! Failed to start server"); + } +} + +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_stop(JNIEnv *env, jobject thisObject) { + g_server_->Stop(); + delete g_server_; + g_server_ = nullptr; + AdapterFactory::RemoveAll(); +} + diff --git a/penguinpeak/ClipboardAgent/jni/DispatchHelper.h b/penguinpeak/ClipboardAgent/jni/DispatchHelper.h new file mode 100644 index 0000000..4b9708b --- /dev/null +++ b/penguinpeak/ClipboardAgent/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/Object;J)V + */ +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_sendMsg + (JNIEnv *, jobject, jstring, jobject, 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/penguinpeak/ClipboardAgent/jni/VsockClientImpl.cpp b/penguinpeak/ClipboardAgent/jni/VsockClientImpl.cpp new file mode 100644 index 0000000..11c0ce7 --- /dev/null +++ b/penguinpeak/ClipboardAgent/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/penguinpeak/ClipboardAgent/jni/VsockClientImpl.h b/penguinpeak/ClipboardAgent/jni/VsockClientImpl.h new file mode 100644 index 0000000..efab088 --- /dev/null +++ b/penguinpeak/ClipboardAgent/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/penguinpeak/ClipboardAgent/jni/adapter.cpp b/penguinpeak/ClipboardAgent/jni/adapter.cpp new file mode 100644 index 0000000..963759f --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/adapter.cpp @@ -0,0 +1,201 @@ +#include "adapter.h" + +JavaVM* gVm = nullptr; +taf::gRPCServer* g_server_ = nullptr; +std::map AdapterFactory::jadapter_map; + +static std::map< std::string, jclass > jclass_map; + +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) { + LOG_ERROR("GetEnv: not attached. Failed to attach"); + } + } else if (getEnvStat == JNI_OK) { + // + } else if (getEnvStat == JNI_EVERSION) { + LOG_ERROR("GetEnv: version not supported"); + } + return env; +} + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv *env; + std::string compName; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ERROR("In OnLoad, failed to GetEnv"); + 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) { + compName = "AppstatusComponent"; + jclass_map.insert({compName , (jclass)env->NewGlobalRef(tmp)}); + AdapterFactory::AddAdapter(compName, new AppStatusAdapter(compName)); + } + tmp = env->FindClass("com/intel/clipboardagent/NotificationComponent"); + if (tmp!= nullptr) { + compName = "NotificationComponent"; + jclass_map.insert({compName , (jclass)env->NewGlobalRef(tmp)}); + AdapterFactory::AddAdapter(compName, new NotificationAdapter(compName)); + } + // Setup gRPC Server + g_server_ = new taf::gRPCServer("vsock:-1:50051"); + //g_server_ = new taf::gRPCServer("127.0.0.1:8787"); + LOG_INFO("Created gRPC Server\n"); + return JNI_VERSION_1_6; +} + +void AdapterFactory::AddAdapter(std::string& name, ServiceAdapter* adapter) { + if (GetAdapter(name) == nullptr) { + jadapter_map.insert({name, adapter}); + } +} + +ServiceAdapter* AdapterFactory::GetAdapter(std::string& name) { + std::map< std::string, ServiceAdapter*>::iterator it; + it = jadapter_map.find(name); + return (it != jadapter_map.end()) ? it->second: nullptr; +} + +void AdapterFactory::RemoveAll() { + for (auto adapter : jadapter_map) { + delete adapter.second; + } + jadapter_map.clear(); +} + +JavaComponent::~JavaComponent(){ + JNIEnv* env = getenv(); + jclass reqClass = GetJClass(); + jobject singleInstance = GetSingletonInstance(reqClass); + jmethodID reqMethod = env->GetMethodID(reqClass, "stop", "()V"); + env->CallVoidMethod(singleInstance, reqMethod); +} + +void JavaComponent::init() { + JNIEnv* env = getenv(); + jclass reqClass = GetJClass(); + jobject singleInstance = GetSingletonInstance(reqClass); + jmethodID reqMethod = env->GetMethodID(reqClass, "init", "()V"); + env->CallVoidMethod(singleInstance, reqMethod); +} + +void JavaComponent::stop() { + JNIEnv* env = getenv(); + jclass reqClass = GetJClass(); + jobject singleInstance = GetSingletonInstance(reqClass); + jmethodID reqMethod = env->GetMethodID(reqClass, "stop", "()V"); + env->CallVoidMethod(singleInstance, reqMethod); +} + +void JavaComponent::ProcessMsg(std::string& msg, uint64_t hndl) { + LOG_INFO("Process msg - %s\n", msg.c_str()); + 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.c_str()); + env->CallVoidMethod(singleInstance, reqMethod, str, static_cast(hndl)); +} + +jclass JavaComponent::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 JavaComponent::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; +} + +void JavaObjectHelper::init() { + JNIEnv *env = getenv(); + class_ = env->GetObjectClass(obj_); +} + +int JavaObjectHelper::GetIntField(const char* fldNm) { + JNIEnv *env = getenv(); + jfieldID fld = env->GetFieldID(class_, fldNm, "I"); + return env->GetIntField(obj_, fld); +} + +const char* JavaObjectHelper::GetStringField(const char* fldNm) { + JNIEnv *env = getenv(); + jfieldID fld = env->GetFieldID(class_, fldNm, "Ljava/lang/String;"); + jstring str = (jstring) env->GetObjectField(obj_, fld); + return env->GetStringUTFChars(str, 0); +} + +void AppStatusAdapter::SendResponse(JavaObjectHelper* jobjHelper) { + AppStatusResponse resp; + resp.set_app_name(jobjHelper->GetStringField("app_name")); + if (svc_ != nullptr && !svc_->Observe_Response(&resp)) { + stop(); + auto stream = svc_->GET_API_STREAM(Observe); + if (stream != nullptr) { + // Something wrong with the stream, end the call + stream->WritesDone(); + stream->Finish(); + } + } +} + +taf::Service* AppStatusAdapter::GetService() { + if (svc_ == nullptr) { + svc_ = new AppStatusAdapter::Service(this); + } + return svc_; +} + +bool AppStatusAdapter::Service::Observe(const AppStatusRequest* /*msg*/) { + LOG_INFO("Initializing AppStatus"); + adapter_->init(); + return true; +} + +void NotificationAdapter::SendResponse(JavaObjectHelper* jobjHelper) { + NotificationResponse resp; + resp.set_package(jobjHelper->GetStringField("packageName")); + resp.set_key(jobjHelper->GetStringField("key")); + resp.set_group_key(jobjHelper->GetStringField("groupKey")); + resp.set_message(jobjHelper->GetStringField("message")); + resp.set_priority(jobjHelper->GetIntField("priority")); + if (svc_ != nullptr && !svc_->Observe_Response(&resp)) { + stop(); + auto stream = svc_->GET_API_STREAM(Observe); + if (stream != nullptr) { + // Something wrong with the stream, end the call + stream->WritesDone(); + stream->Finish(); + } + } +} + +taf::Service* NotificationAdapter::GetService() { + if (svc_ == nullptr) { + svc_ = new NotificationAdapter::Service(this); + } + return svc_; +} + +bool NotificationAdapter::Service::Observe(const NotificationRequest* /*msg*/) { + LOG_INFO("Initializing Notification sync"); + adapter_->init(); + return true; +} + diff --git a/penguinpeak/ClipboardAgent/jni/adapter.h b/penguinpeak/ClipboardAgent/jni/adapter.h new file mode 100644 index 0000000..985e690 --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/adapter.h @@ -0,0 +1,117 @@ +#include +#include "proto/appstatus.h" +#include "proto/notification.h" +#include "grpc/server.h" + +extern JavaVM* gVm; +extern taf::gRPCServer* g_server_; + +using namespace com::android::guest; + +class JavaComponent { +public: + JavaComponent(std::string name) { java_class_name = name; } + ~JavaComponent(); + void init(); + void stop(); + void ProcessMsg(std::string& msg, uint64_t hndl); +private: + jclass GetJClass(); + jobject GetSingletonInstance(jclass reqClass); + + std::string java_class_name; +}; + +class JavaObjectHelper { +public: + JavaObjectHelper(jobject obj) { obj_ = obj; init(); } + int GetIntField(const char* fldNm); + const char* GetStringField(const char* fldNm); +private: + void init(); + + jobject obj_; + jclass class_; +}; + +class ServiceAdapter { +public: + ServiceAdapter(std::string& name) { comp_ = new JavaComponent(name); } + virtual ~ServiceAdapter() { + delete comp_; + comp_ = nullptr; + } + virtual void SendResponse(JavaObjectHelper* payload) = 0; + inline void Register() { + g_server_->RegisterService(GetService()); + } + inline void init() { comp_->init(); } + inline void stop() { comp_->stop(); } + +protected: + inline void ProcessRequest(std::string& payload) { + comp_->ProcessMsg(payload, 0); + } + virtual taf::Service* GetService() = 0; + JavaComponent* comp_; +}; + +class AppStatusAdapter : public ServiceAdapter { +public: + AppStatusAdapter(std::string& name) : ServiceAdapter(name), svc_(nullptr) { } + virtual ~AppStatusAdapter() { + if (svc_ != nullptr) { + delete svc_; + svc_ = nullptr; + } + } + void SendResponse(JavaObjectHelper* payload) override; + + class Service : public AppStatusImpl::Service { + public: + Service(ServiceAdapter* adapter) { adapter_ = adapter; } + bool Observe(const AppStatusRequest*) override; + + ServiceAdapter* adapter_; + }; + +protected: + taf::Service* GetService() override; +private: + Service* svc_; +}; + +class NotificationAdapter : public ServiceAdapter { +public: + NotificationAdapter(std::string& name) : ServiceAdapter(name), svc_(nullptr) { } + virtual ~NotificationAdapter() { + if (svc_ != nullptr) { + delete svc_; + svc_ = nullptr; + } + } + void SendResponse(JavaObjectHelper* payload) override; + + class Service : public NotificationImpl::Service { + public: + Service(ServiceAdapter* adapter) { adapter_ = adapter; } + bool Observe(const NotificationRequest* /*msg*/) override; + + ServiceAdapter* adapter_; + }; + +protected: + taf::Service* GetService() override; +private: + Service* svc_; +}; + +class AdapterFactory { +public: + static void AddAdapter(std::string& name, ServiceAdapter* adapter); + static ServiceAdapter* GetAdapter(std::string& name); + static void RemoveAll(); +private: + static std::map jadapter_map; +}; + diff --git a/penguinpeak/ClipboardAgent/jni/appstatus.cpp b/penguinpeak/ClipboardAgent/jni/appstatus.cpp new file mode 100644 index 0000000..b0ffef5 --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/appstatus.cpp @@ -0,0 +1,19 @@ +#include +#include +#include "grpc/client.h" + +#include "proto/appstatus.h" + +using namespace ::com::android::guest; + +bool AppStatusImpl::Stub::Observe_Response(const AppStatusResponse* msg) { + if (msg != nullptr) { + std::cout << "Received AppStatus message:" << msg->app_name() << std::endl; + char cmd[512]; + snprintf(cmd, sizeof(cmd), "/opt/cfc/mwc/bin/msg_agent localhost 3000 CRASHAPP %s", msg->app_name().c_str()); + std::cout << "Running cmd: " << cmd << std::endl; + system(cmd); + } + return true; +} + diff --git a/penguinpeak/ClipboardAgent/jni/notification.cpp b/penguinpeak/ClipboardAgent/jni/notification.cpp new file mode 100644 index 0000000..8c0c846 --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/notification.cpp @@ -0,0 +1,45 @@ +#include +#include +#include "grpc/client.h" + +#include "proto/appstatus.h" +#include "proto/notification.h" + +using namespace ::com::android::guest; + +bool NotificationImpl::Stub::Observe_Response(const NotificationResponse* msg) { + if (msg != nullptr) { + std::cout << "Received Notification message:" << msg->message() << std::endl; + char cmd[512]; + //snprintf(cmd, sizeof(cmd), "notify-send -a %s '%s'", msg->package().c_str(), msg->message().c_str()); + snprintf(cmd, sizeof(cmd), "python3 /opt/cfc/host_agent/bin/notify.py -P %s -k '%s' -g '%s' -m '%s' -p %d", msg->package().c_str(), msg->key().c_str(), msg->group_key().c_str(), msg->message().c_str(), msg->priority()); + std::cout << "Running cmd: " << cmd << std::endl; + system(cmd); + } + return true; +} + +int main() { + auto client = taf::Client::Create("vsock:3:50051", taf::DispatchKind::kgRPC); + AppStatusImpl::Stub appStatusStub; + NotificationImpl::Stub notificationStub; + std::cout << "Registering Stub" << std::endl; + client->Bind(&appStatusStub); + client->Bind(¬ificationStub); + AppStatusRequest req; + NotificationRequest notificationReq; + std::cout << "Initiate AppStatus stream" << std::endl; + appStatusStub.Observe(&req); + std::cout << "Initiate Notification stream" << std::endl; + notificationStub.Observe(¬ificationReq); + // Wait for both API streams to end + auto appStatusStream = appStatusStub.GET_API_STREAM(Observe); + if(appStatusStream != nullptr) { + appStatusStream->WaitForFinish(); + } + auto notificationStream = notificationStub.GET_API_STREAM(Observe); + if (notificationStream != nullptr) { + notificationStream->WaitForFinish(); + } + std::cout << "Stream ended" << std::endl; +} diff --git a/penguinpeak/ClipboardAgent/jni/proto/appstatus-protogen.inp b/penguinpeak/ClipboardAgent/jni/proto/appstatus-protogen.inp new file mode 100644 index 0000000..69da964 --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/proto/appstatus-protogen.inp @@ -0,0 +1,11 @@ +[Template] +Path=./../../../../../taf-grpc/core/templates/observer.template + +[TemplateParams] +Class_ServiceName=AppStatus +Type_ObserveReq=AppStatusRequest +Type_ObserveResp=AppStatusResponse + +[Proto] +MessagePath=proto/appstatus.proto +PackageName=com.android.guest diff --git a/penguinpeak/ClipboardAgent/jni/proto/appstatus.proto b/penguinpeak/ClipboardAgent/jni/proto/appstatus.proto new file mode 100644 index 0000000..240eaa0 --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/proto/appstatus.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package com.android.guest; + +message AppStatusRequest { + int32 req_num = 1; +} + +message AppStatusResponse { + string app_name = 1; +} + diff --git a/penguinpeak/ClipboardAgent/jni/proto/notification-protogen.inp b/penguinpeak/ClipboardAgent/jni/proto/notification-protogen.inp new file mode 100644 index 0000000..75df9ea --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/proto/notification-protogen.inp @@ -0,0 +1,11 @@ +[Template] +Path=./../../../../../taf-grpc/core/templates/observer.template + +[TemplateParams] +Class_ServiceName=Notification +Type_ObserveReq=NotificationRequest +Type_ObserveResp=NotificationResponse + +[Proto] +MessagePath=proto/notification.proto +PackageName=com.android.guest diff --git a/penguinpeak/ClipboardAgent/jni/proto/notification.proto b/penguinpeak/ClipboardAgent/jni/proto/notification.proto new file mode 100644 index 0000000..dc08963 --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/proto/notification.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package com.android.guest; + +message NotificationRequest { + int32 req_num = 1; +} + +message NotificationResponse { + string package = 1; + string key = 2; + string group_key = 3; + string message = 4; + int32 priority = 5; +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppStatusData.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppStatusData.java new file mode 100644 index 0000000..7907f09 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppStatusData.java @@ -0,0 +1,5 @@ +package com.intel.clipboardagent; + +public class AppStatusData { + String app_name; +} \ No newline at end of file diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppstatusComponent.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppstatusComponent.java new file mode 100644 index 0000000..cad6866 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppstatusComponent.java @@ -0,0 +1,168 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import android.app.Service; +import android.content.Intent; +import android.util.Log; +import java.util.HashMap; +import java.util.List; +import com.intel.clipboardagent.DispatchHelper; +import android.content.Context; +import android.app.ActivityManager; +import android.app.ActivityManager.RecentTaskInfo; +import android.content.pm.PackageManager; + +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; + +public class AppstatusComponent { + private static final String TAG = "AppstatusComponent"; + private static AppstatusComponent single_instance = null; + private DispatchHelper dH; + private ActivityManager mActivityManager; + private HashMap uidPrevImpMap = new HashMap(); + private static final int FOREGROUND_IMPORTANCE_CUTOFF = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; + + private AppstatusComponent(){ + } + + public static AppstatusComponent getInstance() { + if (single_instance == null) { + single_instance = new AppstatusComponent(); + } + return single_instance; + } + + public void init() { + dH = DispatchHelper.getInstance(); + Log.d(TAG, "addOnUidImportanceListener"); + mActivityManager = (ActivityManager) dH.mContext.getSystemService(Context.ACTIVITY_SERVICE); + mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener, FOREGROUND_IMPORTANCE_CUTOFF); + + } + + public void stop() { + if (mActivityManager != null) { + Log.d(TAG, "removeOnUidImportanceListener"); + mActivityManager.removeOnUidImportanceListener(mOnUidImportanceListener); + } + } + + private String getPackageName(int uid) { + String packageName = ""; + String[] packages = dH.mContext.getPackageManager().getPackagesForUid(uid); + if (packages == null) { + Log.d(TAG, "No package is associated with that uid, do nothing"); + } else if (packages.length == 1) { + packageName = packages[0]; + } else { + Log.d(TAG, "Multiple packages associated with the uid, should see what to do"); + } + return packageName; + } + + private boolean isHomeForeground(){ + try { + int homeId = dH.mContext.getPackageManager().getPackageUid("com.android.launcher3", 0); + if (mActivityManager.getUidImportance(homeId) == IMPORTANCE_FOREGROUND) + return true; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return false; + } + + private boolean killLG(String appName) { + List recentTasks = mActivityManager.getRecentTasks(10, 0); + boolean kill = true; + for (ActivityManager.RecentTaskInfo taskInfo : recentTasks) { + //Log.d(TAG, "In AHSFunc, taskinfo.isRunning " + taskInfo.isRunning); + //Log.d(TAG, "In AHSFunc, taskinfo.isVisible " + taskInfo.isVisible()); + if ((taskInfo.baseActivity != null) && taskInfo.isRunning && taskInfo.isVisible() && appName.equals(taskInfo.baseActivity.getPackageName())) { + kill = false; + break; + } + } + return kill; + } + + private void dumpTaskInfo() { + List recentTasks = mActivityManager.getRecentTasks(10, 0); + for (ActivityManager.RecentTaskInfo taskInfo : recentTasks) { + Log.d(TAG, "taskInfo = " + taskInfo.toString()); + } + } + + private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = + new ActivityManager.OnUidImportanceListener() { + @Override + public void onUidImportance(final int uid, final int importance){ + Log.d(TAG, "In onUidImportance event, uid = " + uid + " and importance = " + importance); + String appName = getPackageName(uid); + if (appName.isEmpty()) { + Log.d(TAG, "No app associated with uid, so return"); + return; + } + Log.d(TAG, "In onUidImportance Listener, processing App = " + appName); + // dumpTaskInfo(); + if (uidPrevImpMap.containsKey(uid)) { + int prevImp = uidPrevImpMap.get(uid); + Log.d(TAG, "prev imp value of uid " + uid + " is " + prevImp); + if (prevImp == IMPORTANCE_FOREGROUND) { + if (importance == IMPORTANCE_GONE) { + Log.d(TAG, "App with uid " + uid + " killed, send message to host"); + Log.d(TAG, "1:: Sending message to host"); + AppStatusData appstatusData = new AppStatusData(); + appstatusData.app_name = appName; + dH.sendMsg("AppstatusComponent", appstatusData, 0); + } else if(importance >= IMPORTANCE_VISIBLE) { // && importance <= IMPORTANCE_CACHED) { + Log.d(TAG, "App with uid " + uid + " moved from foreground to background"); + if (killLG(appName)) { + AppStatusData appstatusData = new AppStatusData(); + appstatusData.app_name = appName; + Log.d(TAG, "2:: Sending message to host"); + dH.sendMsg("AppstatusComponent", appstatusData, 0); + } + } + } + if (importance == IMPORTANCE_GONE) { + Log.d(TAG, "App with uid " + uid + " killed, remove from the map"); + uidPrevImpMap.remove(uid); + } else { + uidPrevImpMap.put(uid, importance); + } + } else { + uidPrevImpMap.put(uid, importance); + } + } + }; + + /*public void processMsg(String content, long handle) { + try { + int uid = dH.mContext.getPackageManager().getPackageUid(content, 0); + // can diff uid have same handle? + uidAppnameMap.put(uid, content); + uidChannelMap.put(uid, handle); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + }*/ +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java new file mode 100644 index 0000000..638f8bd --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import android.app.Application; +import android.content.Intent; +import android.util.Log; + +public class ClipboardAgent extends Application { + private static final String TAG = "ClipboardAgent"; + private static final String SERVICE_NAME = "ClipboardAgent"; + + + public void onCreate() { + Log.d(TAG, "Application onCreate"); + super.onCreate(); + + startService(new Intent(this, ClipboardService.class)); + startService(new Intent(this, GuestVsockCommService.class)); + startService(new Intent(this, NotificationListener.class)); + } + + public void onTerminate() { + super.onTerminate(); + } + +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardComponent.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardComponent.java new file mode 100644 index 0000000..16a3e90 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardComponent.java @@ -0,0 +1,86 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import android.app.Service; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Intent; +import android.util.Log; +import com.intel.clipboardagent.DispatchHelper; +import android.content.Context; +import static android.content.Context.CLIPBOARD_SERVICE; + +public class ClipboardComponent { + private static final String TAG = "ClipboardComponent"; + private static final String CLIPBOARD_SERVICE_LABEL = "IntelClipboardService"; + private static ClipboardComponent single_instance = null; + private ClipboardManager mClipboardManager; + private DispatchHelper dH; + private long mChannelHandle = 0; + + private ClipboardComponent(){ + } + + public static ClipboardComponent getInstance() { + if (single_instance == null) { + single_instance = new ClipboardComponent(); + } + return single_instance; + } + + public void init() { + dH = DispatchHelper.getInstance(); + Log.d(TAG, "addPrimaryClipChangedListener"); + mClipboardManager = + (ClipboardManager) dH.mContext.getSystemService(CLIPBOARD_SERVICE); + mClipboardManager.addPrimaryClipChangedListener( + mOnPrimaryClipChangedListener); + } + + public void stop() { + if (mClipboardManager != null) { + Log.d(TAG, "removePrimaryClipChangedListener"); + mClipboardManager.removePrimaryClipChangedListener( + mOnPrimaryClipChangedListener); + } + } + + + private final ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener = + new ClipboardManager.OnPrimaryClipChangedListener() { + @Override + public void onPrimaryClipChanged() { + ClipData mclipData = mClipboardManager.getPrimaryClip(); + // This clip originated from the same service, suppress it. + if (CLIPBOARD_SERVICE_LABEL.equals(mclipData.getDescription().getLabel())) { + return; + } + CharSequence mText = mclipData.getItemAt(0).getText(); + dH.sendMsg("ClipboardComponent", mText.toString(), mChannelHandle); + } + }; + + public void processMsg(String content, long handle) { + ClipData mclipData = mClipboardManager.getPrimaryClip(); + mclipData = ClipData.newPlainText(CLIPBOARD_SERVICE_LABEL, content); + mClipboardManager.setPrimaryClip(mclipData); + mChannelHandle = handle; + } + +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardService.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardService.java new file mode 100644 index 0000000..55c9064 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardService.java @@ -0,0 +1,174 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import android.app.Service; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import com.intel.clipboardagent.VsockClientImpl; +import com.intel.clipboardagent.VsockAddress; + +public class ClipboardService extends Service{ + private static final String TAG = "ClipboardAgent"; + private static final String CLIPBOARD_SERVICE_LABEL = "IntelClipboardService"; + private static final int DEFAULT_DATA_LENGTH = 4096; + private static final int MAX_DATA_LENGTH = 512*1024; + private ExecutorService mThreadPool = Executors.newSingleThreadExecutor(); + private ClipboardManager mClipboardManager; + private Vsock mVsock; + private VsockAddress mVsockAddress; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + Log.d(TAG, "In ClipboardService onCreate"); + + mClipboardManager = + (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + mClipboardManager.addPrimaryClipChangedListener( + mOnPrimaryClipChangedListener); + // TODO: remove hard code on vsock port + mVsockAddress = new VsockAddress(VsockAddress.VMADDR_CID_HOST, 77777); + mVsock = new Vsock(mVsockAddress); + + mThreadPool.execute(new HandleHostVsockContent()); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + if (mClipboardManager != null) { + Log.d(TAG, "removePrimaryClipChangedListener"); + mClipboardManager.removePrimaryClipChangedListener( + mOnPrimaryClipChangedListener); + } + try { + mVsock.close(); + } catch (IOException exception) { + Log.e(TAG, "Error on closing Vsock: " + exception.getMessage()); + } + + } + + private final ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener = + new ClipboardManager.OnPrimaryClipChangedListener() { + @Override + public void onPrimaryClipChanged() { + ClipData mclipData = mClipboardManager.getPrimaryClip(); + // This clip originated from the same service, suppress it. + if (CLIPBOARD_SERVICE_LABEL.equals(mclipData.getDescription().getLabel())) { + return; + } + CharSequence mText = mclipData.getItemAt(0).getText(); + byte[] mBytes = mText.toString().getBytes(StandardCharsets.UTF_8); + + try{ + mVsock.getOutputStream().writeInt(mBytes.length); + int writeLength = (mBytes.length < MAX_DATA_LENGTH) ? mBytes.length : MAX_DATA_LENGTH; + // If Clipboard is cleared, nothing to send + if (writeLength > 0) { + mVsock.getOutputStream().write(mBytes, 0, writeLength); + } + } catch (IOException exception) { + Log.e(TAG, "Error on handling clipboard data: " + exception.getMessage()); + } + } + }; + + // Class HandleHostVsockContent should receive vsock data from remote host + private class HandleHostVsockContent implements Runnable { + private static final String TAG = "ClipboardAgent"; + + private HandleHostVsockContent() { + } + + @Override + public void run() { + // TODO: Data length is hard code here for 4096. + byte[] buffer = new byte[DEFAULT_DATA_LENGTH]; + try { + mVsock.connect(); + } catch (IOException exception) { + Log.e(TAG, "Failed to connect: " + exception.getMessage()); + } + while (true) { + boolean bReconnect = false; + byte[] bytes = buffer; + String content = ""; + try { + int length = mVsock.getInputStream().readInt(); + if (length < 0 || length > MAX_DATA_LENGTH) { + Log.wtf(TAG, "Unexpected data size :"+length, new Exception("Unexpected data size")); + continue; + } + + if (length > DEFAULT_DATA_LENGTH) { + bytes = new byte[length]; + } + + if (length > 0) { + mVsock.getInputStream().read(bytes, 0, length); + content = new String(bytes, 0, length, StandardCharsets.UTF_8); + } + ClipData mclipData = mClipboardManager.getPrimaryClip(); + mclipData = ClipData.newPlainText(CLIPBOARD_SERVICE_LABEL, content); + mClipboardManager.setPrimaryClip(mclipData); + + } catch (IOException exception) { + if (exception.toString().contains("Connection reset") || + exception.toString().contains("Connection is closed by peer")) { + Log.e(TAG, "Connection reset, attempting to reconnect"); + bReconnect = true; + } else { + Log.e(TAG, "Error on handling host Vsock: " + exception.getMessage()); + } + } + if (bReconnect) { + try { + mVsock.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close vsock: " + e.getMessage()); + } + try { + mVsock = new Vsock(mVsockAddress); + mVsock.connect(); + Thread.sleep(1000); + } catch (IOException e) { + Log.e(TAG, "Error reconnecting... " + e.getMessage()); + } catch (InterruptedException x) {} + } + } + } + } +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/DispatchHelper.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/DispatchHelper.java new file mode 100644 index 0000000..e4026dc --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/DispatchHelper.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; +import android.content.Context; + +public class DispatchHelper { + static { + System.loadLibrary("VsockMsgDispatch"); + } + private static final String TAG = "DispatchHelper"; + private static DispatchHelper single_instance = null; + public Context mContext; + private DispatchHelper() { + } + public static DispatchHelper getInstance() { + if (single_instance == null) { + single_instance = new DispatchHelper(); + } + return single_instance; + } + + public native void registerComponent(String className); + public native void sendMsg(String className, Object msg, long handle); + public native void start(); + public native void stop(); + +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/GuestVsockCommService.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/GuestVsockCommService.java new file mode 100644 index 0000000..8094f52 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/GuestVsockCommService.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; +import com.intel.clipboardagent.DispatchHelper; +import android.content.Context; + +public class GuestVsockCommService extends Service{ + private DispatchHelper dH; + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + dH = DispatchHelper.getInstance(); + dH.mContext = this.getApplicationContext(); + dH.registerComponent("ClipboardComponent"); + dH.registerComponent("AppstatusComponent"); + dH.registerComponent("NotificationComponent"); + dH.start(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + dH.stop(); + } +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/MainActivity.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/MainActivity.java new file mode 100644 index 0000000..158be8d --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/MainActivity.java @@ -0,0 +1,31 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + finish(); + } +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationComponent.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationComponent.java new file mode 100644 index 0000000..51ae999 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationComponent.java @@ -0,0 +1,87 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import android.app.Service; +import android.content.Intent; +import android.util.Log; +import com.intel.clipboardagent.DispatchHelper; +import android.content.Context; +import android.app.ActivityManager; +import android.content.pm.PackageManager; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.content.ComponentName; +import android.app.NotificationManager; +import java.util.List; + +public class NotificationComponent { + private static final String TAG = "NotificationComponent"; + private static NotificationComponent single_instance = null; + private DispatchHelper dH; + private NotificationListener mListener; + + private NotificationComponent(){ + } + + public static NotificationComponent getInstance() { + if (single_instance == null) { + single_instance = new NotificationComponent(); + } + return single_instance; + } + + public void init() { + dH = DispatchHelper.getInstance(); + Log.d(TAG, "In init"); + mListener = new NotificationListener(); + mListener.setNotificationsChangedListener(mNotificationsChangedListener); + ComponentName cn = ComponentName.unflattenFromString("com.intel.clipboardagent/com.intel.clipboardagent.NotificationListener"); + NotificationManager nm = dH.mContext.getSystemService(NotificationManager.class); + if (nm.isNotificationListenerAccessGranted(cn)) { + Log.d(TAG, "Has notification acess"); + } + } + + public void stop() { + Log.d(TAG, "In stop"); + if (mListener != null) { + mListener.removeNotificationsChangedListener(); + } + } + + private final NotificationListener.NotificationsChangedListener mNotificationsChangedListener = new NotificationListener.NotificationsChangedListener() { + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + Log.d(TAG, "In NotificationComponent onNotificationPosted"); + Log.d(TAG, "ID :" + sbn.getId() + "\t" + sbn.getNotification().tickerText +"\t" + sbn.getPackageName()); + Log.d(TAG, "ID :" + sbn.isGroup() + "\t" + sbn.getKey() +"\t" + sbn.toString() +"\t" + sbn.getNotification().priority); + NotificationData notificationdata = new NotificationData(); + notificationdata.packageName = sbn.getPackageName(); + notificationdata.key = sbn.getKey(); + notificationdata.groupKey = sbn.getGroupKey(); + if (sbn.getNotification().tickerText != null) { + notificationdata.message = sbn.getNotification().tickerText.toString(); + } + notificationdata.priority = sbn.getNotification().priority; + dH.sendMsg("NotificationComponent", notificationdata, 0); + } + }; + +} + diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationData.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationData.java new file mode 100644 index 0000000..cfe09f6 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationData.java @@ -0,0 +1,9 @@ +package com.intel.clipboardagent; + +public class NotificationData { + String packageName; + String key; + String groupKey; + String message; + int priority; +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationListener.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationListener.java new file mode 100644 index 0000000..bc519e2 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationListener.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import android.app.Service; +import android.content.Intent; +import android.util.Log; +import com.intel.clipboardagent.DispatchHelper; +import android.content.Context; +import android.app.ActivityManager; +import android.content.pm.PackageManager; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.content.ComponentName; + +public class NotificationListener extends NotificationListenerService { + private static final String TAG = "NotificationListener"; + private static NotificationsChangedListener sNotificationsChangedListener; + + public interface NotificationsChangedListener { + void onNotificationPosted(StatusBarNotification sbn); + } + + public NotificationListener() { + sNotificationsChangedListener = null; + } + + public static void setNotificationsChangedListener(NotificationsChangedListener listener) { + sNotificationsChangedListener = listener; + } + + public static void removeNotificationsChangedListener() { + sNotificationsChangedListener = null; + } + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + Log.d(TAG, "In onNotificationPosted"); + if (sNotificationsChangedListener != null) { + sNotificationsChangedListener.onNotificationPosted(sbn); + } + } +} + diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/Vsock.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/Vsock.java new file mode 100644 index 0000000..1a7455d --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/Vsock.java @@ -0,0 +1,74 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketException; + +public final class Vsock extends VsockBaseVSock implements Closeable { + private boolean connected = false; + private VsockOutputStream outputStream; + private VsockInputStream inputStream; + private VsockAddress mAddress; + + public Vsock() { + } + + public Vsock(VsockAddress address) { + mAddress = address; + } + + public void connect() throws SocketException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + if (connected) { + throw new SocketException("Socket already connected"); + } + getImplementation().connect(mAddress); + connected = true; + } + + public synchronized VsockOutputStream getOutputStream() throws IOException { + if (isClosed()) { + throw new SocketException("VSock is closed"); + } + if (outputStream == null) { + outputStream = new VsockOutputStream(getImplementation()); + } + return outputStream; + } + + public synchronized VsockInputStream getInputStream() throws IOException { + if (isClosed()) { + throw new SocketException("VSock is closed"); + } + if (inputStream == null) { + inputStream = new VsockInputStream(getImplementation()); + } + return inputStream; + } + + void postAccept() { + created = true; + connected = true; + } +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockAddress.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockAddress.java new file mode 100644 index 0000000..99e831a --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockAddress.java @@ -0,0 +1,46 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import java.net.SocketAddress; +import java.util.Objects; + +public final class VsockAddress extends SocketAddress { + public static final int VMADDR_CID_ANY = -1; + public static final int VMADDR_CID_HYPERVISOR = 0; + public static final int VMADDR_CID_RESERVED = 1; + public static final int VMADDR_CID_HOST = 2; + public static final int VMADDR_CID_PARENT = 3; + + public static final int VMADDR_PORT_ANY = -1; + final int cid; + final int port; + + public VsockAddress(int cid, int port) { + this.cid = cid; + this.port = port; + } + + public int getCid() { + return cid; + } + + public int getPort() { + return port; + } +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockBaseVSock.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockBaseVSock.java new file mode 100644 index 0000000..bde6fc9 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockBaseVSock.java @@ -0,0 +1,65 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import java.io.Closeable; +import java.io.IOException; +import java.net.SocketException; + +abstract class VsockBaseVSock implements Closeable { + protected final Object closeLock = new Object(); + protected boolean closed = false; + protected boolean created = false; + + private VsockClientImpl implementation; + + private void createImplementation() throws SocketException { + implementation = new VsockClientImpl(); + implementation.create(); + created = true; + } + + protected VsockClientImpl getImplementation() throws SocketException { + if (!created) { + createImplementation(); + } + return implementation; + } + + protected VsockClientImpl setImplementation() throws SocketException { + if(implementation == null) { + implementation = new VsockClientImpl(); + } + return implementation; + } + + @Override + public synchronized void close() throws IOException { + synchronized (closeLock) { + if (isClosed()) + return; + if (created) + getImplementation().close(); + closed = true; + } + } + + protected boolean isClosed() { + return closed; + } +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockClientImpl.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockClientImpl.java new file mode 100644 index 0000000..57caf8c --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockClientImpl.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import java.net.*; +import java.io.*; + +public class VsockClientImpl { + static { + System.loadLibrary("VsocketClientImpl"); + } + + int fd = -1; + + void create() throws SocketException { + socketCreate(); + } + + native void socketCreate() throws SocketException; + native void connect(VsockAddress address) throws SocketException; + native void close() throws IOException; + native void write(byte[] b, int off, int len) throws IOException; + native int read(byte[] b, int off, int len) throws IOException; + + native void writeInt(int value) throws IOException; + native int readInt() throws IOException; +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockInputStream.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockInputStream.java new file mode 100644 index 0000000..0ab8fcf --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockInputStream.java @@ -0,0 +1,55 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import java.io.IOException; +import java.io.InputStream; + +public final class VsockInputStream extends InputStream { + private final VsockClientImpl vSock; + private byte[] temp; + + public VsockInputStream (VsockClientImpl vSock) { + this.vSock = vSock; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return vSock.read(b, off, len); + } + + @Override + public int read() throws IOException { + temp = new byte[1]; + int n = read(temp, 0, 1); + if (n <= 0) { + return -1; + } + return temp[0]; + } + + public int readInt() throws IOException { + return vSock.readInt(); + } + + @Override + public void close() throws IOException { + vSock.close(); + super.close(); + } +} diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockOutputStream.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockOutputStream.java new file mode 100644 index 0000000..6e2bdb6 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockOutputStream.java @@ -0,0 +1,51 @@ +/* + * 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. + */ + +package com.intel.clipboardagent; + +import java.io.IOException; +import java.io.OutputStream; + +public final class VsockOutputStream extends OutputStream { + private final VsockClientImpl vSock; + private final byte[] temp = new byte[1]; + + VsockOutputStream(VsockClientImpl vSock) { + this.vSock = vSock; + } + + @Override + public void write(int b) throws IOException { + temp[0] = (byte) b; + this.write(temp, 0, 1); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + vSock.write(b, off, len); + } + + public void writeInt(int value) throws IOException { + vSock.writeInt(value); + } + + @Override + public void close() throws IOException { + vSock.close(); + super.close(); + } +}