Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 24acd372 authored by John Reck's avatar John Reck Committed by Android (Google) Code Review
Browse files

Merge "HDR info listener" into sc-dev

parents 86872452 35aa049b
Loading
Loading
Loading
Loading
+102 −0
Original line number Diff line number Diff line
/*
 * 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<IBinder, Runnable> toDestroy;
        synchronized (this) {
            toDestroy = mRegisteredListeners;
            mRegisteredListeners = new ArrayMap<>();
        }
        for (Runnable destructor : toDestroy.values()) {
            destructor.run();
        }
    }

    private ArrayMap<IBinder, Runnable> mRegisteredListeners = new ArrayMap<>();

    private static native long nGetDestructor();
    private native long nRegister(IBinder displayToken);
}
+5 −1
Original line number Diff line number Diff line
@@ -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",
+2 −0
Original line number Diff line number Diff line
@@ -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);
@@ -1515,6 +1516,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),
+136 −0
Original line number Diff line number Diff line
/*
 * 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 <android/gui/BnHdrLayerInfoListener.h>
#include <android_runtime/Log.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <utils/RefBase.h>

#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<void**>(&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<SurfaceControlHdrLayerInfoListener>::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<jlong>(reinterpret_cast<intptr_t>(ret));
}

static void destroy(SurfaceControlHdrLayerInfoListener* listener) {
    listener->stopListening();
    listener->decStrong(0);
}

static jlong nGetDestructor(JNIEnv* env, jobject clazz) {
    return static_cast<jlong>(reinterpret_cast<intptr_t>(&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
+26 −0
Original line number Diff line number Diff line
@@ -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