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

Commit 1a621e8e authored by Alec Mouri's avatar Alec Mouri Committed by Android (Google) Code Review
Browse files

Merge "JNI wrapper for FpsListener." into sc-dev

parents 491cbd85 644553d0
Loading
Loading
Loading
Loading
+93 −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.NonNull;

/**
 * Listener for sampling the frames per second for a SurfaceControl and its children.
 * This should only be used by a system component that needs to listen to a SurfaceControl's
 * tree's FPS when it is not actively submitting transactions for that SurfaceControl.
 * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used.
 *
 * @hide
 */
public abstract class SurfaceControlFpsListener {
    private long mNativeListener;

    public SurfaceControlFpsListener() {
        mNativeListener = nativeCreate(this);
    }

    protected void destroy() {
        if (mNativeListener == 0) {
            return;
        }
        unregister();
        nativeDestroy(mNativeListener);
        mNativeListener = 0;
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            destroy();
        } finally {
            super.finalize();
        }
    }

    /**
     * Reports the fps from the registered SurfaceControl
     */
    public abstract void onFpsReported(float fps);

    /**
     * Registers the sampling listener.
     */
    public void register(@NonNull SurfaceControl layer) {
        if (mNativeListener == 0) {
            return;
        }

        nativeRegister(mNativeListener, layer.mNativeObject);
    }

    /**
     * Unregisters the sampling listener.
     */
    public void unregister() {
        if (mNativeListener == 0) {
            return;
        }
        nativeUnregister(mNativeListener);
    }

    /**
     * Dispatch the collected sample.
     *
     * Called from native code on a binder thread.
     */
    private static void dispatchOnFpsReported(SurfaceControlFpsListener listener, float fps) {
        listener.onFpsReported(fps);
    }

    private static native long nativeCreate(SurfaceControlFpsListener thiz);
    private static native void nativeDestroy(long ptr);
    private static native void nativeRegister(long ptr, long layerObject);
    private static native void nativeUnregister(long ptr);
}
+1 −0
Original line number Diff line number Diff line
@@ -121,6 +121,7 @@ cc_library_shared {
                "android_view_PointerIcon.cpp",
                "android_view_Surface.cpp",
                "android_view_SurfaceControl.cpp",
                "android_view_SurfaceControlFpsListener.cpp",
                "android_graphics_BLASTBufferQueue.cpp",
                "android_view_SurfaceSession.cpp",
                "android_view_TextureView.cpp",
+2 −0
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ extern int register_android_view_InputApplicationHandle(JNIEnv* env);
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_SurfaceSession(JNIEnv* env);
extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
extern int register_android_view_TextureView(JNIEnv* env);
@@ -1488,6 +1489,7 @@ static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_android_view_InputWindowHandle),
        REG_JNI(register_android_view_Surface),
        REG_JNI(register_android_view_SurfaceControl),
        REG_JNI(register_android_view_SurfaceControlFpsListener),
        REG_JNI(register_android_view_SurfaceSession),
        REG_JNI(register_android_view_CompositionSamplingListener),
        REG_JNI(register_android_view_TextureView),
+130 −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 "SurfaceControlFpsListener"

#include <android/gui/BnFpsListener.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include <utils/RefBase.h>

#include "android_util_Binder.h"
#include "core_jni_helpers.h"

namespace android {

namespace {

struct {
    jclass mClass;
    jmethodID mDispatchOnFpsReported;
} gListenerClassInfo;

struct SurfaceControlFpsListener : public gui::BnFpsListener {
    SurfaceControlFpsListener(JNIEnv* env, jobject listener)
          : mListener(env->NewWeakGlobalRef(listener)) {}

    binder::Status onFpsReported(float fps) override {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onFpsReported.");

        jobject listener = env->NewGlobalRef(mListener);
        if (listener == NULL) {
            // Weak reference went out of scope
            return binder::Status::ok();
        }
        env->CallStaticVoidMethod(gListenerClassInfo.mClass,
                                  gListenerClassInfo.mDispatchOnFpsReported, listener,
                                  static_cast<jfloat>(fps));
        env->DeleteGlobalRef(listener);

        if (env->ExceptionCheck()) {
            ALOGE("SurfaceControlFpsListener.onFpsReported() failed.");
            LOGE_EX(env);
            env->ExceptionClear();
        }
        return binder::Status::ok();
    }

protected:
    virtual ~SurfaceControlFpsListener() {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        env->DeleteWeakGlobalRef(mListener);
    }

private:
    jweak mListener;
};

jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) {
    SurfaceControlFpsListener* listener = new SurfaceControlFpsListener(env, obj);
    listener->incStrong((void*)nativeCreate);
    return reinterpret_cast<jlong>(listener);
}

void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
    SurfaceControlFpsListener* listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
    listener->decStrong((void*)nativeCreate);
}

void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr, jlong layerObj) {
    sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
    auto layer = reinterpret_cast<SurfaceControl*>(layerObj);
    sp<IBinder> layerHandle = layer != nullptr ? layer->getHandle() : nullptr;
    if (SurfaceComposerClient::addFpsListener(layerHandle, listener) != OK) {
        constexpr auto error_msg = "Couldn't addFpsListener";
        ALOGE(error_msg);
        jniThrowRuntimeException(env, error_msg);
    }
}

void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
    sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);

    if (SurfaceComposerClient::removeFpsListener(listener) != OK) {
        constexpr auto error_msg = "Couldn't removeFpsListener";
        ALOGE(error_msg);
        jniThrowRuntimeException(env, error_msg);
    }
}

const JNINativeMethod gMethods[] = {
        /* name, signature, funcPtr */
        {"nativeCreate", "(Landroid/view/SurfaceControlFpsListener;)J", (void*)nativeCreate},
        {"nativeDestroy", "(J)V", (void*)nativeDestroy},
        {"nativeRegister", "(JJ)V", (void*)nativeRegister},
        {"nativeUnregister", "(J)V", (void*)nativeUnregister}};

} // namespace

int register_android_view_SurfaceControlFpsListener(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "android/view/SurfaceControlFpsListener", gMethods,
                                       NELEM(gMethods));
    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");

    jclass clazz = env->FindClass("android/view/SurfaceControlFpsListener");
    gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz);
    gListenerClassInfo.mDispatchOnFpsReported =
            env->GetStaticMethodID(clazz, "dispatchOnFpsReported",
                                   "(Landroid/view/SurfaceControlFpsListener;F)V");
    return 0;
}

} // namespace android
+46 −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.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class SurfaceControlFpsListenerTest {

    @Test
    public void registersAndUnregisters() {

        SurfaceControlFpsListener listener = new SurfaceControlFpsListener() {
            @Override
            public void onFpsReported(float fps) {
                // Ignore
            }
        };

        listener.register(new SurfaceControl());

        listener.unregister();
    }
}