Receive refresh rate callbacks from DMS

AChoreographer will use DMS as the source of truth for these callbacks
instead of SurfaceFlinger.

Bug: 154874011
Test: ChoreographerNativeTest
Tes: Manually verify that HWUI is processing refresh rate callbacks
Change-Id: I961a7d1ab335800d3e260ba7564ddca9c0595cfc
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 15f966d..b33bc9e 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -36,10 +36,7 @@
 DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
                                                ISurfaceComposer::VsyncSource vsyncSource,
                                                ISurfaceComposer::ConfigChanged configChanged)
-      : mLooper(looper),
-        mReceiver(vsyncSource, configChanged),
-        mWaitingForVsync(false),
-        mConfigChangeFlag(configChanged) {
+      : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
     ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
 }
 
@@ -92,16 +89,12 @@
     return OK;
 }
 
-void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
-    if (mConfigChangeFlag == configChangeFlag) {
-        return;
-    }
-    status_t status = mReceiver.toggleConfigEvents(configChangeFlag);
+void DisplayEventDispatcher::requestLatestConfig() {
+    status_t status = mReceiver.requestLatestConfig();
     if (status) {
         ALOGW("Failed enable config events, status=%d", status);
         return;
     }
-    mConfigChangeFlag = configChangeFlag;
 }
 
 int DisplayEventDispatcher::getFd() const {
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index fd6aaf8..1fed509 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,10 +79,9 @@
     return NO_INIT;
 }
 
-status_t DisplayEventReceiver::toggleConfigEvents(
-        ISurfaceComposer::ConfigChanged configChangeFlag) {
+status_t DisplayEventReceiver::requestLatestConfig() {
     if (mEventConnection != nullptr) {
-        mEventConnection->toggleConfigEvents(configChangeFlag);
+        mEventConnection->requestLatestConfig();
         return NO_ERROR;
     }
     return NO_INIT;
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index dda5acf..aa74bfd 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,8 +26,8 @@
     STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
     SET_VSYNC_RATE,
     REQUEST_NEXT_VSYNC,
-    TOGGLE_CONFIG_EVENTS,
-    LAST = TOGGLE_CONFIG_EVENTS,
+    REQUEST_LATEST_CONFIG,
+    LAST = REQUEST_LATEST_CONFIG,
 };
 
 } // Anonymous namespace
@@ -55,10 +55,9 @@
                 Tag::REQUEST_NEXT_VSYNC);
     }
 
-    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override {
-        callRemoteAsync<decltype(
-                &IDisplayEventConnection::toggleConfigEvents)>(Tag::TOGGLE_CONFIG_EVENTS,
-                                                               configChangeFlag);
+    void requestLatestConfig() override {
+        callRemoteAsync<decltype(&IDisplayEventConnection::requestLatestConfig)>(
+                Tag::REQUEST_LATEST_CONFIG);
     }
 };
 
@@ -81,8 +80,8 @@
             return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
         case Tag::REQUEST_NEXT_VSYNC:
             return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
-        case Tag::TOGGLE_CONFIG_EVENTS:
-            return callLocalAsync(data, reply, &IDisplayEventConnection::toggleConfigEvents);
+        case Tag::REQUEST_LATEST_CONFIG:
+            return callLocalAsync(data, reply, &IDisplayEventConnection::requestLatestConfig);
     }
 }
 
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index fcdf6bf..f210c34 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -31,7 +31,7 @@
     status_t initialize();
     void dispose();
     status_t scheduleVsync();
-    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+    void requestLatestConfig();
     int getFd() const;
     virtual int handleEvent(int receiveFd, int events, void* data);
 
@@ -42,7 +42,6 @@
     sp<Looper> mLooper;
     DisplayEventReceiver mReceiver;
     bool mWaitingForVsync;
-    ISurfaceComposer::ConfigChanged mConfigChangeFlag;
 
     virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
     virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index d9a0253..8d49184 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -147,9 +147,10 @@
     status_t requestNextVsync();
 
     /*
-     * toggleConfigEvents() toggles delivery of config change events.
+     * requestLatestConfig() force-requests the current config for the primary
+     * display.
      */
