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

Commit 644553d0 authored by Alec Mouri's avatar Alec Mouri
Browse files

JNI wrapper for FpsListener.

Hidden api for exposing the fps counter to WM Shell, so that the fps for
a task can be reported.

Bug: 174956756
Test: E2E integration with SurfaceFlinger for angry birds
Change-Id: I9818aded2755ad07fd3abf3f616e8e6940bad268
parent 5febe8f6
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
@@ -102,6 +102,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
@@ -119,6 +119,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);
@@ -1486,6 +1487,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();
    }
}