From 5bb4d519289961038fb6e5c6f181041ba7929414 Mon Sep 17 00:00:00 2001 From: Vinay Prasad Kompella Date: Mon, 12 Jul 2021 17:47:58 +0530 Subject: [PATCH 1/5] Add copy/paste application for PenguinPeak project This application is used to monitor Android clipboard, send clipboard data changes to host side, and set data which is received from host side to clipboard. It depends on changes of clipboard service due to Android 11 limitation, and server application in host side. The communication channel is based on virtio-sock (vsock) serivce provided by native service. Execute following command under src folder to generate native service header file: javah -classpath ./ -d jni com.intel.penguinpeakutils.VsockClientImpl Tracked-On: OAM-97756 Signed-off-by: Vinay Prasad Kompella Co-authored-by: Zhong, Fangjian --- penguinpeak/Android.mk | 18 ++ penguinpeak/PenguinPeakUtils/Android.bp | 36 +++ .../PenguinPeakUtils/AndroidManifest.xml | 32 +++ penguinpeak/PenguinPeakUtils/jni/Android.bp | 37 +++ .../PenguinPeakUtils/jni/VsockClientImpl.cpp | 245 ++++++++++++++++++ .../PenguinPeakUtils/jni/VsockClientImpl.h | 69 +++++ .../penguinpeakutils/ClipboardService.java | 126 +++++++++ .../intel/penguinpeakutils/MainActivity.java | 31 +++ .../penguinpeakutils/PenguinPeakUtils.java | 41 +++ .../src/com/intel/penguinpeakutils/Vsock.java | 82 ++++++ .../intel/penguinpeakutils/VsockAddress.java | 46 ++++ .../penguinpeakutils/VsockBaseVSock.java | 65 +++++ .../penguinpeakutils/VsockClientImpl.java | 39 +++ .../penguinpeakutils/VsockInputStream.java | 51 ++++ .../penguinpeakutils/VsockOutputStream.java | 47 ++++ 15 files changed, 965 insertions(+) create mode 100644 penguinpeak/Android.mk create mode 100644 penguinpeak/PenguinPeakUtils/Android.bp create mode 100644 penguinpeak/PenguinPeakUtils/AndroidManifest.xml create mode 100644 penguinpeak/PenguinPeakUtils/jni/Android.bp create mode 100644 penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.cpp create mode 100644 penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/MainActivity.java create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/PenguinPeakUtils.java create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockAddress.java create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockBaseVSock.java create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java create mode 100644 penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java 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/PenguinPeakUtils/Android.bp b/penguinpeak/PenguinPeakUtils/Android.bp new file mode 100644 index 0000000..98026b3 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/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: "PenguinPeakUtils", + srcs: ["**/*.java"], + // JNI library built from C++ source code + jni_libs: ["libVsocketClientImpl"], + optimize: { + enabled: false, + }, + sdk_version: "current", + dex_preopt: { + enabled: false, + }, + privileged: true, + // To match the signature + certificate: "platform", +} diff --git a/penguinpeak/PenguinPeakUtils/AndroidManifest.xml b/penguinpeak/PenguinPeakUtils/AndroidManifest.xml new file mode 100644 index 0000000..4901c07 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/penguinpeak/PenguinPeakUtils/jni/Android.bp b/penguinpeak/PenguinPeakUtils/jni/Android.bp new file mode 100644 index 0000000..a6e0a8a --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/jni/Android.bp @@ -0,0 +1,37 @@ +// +// 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", +} diff --git a/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.cpp b/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.cpp new file mode 100644 index 0000000..8ec6e6a --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.cpp @@ -0,0 +1,245 @@ +/* + * 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 4096 +#define MAX_DATA_LENGTH 64*1024 + +static const char *vsockClientImplPath = "com/intel/penguinpeakutils/VsockClientImpl"; +static const char *vsockAddressPath = "com/intel/penguinpeakutils/VsockAddress"; +static const char *javaConnException = "java/net/ConnectException"; +static const char *javaIntrIOException = "java/io/InterruptedIOException"; +static const char *sunConnResetException = "sun/net/ConnectionResetException"; + +bool 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) { + 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 false; + } else if (nread != size) { + env->ThrowNew(env->FindClass(javaConnException), + ("vsock read: Failed to read the complete msg")); + return false; + } + return true; +} + +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_penguinpeakutils_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_penguinpeakutils_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) { + env->ThrowNew(env->FindClass(javaConnException), + ("Connect failed: " + std::to_string(errno)).c_str()); + } + } +} + +JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_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_penguinpeakutils_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 data size first + uint32_t size = len; + size = htonl(size); + uint8_t* buffer = (uint8_t*)&size; + if (!write_to_vsock(env, s, buffer, DATA_SIZE_LENGTH)) { + 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_penguinpeakutils_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; + } + + // Read the data size first + uint32_t size = 0; + { + uint8_t buffer[DATA_SIZE_LENGTH + 1] = {0}; + if (!read_from_vsock(env, s, buffer, DATA_SIZE_LENGTH)) { + ALOGD("vsock read: Failed to read data size."); + return 0; + } + size = *(uint32_t*)buffer; + size = ntohl(size); + if (size > MAX_DATA_LENGTH) { + ALOGD("vsock read: Data too long. Possible protocol violation"); + return 0; + } + } + + uint8_t buffer[MAX_CHUNK_LENGTH + 1]; + uint32_t remaining = size; + uint32_t rem_buffer = len-1; + while (remaining > 0) { + uint32_t chunkLen = min(remaining, MAX_CHUNK_LENGTH); + if (!read_from_vsock(env, s, buffer, chunkLen)) { + ALOGD("vsock read: Failed to read complete msg"); + } + // Even if we have exhausted the java buffer, still continue to consume full msg + if (rem_buffer > 0) { + uint32_t to_write = min(rem_buffer, chunkLen); + // Mark EOF, either when there is nothing more to read, + // or we have exhausted the java buffer + if (rem_buffer <= chunkLen || + remaining == chunkLen){ + buffer[to_write]=EOF; + } + env->SetByteArrayRegion(b, off, to_write, (jbyte *)buffer); + rem_buffer -= to_write; + } + remaining -= chunkLen; + off += chunkLen; + } + + if (size > len) { + ALOGD("vsock read: Long message - %u bytes, limited read to - %d bytes", size, len); + return len; + } + return (jint)size; +} diff --git a/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h b/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h new file mode 100644 index 0000000..bddf7d6 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h @@ -0,0 +1,69 @@ +/* + * 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_penguinpeakutils_VsockClientImpl */ + +#ifndef _Included_com_intel_penguinpeakutils_VsockClientImpl +#define _Included_com_intel_penguinpeakutils_VsockClientImpl +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_intel_penguinpeakutils_VsockClientImpl + * Method: socketCreate + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_socketCreate + (JNIEnv *, jobject); + +/* + * Class: com_intel_penguinpeakutils_VsockClientImpl + * Method: connect + * Signature: (Lcom/intel/penguinpeakutils/VsockAddress;)V + */ +JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_connect + (JNIEnv *, jobject, jobject); + +/* + * Class: com_intel_penguinpeakutils_VsockClientImpl + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_close + (JNIEnv *, jobject); + +/* + * Class: com_intel_penguinpeakutils_VsockClientImpl + * Method: write + * Signature: ([BII)V + */ +JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_write + (JNIEnv *, jobject, jbyteArray, jint, jint); + +/* + * Class: com_intel_penguinpeakutils_VsockClientImpl + * Method: read + * Signature: ([BII)I + */ +JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_read + (JNIEnv *, jobject, jbyteArray, jint, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java new file mode 100644 index 0000000..da7da72 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java @@ -0,0 +1,126 @@ +/* + * 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.penguinpeakutils; + +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.penguinpeakutils.VsockClientImpl; +import com.intel.penguinpeakutils.VsockAddress; + +public class ClipboardService extends Service{ + private static final String TAG = "PenguinPeakUtils"; + private ExecutorService mThreadPool = Executors.newSingleThreadExecutor(); + private ClipboardManager mClipboardManager; + private Vsock mVsock; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + + mClipboardManager = + (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + mClipboardManager.addPrimaryClipChangedListener( + mOnPrimaryClipChangedListener); + // TODO: remove hard code on vsock port + mVsock = new Vsock(new VsockAddress(VsockAddress.VMADDR_CID_HOST, 77777)); + + mThreadPool.execute(new HandleHostVsockContent(mVsock)); + } + + @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(); + CharSequence mText = mclipData.getItemAt(0).getText(); + byte[] mBytes = mText.toString().getBytes(StandardCharsets.UTF_8); + + try{ + mVsock.getOutputStream().write(mBytes, 0, mBytes.length); + } 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 = "PenguinPeakUtils"; + private final Vsock mVsock; + + private HandleHostVsockContent(Vsock vsock) { + mVsock = vsock; + } + + @Override + public void run() { + // TODO: Data length is hard code here for 4096. + byte[] bytes = new byte[4096]; + while (true) { + try { + int length; + + length = mVsock.getInputStream().read(bytes, 0, 4096); + if (length > 0) { + String content = new String(bytes, 0, length, StandardCharsets.UTF_8); + + ClipData mclipData = mClipboardManager.getPrimaryClip(); + mclipData = ClipData.newPlainText("PenguinPeak", content); + mClipboardManager.setPrimaryClip(mclipData); + } + + } catch (IOException exception) { + Log.e(TAG, "Error on handling host Vsock: " + exception.getMessage()); + } + } + } + } +} diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/MainActivity.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/MainActivity.java new file mode 100644 index 0000000..e4e3ea4 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/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.penguinpeakutils; + +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/PenguinPeakUtils/src/com/intel/penguinpeakutils/PenguinPeakUtils.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/PenguinPeakUtils.java new file mode 100644 index 0000000..44afa92 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/PenguinPeakUtils.java @@ -0,0 +1,41 @@ +/* + * 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.penguinpeakutils; + +import android.app.Application; +import android.content.Intent; +import android.util.Log; +import com.intel.penguinpeakutils.ClipboardService; + +public class PenguinPeakUtils extends Application { + private static final String TAG = "PenguinPeakUtils"; + private static final String SERVICE_NAME = "PenguinPeak"; + + + public void onCreate() { + Log.d(TAG, "Application onCreate"); + super.onCreate(); + + startService(new Intent(this, ClipboardService.class)); + } + + public void onTerminate() { + super.onTerminate(); + } + +} diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java new file mode 100644 index 0000000..4b51ff8 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java @@ -0,0 +1,82 @@ +/* + * 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.penguinpeakutils; + +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; + + public Vsock() { + } + + public Vsock(VsockAddress address) { + try { + getImplementation().connect(address); + } catch (Exception e) { + try { + close(); + } catch (Exception ce) { + e.addSuppressed(ce); + } + throw new IllegalStateException(e.getMessage(), e); + } + } + + public void connect(VsockAddress address) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + if (connected) { + throw new SocketException("Socket already connected"); + } + getImplementation().connect(address); + connected = true; + } + + public synchronized OutputStream getOutputStream() throws IOException { + if (isClosed()) { + throw new SocketException("VSock is closed"); + } + if (outputStream == null) { + outputStream = new VsockOutputStream(getImplementation()); + } + return outputStream; + } + + public synchronized InputStream 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/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockAddress.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockAddress.java new file mode 100644 index 0000000..b2decc8 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/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.penguinpeakutils; + +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/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockBaseVSock.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockBaseVSock.java new file mode 100644 index 0000000..30ed843 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/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.penguinpeakutils; + +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/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java new file mode 100644 index 0000000..53fa057 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java @@ -0,0 +1,39 @@ +/* + * 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.penguinpeakutils; + +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; +} diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java new file mode 100644 index 0000000..b026902 --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.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.penguinpeakutils; + +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]; + } + + @Override + public void close() throws IOException { + vSock.close(); + super.close(); + } +} diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java new file mode 100644 index 0000000..e3c899c --- /dev/null +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java @@ -0,0 +1,47 @@ +/* + * 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.penguinpeakutils; + +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); + } + + @Override + public void close() throws IOException { + vSock.close(); + super.close(); + } +} From 631f1d5fb5dc99aae5874b62ad7f9ec9ed8922fa Mon Sep 17 00:00:00 2001 From: ahs Date: Sun, 18 Jul 2021 10:22:44 +0530 Subject: [PATCH 2/5] Enable clipboard service to reconnect. Currently we do not tolerate connection reset. This patch enables uninterrupted connection. Tracked-On: OAM-97785 Signed-off-by: ahs --- .../PenguinPeakUtils/jni/VsockClientImpl.cpp | 115 +++++++++--------- .../PenguinPeakUtils/jni/VsockClientImpl.h | 16 +++ .../penguinpeakutils/ClipboardService.java | 79 +++++++++--- .../src/com/intel/penguinpeakutils/Vsock.java | 20 +-- .../penguinpeakutils/VsockClientImpl.java | 3 + .../penguinpeakutils/VsockInputStream.java | 4 + .../penguinpeakutils/VsockOutputStream.java | 4 + 7 files changed, 154 insertions(+), 87 deletions(-) diff --git a/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.cpp b/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.cpp index 8ec6e6a..d896ca4 100644 --- a/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.cpp +++ b/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.cpp @@ -45,8 +45,8 @@ #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #define DATA_SIZE_LENGTH 4 -#define MAX_CHUNK_LENGTH 4096 -#define MAX_DATA_LENGTH 64*1024 +#define MAX_CHUNK_LENGTH 8192 +#define MAX_DATA_LENGTH 512*1024 static const char *vsockClientImplPath = "com/intel/penguinpeakutils/VsockClientImpl"; static const char *vsockAddressPath = "com/intel/penguinpeakutils/VsockAddress"; @@ -54,23 +54,19 @@ static const char *javaConnException = "java/net/ConnectException"; static const char *javaIntrIOException = "java/io/InterruptedIOException"; static const char *sunConnResetException = "sun/net/ConnectionResetException"; -bool read_from_vsock(JNIEnv* env, int sockfd, uint8_t* bytes, uint32_t size) { +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) { + 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 false; - } else if (nread != size) { - env->ThrowNew(env->FindClass(javaConnException), - ("vsock read: Failed to read the complete msg")); - return false; + return nread; } - return true; + return nread; } bool write_to_vsock(JNIEnv* env, int sockfd, uint8_t* bytes, uint32_t size) { @@ -123,7 +119,7 @@ JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_connect 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) { + if (errno == EALREADY || errno == EISCONN ) { env->ThrowNew(env->FindClass(javaConnException), ("Connect failed: " + std::to_string(errno)).c_str()); } @@ -161,15 +157,6 @@ JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_write return; } - { - // Send the data size first - uint32_t size = len; - size = htonl(size); - uint8_t* buffer = (uint8_t*)&size; - if (!write_to_vsock(env, s, buffer, DATA_SIZE_LENGTH)) { - return; - } - } // Send the actual data char buffer[MAX_CHUNK_LENGTH]; @@ -196,50 +183,64 @@ JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_read 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_penguinpeakutils_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; + } - // Read the data size first - uint32_t size = 0; { - uint8_t buffer[DATA_SIZE_LENGTH + 1] = {0}; - if (!read_from_vsock(env, s, buffer, DATA_SIZE_LENGTH)) { - ALOGD("vsock read: Failed to read data size."); - return 0; - } - size = *(uint32_t*)buffer; - size = ntohl(size); - if (size > MAX_DATA_LENGTH) { - ALOGD("vsock read: Data too long. Possible protocol violation"); - return 0; + uint32_t size = length; + size = htonl(size); + uint8_t* buffer = (uint8_t*)&size; + if (!write_to_vsock(env, s, buffer, DATA_SIZE_LENGTH)) { + return; } } +} - uint8_t buffer[MAX_CHUNK_LENGTH + 1]; - uint32_t remaining = size; - uint32_t rem_buffer = len-1; - while (remaining > 0) { - uint32_t chunkLen = min(remaining, MAX_CHUNK_LENGTH); - if (!read_from_vsock(env, s, buffer, chunkLen)) { - ALOGD("vsock read: Failed to read complete msg"); - } - // Even if we have exhausted the java buffer, still continue to consume full msg - if (rem_buffer > 0) { - uint32_t to_write = min(rem_buffer, chunkLen); - // Mark EOF, either when there is nothing more to read, - // or we have exhausted the java buffer - if (rem_buffer <= chunkLen || - remaining == chunkLen){ - buffer[to_write]=EOF; - } - env->SetByteArrayRegion(b, off, to_write, (jbyte *)buffer); - rem_buffer -= to_write; - } - remaining -= chunkLen; - off += chunkLen; + +JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_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; } - if (size > len) { - ALOGD("vsock read: Long message - %u bytes, limited read to - %d bytes", size, len); - return len; + 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/PenguinPeakUtils/jni/VsockClientImpl.h b/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h index bddf7d6..5599370 100644 --- a/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h +++ b/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h @@ -63,6 +63,22 @@ JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_write JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_read (JNIEnv *, jobject, jbyteArray, jint, jint); +/* + * Class: com_intel_penguinpeakutils_VsockClientImpl + * Method: writeInt + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_writeInt + (JNIEnv *, jobject, jint); + +/* + * Class: com_intel_penguinpeakutils_VsockClientImpl + * Method: readInt + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_readInt + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java index da7da72..b6531d8 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java @@ -35,9 +35,13 @@ public class ClipboardService extends Service{ private static final String TAG = "PenguinPeakUtils"; + 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) { @@ -53,9 +57,10 @@ public void onCreate() { mClipboardManager.addPrimaryClipChangedListener( mOnPrimaryClipChangedListener); // TODO: remove hard code on vsock port - mVsock = new Vsock(new VsockAddress(VsockAddress.VMADDR_CID_HOST, 77777)); + mVsockAddress = new VsockAddress(VsockAddress.VMADDR_CID_HOST, 77777); + mVsock = new Vsock(mVsockAddress); - mThreadPool.execute(new HandleHostVsockContent(mVsock)); + mThreadPool.execute(new HandleHostVsockContent()); } @Override @@ -80,11 +85,20 @@ public void onDestroy() { @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().write(mBytes, 0, mBytes.length); + 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()); } @@ -94,31 +108,64 @@ public void onPrimaryClipChanged() { // Class HandleHostVsockContent should receive vsock data from remote host private class HandleHostVsockContent implements Runnable { private static final String TAG = "PenguinPeakUtils"; - private final Vsock mVsock; - private HandleHostVsockContent(Vsock vsock) { - mVsock = vsock; + private HandleHostVsockContent() { } @Override public void run() { // TODO: Data length is hard code here for 4096. - byte[] bytes = new byte[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; + 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; + } - length = mVsock.getInputStream().read(bytes, 0, 4096); - if (length > 0) { - String content = new String(bytes, 0, length, StandardCharsets.UTF_8); + if (length > DEFAULT_DATA_LENGTH) { + bytes = new byte[length]; + } - ClipData mclipData = mClipboardManager.getPrimaryClip(); - mclipData = ClipData.newPlainText("PenguinPeak", content); - mClipboardManager.setPrimaryClip(mclipData); - } + 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) { - Log.e(TAG, "Error on handling host Vsock: " + exception.getMessage()); + 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/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java index 4b51ff8..3ea5e6f 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java @@ -27,35 +27,27 @@ 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) { - try { - getImplementation().connect(address); - } catch (Exception e) { - try { - close(); - } catch (Exception ce) { - e.addSuppressed(ce); - } - throw new IllegalStateException(e.getMessage(), e); - } + mAddress = address; } - public void connect(VsockAddress address) throws SocketException { + public void connect() throws SocketException { if (isClosed()) { throw new SocketException("Socket closed"); } if (connected) { throw new SocketException("Socket already connected"); } - getImplementation().connect(address); + getImplementation().connect(mAddress); connected = true; } - public synchronized OutputStream getOutputStream() throws IOException { + public synchronized VsockOutputStream getOutputStream() throws IOException { if (isClosed()) { throw new SocketException("VSock is closed"); } @@ -65,7 +57,7 @@ public synchronized OutputStream getOutputStream() throws IOException { return outputStream; } - public synchronized InputStream getInputStream() throws IOException { + public synchronized VsockInputStream getInputStream() throws IOException { if (isClosed()) { throw new SocketException("VSock is closed"); } diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java index 53fa057..ec84ddf 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java @@ -36,4 +36,7 @@ void create() 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/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java index b026902..8801b02 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java @@ -43,6 +43,10 @@ public int read() throws IOException { return temp[0]; } + public int readInt() throws IOException { + return vSock.readInt(); + } + @Override public void close() throws IOException { vSock.close(); diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java index e3c899c..f7de6f4 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java +++ b/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java @@ -39,6 +39,10 @@ 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(); From 11d026a29dfa5ab1ea97f45a5eeb9d95e1f20266 Mon Sep 17 00:00:00 2001 From: ahs Date: Thu, 2 Sep 2021 15:17:08 +0530 Subject: [PATCH 3/5] Renaming PenguinPeakUtils App to ClipboardAgent Tracked-On: OAM-99249 Signed-off-by: ahs --- .../Android.bp | 2 +- .../AndroidManifest.xml | 4 +-- .../jni/Android.bp | 0 .../jni/VsockClientImpl.cpp | 18 +++++----- .../jni/VsockClientImpl.h | 36 +++++++++---------- .../intel/clipboardagent/ClipboardAgent.java} | 10 +++--- .../clipboardagent}/ClipboardService.java | 10 +++--- .../intel/clipboardagent}/MainActivity.java | 2 +- .../src/com/intel/clipboardagent}/Vsock.java | 2 +- .../intel/clipboardagent}/VsockAddress.java | 2 +- .../intel/clipboardagent}/VsockBaseVSock.java | 2 +- .../clipboardagent}/VsockClientImpl.java | 2 +- .../clipboardagent}/VsockInputStream.java | 2 +- .../clipboardagent}/VsockOutputStream.java | 2 +- 14 files changed, 47 insertions(+), 47 deletions(-) rename penguinpeak/{PenguinPeakUtils => ClipboardAgent}/Android.bp (97%) rename penguinpeak/{PenguinPeakUtils => ClipboardAgent}/AndroidManifest.xml (93%) rename penguinpeak/{PenguinPeakUtils => ClipboardAgent}/jni/Android.bp (100%) rename penguinpeak/{PenguinPeakUtils => ClipboardAgent}/jni/VsockClientImpl.cpp (91%) rename penguinpeak/{PenguinPeakUtils => ClipboardAgent}/jni/VsockClientImpl.h (52%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils/PenguinPeakUtils.java => ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java} (79%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils => ClipboardAgent/src/com/intel/clipboardagent}/ClipboardService.java (96%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils => ClipboardAgent/src/com/intel/clipboardagent}/MainActivity.java (96%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils => ClipboardAgent/src/com/intel/clipboardagent}/Vsock.java (98%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils => ClipboardAgent/src/com/intel/clipboardagent}/VsockAddress.java (97%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils => ClipboardAgent/src/com/intel/clipboardagent}/VsockBaseVSock.java (98%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils => ClipboardAgent/src/com/intel/clipboardagent}/VsockClientImpl.java (97%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils => ClipboardAgent/src/com/intel/clipboardagent}/VsockInputStream.java (97%) rename penguinpeak/{PenguinPeakUtils/src/com/intel/penguinpeakutils => ClipboardAgent/src/com/intel/clipboardagent}/VsockOutputStream.java (97%) diff --git a/penguinpeak/PenguinPeakUtils/Android.bp b/penguinpeak/ClipboardAgent/Android.bp similarity index 97% rename from penguinpeak/PenguinPeakUtils/Android.bp rename to penguinpeak/ClipboardAgent/Android.bp index 98026b3..ed1c661 100644 --- a/penguinpeak/PenguinPeakUtils/Android.bp +++ b/penguinpeak/ClipboardAgent/Android.bp @@ -19,7 +19,7 @@ // bundles the shared library and calls it using JNI. android_app { - name: "PenguinPeakUtils", + name: "ClipboardAgent", srcs: ["**/*.java"], // JNI library built from C++ source code jni_libs: ["libVsocketClientImpl"], diff --git a/penguinpeak/PenguinPeakUtils/AndroidManifest.xml b/penguinpeak/ClipboardAgent/AndroidManifest.xml similarity index 93% rename from penguinpeak/PenguinPeakUtils/AndroidManifest.xml rename to penguinpeak/ClipboardAgent/AndroidManifest.xml index 4901c07..a4ab943 100644 --- a/penguinpeak/PenguinPeakUtils/AndroidManifest.xml +++ b/penguinpeak/ClipboardAgent/AndroidManifest.xml @@ -1,6 +1,6 @@ @@ -10,7 +10,7 @@ SetIntField(thisObject, fdField, sock); } -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_connect +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"); @@ -126,7 +126,7 @@ JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_connect } } -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_close +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"); @@ -146,7 +146,7 @@ JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_close } } -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_write +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"); @@ -173,7 +173,7 @@ JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_write return; } -JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_read +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"); @@ -199,7 +199,7 @@ JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_read return (jint)len; } -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_writeInt +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"); @@ -221,7 +221,7 @@ JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_writeInt } -JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_readInt +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"); diff --git a/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h b/penguinpeak/ClipboardAgent/jni/VsockClientImpl.h similarity index 52% rename from penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h rename to penguinpeak/ClipboardAgent/jni/VsockClientImpl.h index 5599370..efab088 100644 --- a/penguinpeak/PenguinPeakUtils/jni/VsockClientImpl.h +++ b/penguinpeak/ClipboardAgent/jni/VsockClientImpl.h @@ -16,67 +16,67 @@ /* DO NOT EDIT THIS FILE - it is machine generated */ #include -/* Header for class com_intel_penguinpeakutils_VsockClientImpl */ +/* Header for class com_intel_clipboardagent_VsockClientImpl */ -#ifndef _Included_com_intel_penguinpeakutils_VsockClientImpl -#define _Included_com_intel_penguinpeakutils_VsockClientImpl +#ifndef _Included_com_intel_clipboardagent_VsockClientImpl +#define _Included_com_intel_clipboardagent_VsockClientImpl #ifdef __cplusplus extern "C" { #endif /* - * Class: com_intel_penguinpeakutils_VsockClientImpl + * Class: com_intel_clipboardagent_VsockClientImpl * Method: socketCreate * Signature: ()V */ -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_socketCreate +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_socketCreate (JNIEnv *, jobject); /* - * Class: com_intel_penguinpeakutils_VsockClientImpl + * Class: com_intel_clipboardagent_VsockClientImpl * Method: connect - * Signature: (Lcom/intel/penguinpeakutils/VsockAddress;)V + * Signature: (Lcom/intel/clipboardagent/VsockAddress;)V */ -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_connect +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_connect (JNIEnv *, jobject, jobject); /* - * Class: com_intel_penguinpeakutils_VsockClientImpl + * Class: com_intel_clipboardagent_VsockClientImpl * Method: close * Signature: ()V */ -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_close +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_close (JNIEnv *, jobject); /* - * Class: com_intel_penguinpeakutils_VsockClientImpl + * Class: com_intel_clipboardagent_VsockClientImpl * Method: write * Signature: ([BII)V */ -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_write +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_write (JNIEnv *, jobject, jbyteArray, jint, jint); /* - * Class: com_intel_penguinpeakutils_VsockClientImpl + * Class: com_intel_clipboardagent_VsockClientImpl * Method: read * Signature: ([BII)I */ -JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_read +JNIEXPORT jint JNICALL Java_com_intel_clipboardagent_VsockClientImpl_read (JNIEnv *, jobject, jbyteArray, jint, jint); /* - * Class: com_intel_penguinpeakutils_VsockClientImpl + * Class: com_intel_clipboardagent_VsockClientImpl * Method: writeInt * Signature: (I)V */ -JNIEXPORT void JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_writeInt +JNIEXPORT void JNICALL Java_com_intel_clipboardagent_VsockClientImpl_writeInt (JNIEnv *, jobject, jint); /* - * Class: com_intel_penguinpeakutils_VsockClientImpl + * Class: com_intel_clipboardagent_VsockClientImpl * Method: readInt * Signature: ()I */ -JNIEXPORT jint JNICALL Java_com_intel_penguinpeakutils_VsockClientImpl_readInt +JNIEXPORT jint JNICALL Java_com_intel_clipboardagent_VsockClientImpl_readInt (JNIEnv *, jobject); #ifdef __cplusplus diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/PenguinPeakUtils.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java similarity index 79% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/PenguinPeakUtils.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java index 44afa92..26da4dd 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/PenguinPeakUtils.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java @@ -15,16 +15,16 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import android.app.Application; import android.content.Intent; import android.util.Log; -import com.intel.penguinpeakutils.ClipboardService; +import com.intel.clipboardagent.ClipboardService; -public class PenguinPeakUtils extends Application { - private static final String TAG = "PenguinPeakUtils"; - private static final String SERVICE_NAME = "PenguinPeak"; +public class ClipboardAgent extends Application { + private static final String TAG = "ClipboardAgent"; + private static final String SERVICE_NAME = "ClipboardAgent"; public void onCreate() { diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardService.java similarity index 96% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardService.java index b6531d8..10b8130 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/ClipboardService.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardService.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import android.app.Service; import android.content.ClipData; @@ -30,11 +30,11 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; -import com.intel.penguinpeakutils.VsockClientImpl; -import com.intel.penguinpeakutils.VsockAddress; +import com.intel.clipboardagent.VsockClientImpl; +import com.intel.clipboardagent.VsockAddress; public class ClipboardService extends Service{ - private static final String TAG = "PenguinPeakUtils"; + 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; @@ -107,7 +107,7 @@ public void onPrimaryClipChanged() { // Class HandleHostVsockContent should receive vsock data from remote host private class HandleHostVsockContent implements Runnable { - private static final String TAG = "PenguinPeakUtils"; + private static final String TAG = "ClipboardAgent"; private HandleHostVsockContent() { } diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/MainActivity.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/MainActivity.java similarity index 96% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/MainActivity.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/MainActivity.java index e4e3ea4..158be8d 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/MainActivity.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/MainActivity.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import android.app.Activity; import android.os.Bundle; diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/Vsock.java similarity index 98% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/Vsock.java index 3ea5e6f..1a7455d 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/Vsock.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/Vsock.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import java.io.Closeable; import java.io.IOException; diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockAddress.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockAddress.java similarity index 97% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockAddress.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockAddress.java index b2decc8..99e831a 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockAddress.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockAddress.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import java.net.SocketAddress; import java.util.Objects; diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockBaseVSock.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockBaseVSock.java similarity index 98% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockBaseVSock.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockBaseVSock.java index 30ed843..bde6fc9 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockBaseVSock.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockBaseVSock.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import java.io.Closeable; import java.io.IOException; diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockClientImpl.java similarity index 97% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockClientImpl.java index ec84ddf..57caf8c 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockClientImpl.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockClientImpl.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import java.net.*; import java.io.*; diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockInputStream.java similarity index 97% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockInputStream.java index 8801b02..0ab8fcf 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockInputStream.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockInputStream.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import java.io.IOException; import java.io.InputStream; diff --git a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockOutputStream.java similarity index 97% rename from penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java rename to penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockOutputStream.java index f7de6f4..6e2bdb6 100644 --- a/penguinpeak/PenguinPeakUtils/src/com/intel/penguinpeakutils/VsockOutputStream.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/VsockOutputStream.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.intel.penguinpeakutils; +package com.intel.clipboardagent; import java.io.IOException; import java.io.OutputStream; From 575cd8470d4426ea8c8e852cf396528299b6580d Mon Sep 17 00:00:00 2001 From: ahs Date: Thu, 16 Dec 2021 22:41:05 +0530 Subject: [PATCH 4/5] Clipboard copy/paste on single vsock channel Tracked-On: OAM-100054 Signed-off-by: ahs --- penguinpeak/ClipboardAgent/Android.bp | 4 +- .../ClipboardAgent/AndroidManifest.xml | 7 +- penguinpeak/ClipboardAgent/jni/Android.bp | 17 + .../ClipboardAgent/jni/DispatchHelper.cpp | 142 +++++++ .../ClipboardAgent/jni/DispatchHelper.h | 45 +++ .../ClipboardAgent/jni/VsockMsgDispatcher.cpp | 368 ++++++++++++++++++ .../ClipboardAgent/jni/VsockMsgDispatcher.h | 173 ++++++++ .../clipboardagent/AppstatusComponent.java | 164 ++++++++ .../intel/clipboardagent/ClipboardAgent.java | 2 + .../clipboardagent/ClipboardComponent.java | 86 ++++ .../clipboardagent/ClipboardService.java | 1 + .../intel/clipboardagent/DispatchHelper.java | 42 ++ .../clipboardagent/GuestVsockCommService.java | 49 +++ 13 files changed, 1097 insertions(+), 3 deletions(-) create mode 100644 penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp create mode 100644 penguinpeak/ClipboardAgent/jni/DispatchHelper.h create mode 100755 penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.cpp create mode 100755 penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.h create mode 100644 penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppstatusComponent.java create mode 100644 penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardComponent.java create mode 100644 penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/DispatchHelper.java create mode 100644 penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/GuestVsockCommService.java diff --git a/penguinpeak/ClipboardAgent/Android.bp b/penguinpeak/ClipboardAgent/Android.bp index ed1c661..9157286 100644 --- a/penguinpeak/ClipboardAgent/Android.bp +++ b/penguinpeak/ClipboardAgent/Android.bp @@ -22,11 +22,11 @@ android_app { name: "ClipboardAgent", srcs: ["**/*.java"], // JNI library built from C++ source code - jni_libs: ["libVsocketClientImpl"], + jni_libs: ["libVsockMsgDispatch", "libVsocketClientImpl"], optimize: { enabled: false, }, - sdk_version: "current", + sdk_version: "system_current", dex_preopt: { enabled: false, }, diff --git a/penguinpeak/ClipboardAgent/AndroidManifest.xml b/penguinpeak/ClipboardAgent/AndroidManifest.xml index a4ab943..95e7315 100644 --- a/penguinpeak/ClipboardAgent/AndroidManifest.xml +++ b/penguinpeak/ClipboardAgent/AndroidManifest.xml @@ -8,6 +8,8 @@ + + + + - diff --git a/penguinpeak/ClipboardAgent/jni/Android.bp b/penguinpeak/ClipboardAgent/jni/Android.bp index a6e0a8a..816eb11 100644 --- a/penguinpeak/ClipboardAgent/jni/Android.bp +++ b/penguinpeak/ClipboardAgent/jni/Android.bp @@ -35,3 +35,20 @@ cc_library_shared { stl: "c++_static", sdk_version: "current", } + +cc_library_shared { + name: "libVsockMsgDispatch", + srcs: [ + "VsockMsgDispatcher.cpp", + "DispatchHelper.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wno-unused-label", + ], + shared_libs: ["libbase", "liblog"], + sdk_version: "current", +} + diff --git a/penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp b/penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp new file mode 100644 index 0000000..e32132f --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp @@ -0,0 +1,142 @@ +#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) { + LOGIT("GetEnv: not attached"); + 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() { + LOGIT("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) { + LOGIT("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) { + 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/penguinpeak/ClipboardAgent/jni/DispatchHelper.h b/penguinpeak/ClipboardAgent/jni/DispatchHelper.h new file mode 100644 index 0000000..3b67be4 --- /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/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/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.cpp b/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.cpp new file mode 100755 index 0000000..a9a39a1 --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.cpp @@ -0,0 +1,368 @@ +#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) { + LOGIT("FAILED TO CREATE SOCKET"); + 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; + LOGIT("Binding as server\n"); + 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; + LOGIT("Connecting as client\n"); + 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:"); + } + LOGIT("ACCEPTING..\n"); + //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); + LOGIT("Start Run thread\n"); + CHECK_PTHREAD_CALL(pthread_create, (&thread, nullptr, &Run, channel), "Failed to create client handler thread"); + dispatcher->t_clients_[dispatcher->nclients_] = thread; + } + LOGIT("Exiting Listen thread"); + pthread_exit(nullptr); + return (void*)0; +} + +void* MsgDispatcher::Run(void* arg) { + Channel* channel = reinterpret_cast(arg); + MsgDispatcher* dispatcher = channel->dispatcher_; + LOGIT("In Run thread\n"); + 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; + LOGIT("Reconnecting...\n"); + 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); + } + LOGIT("Exiting Run thread"); + 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); + } + } + LOGIT("Exiting WorkerStart thread"); + 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) { + LOGIT("Stop worker thread-%d\n", i); + CHECK_PTHREAD_CALL(pthread_join, (t_workers_[i], nullptr), "Failed to join on worker thread"); + } + LOGIT("Stop main 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) { + LOGIT("Failed to send msg: Invalid Channel"); + 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) { + LOGIT("Registering 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() { + LOGIT("Looping components"); + for (auto component : components_) { + LOGIT("Initializing component"); + component->setDispatcher(this); + component->Initialize(); + } +} + +} diff --git a/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.h b/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.h new file mode 100755 index 0000000..4b9071f --- /dev/null +++ b/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.h @@ -0,0 +1,173 @@ +#ifndef VSOCK_MSG_DISPATCHER_H +#define VSOCK_MSG_DISPATCHER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NODEBUG 0 //set this to 0 to turn on logging + +#ifdef __ANDROID__ +#define LOG_TAG "vsock" +#include +#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__) + +#ifdef NODEBUG +#define LOGIT(...) +#else +#define LOGIT(...) ALOGV(__VA_ARGS__) +#endif +#else +#define CHECK_PTHREAD_CALL(call, args, what) \ + do { \ + int rc = call args; \ + if (rc != 0) { \ + errno = rc; \ + std::cout << #call << " failed for " << (what); \ + exit(-1); \ + } \ + } while (false) +#ifdef NODEBUG +#define LOGIT(...) +#else +#define LOGIT(...) printf(__VA_ARGS__); +#endif +#endif + +#define ERR(msg) perror(msg); + +#define MSG_VERSION 1 +#define MAX_CLIENT_CONNECTIONS 4 +#define WORKER_POOL 4 +#define Q_SIZE 32 + +namespace vsock { + +enum MSG_TYPE : char { + MSG_TYPE_CAPABILITY, + MSG_TYPE_CLIPBOARD, + MSG_TYPE_APPSTATUS, + MSG_TYPE_INVALID, +}; + + +struct MessageHeader { + char _ver; + MSG_TYPE _type; + int _id; + int _size; +}; + +struct Message { + MessageHeader header; + char* payload; +}; + +class MsgDispatcher; +class Component { + public: + Component () { m_bInitDone = false; } + MsgDispatcher* GetDispatcher() { return dispatcher_; } + + virtual ~Component() {} + virtual void ProcessMsg(Message& msg, uint64_t hndl) {} + virtual void init() {} + private: + void setDispatcher(MsgDispatcher* dispatcher) { dispatcher_ = dispatcher; } + void Initialize() { + if (!m_bInitDone) { + init(); + } + m_bInitDone = true; + } + bool m_bInitDone; + MsgDispatcher* dispatcher_; + friend class MsgDispatcher; +}; + + +class MsgDispatcher { + public: + MsgDispatcher(); + bool Start(); + void Stop(); + void RegisterComponent(std::vector& msg_list, Component* component); + bool SendMsg(uint64_t hndl, MSG_TYPE msg_type, const char* buffer, int size); + uint64_t GetHandleForMsgType(MSG_TYPE msg_type); + static MsgDispatcher *instance; + static MsgDispatcher *getInstance() { + if (instance == nullptr) { + instance = new MsgDispatcher(); + } + return instance; + } + private: + + class Channel { + public: + Channel(MsgDispatcher* dispatcher, int fd, int id) { + dispatcher_ = dispatcher; + fd_ = fd; + id_ = id; + } + + ~Channel() { + close(fd_); + while(!msg_queue.empty()) { + auto msg = msg_queue.front(); + delete msg.payload; + msg.payload = nullptr; + msg_queue.pop(); + } + } + private: + MsgDispatcher* dispatcher_; + int fd_; + int id_; + std::queue msg_queue; + friend class MsgDispatcher; + }; + + static void Enqueue(Channel* channel, Message& msg); + + static void* Listen(void* arg); + static void* Run(void* arg); + static void* WorkerStart(void* arg); + + static void SaveCapabilities(Channel* ) {} + static void SendCapabilities(Channel* ) {} + + static bool ValidateMsg(Channel* channel, MessageHeader hdr) { return true; } + void InitComponentsIfReq(); + std::vector& GetComponentsForMsgType(MSG_TYPE msg_type); + + //Dont close, managed by a channel + int fd_; + bool m_bStop; + pthread_t t_main_; + pthread_t t_workers_[WORKER_POOL]; + std::map t_clients_; + int nclients_; + std::vector channels_; + std::vector components_; + std::map> msg_type_map; + + std::mutex client_lock_; + std::mutex q_lock_; +}; +} + + +#endif //VSOCK_MSG_DISPATCHER_H 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..7b783ca --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppstatusComponent.java @@ -0,0 +1,164 @@ +/* + * 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"); + dH.sendMsg("AppstatusComponent", appName, 0); + } else if(importance >= IMPORTANCE_VISIBLE) { // && importance <= IMPORTANCE_CACHED) { + Log.d(TAG, "App with uid " + uid + " moved from foreground to background"); + if (killLG(appName)) { + Log.d(TAG, "2:: Sending message to host"); + dH.sendMsg("AppstatusComponent", appName, 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 index 26da4dd..d3be993 100644 --- a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java @@ -20,6 +20,7 @@ import android.app.Application; import android.content.Intent; import android.util.Log; +import com.intel.clipboardagent.GuestVsockCommService; import com.intel.clipboardagent.ClipboardService; public class ClipboardAgent extends Application { @@ -32,6 +33,7 @@ public void onCreate() { super.onCreate(); startService(new Intent(this, ClipboardService.class)); + startService(new Intent(this, GuestVsockCommService.class)); } public void 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 index 10b8130..55c9064 100644 --- a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardService.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardService.java @@ -51,6 +51,7 @@ public IBinder onBind(Intent intent) { @Override public void onCreate() { super.onCreate(); + Log.d(TAG, "In ClipboardService onCreate"); mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); 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..d2c82ac --- /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, String 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..f916e12 --- /dev/null +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/GuestVsockCommService.java @@ -0,0 +1,49 @@ +/* + * 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.start(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + dH.stop(); + } +} From feb55e24842c26351e72a4cd3338858559a75992 Mon Sep 17 00:00:00 2001 From: ahs Date: Fri, 10 Jun 2022 14:57:09 +0530 Subject: [PATCH 5/5] grpc impl for AppStatus and notification sync Signed-off-by: ahs --- .../ClipboardAgent/AndroidManifest.xml | 8 + penguinpeak/ClipboardAgent/jni/Android.bp | 91 ++++- .../ClipboardAgent/jni/DispatchHelper.cpp | 148 ++----- .../ClipboardAgent/jni/DispatchHelper.h | 4 +- .../ClipboardAgent/jni/VsockMsgDispatcher.cpp | 368 ------------------ .../ClipboardAgent/jni/VsockMsgDispatcher.h | 173 -------- penguinpeak/ClipboardAgent/jni/adapter.cpp | 201 ++++++++++ penguinpeak/ClipboardAgent/jni/adapter.h | 117 ++++++ penguinpeak/ClipboardAgent/jni/appstatus.cpp | 19 + .../ClipboardAgent/jni/notification.cpp | 45 +++ .../jni/proto/appstatus-protogen.inp | 11 + .../ClipboardAgent/jni/proto/appstatus.proto | 12 + .../jni/proto/notification-protogen.inp | 11 + .../jni/proto/notification.proto | 15 + .../intel/clipboardagent/AppStatusData.java | 5 + .../clipboardagent/AppstatusComponent.java | 78 ++-- .../intel/clipboardagent/ClipboardAgent.java | 3 +- .../intel/clipboardagent/DispatchHelper.java | 2 +- .../clipboardagent/GuestVsockCommService.java | 1 + .../clipboardagent/NotificationComponent.java | 87 +++++ .../clipboardagent/NotificationData.java | 9 + .../clipboardagent/NotificationListener.java | 69 ++++ 22 files changed, 768 insertions(+), 709 deletions(-) delete mode 100755 penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.cpp delete mode 100755 penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.h create mode 100644 penguinpeak/ClipboardAgent/jni/adapter.cpp create mode 100644 penguinpeak/ClipboardAgent/jni/adapter.h create mode 100644 penguinpeak/ClipboardAgent/jni/appstatus.cpp create mode 100644 penguinpeak/ClipboardAgent/jni/notification.cpp create mode 100644 penguinpeak/ClipboardAgent/jni/proto/appstatus-protogen.inp create mode 100644 penguinpeak/ClipboardAgent/jni/proto/appstatus.proto create mode 100644 penguinpeak/ClipboardAgent/jni/proto/notification-protogen.inp create mode 100644 penguinpeak/ClipboardAgent/jni/proto/notification.proto create mode 100644 penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppStatusData.java create mode 100644 penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationComponent.java create mode 100644 penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationData.java create mode 100644 penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/NotificationListener.java diff --git a/penguinpeak/ClipboardAgent/AndroidManifest.xml b/penguinpeak/ClipboardAgent/AndroidManifest.xml index 95e7315..3764a52 100644 --- a/penguinpeak/ClipboardAgent/AndroidManifest.xml +++ b/penguinpeak/ClipboardAgent/AndroidManifest.xml @@ -10,6 +10,7 @@ + + + + + + diff --git a/penguinpeak/ClipboardAgent/jni/Android.bp b/penguinpeak/ClipboardAgent/jni/Android.bp index 816eb11..52c086e 100644 --- a/penguinpeak/ClipboardAgent/jni/Android.bp +++ b/penguinpeak/ClipboardAgent/jni/Android.bp @@ -36,11 +36,84 @@ cc_library_shared { 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: [ - "VsockMsgDispatcher.cpp", "DispatchHelper.cpp", + "adapter.cpp", ], cflags: [ "-Wall", @@ -48,7 +121,19 @@ cc_library_shared { "-Wno-unused-parameter", "-Wno-unused-label", ], - shared_libs: ["libbase", "liblog"], + 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 index e32132f..bc41fea 100644 --- a/penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp +++ b/penguinpeak/ClipboardAgent/jni/DispatchHelper.cpp @@ -1,142 +1,44 @@ -#include "VsockMsgDispatcher.h" #include "DispatchHelper.h" #include #include +#include "adapter.h" -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; +#undef LOG_TAG +#define LOG_TAG "DispatchHelper" -static JavaVM* gVm = nullptr; -JNIEnv* getenv() { - JNIEnv *env = nullptr; - int getEnvStat = gVm->GetEnv((void **)&env, JNI_VERSION_1_6); - if (getEnvStat == JNI_EDETACHED) { - LOGIT("GetEnv: not attached"); - 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() { - LOGIT("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) { - LOGIT("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) { - 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); + 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, 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; +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); - it = comp_msg_map.find(name); - if (it != comp_msg_map.end()) { - msg_list = it->second; + ServiceAdapter* adapter = AdapterFactory::GetAdapter(name); + if (adapter == nullptr) { + LOG_ERROR("Service adapter not found for %s\n", name.c_str()); + return; } - if (handle == 0) { - handle = dispatcher->GetHandleForMsgType(msg_list.front()); - } - dispatcher->SendMsg(handle, msg_list.front(), payload.c_str(), size); + adapter->SendResponse(&jobjHelper); } - JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_start(JNIEnv *env, jobject thisObject) { - MsgDispatcher* dispatcher = MsgDispatcher::getInstance(); - dispatcher->Start(); + if (!g_server_->Start()) { + LOG_ERROR("FATAL! Failed to start server"); + } } JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_stop(JNIEnv *env, jobject thisObject) { - MsgDispatcher* dispatcher = MsgDispatcher::getInstance(); - dispatcher->Stop(); + 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 index 3b67be4..4b9708b 100644 --- a/penguinpeak/ClipboardAgent/jni/DispatchHelper.h +++ b/penguinpeak/ClipboardAgent/jni/DispatchHelper.h @@ -18,10 +18,10 @@ JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_registerComp /* * Class: com_intel_clipboardagent_DispatchHelper * Method: sendMsg - * Signature: (Ljava/lang/String;Ljava/lang/String;J)V + * Signature: (Ljava/lang/String;Ljava/lang/Object;J)V */ JNIEXPORT void JNICALL Java_com_intel_clipboardagent_DispatchHelper_sendMsg - (JNIEnv *, jobject, jstring, jstring, jlong); + (JNIEnv *, jobject, jstring, jobject, jlong); /* * Class: com_intel_clipboardagent_DispatchHelper diff --git a/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.cpp b/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.cpp deleted file mode 100755 index a9a39a1..0000000 --- a/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.cpp +++ /dev/null @@ -1,368 +0,0 @@ -#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) { - LOGIT("FAILED TO CREATE SOCKET"); - 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; - LOGIT("Binding as server\n"); - 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; - LOGIT("Connecting as client\n"); - 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:"); - } - LOGIT("ACCEPTING..\n"); - //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); - LOGIT("Start Run thread\n"); - CHECK_PTHREAD_CALL(pthread_create, (&thread, nullptr, &Run, channel), "Failed to create client handler thread"); - dispatcher->t_clients_[dispatcher->nclients_] = thread; - } - LOGIT("Exiting Listen thread"); - pthread_exit(nullptr); - return (void*)0; -} - -void* MsgDispatcher::Run(void* arg) { - Channel* channel = reinterpret_cast(arg); - MsgDispatcher* dispatcher = channel->dispatcher_; - LOGIT("In Run thread\n"); - 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; - LOGIT("Reconnecting...\n"); - 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); - } - LOGIT("Exiting Run thread"); - 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); - } - } - LOGIT("Exiting WorkerStart thread"); - 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) { - LOGIT("Stop worker thread-%d\n", i); - CHECK_PTHREAD_CALL(pthread_join, (t_workers_[i], nullptr), "Failed to join on worker thread"); - } - LOGIT("Stop main 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) { - LOGIT("Failed to send msg: Invalid Channel"); - 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) { - LOGIT("Registering 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() { - LOGIT("Looping components"); - for (auto component : components_) { - LOGIT("Initializing component"); - component->setDispatcher(this); - component->Initialize(); - } -} - -} diff --git a/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.h b/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.h deleted file mode 100755 index 4b9071f..0000000 --- a/penguinpeak/ClipboardAgent/jni/VsockMsgDispatcher.h +++ /dev/null @@ -1,173 +0,0 @@ -#ifndef VSOCK_MSG_DISPATCHER_H -#define VSOCK_MSG_DISPATCHER_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NODEBUG 0 //set this to 0 to turn on logging - -#ifdef __ANDROID__ -#define LOG_TAG "vsock" -#include -#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__) - -#ifdef NODEBUG -#define LOGIT(...) -#else -#define LOGIT(...) ALOGV(__VA_ARGS__) -#endif -#else -#define CHECK_PTHREAD_CALL(call, args, what) \ - do { \ - int rc = call args; \ - if (rc != 0) { \ - errno = rc; \ - std::cout << #call << " failed for " << (what); \ - exit(-1); \ - } \ - } while (false) -#ifdef NODEBUG -#define LOGIT(...) -#else -#define LOGIT(...) printf(__VA_ARGS__); -#endif -#endif - -#define ERR(msg) perror(msg); - -#define MSG_VERSION 1 -#define MAX_CLIENT_CONNECTIONS 4 -#define WORKER_POOL 4 -#define Q_SIZE 32 - -namespace vsock { - -enum MSG_TYPE : char { - MSG_TYPE_CAPABILITY, - MSG_TYPE_CLIPBOARD, - MSG_TYPE_APPSTATUS, - MSG_TYPE_INVALID, -}; - - -struct MessageHeader { - char _ver; - MSG_TYPE _type; - int _id; - int _size; -}; - -struct Message { - MessageHeader header; - char* payload; -}; - -class MsgDispatcher; -class Component { - public: - Component () { m_bInitDone = false; } - MsgDispatcher* GetDispatcher() { return dispatcher_; } - - virtual ~Component() {} - virtual void ProcessMsg(Message& msg, uint64_t hndl) {} - virtual void init() {} - private: - void setDispatcher(MsgDispatcher* dispatcher) { dispatcher_ = dispatcher; } - void Initialize() { - if (!m_bInitDone) { - init(); - } - m_bInitDone = true; - } - bool m_bInitDone; - MsgDispatcher* dispatcher_; - friend class MsgDispatcher; -}; - - -class MsgDispatcher { - public: - MsgDispatcher(); - bool Start(); - void Stop(); - void RegisterComponent(std::vector& msg_list, Component* component); - bool SendMsg(uint64_t hndl, MSG_TYPE msg_type, const char* buffer, int size); - uint64_t GetHandleForMsgType(MSG_TYPE msg_type); - static MsgDispatcher *instance; - static MsgDispatcher *getInstance() { - if (instance == nullptr) { - instance = new MsgDispatcher(); - } - return instance; - } - private: - - class Channel { - public: - Channel(MsgDispatcher* dispatcher, int fd, int id) { - dispatcher_ = dispatcher; - fd_ = fd; - id_ = id; - } - - ~Channel() { - close(fd_); - while(!msg_queue.empty()) { - auto msg = msg_queue.front(); - delete msg.payload; - msg.payload = nullptr; - msg_queue.pop(); - } - } - private: - MsgDispatcher* dispatcher_; - int fd_; - int id_; - std::queue msg_queue; - friend class MsgDispatcher; - }; - - static void Enqueue(Channel* channel, Message& msg); - - static void* Listen(void* arg); - static void* Run(void* arg); - static void* WorkerStart(void* arg); - - static void SaveCapabilities(Channel* ) {} - static void SendCapabilities(Channel* ) {} - - static bool ValidateMsg(Channel* channel, MessageHeader hdr) { return true; } - void InitComponentsIfReq(); - std::vector& GetComponentsForMsgType(MSG_TYPE msg_type); - - //Dont close, managed by a channel - int fd_; - bool m_bStop; - pthread_t t_main_; - pthread_t t_workers_[WORKER_POOL]; - std::map t_clients_; - int nclients_; - std::vector channels_; - std::vector components_; - std::map> msg_type_map; - - std::mutex client_lock_; - std::mutex q_lock_; -}; -} - - -#endif //VSOCK_MSG_DISPATCHER_H 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 index 7b783ca..cad6866 100644 --- a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppstatusComponent.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/AppstatusComponent.java @@ -94,8 +94,8 @@ 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()); + //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; @@ -114,41 +114,45 @@ private void dumpTaskInfo() { 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"); - dH.sendMsg("AppstatusComponent", appName, 0); - } else if(importance >= IMPORTANCE_VISIBLE) { // && importance <= IMPORTANCE_CACHED) { - Log.d(TAG, "App with uid " + uid + " moved from foreground to background"); - if (killLG(appName)) { - Log.d(TAG, "2:: Sending message to host"); - dH.sendMsg("AppstatusComponent", appName, 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 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) { diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java index d3be993..638f8bd 100644 --- a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/ClipboardAgent.java @@ -20,8 +20,6 @@ import android.app.Application; import android.content.Intent; import android.util.Log; -import com.intel.clipboardagent.GuestVsockCommService; -import com.intel.clipboardagent.ClipboardService; public class ClipboardAgent extends Application { private static final String TAG = "ClipboardAgent"; @@ -34,6 +32,7 @@ public void onCreate() { startService(new Intent(this, ClipboardService.class)); startService(new Intent(this, GuestVsockCommService.class)); + startService(new Intent(this, NotificationListener.class)); } public void onTerminate() { diff --git a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/DispatchHelper.java b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/DispatchHelper.java index d2c82ac..e4026dc 100644 --- a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/DispatchHelper.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/DispatchHelper.java @@ -35,7 +35,7 @@ public static DispatchHelper getInstance() { } public native void registerComponent(String className); - public native void sendMsg(String className, String msg, long handle); + 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 index f916e12..8094f52 100644 --- a/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/GuestVsockCommService.java +++ b/penguinpeak/ClipboardAgent/src/com/intel/clipboardagent/GuestVsockCommService.java @@ -38,6 +38,7 @@ public void onCreate() { dH.mContext = this.getApplicationContext(); dH.registerComponent("ClipboardComponent"); dH.registerComponent("AppstatusComponent"); + dH.registerComponent("NotificationComponent"); dH.start(); } 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); + } + } +} +