-    status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+    status_t requestLatestConfig();
 
 private:
     sp<IDisplayEventConnection> mEventConnection;
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index 8b35ef6..674aafd 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -53,11 +53,9 @@
     virtual void requestNextVsync() = 0; // Asynchronous
 
     /*
-     * togglesConfigEvents() configures whether or not display config changes
-     * should be propagated.
+     * requestLatestConfig() requests the config for the primary display.
      */
-    virtual void toggleConfigEvents(
-            ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous
+    virtual void requestLatestConfig() = 0; // Asynchronous
 };
 
 class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 0ff33ac..ea51245 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -17,24 +17,63 @@
 #define LOG_TAG "Choreographer"
 //#define LOG_NDEBUG 0
 
-#include <apex/choreographer.h>
+#include <android-base/thread_annotations.h>
 #include <gui/DisplayEventDispatcher.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <private/android/choreographer.h>
 #include <utils/Looper.h>
-#include <utils/Mutex.h>
 #include <utils/Timers.h>
 
 #include <cinttypes>
+#include <mutex>
 #include <optional>
 #include <queue>
 #include <thread>
 
-namespace android {
+namespace {
+struct {
+    // Global JVM that is provided by zygote
+    JavaVM* jvm = nullptr;
+    struct {
+        jclass clazz;
+        jmethodID getInstance;
+        jmethodID registerNativeChoreographerForRefreshRateCallbacks;
+        jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
+    } displayManagerGlobal;
+} gJni;
 
-static inline const char* toString(bool value) {
+// Gets the JNIEnv* for this thread, and performs one-off initialization if we
+// have never retrieved a JNIEnv* pointer before.
+JNIEnv* getJniEnv() {
+    if (gJni.jvm == nullptr) {
+        ALOGW("AChoreographer: No JVM provided!");
+        return nullptr;
+    }
+
+    JNIEnv* env = nullptr;
+    if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGD("Attaching thread to JVM for AChoreographer");
+        JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
+        jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+        if (attachResult != JNI_OK) {
+            ALOGE("Unable to attach thread. Error: %d", attachResult);
+            return nullptr;
+        }
+    }
+    if (env == nullptr) {
+        ALOGW("AChoreographer: No JNI env available!");
+    }
+    return env;
+}
+
+inline const char* toString(bool value) {
     return value ? "true" : "false";
 }
+} // namespace
+
+namespace android {
 
 struct FrameCallback {
     AChoreographer_frameCallback callback;
@@ -52,24 +91,43 @@
 struct RefreshRateCallback {
     AChoreographer_refreshRateCallback callback;
     void* data;
+    bool firstCallbackFired = false;
 };
 
+class Choreographer;
+
+struct {
+    std::mutex lock;
+    std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+    bool registeredToDisplayManager GUARDED_BY(lock) = false;
+
+    std::atomic<nsecs_t> mLastKnownVsync = -1;
+} gChoreographers;
+
 class Choreographer : public DisplayEventDispatcher, public MessageHandler {
 public:
-    explicit Choreographer(const sp<Looper>& looper);
+    explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
     void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                   AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
-    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
+            EXCLUDES(gChoreographers.lock);
     void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+    // Drains the queue of pending vsync periods and dispatches refresh rate
+    // updates to callbacks.
+    // The assumption is that this method is only called on a single
+    // processing thread, either by looper or by AChoreographer_handleEvents
+    void handleRefreshRateUpdates();
+    void scheduleLatestConfigRequest();
 
     enum {
         MSG_SCHEDULE_CALLBACKS = 0,
-        MSG_SCHEDULE_VSYNC = 1
+        MSG_SCHEDULE_VSYNC = 1,
+        MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
     };
     virtual void handleMessage(const Message& message) override;
 
     static Choreographer* getForThread();
-    virtual ~Choreographer() = default;
+    virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
 
 private:
     Choreographer(const Choreographer&) = delete;
@@ -81,21 +139,17 @@
 
     void scheduleCallbacks();
 
+    std::mutex mLock;
     // Protected by mLock
     std::priority_queue<FrameCallback> mFrameCallbacks;
-
-    // Protected by mLock
     std::vector<RefreshRateCallback> mRefreshRateCallbacks;
-    nsecs_t mVsyncPeriod = 0;
 
-    mutable Mutex mLock;
+    nsecs_t mLatestVsyncPeriod = -1;
 
     const sp<Looper> mLooper;
     const std::thread::id mThreadId;
-    const std::optional<PhysicalDisplayId> mInternalDisplayId;
 };
 
-
 static thread_local Choreographer* gChoreographer;
 Choreographer* Choreographer::getForThread() {
     if (gChoreographer == nullptr) {
@@ -115,17 +169,47 @@
 }
 
 Choreographer::Choreographer(const sp<Looper>& looper)
-      : DisplayEventDispatcher(looper),
+      : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+                               ISurfaceComposer::ConfigChanged::eConfigChangedDispatch),
         mLooper(looper),
-        mThreadId(std::this_thread::get_id()),
-        mInternalDisplayId(SurfaceComposerClient::getInternalDisplayId()) {}
+        mThreadId(std::this_thread::get_id()) {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.ptrs.push_back(this);
+}
+
+Choreographer::~Choreographer() {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
+                                              gChoreographers.ptrs.end(),
+                                              [=](Choreographer* c) { return c == this; }),
+                               gChoreographers.ptrs.end());
+    // Only poke DisplayManagerGlobal to unregister if we previously registered
+    // callbacks.
+    if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+        JNIEnv* env = getJniEnv();
+        if (env == nullptr) {
+            ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
+            return;
+        }
+        jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+                                                  gJni.displayManagerGlobal.getInstance);
+        if (dmg == nullptr) {
+            ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
+        } else {
+            env->CallVoidMethod(dmg,
+                                gJni.displayManagerGlobal
+                                        .unregisterNativeChoreographerForRefreshRateCallbacks);
+            env->DeleteLocalRef(dmg);
+        }
+    }
+}
 
 void Choreographer::postFrameCallbackDelayed(
         AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     FrameCallback callback{cb, cb64, data, now + delay};
     {
-        AutoMutex _l{mLock};
+        std::lock_guard<std::mutex> _l{mLock};
         mFrameCallbacks.push(callback);
     }
     if (callback.dueTime <= now) {
@@ -150,37 +234,68 @@
 }
 
 void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+    std::lock_guard<std::mutex> _l{mLock};
+    for (const auto& callback : mRefreshRateCallbacks) {
+        // Don't re-add callbacks.
+        if (cb == callback.callback && data == callback.data) {
+            return;
+        }
+    }
+    mRefreshRateCallbacks.emplace_back(
+            RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
+    bool needsRegistration = false;
     {
-        AutoMutex _l{mLock};
-        for (const auto& callback : mRefreshRateCallbacks) {
-            // Don't re-add callbacks.
-            if (cb == callback.callback && data == callback.data) {
-                return;
+        std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+        needsRegistration = !gChoreographers.registeredToDisplayManager;
+    }
+    if (needsRegistration) {
+        JNIEnv* env = getJniEnv();
+        jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+                                                  gJni.displayManagerGlobal.getInstance);
+        if (env == nullptr) {
+            ALOGW("JNI environment is unavailable, skipping registeration");
+            return;
+        }
+        if (dmg == nullptr) {
+            ALOGW("DMS is not initialized yet: skipping registration");
+            return;
+        } else {
+            env->CallVoidMethod(dmg,
+                                gJni.displayManagerGlobal
+                                        .registerNativeChoreographerForRefreshRateCallbacks,
+                                reinterpret_cast<int64_t>(this));
+            env->DeleteLocalRef(dmg);
+            {
+                std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+                gChoreographers.registeredToDisplayManager = true;
             }
         }
-        mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data});
-        toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch);
+    } else {
+        scheduleLatestConfigRequest();
     }
 }
 
 void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
                                                   void* data) {
-    {
-        AutoMutex _l{mLock};
-        mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
-                                                   mRefreshRateCallbacks.end(),
-                                                   [&](const RefreshRateCallback& callback) {
-                                                       return cb == callback.callback &&
-                                                               data == callback.data;
-                                                   }),
-                                    mRefreshRateCallbacks.end());
-        if (mRefreshRateCallbacks.empty()) {
-            toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress);
-            // If callbacks are empty then clear out the most recently seen
-            // vsync period so that when another callback is registered then the
-            // up-to-date refresh rate can be communicated to the app again.
-            mVsyncPeriod = 0;
-        }
+    std::lock_guard<std::mutex> _l{mLock};
+    mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+                                               mRefreshRateCallbacks.end(),
+                                               [&](const RefreshRateCallback& callback) {
+                                                   return cb == callback.callback &&
+                                                           data == callback.data;
+                                               }),
+                                mRefreshRateCallbacks.end());
+}
+
+void Choreographer::scheduleLatestConfigRequest() {
+    if (mLooper != nullptr) {
+        Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
+        mLooper->sendMessage(this, m);
+    } else {
+        // If the looper thread is detached from Choreographer, then refresh rate
+        // changes will be handled in AChoreographer_handlePendingEvents, so we
+        // need to redispatch a config from SF
+        requestLatestConfig();
     }
 }
 
