Merge "[RenderEngine] Make BindNativeBufferAsFramebuffer more generic."
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 9216e75..4362019 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1290,6 +1290,8 @@
 
     DumpPacketStats();
 
+    RunDumpsys("EBPF MAP STATS", {"netd", "trafficcontroller"});
+
     DoKmsg();
 
     DumpIpAddrAndRules();
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index 1f5d535..e5c5e5b 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -97,6 +97,10 @@
     //
     int unflatten(void const* buffer, size_t size);
 
+    // clear flushes out all contents of the cache then the BlobCache, leaving
+    // it in an empty state.
+    void clear() { mCacheEntries.clear(); }
+
 protected:
     // mMaxTotalSize is the maximum size that all cache entries can occupy. This
     // includes space for both keys and values. When a call to BlobCache::set
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 05535a5..da57511 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -125,6 +125,7 @@
         "Scheduler/EventControlThread.cpp",
         "Scheduler/EventThread.cpp",
         "Scheduler/MessageQueue.cpp",
+        "Scheduler/Scheduler.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceInterceptor.cpp",
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index fb7655b..29de50c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -672,10 +672,6 @@
     onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform);
 }
 
-void Layer::draw(const RenderArea& renderArea) {
-    onDraw(renderArea, Region(renderArea.getBounds()), false);
-}
-
 void Layer::clearWithOpenGL(const RenderArea& renderArea, float red, float green, float blue,
                             float alpha) const {
     auto& engine(mFlinger->getRenderEngine());
@@ -684,11 +680,6 @@
     engine.drawMesh(getBE().mMesh);
 }
 
