Loading core/java/android/view/SurfaceControlFpsListener.java 0 → 100644 +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); } core/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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", Loading core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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), Loading core/jni/android_view_SurfaceControlFpsListener.cpp 0 → 100644 +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 core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java 0 → 100644 +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(); } } Loading
core/java/android/view/SurfaceControlFpsListener.java 0 → 100644 +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); }
core/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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", Loading
core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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), Loading
core/jni/android_view_SurfaceControlFpsListener.cpp 0 → 100644 +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
core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java 0 → 100644 +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(); } }