/* * Copyright 2018 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 "ASurfaceControlTest" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) namespace { static struct { jclass clazz; jmethodID onTransactionComplete; } gTransactionCompleteListenerClassInfo; #define NANOS_PER_SECOND 1000000000LL int64_t systemTime() { struct timespec time; int result = clock_gettime(CLOCK_MONOTONIC, &time); if (result < 0) { return -errno; } return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec; } static AHardwareBuffer* allocateBuffer(int32_t width, int32_t height) { AHardwareBuffer* buffer = nullptr; AHardwareBuffer_Desc desc = {}; desc.width = width; desc.height = height; desc.layers = 1; desc.usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; AHardwareBuffer_allocate(&desc, &buffer); return buffer; } static void fillRegion(void* data, int32_t left, int32_t top, int32_t right, int32_t bottom, uint32_t color, uint32_t stride) { uint32_t* ptr = static_cast(data); ptr += stride * top; for (uint32_t y = top; y < bottom; y++) { for (uint32_t x = left; x < right; x++) { ptr[x] = color; } ptr += stride; } } static bool getSolidBuffer(int32_t width, int32_t height, uint32_t color, AHardwareBuffer** outHardwareBuffer, int* outFence) { AHardwareBuffer* buffer = allocateBuffer(width, height); if (!buffer) { return true; } AHardwareBuffer_Desc desc = {}; AHardwareBuffer_describe(buffer, &desc); void* data = nullptr; const ARect rect{0, 0, width, height}; AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, &rect, &data); if (!data) { return true; } fillRegion(data, 0, 0, width, height, color, desc.stride); AHardwareBuffer_unlock(buffer, outFence); *outHardwareBuffer = buffer; return false; } static bool getQuadrantBuffer(int32_t width, int32_t height, jint colorTopLeft, jint colorTopRight, jint colorBottomRight, jint colorBottomLeft, AHardwareBuffer** outHardwareBuffer, int* outFence) { AHardwareBuffer* buffer = allocateBuffer(width, height); if (!buffer) { return true; } AHardwareBuffer_Desc desc = {}; AHardwareBuffer_describe(buffer, &desc); void* data = nullptr; const ARect rect{0, 0, width, height}; AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, &rect, &data); if (!data) { return true; } fillRegion(data, 0, 0, width / 2, height / 2, colorTopLeft, desc.stride); fillRegion(data, width / 2, 0, width, height / 2, colorTopRight, desc.stride); fillRegion(data, 0, height / 2, width / 2, height, colorBottomLeft, desc.stride); fillRegion(data, width / 2, height / 2, width, height, colorBottomRight, desc.stride); AHardwareBuffer_unlock(buffer, outFence); *outHardwareBuffer = buffer; return false; } jlong SurfaceTransaction_create(JNIEnv* /*env*/, jclass) { return reinterpret_cast(ASurfaceTransaction_create()); } void SurfaceTransaction_delete(JNIEnv* /*env*/, jclass, jlong surfaceTransaction) { ASurfaceTransaction_delete( reinterpret_cast(surfaceTransaction)); } void SurfaceTransaction_apply(JNIEnv* /*env*/, jclass, jlong surfaceTransaction) { ASurfaceTransaction_apply( reinterpret_cast(surfaceTransaction)); } long SurfaceControl_createFromWindow(JNIEnv* env, jclass, jobject jSurface) { if (!jSurface) { return 0; } ANativeWindow* window = ANativeWindow_fromSurface(env, jSurface); if (!window) { return 0; } const std::string debugName = "SurfaceControl_createFromWindowLayer"; ASurfaceControl* surfaceControl = ASurfaceControl_createFromWindow(window, debugName.c_str()); if (!surfaceControl) { return 0; } ANativeWindow_release(window); return reinterpret_cast(surfaceControl); } jlong SurfaceControl_create(JNIEnv* /*env*/, jclass, jlong parentSurfaceControlId) { ASurfaceControl* surfaceControl = nullptr; const std::string debugName = "SurfaceControl_create"; surfaceControl = ASurfaceControl_create( reinterpret_cast(parentSurfaceControlId), debugName.c_str()); return reinterpret_cast(surfaceControl); } void SurfaceControl_acquire(JNIEnv* /*env*/, jclass, jlong surfaceControl) { ASurfaceControl_acquire(reinterpret_cast(surfaceControl)); } void SurfaceControl_release(JNIEnv* /*env*/, jclass, jlong surfaceControl) { ASurfaceControl_release(reinterpret_cast(surfaceControl)); } jlong SurfaceTransaction_setSolidBuffer(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jint width, jint height, jint color) { AHardwareBuffer* buffer = nullptr; int fence = -1; bool err = getSolidBuffer(width, height, color, &buffer, &fence); if (err) { return 0; } ASurfaceTransaction_setBuffer( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), buffer, fence); ASurfaceTransaction_setBufferDataSpace( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), ADATASPACE_UNKNOWN); return reinterpret_cast(buffer); } void SurfaceTransaction_setBuffer(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jlong buffer) { ASurfaceTransaction_setBuffer(reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), reinterpret_cast(buffer), -1 /* fence */); ASurfaceTransaction_setBufferDataSpace(reinterpret_cast( surfaceTransaction), reinterpret_cast(surfaceControl), ADATASPACE_UNKNOWN); } jlong SurfaceTransaction_setQuadrantBuffer( JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jint width, jint height, jint colorTopLeft, jint colorTopRight, jint colorBottomRight, jint colorBottomLeft) { AHardwareBuffer* buffer = nullptr; int fence = -1; bool err = getQuadrantBuffer(width, height, colorTopLeft, colorTopRight, colorBottomRight, colorBottomLeft, &buffer, &fence); if (err) { return 0; } ASurfaceTransaction_setBuffer( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), buffer, fence); ASurfaceTransaction_setBufferDataSpace( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), ADATASPACE_UNKNOWN); return reinterpret_cast(buffer); } void SurfaceTransaction_releaseBuffer(JNIEnv* /*env*/, jclass, jlong buffer) { AHardwareBuffer_release(reinterpret_cast(buffer)); } void SurfaceTransaction_setVisibility(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jboolean show) { int8_t visibility = (show) ? ASURFACE_TRANSACTION_VISIBILITY_SHOW : ASURFACE_TRANSACTION_VISIBILITY_HIDE; ASurfaceTransaction_setVisibility( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), visibility); } void SurfaceTransaction_setBufferOpaque(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jboolean opaque) { int8_t transparency = (opaque) ? ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE : ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT; ASurfaceTransaction_setBufferTransparency( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), transparency); } void SurfaceTransaction_setGeometry(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jint srcLeft, jint srcTop, jint srcRight, jint srcBottom, jint dstLeft, jint dstTop, jint dstRight, jint dstBottom, jint transform) { const ARect src{srcLeft, srcTop, srcRight, srcBottom}; const ARect dst{dstLeft, dstTop, dstRight, dstBottom}; ASurfaceTransaction_setGeometry( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), src, dst, transform); } void SurfaceTransaction_setCrop(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jint left, jint top, jint right, jint bottom) { const ARect crop{left, top, right, bottom}; ASurfaceTransaction_setCrop(reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), crop); } void SurfaceTransaction_setPosition(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jint x, jint y) { ASurfaceTransaction_setPosition(reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), x, y); } void SurfaceTransaction_setBufferTransform(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jint transform) { ASurfaceTransaction_setBufferTransform(reinterpret_cast( surfaceTransaction), reinterpret_cast(surfaceControl), transform); } void SurfaceTransaction_setScale(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jfloat xScale, jfloat yScale) { ASurfaceTransaction_setScale(reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), xScale, yScale); } void SurfaceTransaction_setDamageRegion(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jint left, jint top, jint right, jint bottom) { const ARect rect[] = {{left, top, right, bottom}}; ASurfaceTransaction_setDamageRegion( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), rect, 1); } void SurfaceTransaction_setZOrder(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jint z) { ASurfaceTransaction_setZOrder( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), z); } class CallbackListenerWrapper { public: explicit CallbackListenerWrapper(JNIEnv* env, jobject object, bool waitForFence) { env->GetJavaVM(&mVm); mCallbackListenerObject = env->NewGlobalRef(object); mWaitForFence = waitForFence; if (!mCallbackListenerObject) { ALOGE("Failed to make global ref"); } } ~CallbackListenerWrapper() { getenv()->DeleteGlobalRef(mCallbackListenerObject); } /** * This is duplicate code from sync.c, but the sync_wait function is not exposed to the ndk. * The documentation recommends using poll instead of exposing sync_wait, but the sync_wait * also handles errors and retries so copied the code here. */ static int sync_wait(int fd, int timeout) { struct pollfd fds; int ret; if (fd < 0) { errno = EINVAL; return -1; } fds.fd = fd; fds.events = POLLIN; do { ret = poll(&fds, 1, timeout); if (ret > 0) { if (fds.revents & (POLLERR | POLLNVAL)) { errno = EINVAL; return -1; } return 0; } else if (ret == 0) { errno = ETIME; return -1; } } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); return ret; } static uint64_t getPresentTime(int presentFence) { uint64_t presentTime = 0; int error = sync_wait(presentFence, 500); if (error < 0) { ALOGE("Failed to sync fence error=%d", error); return 0; } struct sync_file_info* syncFileInfo = sync_file_info(presentFence); if (!syncFileInfo) { ALOGE("invalid fence"); sync_file_info_free(syncFileInfo); return 0; } if (syncFileInfo->status != 1) { ALOGE("fence did not signal status=%d", syncFileInfo->status); return 0; } struct sync_fence_info* syncFenceInfo = sync_get_fence_info(syncFileInfo); for (size_t i = 0; i < syncFileInfo->num_fences; i++) { if (syncFenceInfo[i].timestamp_ns > presentTime) { presentTime = syncFenceInfo[i].timestamp_ns; } } sync_file_info_free(syncFileInfo); close(presentFence); return presentTime; } void callback(ASurfaceTransactionStats* stats) { JNIEnv* env = getenv(); int64_t latchTime = ASurfaceTransactionStats_getLatchTime(stats); uint64_t presentTime = systemTime(); if (mWaitForFence) { int presentFence = ASurfaceTransactionStats_getPresentFenceFd(stats); if (presentFence >= 0) { presentTime = getPresentTime(presentFence); } } env->CallVoidMethod(mCallbackListenerObject, gTransactionCompleteListenerClassInfo.onTransactionComplete, latchTime, presentTime); } static void transactionCallbackThunk(void* context, ASurfaceTransactionStats* stats) { CallbackListenerWrapper* listener = reinterpret_cast(context); listener->callback(stats); delete listener; } private: jobject mCallbackListenerObject; JavaVM* mVm; bool mWaitForFence; JNIEnv* getenv() { JNIEnv* env; mVm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); return env; } }; jlong SurfaceTransaction_setDesiredPresentTime(JNIEnv* /*env*/, jclass, jlong surfaceTransaction, jlong desiredPresentTimeOffset) { struct timespec t; t.tv_sec = t.tv_nsec = 0; clock_gettime(CLOCK_MONOTONIC, &t); int64_t currentTime = ((int64_t) t.tv_sec)*1000000000LL + t.tv_nsec; int64_t desiredPresentTime = currentTime + desiredPresentTimeOffset; ASurfaceTransaction_setDesiredPresentTime( reinterpret_cast(surfaceTransaction), desiredPresentTime); return desiredPresentTime; } void SurfaceTransaction_setBufferAlpha(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jdouble alpha) { ASurfaceTransaction_setBufferAlpha( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), alpha); } void SurfaceTransaction_reparent(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong newParentSurfaceControl, jlong surfaceTransaction) { ASurfaceTransaction_reparent( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), reinterpret_cast(newParentSurfaceControl)); } void SurfaceTransaction_setColor(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jfloat r, jfloat g, jfloat b, jfloat alpha) { ASurfaceTransaction_setColor( reinterpret_cast(surfaceTransaction), reinterpret_cast(surfaceControl), r, g, b, alpha, ADATASPACE_UNKNOWN); } void SurfaceTransaction_setEnableBackPressure(JNIEnv* /*env*/, jclass, jlong surfaceControl, jlong surfaceTransaction, jboolean enableBackPressure) { ASurfaceTransaction_setEnableBackPressure(reinterpret_cast( surfaceTransaction), reinterpret_cast(surfaceControl), enableBackPressure); } void SurfaceTransaction_setOnCompleteCallback(JNIEnv* env, jclass, jlong surfaceTransaction, jboolean waitForFence, jobject callback) { void* context = new CallbackListenerWrapper(env, callback, waitForFence); ASurfaceTransaction_setOnComplete(reinterpret_cast(surfaceTransaction), reinterpret_cast(context), CallbackListenerWrapper::transactionCallbackThunk); } void SurfaceTransaction_setOnCommitCallback(JNIEnv* env, jclass, jlong surfaceTransaction, jobject callback) { void* context = new CallbackListenerWrapper(env, callback, false /* waitForFence */); ASurfaceTransaction_setOnCommit(reinterpret_cast(surfaceTransaction), reinterpret_cast(context), CallbackListenerWrapper::transactionCallbackThunk); } // Save context so we can test callbacks without a context provided. static CallbackListenerWrapper* listener = nullptr; static void transactionCallbackWithoutContextThunk(void* /* context */, ASurfaceTransactionStats* stats) { CallbackListenerWrapper::transactionCallbackThunk(listener, stats); listener = nullptr; } void SurfaceTransaction_setOnCompleteCallbackWithoutContext(JNIEnv* env, jclass, jlong surfaceTransaction, jboolean waitForFence, jobject callback) { listener = new CallbackListenerWrapper(env, callback, waitForFence); ASurfaceTransaction_setOnComplete(reinterpret_cast(surfaceTransaction), nullptr, transactionCallbackWithoutContextThunk); } void SurfaceTransaction_setOnCommitCallbackWithoutContext(JNIEnv* env, jclass, jlong surfaceTransaction, jobject callback) { listener = new CallbackListenerWrapper(env, callback, false /* waitForFence */); ASurfaceTransaction_setOnCommit(reinterpret_cast(surfaceTransaction), nullptr, transactionCallbackWithoutContextThunk); } static const JNINativeMethod JNI_METHODS[] = { {"nSurfaceTransaction_create", "()J", (void*)SurfaceTransaction_create}, {"nSurfaceTransaction_delete", "(J)V", (void*)SurfaceTransaction_delete}, {"nSurfaceTransaction_apply", "(J)V", (void*)SurfaceTransaction_apply}, {"nSurfaceControl_createFromWindow", "(Landroid/view/Surface;)J", (void*)SurfaceControl_createFromWindow}, {"nSurfaceControl_create", "(J)J", (void*)SurfaceControl_create}, {"nSurfaceControl_acquire", "(J)V", (void*)SurfaceControl_acquire}, {"nSurfaceControl_release", "(J)V", (void*)SurfaceControl_release}, {"nSurfaceTransaction_setSolidBuffer", "(JJIII)J", (void*)SurfaceTransaction_setSolidBuffer}, {"nSurfaceTransaction_setBuffer", "(JJJ)V", (void*)SurfaceTransaction_setBuffer}, {"nSurfaceTransaction_setQuadrantBuffer", "(JJIIIIII)J", (void*)SurfaceTransaction_setQuadrantBuffer}, {"nSurfaceTransaction_releaseBuffer", "(J)V", (void*)SurfaceTransaction_releaseBuffer}, {"nSurfaceTransaction_setVisibility", "(JJZ)V", (void*)SurfaceTransaction_setVisibility}, {"nSurfaceTransaction_setBufferOpaque", "(JJZ)V", (void*)SurfaceTransaction_setBufferOpaque}, {"nSurfaceTransaction_setGeometry", "(JJIIIIIIIII)V", (void*)SurfaceTransaction_setGeometry}, {"nSurfaceTransaction_setDamageRegion", "(JJIIII)V", (void*)SurfaceTransaction_setDamageRegion}, {"nSurfaceTransaction_setZOrder", "(JJI)V", (void*)SurfaceTransaction_setZOrder}, {"nSurfaceTransaction_setDesiredPresentTime", "(JJ)J", (void*)SurfaceTransaction_setDesiredPresentTime}, {"nSurfaceTransaction_setBufferAlpha", "(JJD)V", (void*)SurfaceTransaction_setBufferAlpha}, {"nSurfaceTransaction_reparent", "(JJJ)V", (void*)SurfaceTransaction_reparent}, {"nSurfaceTransaction_setColor", "(JJFFFF)V", (void*)SurfaceTransaction_setColor}, {"nSurfaceTransaction_setEnableBackPressure", "(JJZ)V", (void*)SurfaceTransaction_setEnableBackPressure}, {"nSurfaceTransaction_setOnCompleteCallback", "(JZLandroid/view/cts/util/ASurfaceControlTestUtils$TransactionCompleteListener;)V", (void*)SurfaceTransaction_setOnCompleteCallback}, {"nSurfaceTransaction_setOnCommitCallback", "(JLandroid/view/cts/util/ASurfaceControlTestUtils$TransactionCompleteListener;)V", (void*)SurfaceTransaction_setOnCommitCallback}, {"nSurfaceTransaction_setCrop", "(JJIIII)V", (void*)SurfaceTransaction_setCrop}, {"nSurfaceTransaction_setPosition", "(JJII)V", (void*)SurfaceTransaction_setPosition}, {"nSurfaceTransaction_setBufferTransform", "(JJI)V", (void*)SurfaceTransaction_setBufferTransform}, {"nSurfaceTransaction_setScale", "(JJFF)V", (void*)SurfaceTransaction_setScale}, {"nSurfaceTransaction_setOnCompleteCallbackWithoutContext", "(JZLandroid/view/cts/util/ASurfaceControlTestUtils$TransactionCompleteListener;)V", (void*)SurfaceTransaction_setOnCompleteCallbackWithoutContext}, {"nSurfaceTransaction_setOnCommitCallbackWithoutContext", "(JLandroid/view/cts/util/ASurfaceControlTestUtils$TransactionCompleteListener;)V", (void*)SurfaceTransaction_setOnCommitCallbackWithoutContext}, }; } // anonymous namespace jint register_android_view_cts_ASurfaceControlTest(JNIEnv* env) { jclass transactionCompleteListenerClazz = env->FindClass( "android/view/cts/util/ASurfaceControlTestUtils$TransactionCompleteListener"); gTransactionCompleteListenerClassInfo.clazz = static_cast(env->NewGlobalRef(transactionCompleteListenerClazz)); gTransactionCompleteListenerClassInfo.onTransactionComplete = env->GetMethodID(transactionCompleteListenerClazz, "onTransactionComplete", "(JJ)V"); jclass clazz = env->FindClass("android/view/cts/util/ASurfaceControlTestUtils"); return env->RegisterNatives(clazz, JNI_METHODS, sizeof(JNI_METHODS) / sizeof(JNINativeMethod)); }