-void Layer::clearWithOpenGL(const RenderArea& renderArea) const {
-    getBE().compositionInfo.firstClear = true;
-    computeGeometry(renderArea, getBE().mMesh, false);
-}
-
 void Layer::setCompositionType(int32_t displayId, HWC2::Composition type, bool /*callIntoHwc*/) {
     if (getBE().mHwcLayers.count(displayId) == 0) {
         ALOGE("setCompositionType called without a valid HWC layer");
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 67c16aa..9bd28b8 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -407,7 +407,6 @@
      */
     void draw(const RenderArea& renderArea, const Region& clip);
     void draw(const RenderArea& renderArea, bool useIdentityTransform);
-    void draw(const RenderArea& renderArea);
 
     /*
      * doTransaction - process the transaction. This is a good place to figure
@@ -505,8 +504,6 @@
 
     // -----------------------------------------------------------------------
 
-    void clearWithOpenGL(const RenderArea& renderArea) const;
-
     inline const State& getDrawingState() const { return mDrawingState; }
     inline const State& getCurrentState() const { return mCurrentState; }
     inline State& getCurrentState() { return mCurrentState; }
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index b84177c..fa2b0a6 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -45,11 +45,27 @@
 
 namespace impl {
 
+EventThread::EventThread(std::unique_ptr<VSyncSource> src,
+                         ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+      : EventThread(nullptr, std::move(src), resyncWithRateLimitCallback, interceptVSyncsCallback,
+                    threadName) {}
+
 EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                          InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+      : EventThread(src, nullptr, resyncWithRateLimitCallback, interceptVSyncsCallback,
+                    threadName) {}
+
+EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
+                         ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
       : mVSyncSource(src),
+        mVSyncSourceUnique(std::move(uniqueSrc)),
         mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
         mInterceptVSyncsCallback(interceptVSyncsCallback) {
+    if (src == nullptr) {
+        mVSyncSource = mVSyncSourceUnique.get();
+    }
     for (auto& event : mVSyncEvent) {
         event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
         event.header.id = 0;
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a0262b2..127891e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -108,8 +108,12 @@
     using ResyncWithRateLimitCallback = std::function<void()>;
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
 
+    // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
     EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                 InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+    EventThread(std::unique_ptr<VSyncSource> src,
+                ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
     ~EventThread();
 
     sp<BnDisplayEventConnection> createEventConnection() const override;
@@ -134,6 +138,11 @@
 private:
     friend EventThreadTest;
 
+    // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
+    EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
+                ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+
     void threadMain();
     Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
                                                            DisplayEventReceiver::Event* event)
@@ -146,8 +155,10 @@
     // Implements VSyncSource::Callback
     void onVSyncEvent(nsecs_t timestamp) override;
 
+    // TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete.
+    VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
+    std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
     // constants
-    VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr;
     const ResyncWithRateLimitCallback mResyncWithRateLimitCallback;
     const InterceptVSyncsCallback mInterceptVSyncsCallback;
 
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 056d381..58355ae 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -101,6 +101,17 @@
                    this);
 }
 
+void MessageQueue::setEventConnection(const sp<BnDisplayEventConnection>& connection) {
+    if (mEventTube.getFd() >= 0) {
+        mLooper->removeFd(mEventTube.getFd());
+    }
+
+    mEvents = connection;
+    mEvents->stealReceiveChannel(&mEventTube);
+    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
+                   this);
+}
+
 void MessageQueue::waitMessage() {
     do {
         IPCThreadState::self()->flushCommands();
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 90d1c72..2ec697e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -85,7 +85,9 @@
     virtual ~MessageQueue();
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
+    // TODO(akrulec): Remove this function once everything is migrated to Scheduler.
     virtual void setEventThread(EventThread* events) = 0;
+    virtual void setEventConnection(const sp<BnDisplayEventConnection>& connection) = 0;
     virtual void waitMessage() = 0;
     virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
     virtual void invalidate() = 0;
@@ -125,6 +127,7 @@
     ~MessageQueue() override = default;
     void init(const sp<SurfaceFlinger>& flinger) override;
     void setEventThread(android::EventThread* events) override;
+    void setEventConnection(const sp<BnDisplayEventConnection>& connection) override;
 
     void waitMessage() override;
     status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
new file mode 100644
index 0000000..a7c08cf
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Scheduler.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <memory>
+
+#include <gui/ISurfaceComposer.h>
+
+#include "DispSync.h"
+#include "DispSyncSource.h"
+#include "EventThread.h"
+#include "InjectVSyncSource.h"
+
+namespace android {
+
+std::atomic<int64_t> Scheduler::sNextId = 0;
+
+sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
+        const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+        impl::EventThread::ResyncWithRateLimitCallback resyncCallback,
+        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+    const int64_t id = sNextId++;
+    ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
+
+    std::unique_ptr<VSyncSource> eventThreadSource =
+            std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
+    std::unique_ptr<EventThread> eventThread =
+            std::make_unique<impl::EventThread>(std::move(eventThreadSource), resyncCallback,
+                                                interceptCallback, connectionName);
+    auto connection = std::make_unique<Connection>(new ConnectionHandle(id),
+                                                   eventThread->createEventConnection(),
+                                                   std::move(eventThread));
+    mConnections.insert(std::make_pair(id, std::move(connection)));
+    return mConnections[id]->handle;
+}
+
+sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
+        const sp<Scheduler::ConnectionHandle>& handle) {
+    if (mConnections.count(handle->id) != 0) {
+        return mConnections[handle->id]->thread->createEventConnection();
+    }
+    return nullptr;
+}
+
+EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
+    if (mConnections.count(handle->id) != 0) {
+        return mConnections[handle->id]->thread.get();
+    }
+    return nullptr;
+}
+
+sp<BnDisplayEventConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) {
+    if (mConnections.find(handle->id) != mConnections.end()) {
+        return mConnections[handle->id]->eventConnection;
+    }
+    return nullptr;
+}
+
+void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle,
+                                EventThread::DisplayType displayType, bool connected) {
+    if (mConnections.find(handle->id) != mConnections.end()) {
+        mConnections[handle->id]->thread->onHotplugReceived(displayType, connected);
+    }
+}
+
+void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) {
+    if (mConnections.find(handle->id) != mConnections.end()) {
+        mConnections[handle->id]->thread->onScreenAcquired();
+    }
+}
+
+void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) {
+    if (mConnections.find(handle->id) != mConnections.end()) {
+        mConnections[handle->id]->thread->onScreenReleased();
+    }
+}
+
+void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, String8& result) const {
+    if (mConnections.find(handle->id) != mConnections.end()) {
+        mConnections.at(handle->id)->thread->dump(result);
+    }
+}
+
+void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, nsecs_t phaseOffset) {
+    if (mConnections.find(handle->id) != mConnections.end()) {
+        mConnections[handle->id]->thread->setPhaseOffset(phaseOffset);
+    }
+}
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
new file mode 100644
index 0000000..caccd6f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2018 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 <cstdint>
+#include <memory>
+
+#include <gui/ISurfaceComposer.h>
+
+#include "DispSync.h"
+#include "EventThread.h"
+#include "InjectVSyncSource.h"
+
+namespace android {
+
+class Scheduler {
+public:
+    /* The scheduler handle is a BBinder object passed to the client from which we can extract
+     * an ID for subsequent operations.
+     */
+    class ConnectionHandle : public BBinder {
+    public:
+        ConnectionHandle(int64_t id) : id(id) {}
+        ~ConnectionHandle() = default;
+        const int64_t id;
+    };
+
+    class Connection {
+    public:
+        Connection(sp<ConnectionHandle> handle, sp<BnDisplayEventConnection> eventConnection,
+                   std::unique_ptr<EventThread> eventThread)
+              : handle(handle), eventConnection(eventConnection), thread(std::move(eventThread)) {}
+        ~Connection() = default;
+
+        sp<ConnectionHandle> handle;
+        sp<BnDisplayEventConnection> eventConnection;
+        const std::unique_ptr<EventThread> thread;
+    };
+
+    Scheduler() = default;
+    ~Scheduler() = default;
+
+    /** Creates an EventThread connection. */
+    sp<ConnectionHandle> createConnection(
+            const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+            impl::EventThread::ResyncWithRateLimitCallback resyncCallback,
+            impl::EventThread::InterceptVSyncsCallback interceptCallback);
+    sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle);
+
+    // Getter methods.
+    EventThread* getEventThread(const sp<ConnectionHandle>& handle);
+    sp<BnDisplayEventConnection> getEventConnection(const sp<ConnectionHandle>& handle);
+
+    // Should be called when receiving a hotplug event.
+    void hotplugReceived(const sp<ConnectionHandle>& handle, EventThread::DisplayType displayType,
+                         bool connected);
+    // Should be called after the screen is turned on.
+    void onScreenAcquired(const sp<ConnectionHandle>& handle);
+    // Should be called before the screen is turned off.
+    void onScreenReleased(const sp<ConnectionHandle>& handle);
+    // Should be called when dumpsys command is received.
+    void dump(const sp<ConnectionHandle>& handle, String8& result) const;
+    // Offers ability to modify phase offset in the event thread.
+    void setPhaseOffset(const sp<ConnectionHandle>& handle, nsecs_t phaseOffset);
+
+private:
+    static std::atomic<int64_t> sNextId;
+    std::unordered_map<int64_t, std::unique_ptr<Connection>> mConnections;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 124b773..8c46e8a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -87,6 +87,7 @@
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/InjectVSyncSource.h"
+#include "Scheduler/Scheduler.h"
 
 #include <cutils/compiler.h>
 
@@ -399,6 +400,9 @@
     property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
     const int earlyGlAppOffsetNs = atoi(value);
 
+    property_get("debug.sf.use_scheduler", value, "0");
+    mUseScheduler = atoi(value);
+
     const VSyncModulator::Offsets earlyOffsets =
             {earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
             earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
@@ -595,26 +599,47 @@
     Mutex::Autolock _l(mStateLock);
 
     // start the EventThread
-    mEventThreadSource =
-            std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
-                                             SurfaceFlinger::vsyncPhaseOffsetNs, true, "app");
-    mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
-                                                       [this] { resyncWithRateLimit(); },
-                                                       impl::EventThread::InterceptVSyncsCallback(),
-                                                       "appEventThread");
-    mSfEventThreadSource =
-            std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
-                                             SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
+    if (mUseScheduler) {
+        mScheduler = std::make_unique<Scheduler>();
+        mAppConnectionHandle =
+                mScheduler->createConnection("appConnection", mPrimaryDispSync.get(),
+                                             SurfaceFlinger::vsyncPhaseOffsetNs,
+                                             [this] { resyncWithRateLimit(); },
+                                             impl::EventThread::InterceptVSyncsCallback());
+        mSfConnectionHandle =
+                mScheduler->createConnection("sfConnection", mPrimaryDispSync.get(),
+                                             SurfaceFlinger::sfVsyncPhaseOffsetNs,
+                                             [this] { resyncWithRateLimit(); },
+                                             [this](nsecs_t timestamp) {
+                                                 mInterceptor->saveVSyncEvent(timestamp);
+                                             });
 
-    mSFEventThread =
-            std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
-                                                [this] { resyncWithRateLimit(); },
-                                                [this](nsecs_t timestamp) {
-                                                    mInterceptor->saveVSyncEvent(timestamp);
-                                                },
-                                                "sfEventThread");
-    mEventQueue->setEventThread(mSFEventThread.get());
-    mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get());
+        mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+        mVsyncModulator.setEventThreads(mScheduler->getEventThread(mSfConnectionHandle),
+                                        mScheduler->getEventThread(mAppConnectionHandle));
+    } else {
+        mEventThreadSource =
+                std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
+                                                 SurfaceFlinger::vsyncPhaseOffsetNs, true, "app");
+        mEventThread =
+                std::make_unique<impl::EventThread>(mEventThreadSource.get(),
+                                                    [this] { resyncWithRateLimit(); },
+                                                    impl::EventThread::InterceptVSyncsCallback(),
+                                                    "appEventThread");
+        mSfEventThreadSource =
+                std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
+                                                 SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
+
+        mSFEventThread =
+                std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
+                                                    [this] { resyncWithRateLimit(); },
+                                                    [this](nsecs_t timestamp) {
+                                                        mInterceptor->saveVSyncEvent(timestamp);
+                                                    },
+                                                    "sfEventThread");
+        mEventQueue->setEventThread(mSFEventThread.get());
+        mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get());
+    }
 
     // Get a RenderEngine for the given display / config (can't fail)
     int32_t renderEngineFeature = 0;
