From 35aa049bc4e297ae5969b043b116f8842bda2b79 Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 18 Mar 2021 11:26:38 -0400 Subject: [PATCH] HDR info listener Bug: 182312559 Test: SilkFX listener is called Change-Id: I2c93f6ab51263a905c17a7afabb845317d33419d --- .../SurfaceControlHdrLayerInfoListener.java | 102 +++++++++++++ core/jni/Android.bp | 6 +- core/jni/AndroidRuntime.cpp | 2 + ...iew_SurfaceControlHdrLayerInfoListener.cpp | 136 ++++++++++++++++++ .../test/silkfx/common/ColorModeControls.kt | 26 ++++ 5 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 core/java/android/view/SurfaceControlHdrLayerInfoListener.java create mode 100644 core/jni/android_view_SurfaceControlHdrLayerInfoListener.cpp diff --git a/core/java/android/view/SurfaceControlHdrLayerInfoListener.java b/core/java/android/view/SurfaceControlHdrLayerInfoListener.java new file mode 100644 index 0000000000000..13d68d021e311 --- /dev/null +++ b/core/java/android/view/SurfaceControlHdrLayerInfoListener.java @@ -0,0 +1,102 @@ +/* + * Copyright 2021 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. + */ + +package android.view; + +import android.annotation.RequiresPermission; +import android.os.IBinder; +import android.util.ArrayMap; + +import libcore.util.NativeAllocationRegistry; + +import java.util.Objects; + +/** + * Allows for the monitoring of layers with HDR content + * + * @hide */ +public abstract class SurfaceControlHdrLayerInfoListener { + private static final NativeAllocationRegistry sRegistry = + NativeAllocationRegistry.createMalloced( + SurfaceControlHdrLayerInfoListener.class.getClassLoader(), nGetDestructor()); + + /** + * Callback when the HDR information about the given display has changed + * + * @param displayToken The display this callback is about + * @param numberOfHdrLayers How many HDR layers are visible on the display + * @param maxW The width of the HDR layer with the largest area + * @param maxH The height of the HDR layer with the largest area + * @param flags Additional metadata flags, currently always 0 + * TODO(b/182312559): Add some flags + * + * @hide */ + public abstract void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, + int maxW, int maxH, int flags); + + /** + * Registers this as an HDR info listener on the provided display + * @param displayToken + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS) + public void register(IBinder displayToken) { + Objects.requireNonNull(displayToken); + synchronized (this) { + if (mRegisteredListeners.containsKey(displayToken)) { + return; + } + long nativePtr = nRegister(displayToken); + Runnable destructor = sRegistry.registerNativeAllocation(this, nativePtr); + mRegisteredListeners.put(displayToken, destructor); + } + } + + /** + * Unregisters this as an HDR info listener on the provided display + * @param displayToken + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS) + public void unregister(IBinder displayToken) { + Objects.requireNonNull(displayToken); + final Runnable destructor; + synchronized (this) { + destructor = mRegisteredListeners.remove(displayToken); + } + if (destructor != null) { + destructor.run(); + } + } + + /** + * Unregisters this on all previously registered displays + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS) + public void unregisterAll() { + final ArrayMap toDestroy; + synchronized (this) { + toDestroy = mRegisteredListeners; + mRegisteredListeners = new ArrayMap<>(); + } + for (Runnable destructor : toDestroy.values()) { + destructor.run(); + } + } + + private ArrayMap mRegisteredListeners = new ArrayMap<>(); + + private static native long nGetDestructor(); + private native long nRegister(IBinder displayToken); +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 20d257e982788..98dd3fe4322fd 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -70,7 +70,10 @@ cc_library_shared { "libz", ], - static_libs: ["libnativehelper_lazy", "libziparchive_for_incfs", ], + static_libs: [ + "libnativehelper_lazy", + "libziparchive_for_incfs", + ], export_include_dirs: [ ".", @@ -121,6 +124,7 @@ cc_library_shared { "android_view_Surface.cpp", "android_view_SurfaceControl.cpp", "android_view_SurfaceControlFpsListener.cpp", + "android_view_SurfaceControlHdrLayerInfoListener.cpp", "android_graphics_BLASTBufferQueue.cpp", "android_view_SurfaceSession.cpp", "android_view_TextureView.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 0c3f265b84cc1..5078d584961cb 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -122,6 +122,7 @@ extern int register_android_view_InputWindowHandle(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); extern int register_android_view_SurfaceControl(JNIEnv* env); extern int register_android_view_SurfaceControlFpsListener(JNIEnv* env); +extern int register_android_view_SurfaceControlHdrLayerInfoListener(JNIEnv* env); extern int register_android_view_SurfaceSession(JNIEnv* env); extern int register_android_view_CompositionSamplingListener(JNIEnv* env); extern int register_android_view_TextureView(JNIEnv* env); @@ -1514,6 +1515,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_Surface), REG_JNI(register_android_view_SurfaceControl), REG_JNI(register_android_view_SurfaceControlFpsListener), + REG_JNI(register_android_view_SurfaceControlHdrLayerInfoListener), REG_JNI(register_android_view_SurfaceSession), REG_JNI(register_android_view_CompositionSamplingListener), REG_JNI(register_android_view_TextureView), diff --git a/core/jni/android_view_SurfaceControlHdrLayerInfoListener.cpp b/core/jni/android_view_SurfaceControlHdrLayerInfoListener.cpp new file mode 100644 index 0000000000000..adbd7916a3f4d --- /dev/null +++ b/core/jni/android_view_SurfaceControlHdrLayerInfoListener.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2021 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 "SurfaceControlHdrLayerInfoListener" + +#include +#include +#include +#include +#include +#include +#include + +#include "android_util_Binder.h" +#include "core_jni_helpers.h" + +namespace android { + +namespace { + +struct { + jclass mClass; + jmethodID mOnHdrInfoChanged; +} gListenerClassInfo; + +struct SurfaceControlHdrLayerInfoListener : public gui::BnHdrLayerInfoListener { + SurfaceControlHdrLayerInfoListener(JNIEnv* env, jobject listener, jobject displayToken) + : mListener(env->NewGlobalRef(listener)), mDisplayToken(env->NewGlobalRef(displayToken)) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mVm) != JNI_OK, "Failed to GetJavaVm"); + } + + binder::Status onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH, + int flags) override { + JNIEnv* env = requireEnv(); + + env->CallVoidMethod(mListener, gListenerClassInfo.mOnHdrInfoChanged, mDisplayToken, + numberOfHdrLayers, maxW, maxH, flags); + + if (env->ExceptionCheck()) { + ALOGE("SurfaceControlHdrLayerInfoListener.onHdrInfoChanged() failed."); + LOGE_EX(env); + env->ExceptionClear(); + } + return binder::Status::ok(); + } + + status_t startListening() { + auto token = ibinderForJavaObject(requireEnv(), mDisplayToken); + return SurfaceComposerClient::addHdrLayerInfoListener(token, this); + } + + status_t stopListening() { + auto token = ibinderForJavaObject(requireEnv(), mDisplayToken); + return SurfaceComposerClient::removeHdrLayerInfoListener(token, this); + } + +protected: + virtual ~SurfaceControlHdrLayerInfoListener() { + JNIEnv* env = requireEnv(); + env->DeleteGlobalRef(mListener); + env->DeleteGlobalRef(mDisplayToken); + } + + JNIEnv* requireEnv() { + JNIEnv* env = nullptr; + if (mVm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (mVm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; + } + +private: + jobject mListener; + jobject mDisplayToken; + JavaVM* mVm; +}; + +jlong nRegister(JNIEnv* env, jobject jthis, jobject jbinderToken) { + auto callback = sp::make(env, jthis, jbinderToken); + status_t err = callback->startListening(); + if (err != OK) { + auto errStr = statusToString(err); + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Failed to register HdrLayerInfoListener, err = %d (%s)", err, + errStr.c_str()); + return 0; + } + SurfaceControlHdrLayerInfoListener* ret = callback.get(); + ret->incStrong(0); + return static_cast(reinterpret_cast(ret)); +} + +static void destroy(SurfaceControlHdrLayerInfoListener* listener) { + listener->stopListening(); + listener->decStrong(0); +} + +static jlong nGetDestructor(JNIEnv* env, jobject clazz) { + return static_cast(reinterpret_cast(&destroy)); +} + +const JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"nGetDestructor", "()J", (void*)nGetDestructor}, + {"nRegister", "(Landroid/os/IBinder;)J", (void*)nRegister}}; + +} // namespace + +int register_android_view_SurfaceControlHdrLayerInfoListener(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "android/view/SurfaceControlHdrLayerInfoListener", + gMethods, NELEM(gMethods)); + LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); + + jclass clazz = env->FindClass("android/view/SurfaceControlHdrLayerInfoListener"); + gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz); + gListenerClassInfo.mOnHdrInfoChanged = + env->GetMethodID(clazz, "onHdrInfoChanged", "(Landroid/os/IBinder;IIII)V"); + return 0; +} + +} // namespace android diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt index 5c2644844e14a..b41ee3a9ef2c0 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt @@ -19,7 +19,11 @@ package com.android.test.silkfx.common import android.content.Context import android.content.pm.ActivityInfo import android.hardware.display.DisplayManager +import android.os.IBinder import android.util.AttributeSet +import android.util.Log +import android.view.SurfaceControl +import android.view.SurfaceControlHdrLayerInfoListener import android.view.Window import android.widget.Button import android.widget.LinearLayout @@ -35,6 +39,7 @@ class ColorModeControls : LinearLayout, WindowObserver { constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { displayManager = context.getSystemService(DisplayManager::class.java)!! displayId = context.getDisplayId() + displayToken = SurfaceControl.getInternalDisplayToken() } private var window: Window? = null @@ -42,6 +47,7 @@ class ColorModeControls : LinearLayout, WindowObserver { private val displayManager: DisplayManager private var targetSdrWhitePointIndex = 0 private var displayId: Int + private var displayToken: IBinder private val whitePoint get() = SDR_WHITE_POINTS[targetSdrWhitePointIndex] @@ -109,6 +115,7 @@ class ColorModeControls : LinearLayout, WindowObserver { // Imperfect, but close enough, synchronization by waiting for frame commit to set the value viewTreeObserver.registerFrameCommitCallback { try { + SurfaceControl.setDisplayBrightness(displayToken, level) displayManager.setTemporaryBrightness(displayId, level) } catch (ex: Exception) { // Ignore a permission denied rejection - it doesn't meaningfully change much @@ -116,9 +123,28 @@ class ColorModeControls : LinearLayout, WindowObserver { } } + private val listener = object : SurfaceControlHdrLayerInfoListener() { + override fun onHdrInfoChanged( + displayToken: IBinder?, + numberOfHdrLayers: Int, + maxW: Int, + maxH: Int, + flags: Int + ) { + Log.d("HDRInfo", "onHdrInfoChanged: numLayer = $numberOfHdrLayers ($maxW x $maxH)" + + ", flags = $flags") + } + } + override fun onAttachedToWindow() { super.onAttachedToWindow() threadedRenderer?.setColorMode(window!!.colorMode, whitePoint) + listener.register(displayToken) + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + listener.unregister(displayToken) } } \ No newline at end of file