/* ** ** Copyright 2017, 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. */ #define LOG_TAG "android_emulator_multidisplay_JNI" #include #include #include #include #include #include #include #include #include "utils/Log.h" #include "nativehelper/JNIHelp.h" #include #include "jni.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #define MAX_DISPLAYS 10 using namespace android; static int gFd = 0; static const uint8_t ADD = 1; static const uint8_t DEL = 2; static const uint8_t QUERY = 3; static const uint8_t BIND = 4; static const uint8_t SET_DISPLAY = 0x10; static void fillMsg(std::vector& buf, uint8_t cmd, uint8_t* data, uint32_t size) { // msg format is size(4B) + cmd(1B) + data(size B) uint32_t totalSize = size + 1; uint8_t* p = (uint8_t*)&totalSize; buf.insert(buf.end(), p, p + 4); buf.push_back(cmd); if (data) { buf.insert(buf.end(), data, data + size); } } struct FrameListener : public ConsumerBase::FrameAvailableListener { sp mConsumer; uint32_t mId; uint32_t mCb; public: void onFrameAvailable(const BufferItem& item) override { BufferItem bufferItem; mConsumer->acquireBuffer(&bufferItem, 0); ANativeWindowBuffer* b = bufferItem.mGraphicBuffer->getNativeBuffer(); if (b && b->handle) { const cb_handle_t* cb = cb_handle_t::from(b->handle); if (mCb != cb->hostHandle) { ALOGI("sent cb %d", cb->hostHandle); mCb = cb->hostHandle; uint32_t data[] = {mId, mCb}; std::vector buf; fillMsg(buf, BIND, (uint8_t*)data, sizeof(data)); qemu_pipe_write_fully(gFd, buf.data(), buf.size()); } } else { ALOGE("cannot get native buffer handler"); } mConsumer->releaseBuffer(bufferItem); } void setDefaultBufferSize(uint32_t w, uint32_t h) { mConsumer->setDefaultBufferSize(w, h); } FrameListener(sp& consumer, uint32_t id) : mConsumer(consumer), mId(id), mCb(0) { } }; sp gFrameListener[MAX_DISPLAYS + 1]; static jobject nativeCreateSurface(JNIEnv *env, jobject obj, jint id, jint width, jint height) { ALOGI("create surface for %d", id); // Create surface for this new display sp producer; sp consumer; sp bufferItemConsumer; BufferQueue::createBufferQueue(&producer, &consumer); bufferItemConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_RENDER); gFrameListener[id] = new FrameListener(bufferItemConsumer, id); gFrameListener[id]->setDefaultBufferSize(width, height); bufferItemConsumer->setFrameAvailableListener(gFrameListener[id]); return android_view_Surface_createFromIGraphicBufferProducer(env, producer); } static jint nativeOpen(JNIEnv* env, jobject obj) { // Open pipe gFd = qemu_pipe_open_ns(NULL, "multidisplay", O_RDWR); if (gFd < 0) { ALOGE("Error opening multidisplay pipe %d", gFd); } else { std::vector buf; fillMsg(buf, QUERY, nullptr, 0); qemu_pipe_write_fully(gFd, buf.data(), buf.size()); ALOGI("multidisplay pipe connected!!!"); } return gFd; } static bool nativeReadPipe(JNIEnv* env, jobject obj, jintArray arr) { int* arrp = env->GetIntArrayElements(arr, 0); uint32_t length; qemu_pipe_read_fully(gFd, &length, sizeof(length)); std::vector args(length, 0); qemu_pipe_read_fully(gFd, args.data(), (size_t)length); switch(args[0]) { case SET_DISPLAY: ALOGV("received setdisplay event"); *arrp = SET_DISPLAY; for (int i = 1; i < 6; i++) { *(arrp + i) = *(uint32_t*)(&args[(i - 1) * 4 + 1]); } env->ReleaseIntArrayElements(arr, arrp, JNI_COMMIT); break; case ADD: { ALOGV("received add event"); *arrp = ADD; for (int i = 1; i < 6; i++) { *(arrp + i) = *(uint32_t*)(&args[(i - 1) * 4 + 1]); } env->ReleaseIntArrayElements(arr, arrp, JNI_COMMIT); break; } case DEL: { ALOGV("received del event"); *arrp = DEL; *(arrp + 1) = *(uint32_t*)(&args[1]); env->ReleaseIntArrayElements(arr, arrp, JNI_COMMIT); break; } default: { ALOGE("unexpected event %d", args[0]); return false; } } return true; } static bool nativeReleaseListener(JNIEnv* env, jobject obj, jint id) { if (gFrameListener[id].get()) { ALOGV("clear gFrameListener %d", id); gFrameListener[id].clear(); gFrameListener[id] = nullptr; } return true; } static bool nativeResizeListener(JNIEnv* env, jobject obj, jint id, jint w, jint h) { if (gFrameListener[id]) { gFrameListener[id]->setDefaultBufferSize(w, h); return true; } return false; } static JNINativeMethod sMethods[] = { { "nativeOpen", "()I", (void*) nativeOpen }, { "nativeCreateSurface", "(III)Landroid/view/Surface;", (void*) nativeCreateSurface }, { "nativeReadPipe", "([I)Z", (void*) nativeReadPipe}, { "nativeReleaseListener", "(I)Z", (void*) nativeReleaseListener}, { "nativeResizeListener", "(III)Z", (void*) nativeResizeListener}, }; /* * JNI Initialization */ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *env; // Check JNI version if (jvm->GetEnv((void **)&env, JNI_VERSION_1_6)) { ALOGE("JNI version mismatch error"); return JNI_ERR; } jniRegisterNativeMethods(env, "com/android/emulator/multidisplay/MultiDisplayService", sMethods, NELEM(sMethods)); return JNI_VERSION_1_6; }