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

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

Merge "Grab display information for render thread through java"

parents 76666de4 e55aa630
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -6403,6 +6403,9 @@ public final class ActivityThread extends ClientTransactionHandler {
        HardwareRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE);
        HardwareRenderer.setPackageName(data.appInfo.packageName);

        // Pass the current context to HardwareRenderer
        HardwareRenderer.setContextForInit(getSystemContext());

        /**
         * Initialize the default http proxy in this process for the reasons we set the time zone.
         */
+112 −0
Original line number Diff line number Diff line
@@ -22,13 +22,17 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.TimeUtils;
import android.view.Display;
import android.view.Display.Mode;
import android.view.IGraphicsStats;
import android.view.IGraphicsStatsCallback;
import android.view.NativeVectorDrawableAnimator;
@@ -42,7 +46,10 @@ import java.io.File;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.stream.Stream;

import sun.misc.Cleaner;

@@ -907,6 +914,7 @@ public class HardwareRenderer {
     */
    public static void setIsolatedProcess(boolean isIsolated) {
        nSetIsolatedProcess(isIsolated);
        ProcessInitializer.sInstance.setIsolated(isIsolated);
    }

    /**
@@ -991,6 +999,17 @@ public class HardwareRenderer {
        ProcessInitializer.sInstance.setPackageName(packageName);
    }

    /**
     * Gets a context for process initialization
     *
     * TODO: Remove this once there is a static method for retrieving an application's context.
     *
     * @hide
     */
    public static void setContextForInit(Context context) {
        ProcessInitializer.sInstance.setContext(context);
    }

    private static final class DestroyContextRunnable implements Runnable {
        private final long mNativeInstance;

@@ -1007,8 +1026,34 @@ public class HardwareRenderer {
    private static class ProcessInitializer {
        static ProcessInitializer sInstance = new ProcessInitializer();

        // Magic values from android/data_space.h
        private static final int INTERNAL_DATASPACE_SRGB = 142671872;
        private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
        private static final int INTERNAL_DATASPACE_SCRGB = 411107328;

        private enum Dataspace {
            DISPLAY_P3(ColorSpace.Named.DISPLAY_P3, INTERNAL_DATASPACE_DISPLAY_P3),
            SCRGB(ColorSpace.Named.EXTENDED_SRGB, INTERNAL_DATASPACE_SCRGB),
            SRGB(ColorSpace.Named.SRGB, INTERNAL_DATASPACE_SRGB);

            private final ColorSpace.Named mColorSpace;
            private final int mNativeDataspace;
            Dataspace(ColorSpace.Named colorSpace, int nativeDataspace) {
                this.mColorSpace = colorSpace;
                this.mNativeDataspace = nativeDataspace;
            }

            static Optional<Dataspace> find(ColorSpace colorSpace) {
                return Stream.of(Dataspace.values())
                        .filter(d -> ColorSpace.get(d.mColorSpace).equals(colorSpace))
                        .findFirst();
            }
        }

        private boolean mInitialized = false;

        private boolean mIsolated = false;
        private Context mContext;
        private String mPackageName;
        private IGraphicsStats mGraphicsStatsService;
        private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() {
@@ -1026,12 +1071,23 @@ public class HardwareRenderer {
            mPackageName = name;
        }

        synchronized void setIsolated(boolean isolated) {
            if (mInitialized) return;
            mIsolated = isolated;
        }

        synchronized void setContext(Context context) {
            if (mInitialized) return;
            mContext = context;
        }

        synchronized void init(long renderProxy) {
            if (mInitialized) return;
            mInitialized = true;

            initSched(renderProxy);
            initGraphicsStats();
            initDisplayInfo();
        }

        private void initSched(long renderProxy) {
@@ -1056,6 +1112,58 @@ public class HardwareRenderer {
            }
        }

        private void initDisplayInfo() {
            if (mContext == null) return;

            // If we're in an isolated sandbox mode then we shouldn't try to communicate with DMS
            if (mIsolated) {
                // Defensively clear out the context in case we were passed a context that can leak
                // if we live longer than it, e.g. an activity context.
                mContext = null;
                return;
            }

            DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
            if (dm == null) {
                Log.d(LOG_TAG, "Failed to find DisplayManager for display-based configuration");
                return;
            }

            Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
            if (display == null) {
                Log.d(LOG_TAG, "Failed to find default display for display-based configuration");
                return;
            }

            Dataspace wideColorDataspace =
                    Optional.ofNullable(display.getPreferredWideGamutColorSpace())
                            .flatMap(Dataspace::find)
                            // Default to SRGB if the display doesn't support wide color
                            .orElse(Dataspace.SRGB);

            float maxRefreshRate =
                    (float) Arrays.stream(display.getSupportedModes())
                            .mapToDouble(Mode::getRefreshRate)
                            .max()
                            .orElseGet(() -> {
                                Log.i(LOG_TAG, "Failed to find the maximum display refresh rate");
                                // Assume that the max refresh rate is 60hz if we can't find one.
                                return 60.0;
                            });
            // Grab the physical screen dimensions from the active display mode
            // Strictly speaking the screen resolution may not always be constant - it is for
            // sizing the font cache for the underlying rendering thread. Since it's a
            // heuristic we don't need to be always 100% correct.
            Mode activeMode = display.getMode();
            nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
                    activeMode.getRefreshRate(), maxRefreshRate,
                    wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(),
                    display.getPresentationDeadlineNanos());

            // Defensively clear out the context
            mContext = null;
        }

        private void rotateBuffer() {
            nRotateProcessStatsBuffer();
            requestBuffer();
@@ -1207,4 +1315,8 @@ public class HardwareRenderer {
    private static native void nSetForceDark(long nativeProxy, boolean enabled);

    private static native void nSetDisplayDensityDpi(int densityDpi);

    private static native void nInitDisplayInfo(int width, int height, float refreshRate,
            float maxRefreshRate, int wideColorDataspace, long appVsyncOffsetNanos,
            long presentationDeadlineNanos);
}
+58 −70
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */

#include <DeviceInfo.h>
#include <android/hardware_buffer.h>
#include <apex/display.h>
#include <log/log.h>
#include <utils/Errors.h>

@@ -34,88 +36,74 @@ DeviceInfo::DeviceInfo() {
#else
    mMaxTextureSize = -1;
#endif
        updateDisplayInfo();
}
DeviceInfo::~DeviceInfo() {
    ADisplay_release(mDisplays);
}

int DeviceInfo::maxTextureSize() const {
    LOG_ALWAYS_FATAL_IF(mMaxTextureSize < 0, "MaxTextureSize has not been initialized yet.");
    return mMaxTextureSize;
void DeviceInfo::updateDisplayInfo() {
    if (Properties::isolatedProcess) {
        return;
    }

void DeviceInfo::setMaxTextureSize(int maxTextureSize) {
    DeviceInfo::get()->mMaxTextureSize = maxTextureSize;
}
    ADisplay** displays;
    int size = ADisplay_acquirePhysicalDisplays(&displays);

void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) {
    mVsyncPeriod = vsyncPeriod;
    if (size <= 0) {
        LOG_ALWAYS_FATAL("Failed to acquire physical displays for WCG support!");
    }

void DeviceInfo::updateDisplayInfo() {
    if (Properties::isolatedProcess) {
    for (int i = 0; i < size; ++i) {
        // Pick the first internal display for querying the display type
        // In practice this is controlled by a sysprop so it doesn't really
        // matter which display we use.
        if (ADisplay_getDisplayType(displays[i]) == DISPLAY_TYPE_INTERNAL) {
            // We get the dataspace from DisplayManager already. Allocate space
            // for the result here but we don't actually care about using it.
            ADataSpace dataspace;
            AHardwareBuffer_Format pixelFormat;
            ADisplay_getPreferredWideColorFormat(displays[i], &dataspace, &pixelFormat);

            if (pixelFormat == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM) {
                mWideColorType = SkColorType::kN32_SkColorType;
            } else if (pixelFormat == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT) {
                mWideColorType = SkColorType::kRGBA_F16_SkColorType;
            } else {
                LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format: %d", pixelFormat);
            }
            ADisplay_release(displays);
            return;
        }

    if (mCurrentConfig == nullptr) {
        mDisplaysSize = ADisplay_acquirePhysicalDisplays(&mDisplays);
        LOG_ALWAYS_FATAL_IF(mDisplays == nullptr || mDisplaysSize <= 0,
                            "Failed to get physical displays: no connected display: %d!", mDisplaysSize);
        for (size_t i = 0; i < mDisplaysSize; i++) {
            ADisplayType type = ADisplay_getDisplayType(mDisplays[i]);
            if (type == ADisplayType::DISPLAY_TYPE_INTERNAL) {
                mPhysicalDisplayIndex = i;
                break;
    }
    LOG_ALWAYS_FATAL("Failed to find a valid physical display for WCG support!");
}

int DeviceInfo::maxTextureSize() const {
    LOG_ALWAYS_FATAL_IF(mMaxTextureSize < 0, "MaxTextureSize has not been initialized yet.");
    return mMaxTextureSize;
}
        LOG_ALWAYS_FATAL_IF(mPhysicalDisplayIndex < 0, "Failed to find a connected physical display!");

void DeviceInfo::setMaxTextureSize(int maxTextureSize) {
    DeviceInfo::get()->mMaxTextureSize = maxTextureSize;
}

        // Since we now just got the primary display for the first time, then
        // store the primary display metadata here.
        ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex];
        mMaxRefreshRate = ADisplay_getMaxSupportedFps(primaryDisplay);
        ADataSpace dataspace;
        AHardwareBuffer_Format format;
        ADisplay_getPreferredWideColorFormat(primaryDisplay, &dataspace, &format);
void DeviceInfo::setWideColorDataspace(ADataSpace dataspace) {
    switch (dataspace) {
        case ADATASPACE_DISPLAY_P3:
                mWideColorSpace =
            get()->mWideColorSpace =
                    SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
            break;
        case ADATASPACE_SCRGB:
                mWideColorSpace = SkColorSpace::MakeSRGB();
            get()->mWideColorSpace = SkColorSpace::MakeSRGB();
            break;
        case ADATASPACE_SRGB:
            // when sRGB is returned, it means wide color gamut is not supported.
                mWideColorSpace = SkColorSpace::MakeSRGB();
            get()->mWideColorSpace = SkColorSpace::MakeSRGB();
            break;
        default:
            LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
    }
        switch (format) {
            case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
                mWideColorType = SkColorType::kN32_SkColorType;
                break;
            case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
                mWideColorType = SkColorType::kRGBA_F16_SkColorType;
                break;
            default:
                LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format.");
}
    }
    // This method may have been called when the display config changed, so
    // sync with the current configuration.
    ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex];
    status_t status = ADisplay_getCurrentConfig(primaryDisplay, &mCurrentConfig);
    LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status);

    mWidth = ADisplayConfig_getWidth(mCurrentConfig);
    mHeight = ADisplayConfig_getHeight(mCurrentConfig);
    mVsyncPeriod = static_cast<int64_t>(1000000000 / ADisplayConfig_getFps(mCurrentConfig));
    mCompositorOffset = ADisplayConfig_getCompositorOffsetNanos(mCurrentConfig);
    mAppOffset = ADisplayConfig_getAppVsyncOffsetNanos(mCurrentConfig);

void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) {
    mVsyncPeriod = vsyncPeriod;
}

std::atomic<float> DeviceInfo::sDensity = 2.0;
+32 −9
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@
#ifndef DEVICEINFO_H
#define DEVICEINFO_H

#include <apex/display.h>
#include <SkImageInfo.h>
#include <android/data_space.h>

#include <mutex>

#include "utils/Macros.h"

@@ -39,16 +41,34 @@ public:
    // Gets the density in density-independent pixels
    static float getDensity() { return sDensity.load(); }
    static int64_t getVsyncPeriod() { return get()->mVsyncPeriod; }
    static int64_t getCompositorOffset() { return get()->mCompositorOffset; }
    static int64_t getAppOffset() { return get()->mAppOffset; }
    static int64_t getCompositorOffset() { return get()->getCompositorOffsetInternal(); }
    static int64_t getAppOffset() { return get()->mAppVsyncOffsetNanos; }
    // Sets the density in density-independent pixels
    static void setDensity(float density) { sDensity.store(density); }
    static void setMaxRefreshRate(float refreshRate) { get()->mMaxRefreshRate = refreshRate; }
    static void setWidth(int32_t width) { get()->mWidth = width; }
    static void setHeight(int32_t height) { get()->mHeight = height; }
    static void setRefreshRate(float refreshRate) {
        get()->mVsyncPeriod = static_cast<int64_t>(1000000000 / refreshRate);
    }
    static void setPresentationDeadlineNanos(int64_t deadlineNanos) {
        get()->mPresentationDeadlineNanos = deadlineNanos;
    }
    static void setAppVsyncOffsetNanos(int64_t offsetNanos) {
        get()->mAppVsyncOffsetNanos = offsetNanos;
    }
    static void setWideColorDataspace(ADataSpace dataspace);

    // this value is only valid after the GPU has been initialized and there is a valid graphics
    // context or if you are using the HWUI_NULL_GPU
    int maxTextureSize() const;
    sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; }
    SkColorType getWideColorType() const { return mWideColorType; }
    SkColorType getWideColorType() {
        static std::once_flag kFlag;
        // lazily update display info from SF here, so that the call is performed by RenderThread.
        std::call_once(kFlag, [&, this]() { updateDisplayInfo(); });
        return mWideColorType;
    }

    // This method should be called whenever the display refresh rate changes.
    void onRefreshRateChanged(int64_t vsyncPeriod);
@@ -57,23 +77,26 @@ private:
    friend class renderthread::RenderThread;
    static void setMaxTextureSize(int maxTextureSize);
    void updateDisplayInfo();
    int64_t getCompositorOffsetInternal() const {
        // Assume that SF takes around a millisecond to latch buffers after
        // waking up
        return mVsyncPeriod - (mPresentationDeadlineNanos - 1000000);
    }

    DeviceInfo();
    ~DeviceInfo();
    ~DeviceInfo() = default;

    int mMaxTextureSize;
    sk_sp<SkColorSpace> mWideColorSpace = SkColorSpace::MakeSRGB();
    SkColorType mWideColorType = SkColorType::kN32_SkColorType;
    ADisplayConfig* mCurrentConfig = nullptr;
    ADisplay** mDisplays = nullptr;
    int mDisplaysSize = 0;
    int mPhysicalDisplayIndex = -1;
    float mMaxRefreshRate = 60.0;
    int32_t mWidth = 1080;
    int32_t mHeight = 1920;
    int64_t mVsyncPeriod = 16666666;
    int64_t mCompositorOffset = 0;
    int64_t mAppOffset = 0;
    int64_t mPresentationDeadlineNanos = 0;
    int64_t mAppVsyncOffsetNanos = 0;

    // Density is not retrieved from the ADisplay apis, so this may potentially
    // be called on multiple threads.
+92 −62
Original line number Diff line number Diff line
@@ -600,6 +600,21 @@ static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass,
    DeviceInfo::setDensity(density);
}

static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint physicalWidth,
                                                          jint physicalHeight, jfloat refreshRate,
                                                          jfloat maxRefreshRate,
                                                          jint wideColorDataspace,
                                                          jlong appVsyncOffsetNanos,
                                                          jlong presentationDeadlineNanos) {
    DeviceInfo::setWidth(physicalWidth);
    DeviceInfo::setHeight(physicalHeight);
    DeviceInfo::setRefreshRate(refreshRate);
    DeviceInfo::setMaxRefreshRate(maxRefreshRate);
    DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace));
    DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos);
    DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
}

// ----------------------------------------------------------------------------
// HardwareRendererObserver
// ----------------------------------------------------------------------------
@@ -644,15 +659,19 @@ static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, job
const char* const kClassPathName = "android/graphics/HardwareRenderer";

static const JNINativeMethod gMethods[] = {
    { "nRotateProcessStatsBuffer", "()V", (void*) android_view_ThreadedRenderer_rotateProcessStatsBuffer },
    { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
        {"nRotateProcessStatsBuffer", "()V",
         (void*)android_view_ThreadedRenderer_rotateProcessStatsBuffer},
        {"nSetProcessStatsBuffer", "(I)V",
         (void*)android_view_ThreadedRenderer_setProcessStatsBuffer},
        {"nGetRenderThreadTid", "(J)I", (void*)android_view_ThreadedRenderer_getRenderThreadTid},
        {"nCreateRootRenderNode", "()J", (void*)android_view_ThreadedRenderer_createRootRenderNode},
        {"nCreateProxy", "(ZZJ)J", (void*)android_view_ThreadedRenderer_createProxy},
        {"nDeleteProxy", "(J)V", (void*)android_view_ThreadedRenderer_deleteProxy},
    { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties },
        {"nLoadSystemProperties", "(J)Z",
         (void*)android_view_ThreadedRenderer_loadSystemProperties},
        {"nSetName", "(JLjava/lang/String;)V", (void*)android_view_ThreadedRenderer_setName},
    { "nSetSurface", "(JLandroid/view/Surface;Z)V", (void*) android_view_ThreadedRenderer_setSurface },
        {"nSetSurface", "(JLandroid/view/Surface;Z)V",
         (void*)android_view_ThreadedRenderer_setSurface},
        {"nPause", "(J)Z", (void*)android_view_ThreadedRenderer_pause},
        {"nSetStopped", "(JZ)V", (void*)android_view_ThreadedRenderer_setStopped},
        {"nSetLightAlpha", "(JFF)V", (void*)android_view_ThreadedRenderer_setLightAlpha},
@@ -661,33 +680,42 @@ static const JNINativeMethod gMethods[] = {
        {"nSetWideGamut", "(JZ)V", (void*)android_view_ThreadedRenderer_setWideGamut},
        {"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
        {"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
    { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
    { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator },
        {"nRegisterAnimatingRenderNode", "(JJ)V",
         (void*)android_view_ThreadedRenderer_registerAnimatingRenderNode},
        {"nRegisterVectorDrawableAnimator", "(JJ)V",
         (void*)android_view_ThreadedRenderer_registerVectorDrawableAnimator},
        {"nInvokeFunctor", "(JZ)V", (void*)android_view_ThreadedRenderer_invokeFunctor},
        {"nCreateTextureLayer", "(J)J", (void*)android_view_ThreadedRenderer_createTextureLayer},
        {"nBuildLayer", "(JJ)V", (void*)android_view_ThreadedRenderer_buildLayer},
        {"nCopyLayerInto", "(JJJ)Z", (void*)android_view_ThreadedRenderer_copyLayerInto},
        {"nPushLayerUpdate", "(JJ)V", (void*)android_view_ThreadedRenderer_pushLayerUpdate},
        {"nCancelLayerUpdate", "(JJ)V", (void*)android_view_ThreadedRenderer_cancelLayerUpdate},
    { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture },
    { "nDestroyHardwareResources", "(J)V", (void*) android_view_ThreadedRenderer_destroyHardwareResources },
        {"nDetachSurfaceTexture", "(JJ)V",
         (void*)android_view_ThreadedRenderer_detachSurfaceTexture},
        {"nDestroyHardwareResources", "(J)V",
         (void*)android_view_ThreadedRenderer_destroyHardwareResources},
        {"nTrimMemory", "(I)V", (void*)android_view_ThreadedRenderer_trimMemory},
    { "nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V",  (void*) android_view_ThreadedRenderer_overrideProperty },
        {"nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V",
         (void*)android_view_ThreadedRenderer_overrideProperty},
        {"nFence", "(J)V", (void*)android_view_ThreadedRenderer_fence},
        {"nStopDrawing", "(J)V", (void*)android_view_ThreadedRenderer_stopDrawing},
        {"nNotifyFramePending", "(J)V", (void*)android_view_ThreadedRenderer_notifyFramePending},
    { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
        {"nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V",
         (void*)android_view_ThreadedRenderer_dumpProfileInfo},
        {"setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V",
         (void*)android_view_ThreadedRenderer_setupShadersDiskCache},
        {"nAddRenderNode", "(JJZ)V", (void*)android_view_ThreadedRenderer_addRenderNode},
        {"nRemoveRenderNode", "(JJ)V", (void*)android_view_ThreadedRenderer_removeRenderNode},
        {"nDrawRenderNode", "(JJ)V", (void*)android_view_ThreadedRendererd_drawRenderNode},
    { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
    { "nSetPictureCaptureCallback", "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V",
        {"nSetContentDrawBounds", "(JIIII)V",
         (void*)android_view_ThreadedRenderer_setContentDrawBounds},
        {"nSetPictureCaptureCallback",
         "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V",
         (void*)android_view_ThreadedRenderer_setPictureCapturedCallbackJNI},
        {"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
         (void*)android_view_ThreadedRenderer_setFrameCallback},
    { "nSetFrameCompleteCallback", "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V",
        {"nSetFrameCompleteCallback",
         "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V",
         (void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
        {"nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver},
        {"nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver},
@@ -704,7 +732,9 @@ static const JNINativeMethod gMethods[] = {
        {"nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority},
        {"nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers},
        {"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark},
    { "nSetDisplayDensityDpi", "(I)V", (void*)android_view_ThreadedRenderer_setDisplayDensityDpi },
        {"nSetDisplayDensityDpi", "(I)V",
         (void*)android_view_ThreadedRenderer_setDisplayDensityDpi},
        {"nInitDisplayInfo", "(IIFFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
        {"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
};