@@ -188,7 +303,7 @@
     const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     nsecs_t dueTime;
     {
-        AutoMutex _{mLock};
+        std::lock_guard<std::mutex> _l{mLock};
         // If there are no pending callbacks then don't schedule a vsync
         if (mFrameCallbacks.empty()) {
             return;
@@ -203,13 +318,35 @@
     }
 }
 
+void Choreographer::handleRefreshRateUpdates() {
+    std::vector<RefreshRateCallback> callbacks{};
+    const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
+    const nsecs_t lastPeriod = mLatestVsyncPeriod;
+    if (pendingPeriod > 0) {
+        mLatestVsyncPeriod = pendingPeriod;
+    }
+    {
+        std::lock_guard<std::mutex> _l{mLock};
+        for (auto& cb : mRefreshRateCallbacks) {
+            callbacks.push_back(cb);
+            cb.firstCallbackFired = true;
+        }
+    }
+
+    for (auto& cb : callbacks) {
+        if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
+            cb.callback(pendingPeriod, cb.data);
+        }
+    }
+}
+
 // TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
 // internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
 // the internal display implicitly.
 void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
     std::vector<FrameCallback> callbacks{};
     {
-        AutoMutex _l{mLock};
+        std::lock_guard<std::mutex> _l{mLock};
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
         while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
             callbacks.push_back(mFrameCallbacks.top());
@@ -236,20 +373,29 @@
 // display, so as such Choreographer does not support the notion of multiple
 // displays. When multi-display choreographer is properly supported, then
 // PhysicalDisplayId should no longer be ignored.
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t,
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
                                           nsecs_t vsyncPeriod) {
+    ALOGV("choreographer %p ~ received config change event "
+          "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).",
+          this, displayId, configId);
+
+    const nsecs_t lastPeriod = mLatestVsyncPeriod;
+    std::vector<RefreshRateCallback> callbacks{};
     {
-        AutoMutex _l{mLock};
-        for (const auto& cb : mRefreshRateCallbacks) {
-            // Only perform the callback when the old refresh rate is different
-            // from the new refresh rate, so that we don't dispatch the callback
-            // on every single configuration change.
-            if (mVsyncPeriod != vsyncPeriod) {
-                cb.callback(vsyncPeriod, cb.data);
-            }
+        std::lock_guard<std::mutex> _l{mLock};
+        for (auto& cb : mRefreshRateCallbacks) {
+            callbacks.push_back(cb);
+            cb.firstCallbackFired = true;
         }
-        mVsyncPeriod = vsyncPeriod;
     }
+
+    for (auto& cb : callbacks) {
+        if (!cb.firstCallbackFired || (vsyncPeriod > 0 && vsyncPeriod != lastPeriod)) {
+            cb.callback(vsyncPeriod, cb.data);
+        }
+    }
+
+    mLatestVsyncPeriod = vsyncPeriod;
 }
 
 void Choreographer::handleMessage(const Message& message) {
@@ -260,19 +406,80 @@
     case MSG_SCHEDULE_VSYNC:
         scheduleVsync();
         break;
+    case MSG_HANDLE_REFRESH_RATE_UPDATES:
+        handleRefreshRateUpdates();
+        break;
     }
 }
 