@@ -1083,6 +1108,8 @@
             return;
         }
 
+        // TODO(akrulec): Part of the Injector should be refactored, so that it
+        // can be passed to Scheduler.
         if (enable) {
             ALOGV("VSync Injections enabled");
             if (mVSyncInjector.get() == nullptr) {
@@ -1141,10 +1168,18 @@
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
         ISurfaceComposer::VsyncSource vsyncSource) {
-    if (vsyncSource == eVsyncSourceSurfaceFlinger) {
-        return mSFEventThread->createEventConnection();
+    if (mUseScheduler) {
+        if (vsyncSource == eVsyncSourceSurfaceFlinger) {
+            return mScheduler->createDisplayEventConnection(mSfConnectionHandle);
+        } else {
+            return mScheduler->createDisplayEventConnection(mAppConnectionHandle);
+        }
     } else {
-        return mEventThread->createEventConnection();
+        if (vsyncSource == eVsyncSourceSurfaceFlinger) {
+            return mSFEventThread->createEventConnection();
+        } else {
+            return mEventThread->createEventConnection();
+        }
     }
 }
 
@@ -2508,9 +2543,19 @@
                     display->disconnect(getHwComposer());
                 }
                 if (draw[i].type == DisplayDevice::DISPLAY_PRIMARY) {
-                    mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
+                    if (mUseScheduler) {
+                        mScheduler->hotplugReceived(mAppConnectionHandle,
+                                                    EventThread::DisplayType::Primary, false);
+                    } else {
+                        mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
+                    }
                 } else if (draw[i].type == DisplayDevice::DISPLAY_EXTERNAL) {
-                    mEventThread->onHotplugReceived(EventThread::DisplayType::External, false);
+                    if (mUseScheduler) {
+                        mScheduler->hotplugReceived(mAppConnectionHandle,
+                                                    EventThread::DisplayType::External, false);
+                    } else {
+                        mEventThread->onHotplugReceived(EventThread::DisplayType::External, false);
+                    }
                 }
                 mDisplays.erase(draw.keyAt(i));
             } else {
@@ -2613,11 +2658,23 @@
                                                                     dispSurface, producer));
                     if (!state.isVirtual()) {
                         if (state.type == DisplayDevice::DISPLAY_PRIMARY) {
-                            mEventThread->onHotplugReceived(EventThread::DisplayType::Primary,
+                            if (mUseScheduler) {
+                                mScheduler->hotplugReceived(mAppConnectionHandle,
+                                                            EventThread::DisplayType::Primary,
                                                             true);
+                            } else {
+                                mEventThread->onHotplugReceived(EventThread::DisplayType::Primary,
+                                                                true);
+                            }
                         } else if (state.type == DisplayDevice::DISPLAY_EXTERNAL) {
-                            mEventThread->onHotplugReceived(EventThread::DisplayType::External,
+                            if (mUseScheduler) {
+                                mScheduler->hotplugReceived(mAppConnectionHandle,
+                                                            EventThread::DisplayType::External,
                                                             true);
+                            } else {
+                                mEventThread->onHotplugReceived(EventThread::DisplayType::External,
+                                                                true);
+                            }
                         }
                     }
                 }
@@ -3942,7 +3999,11 @@
         getHwComposer().setPowerMode(type, mode);
         if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
             // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenAcquired();
+            if (mUseScheduler) {
+                mScheduler->onScreenAcquired(mAppConnectionHandle);
+            } else {
+                mEventThread->onScreenAcquired();
+            }
             resyncToHardwareVsync(true);
         }
 