-}
-
-/* Glue for the NDK interface */
-
+} // namespace android
 using namespace android;
 
 static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
     return reinterpret_cast<Choreographer*>(choreographer);
 }
 
+// Glue for private C api
+namespace android {
+void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.mLastKnownVsync.store(vsyncPeriod);
+    for (auto c : gChoreographers.ptrs) {
+        c->scheduleLatestConfigRequest();
+    }
+}
+
+void AChoreographer_initJVM(JNIEnv* env) {
+    env->GetJavaVM(&gJni.jvm);
+    // Now we need to find the java classes.
+    jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
+    gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
+    gJni.displayManagerGlobal.getInstance =
+            env->GetStaticMethodID(dmgClass, "getInstance",
+                                   "()Landroid/hardware/display/DisplayManagerGlobal;");
+    gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
+            env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
+    gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
+            env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
+                             "()V");
+}
+
+AChoreographer* AChoreographer_routeGetInstance() {
+    return AChoreographer_getInstance();
+}
+void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
+                                           AChoreographer_frameCallback callback, void* data) {
+    return AChoreographer_postFrameCallback(choreographer, callback, data);
+}
+void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
+                                                  AChoreographer_frameCallback callback, void* data,
+                                                  long delayMillis) {
+    return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis);
+}
+void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
+                                             AChoreographer_frameCallback64 callback, void* data) {
+    return AChoreographer_postFrameCallback64(choreographer, callback, data);
+}
+void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
+                                                    AChoreographer_frameCallback64 callback,
+                                                    void* data, uint32_t delayMillis) {
+    return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
+}
+void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
+                                                     AChoreographer_refreshRateCallback callback,
+                                                     void* data) {
+    return AChoreographer_registerRefreshRateCallback(choreographer, callback, data);
+}
+void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                       AChoreographer_refreshRateCallback callback,
+                                                       void* data) {
+    return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
+}
+
+} // namespace android
+
+/* Glue for the NDK interface */
+
 static inline const Choreographer* AChoreographer_to_Choreographer(
         const AChoreographer* choreographer) {
     return reinterpret_cast<const Choreographer*>(choreographer);
@@ -343,5 +550,6 @@
     // Pass dummy fd and events args to handleEvent, since the underlying
     // DisplayEventDispatcher doesn't need them outside of validating that a
     // Looper instance didn't break, but these args circumvent those checks.
-    AChoreographer_to_Choreographer(choreographer)->handleEvent(-1, Looper::EVENT_INPUT, data);
+    Choreographer* impl = AChoreographer_to_Choreographer(choreographer);
+    impl->handleEvent(-1, Looper::EVENT_INPUT, data);
 }
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index c956578..f56b3a2 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -12,23 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-ndk_headers {
-    name: "libnativedisplay_ndk_headers",
-    from: "include/android",
-    to: "android",
-    srcs: ["include/android/*.h"],
-    license: "NOTICE",
-}
-
 cc_library_headers {
     name: "libnativedisplay_headers",
-    export_include_dirs: ["include"],
+    export_include_dirs: ["include",],
 }
 
-cc_library {
+cc_library_shared {
     name: "libnativedisplay",
     export_include_dirs: [
         "include",
+        "include-private",
     ],
 
     clang: true,
@@ -63,6 +56,10 @@
         "libnativehelper",
     ],
 
+    export_shared_lib_headers: [
+        "libnativehelper",
+    ],
+
     header_libs: [
         "libnativedisplay_headers",
     ],
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
new file mode 100644
index 0000000..2164930
--- /dev/null
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <apex/choreographer.h>
+#include <inttypes.h>
+#include <nativehelper/JNIHelp.h>
+
+namespace android {
+
+// Registers the global JVM for AChoreographer
+void AChoreographer_initJVM(JNIEnv* env);
+
+// Signals all AChoregorapher* instances that a new vsync period is available
+// for consumption by callbacks.
+void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
+
+// Trampoline functions allowing libandroid.so to define the NDK symbols without including
+// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
+// maintains global state, libnativedisplay can never be directly statically
+// linked so that global state won't be duplicated. This way libandroid.so can
+// reroute the NDK methods into the implementations defined by libnativedisplay
+AChoreographer* AChoreographer_routeGetInstance();
+void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
+                                           AChoreographer_frameCallback callback, void* data);
+void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
+                                                  AChoreographer_frameCallback callback, void* data,
+                                                  long delayMillis);
+void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
+                                             AChoreographer_frameCallback64 callback, void* data);
+void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
+                                                    AChoreographer_frameCallback64 callback,
+                                                    void* data, uint32_t delayMillis);
+void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
+                                                     AChoreographer_refreshRateCallback callback,
+                                                     void* data);
+void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                       AChoreographer_refreshRateCallback callback,
+                                                       void* data);
+
+} // namespace android
diff --git a/libs/nativedisplay/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h
deleted file mode 100644
index 5fd3de9..0000000
--- a/libs/nativedisplay/include/android/choreographer.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-/**
- * @addtogroup Choreographer
- * @{
- */
-
-/**
- * @file choreographer.h
- */
-
-#ifndef ANDROID_CHOREOGRAPHER_H
-#define ANDROID_CHOREOGRAPHER_H
-
-#include <stdint.h>
-#include <sys/cdefs.h>
-
-__BEGIN_DECLS
-
-struct AChoreographer;
-typedef struct AChoreographer AChoreographer;
-
-/**
- * Prototype of the function that is called when a new frame is being rendered.
- * It's passed the time that the frame is being rendered as nanoseconds in the
- * CLOCK_MONOTONIC time base, as well as the data pointer provided by the
- * application that registered a callback. All callbacks that run as part of
- * rendering a frame will observe the same frame time, so it should be used
- * whenever events need to be synchronized (e.g. animations).
- */
-typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);
-
-/**
- * Prototype of the function that is called when a new frame is being rendered.
- * It's passed the time that the frame is being rendered as nanoseconds in the
- * CLOCK_MONOTONIC time base, as well as the data pointer provided by the
- * application that registered a callback. All callbacks that run as part of
- * rendering a frame will observe the same frame time, so it should be used
- * whenever events need to be synchronized (e.g. animations).
- */
-typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
-
-/**
- * Prototype of the function that is called when the display refresh rate
- * changes. It's passed the new vsync period in nanoseconds, as well as the data
- * pointer provided by the application that registered a callback.
- */
-typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
-
-#if __ANDROID_API__ >= 24
-
-/**
- * Get the AChoreographer instance for the current thread. This must be called
- * on an ALooper thread.
- *
- * Available since API level 24.
- */
-AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24);
-
-/**
- * Deprecated: Use AChoreographer_postFrameCallback64 instead.
- */
-void AChoreographer_postFrameCallback(AChoreographer* choreographer,
-        AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
-
-/**
- * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead.
- */
-void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
-                AChoreographer_frameCallback callback, void* data,
-                long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
-
-#endif /* __ANDROID_API__ >= 24 */
-
-#if __ANDROID_API__ >= 29
-
-/**
- * Power a callback to be run on the next frame.  The data pointer provided will
- * be passed to the callback function when it's called.
- *
- * Available since API level 29.
- */
-void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
-                AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
-
-/**
- * Post a callback to be run on the frame following the specified delay.  The
- * data pointer provided will be passed to the callback function when it's
- * called.
- *
- * Available since API level 29.
- */
-void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
-                AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29);
-
-#endif /* __ANDROID_API__ >= 29 */
-
-#if __ANDROID_API__ >= 30
-
-/**
- * Registers a callback to be run when the display refresh rate changes. The
- * data pointer provided will be passed to the callback function when it's
- * called. The same callback may be registered multiple times, provided that a
- * different data pointer is provided each time.
- *
- * If an application registers a callback for this choreographer instance when
- * no new callbacks were previously registered, that callback is guaranteed to
- * be dispatched. However, if the callback and associated data pointer are
- * unregistered prior to running the callback, then the callback may be silently
- * dropped.
- *
- * This api is thread-safe. Any thread is allowed to register a new refresh
- * rate callback for the choreographer instance.
- */
-void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
-                                                AChoreographer_refreshRateCallback, void* data);
-
-/**
- * Unregisters a callback to be run when the display refresh rate changes, along
- * with the data pointer previously provided when registering the callback. The
- * callback is only unregistered when the data pointer matches one that was
- * previously registered.
- *
- * This api is thread-safe. Any thread is allowed to unregister an existing
- * refresh rate callback for the choreographer instance. When a refresh rate
- * callback and associated data pointer are unregistered, then there is a
- * guarantee that when the unregistration completes that that callback will not
- * be run with the data pointer passed.
- */
-void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
-                                                  AChoreographer_refreshRateCallback, void* data);
-#endif /* __ANDROID_API__ >= 30 */
-
-__END_DECLS
-
-#endif // ANDROID_CHOREOGRAPHER_H
-
-/** @} */
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 6a94a77..e2d036b 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -19,7 +19,7 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-
+#include <nativehelper/JNIHelp.h>
 #include <system/graphics.h>
 
 // This file provides a facade API on top of SurfaceTexture, which avoids using