@@ -3966,7 +4027,11 @@
             disableHardwareVsync(true); // also cancels any in-progress resync
 
             // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenReleased();
+            if (mUseScheduler) {
+                mScheduler->onScreenReleased(mAppConnectionHandle);
+            } else {
+                mEventThread->onScreenReleased();
+            }
         }
 
         getHwComposer().setPowerMode(type, mode);
@@ -3978,7 +4043,11 @@
         getHwComposer().setPowerMode(type, mode);
         if (display->isPrimary() && currentMode == HWC_POWER_MODE_DOZE_SUSPEND) {
             // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenAcquired();
+            if (mUseScheduler) {
+                mScheduler->onScreenAcquired(mAppConnectionHandle);
+            } else {
+                mEventThread->onScreenAcquired();
+            }
             resyncToHardwareVsync(true);
         }
     } else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) {
@@ -3986,7 +4055,11 @@
         if (display->isPrimary()) {
             disableHardwareVsync(true); // also cancels any in-progress resync
             // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenReleased();
+            if (mUseScheduler) {
+                mScheduler->onScreenReleased(mAppConnectionHandle);
+            } else {
+                mEventThread->onScreenReleased();
+            }
         }
         getHwComposer().setPowerMode(type, mode);
     } else {
@@ -4584,10 +4657,15 @@
     result.appendFormat("  transaction time: %f us\n",
             inTransactionDuration/1000.0);
 
+    result.appendFormat("  use Scheduler: %s\n", mUseScheduler ? "true" : "false");
     /*
      * VSYNC state
      */
-    mEventThread->dump(result);
+    if (mUseScheduler) {
+        mScheduler->dump(mAppConnectionHandle, result);
+    } else {
+        mEventThread->dump(result);
+    }
     result.append("\n");
 
     /*
@@ -4923,12 +5001,20 @@
             }
             case 1018: { // Modify Choreographer's phase offset
                 n = data.readInt32();
-                mEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                if (mUseScheduler) {
+                    mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n));
+                } else {
+                    mEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                }
                 return NO_ERROR;
             }
             case 1019: { // Modify SurfaceFlinger's phase offset
                 n = data.readInt32();
-                mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                if (mUseScheduler) {
+                    mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n));
+                } else {
+                    mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                }
                 return NO_ERROR;
             }
             case 1020: { // Layer updates interceptor
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c000ea2..3e76904 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -69,6 +69,7 @@
 #include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/MessageQueue.h"
+#include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncModulator.h"
 #include "TimeStats/TimeStats.h"
 
@@ -935,6 +936,11 @@
     CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface;
 
     SurfaceFlingerBE mBE;
+
+    bool mUseScheduler = false;
+    std::unique_ptr<Scheduler> mScheduler;
+    sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
+    sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
 };
 }; // namespace android
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 8d503f4..f2f3675 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -30,6 +30,7 @@
 
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
     MOCK_METHOD1(setEventThread, void(android::EventThread*));
+    MOCK_METHOD1(setEventConnection, void(const sp<BnDisplayEventConnection>& connection));
     MOCK_METHOD0(waitMessage, void());
     MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
     MOCK_METHOD0(invalidate, void());
diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp
index 1a7264c..d940b79 100644
--- a/services/vr/performanced/cpu_set.cpp
+++ b/services/vr/performanced/cpu_set.cpp
@@ -106,7 +106,7 @@
   return sets;
 }
 
-std::string CpuSetManager::DumpState() const {
+void CpuSetManager::DumpState(std::ostringstream& stream) const {
   size_t max_path = 0;
   std::vector<CpuSet*> sets;
 
@@ -119,8 +119,6 @@
     return a->path() < b->path();
   });
 
-  std::ostringstream stream;
-
   stream << std::left;
   stream << std::setw(max_path) << "Path";
   stream << " ";
@@ -146,8 +144,6 @@
     stream << std::setw(6) << set->GetTasks().size();
     stream << std::endl;
   }
-
-  return stream.str();
 }
 
 void CpuSetManager::MoveUnboundTasks(const std::string& target_set) {
diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h
index 6879272..4c25e9e 100644
--- a/services/vr/performanced/cpu_set.h
+++ b/services/vr/performanced/cpu_set.h
@@ -5,6 +5,7 @@
 
 #include <memory>
 #include <mutex>
+#include <sstream>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -83,7 +84,7 @@
   // to shield the system from interference from unbound kernel threads.
   void MoveUnboundTasks(const std::string& target_set);
 
-  std::string DumpState() const;
+  void DumpState(std::ostringstream& stream) const;
 
   operator bool() const { return root_set_ != nullptr; }
 
diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp
index d304bac..73dcf76 100644
--- a/services/vr/performanced/performance_service.cpp
+++ b/services/vr/performanced/performance_service.cpp
@@ -1,5 +1,7 @@
 #include "performance_service.h"
 
+#include <sstream>
+
 #include <sched.h>
 #include <sys/prctl.h>
 #include <unistd.h>
@@ -31,6 +33,10 @@
 
 const char kRootCpuSet[] = "/";
 
+const char kVrAppRenderPolicy[] = "vr:app:render";
+
+const bool kAllowAppsToRequestVrAppRenderPolicy = false;
+
 constexpr unsigned long kTimerSlackForegroundNs = 50000;
 constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
 
@@ -133,6 +139,17 @@
   using AllowRootSystemTrusted =
       CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>;
 
+  auto vr_app_render_permission_check = [](
+      const pdx::Message& sender, const Task& task) {
+          // For vr:app:render, in addition to system/root apps and VrCore, we
+          // also allow apps to request vr:app:render if
+          // kAllowAppsToRequestVrAppRenderPolicy == true, but not for other
+          // apps, only for themselves.
+          return (task && task.thread_group_id() == sender.GetProcessId() &&
+                  kAllowAppsToRequestVrAppRenderPolicy)
+              || AllowRootSystemTrusted::Check(sender, task);
+      };
+
   partition_permission_check_ = AllowRootSystemTrusted::Check;
 
   // Setup the scheduler classes.
@@ -184,11 +201,11 @@
         .priority = fifo_medium + 2,
         .permission_check = AllowRootSystemTrusted::Check,
         "/system/performance"}},
-      {"vr:app:render",
+      {kVrAppRenderPolicy,
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
         .priority = fifo_medium + 1,
-        .permission_check = AllowRootSystemTrusted::Check,
+        .permission_check = vr_app_render_permission_check,
         "/application/performance"}},
       {"normal",
        {.timer_slack = kTimerSlackForegroundNs,
@@ -215,7 +232,10 @@
 }
 
 std::string PerformanceService::DumpState(size_t /*max_length*/) {
-  return cpuset_.DumpState();
+  std::ostringstream stream;
+  stream << "vr_app_render_thread: " << vr_app_render_thread_ << std::endl;
+  cpuset_.DumpState(stream);
+  return stream.str();
 }
 
 Status<void> PerformanceService::OnSetSchedulerPolicy(
@@ -241,7 +261,12 @@
     // Make sure the sending process is allowed to make the requested change to
     // this task.
     if (!config.IsAllowed(message, task))
-      return ErrorStatus(EINVAL);
+      return ErrorStatus(EPERM);
+
+    if (scheduler_policy == kVrAppRenderPolicy) {
+      // We only allow one vr:app:render thread at a time
+      SetVrAppRenderThread(task_id);
+    }
 
     // Get the thread group's cpu set. Policies that do not specify a cpuset
     // should default to this cpuset.
@@ -299,14 +324,16 @@
 Status<void> PerformanceService::OnSetCpuPartition(
     Message& message, pid_t task_id, const std::string& partition) {
   Task task(task_id);
-  if (!task || task.thread_group_id() != message.GetProcessId())
+  if (!task)
     return ErrorStatus(EINVAL);
+  if (task.thread_group_id() != message.GetProcessId())
+    return ErrorStatus(EPERM);
 
   // Temporary permission check.
   // TODO(eieio): Replace this with a configuration file.
   if (partition_permission_check_ &&
       !partition_permission_check_(message, task)) {
-    return ErrorStatus(EINVAL);
+    return ErrorStatus(EPERM);
   }
 
   auto target_set = cpuset_.Lookup(partition);
@@ -333,7 +360,12 @@
     // Make sure the sending process is allowed to make the requested change to
     // this task.
     if (!config.IsAllowed(message, task))
-      return ErrorStatus(EINVAL);
+      return ErrorStatus(EPERM);
+
+    if (scheduler_class == kVrAppRenderPolicy) {
+      // We only allow one vr:app:render thread at a time
+      SetVrAppRenderThread(task_id);
+    }
 
     struct sched_param param;
     param.sched_priority = config.priority;
@@ -356,8 +388,10 @@
                                                           pid_t task_id) {
   // Make sure the task id is valid and belongs to the sending process.
   Task task(task_id);
-  if (!task || task.thread_group_id() != message.GetProcessId())
+  if (!task)
     return ErrorStatus(EINVAL);
+  if (task.thread_group_id() != message.GetProcessId())
+    return ErrorStatus(EPERM);
 
   return task.GetCpuSetPath();
 }
@@ -390,5 +424,38 @@
   }
 }
 