@@ -30,6 +30,20 @@
 
 namespace android {
 
+// Trampoline functions allowing libandroid.so to define the NDK symbols without including
+// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
+// maintains global state, libnativedisplay can never be directly statically
+// linked so that global state won't be duplicated. This way libandroid.so can
+// reroute the NDK methods into the implementations defined by libnativedisplay
+ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st);
+int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName);
+int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st);
+void ASurfaceTexture_routeRelease(ASurfaceTexture* st);
+int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st);
+void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]);
+int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st);
+ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture);
+
 /**
  * ASurfaceTexture_getCurrentTextureTarget returns the texture target of the
  * current texture.
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 483fb25..fc59431 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -20,6 +20,15 @@
 LIBNATIVEDISPLAY_PLATFORM {
   global:
     extern "C++" {
+      android::AChoreographer_initJVM*;
+      android::AChoreographer_routeGetInstance*;
+      android::AChoreographer_routePostFrameCallback*;
+      android::AChoreographer_routePostFrameCallbackDelayed*;
+      android::AChoreographer_routePostFrameCallback64*;
+      android::AChoreographer_routePostFrameCallbackDelayed64*;
+      android::AChoreographer_routeRegisterRefreshRateCallback*;
+      android::AChoreographer_routeUnregisterRefreshRateCallback*;
+      android::AChoreographer_signalRefreshRateCallbacks*;
       android::ADisplay_acquirePhysicalDisplays*;
       android::ADisplay_release*;
       android::ADisplay_getMaxSupportedFps*;
@@ -36,6 +45,14 @@
       android::ASurfaceTexture_takeConsumerOwnership*;
       android::ASurfaceTexture_releaseConsumerOwnership*;
       android::ASurfaceTexture_dequeueBuffer*;
+      android::ASurfaceTexture_routeAcquireANativeWindow*;
+      android::ASurfaceTexture_routeAttachToGLContext*;
+      android::ASurfaceTexture_routeDetachFromGLContext*;
+      android::ASurfaceTexture_routeGetTimestamp*;
+      android::ASurfaceTexture_routeGetTransformMatrix*;
+      android::ASurfaceTexture_routeUpdateTexImage*;
+      android::ASurfaceTexture_routeFromSurfaceTexture*;
+      android::ASurfaceTexture_routeRelease*;
       android::SurfaceTexture*;
     };
     ASurfaceTexture_acquireANativeWindow;
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index 1670fbb..d1bcd8d 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -149,6 +149,37 @@
 
 // The following functions are private/unstable API.
 namespace android {
+ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st) {
+    return ASurfaceTexture_acquireANativeWindow(st);
+}
+
+int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName) {
+    return ASurfaceTexture_attachToGLContext(st, texName);
+}
+
+void ASurfaceTexture_routeRelease(ASurfaceTexture* st) {
+    return ASurfaceTexture_release(st);
+}
+
+int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st) {
+    return ASurfaceTexture_detachFromGLContext(st);
+}
+
+int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st) {
+    return ASurfaceTexture_updateTexImage(st);
+}
+
+void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+    return ASurfaceTexture_getTransformMatrix(st, mtx);
+}
+
+int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st) {
+    return ASurfaceTexture_getTimestamp(st);
+}
+
+ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+    return ASurfaceTexture_fromSurfaceTexture(env, surfacetexture);
+}
 
 unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
     return st->consumer->getCurrentTextureTarget();