+void PerformanceService::SetVrAppRenderThread(pid_t new_vr_app_render_thread) {
+  ALOGI("SetVrAppRenderThread old=%d new=%d",
+      vr_app_render_thread_, new_vr_app_render_thread);
+
+  if (vr_app_render_thread_ >= 0 &&
+      vr_app_render_thread_ != new_vr_app_render_thread) {
+    // Restore the default scheduler policy and priority on the previous
+    // vr:app:render thread.
+    struct sched_param param;
+    param.sched_priority = 0;
+    if (sched_setscheduler(vr_app_render_thread_, SCHED_NORMAL, &param) < 0) {
+      if (errno == ESRCH) {
+        ALOGI("Failed to revert %s scheduler policy. Couldn't find thread %d."
+            " Was the app killed?", kVrAppRenderPolicy, vr_app_render_thread_);
+      } else {
+        ALOGE("Failed to revert %s scheduler policy: %s",
+            kVrAppRenderPolicy, strerror(errno));
+      }
+    }
+
+    // Restore the default timer slack on the previous vr:app:render thread.
+    prctl(PR_SET_TIMERSLACK_PID, kTimerSlackForegroundNs,
+        vr_app_render_thread_);
+  }
+
+  // We could also reset the thread's cpuset here, but the cpuset is already
+  // managed by Android. Better to let Android adjust the cpuset as the app
+  // moves to the background, rather than adjust it ourselves here, and risk
+  // stomping on the value set by Android.
+
+  vr_app_render_thread_ = new_vr_app_render_thread;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h
index 6b519ab..fe63756 100644
--- a/services/vr/performanced/performance_service.h
+++ b/services/vr/performanced/performance_service.h
@@ -39,6 +39,14 @@
   pdx::Status<std::string> OnGetCpuPartition(pdx::Message& message,
                                              pid_t task_id);
 
+  // Set which thread gets the vr:app:render policy. Only one thread at a time
+  // is allowed to have vr:app:render. If multiple threads are allowed
+  // vr:app:render, and those threads busy loop, the system can freeze. When
+  // SetVrAppRenderThread() is called, the thread which we had previously
+  // assigned vr:app:render will have its scheduling policy reset to default
+  // values.
+  void SetVrAppRenderThread(pid_t new_vr_app_render_thread);
+
   CpuSetManager cpuset_;
 
   int sched_fifo_min_priority_;
@@ -70,6 +78,8 @@
   std::function<bool(const pdx::Message& message, const Task& task)>
       partition_permission_check_;
 
+  pid_t vr_app_render_thread_ = -1;
+
   PerformanceService(const PerformanceService&) = delete;
   void operator=(const PerformanceService&) = delete;
 };