Merge "Rename # vndk tag to # llndk"
diff --git a/Android.bp b/Android.bp
index bf4cf5d..9829c7f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7,6 +7,7 @@
 }
 
 subdirs = [
+    "adbd_auth",
     "cmds/*",
     "headers",
     "libs/*",
diff --git a/cmds/installd/migrate_legacy_obb_data.sh b/cmds/installd/migrate_legacy_obb_data.sh
index 1075688..0e6d7b9 100644
--- a/cmds/installd/migrate_legacy_obb_data.sh
+++ b/cmds/installd/migrate_legacy_obb_data.sh
@@ -15,17 +15,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-rm -rf /sdcard/Android/obb/test_probe
-mkdir -p /sdcard/Android/obb/
-touch /sdcard/Android/obb/test_probe
+rm -rf /data/media/Android/obb/test_probe
+mkdir -p /data/media/Android/obb/
+touch /data/media/Android/obb/test_probe
 if ! test -f /data/media/0/Android/obb/test_probe ; then
   log -p i -t migrate_legacy_obb_data "No support for 'unshared_obb'. Not migrating"
-  rm -rf /sdcard/Android/obb/test_probe
+  rm -rf /data/media/Android/obb/test_probe
   exit 0
 fi
 
 # Delete the test file, and remove the obb folder if it is empty
-rm -rf /sdcard/Android/obb/test_probe
+rm -rf /data/media/Android/obb/test_probe
 rmdir /data/media/obb
 
 if ! test -d /data/media/obb ; then
diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h
index b6edaa9..81ee5fb 100644
--- a/headers/media_plugin/media/openmax/OMX_Video.h
+++ b/headers/media_plugin/media/openmax/OMX_Video.h
@@ -90,6 +90,7 @@
     OMX_VIDEO_CodingHEVC,       /**< ITU H.265/HEVC */
     OMX_VIDEO_CodingDolbyVision,/**< Dolby Vision */
     OMX_VIDEO_CodingImageHEIC,  /**< HEIF image encoded with HEVC */
+    OMX_VIDEO_CodingAV1,        /**< AV1 */
     OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
     OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
     OMX_VIDEO_CodingMax = 0x7FFFFFFF
diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp
new file mode 100644
index 0000000..9cf0143
--- /dev/null
+++ b/libs/adbd_auth/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2019 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.
+
+cc_library {
+    name: "libadbd_auth",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+    srcs: ["adbd_auth.cpp"],
+    export_include_dirs: ["include"],
+
+    version_script: "libadbd_auth.map.txt",
+    stubs: {
+        symbol_file: "libadbd_auth.map.txt",
+    },
+
+    host_supported: true,
+    recovery_available: true,
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    },
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+}
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
new file mode 100644
index 0000000..6479109
--- /dev/null
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION
+
+#include "include/adbd_auth.h"
+
+#include <inttypes.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/uio.h>
+
+#include <chrono>
+#include <deque>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <unordered_map>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+using android::base::unique_fd;
+
+struct AdbdAuthPacketAuthenticated {
+    std::string public_key;
+};
+
+struct AdbdAuthPacketDisconnected {
+    std::string public_key;
+};
+
+struct AdbdAuthPacketRequestAuthorization {
+    std::string public_key;
+};
+
+using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, AdbdAuthPacketDisconnected,
+                                    AdbdAuthPacketRequestAuthorization>;
+
+struct AdbdAuthContext {
+    static constexpr uint64_t kEpollConstSocket = 0;
+    static constexpr uint64_t kEpollConstEventFd = 1;
+    static constexpr uint64_t kEpollConstFramework = 2;
+
+public:
+    explicit AdbdAuthContext(AdbdAuthCallbacksV1* callbacks) : next_id_(0), callbacks_(*callbacks) {
+        epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+        if (epoll_fd_ == -1) {
+            PLOG(FATAL) << "failed to create epoll fd";
+        }
+
+        event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+        if (event_fd_ == -1) {
+            PLOG(FATAL) << "failed to create eventfd";
+        }
+
+        sock_fd_.reset(android_get_control_socket("adbd"));
+        if (sock_fd_ == -1) {
+            PLOG(ERROR) << "failed to get adbd authentication socket";
+        } else {
+            if (fcntl(sock_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
+                PLOG(FATAL) << "failed to make adbd authentication socket cloexec";
+            }
+
+            if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) {
+                PLOG(FATAL) << "failed to make adbd authentication socket nonblocking";
+            }
+
+            if (listen(sock_fd_.get(), 4) != 0) {
+                PLOG(FATAL) << "failed to listen on adbd authentication socket";
+            }
+        }
+    }
+
+    AdbdAuthContext(const AdbdAuthContext& copy) = delete;
+    AdbdAuthContext(AdbdAuthContext&& move) = delete;
+    AdbdAuthContext& operator=(const AdbdAuthContext& copy) = delete;
+    AdbdAuthContext& operator=(AdbdAuthContext&& move) = delete;
+
+    uint64_t NextId() { return next_id_++; }
+
+    void DispatchPendingPrompt() REQUIRES(mutex_) {
+        if (dispatched_prompt_) {
+            LOG(INFO) << "adbd_auth: prompt currently pending, skipping";
+            return;
+        }
+
+        if (pending_prompts_.empty()) {
+            LOG(INFO) << "adbd_auth: no prompts to send";
+            return;
+        }
+
+        LOG(INFO) << "adbd_auth: prompting user for adb authentication";
+        auto [id, public_key, arg] = std::move(pending_prompts_.front());
+        pending_prompts_.pop_front();
+
+        this->output_queue_.emplace_back(
+                AdbdAuthPacketRequestAuthorization{.public_key = public_key});
+
+        Interrupt();
+        dispatched_prompt_ = std::make_tuple(id, public_key, arg);
+    }
+
+    void UpdateFrameworkWritable() REQUIRES(mutex_) {
+        // This might result in redundant calls to EPOLL_CTL_MOD if, for example, we get notified
+        // at the same time as a framework connection, but that's unlikely and this doesn't need to
+        // be fast anyway.
+        if (framework_fd_ != -1) {
+            struct epoll_event event;
+            event.events = EPOLLIN;
+            if (!output_queue_.empty()) {
+                LOG(INFO) << "marking framework writable";
+                event.events |= EPOLLOUT;
+            }
+            event.data.u64 = kEpollConstFramework;
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, framework_fd_.get(), &event));
+        }
+    }
+
+    void ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) {
+        LOG(INFO) << "received new framework fd " << new_fd.get()
+                  << " (current = " << framework_fd_.get() << ")";
+
+        // If we already had a framework fd, clean up after ourselves.
+        if (framework_fd_ != -1) {
+            output_queue_.clear();
+            dispatched_prompt_.reset();
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, framework_fd_.get(), nullptr));
+            framework_fd_.reset();
+        }
+
+        if (new_fd != -1) {
+            struct epoll_event event;
+            event.events = EPOLLIN;
+            if (!output_queue_.empty()) {
+                LOG(INFO) << "marking framework writable";
+                event.events |= EPOLLOUT;
+            }
+            event.data.u64 = kEpollConstFramework;
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, new_fd.get(), &event));
+            framework_fd_ = std::move(new_fd);
+        }
+    }
+
+    void HandlePacket(std::string_view packet) REQUIRES(mutex_) {
+        LOG(INFO) << "received packet: " << packet;
+
+        if (packet.length() < 2) {
+          LOG(ERROR) << "received packet of invalid length";
+          ReplaceFrameworkFd(unique_fd());
+        }
+
+        if (packet[0] == 'O' && packet[1] == 'K') {
+          CHECK(this->dispatched_prompt_.has_value());
+          auto& [id, key, arg] = *this->dispatched_prompt_;
+          keys_.emplace(id, std::move(key));
+
+          this->callbacks_.key_authorized(arg, id);
+          this->dispatched_prompt_ = std::nullopt;
+        } else if (packet[0] == 'N' && packet[1] == 'O') {
+          CHECK_EQ(2UL, packet.length());
+          // TODO: Do we want a callback if the key is denied?
+          this->dispatched_prompt_ = std::nullopt;
+          DispatchPendingPrompt();
+        } else {
+          LOG(ERROR) << "unhandled packet: " << packet;
+          ReplaceFrameworkFd(unique_fd());
+        }
+    }
+
+    bool SendPacket() REQUIRES(mutex_) {
+        if (output_queue_.empty()) {
+            return false;
+        }
+
+        CHECK_NE(-1, framework_fd_.get());
+
+        auto& packet = output_queue_.front();
+        struct iovec iovs[2];
+        if (auto* p = std::get_if<AdbdAuthPacketAuthenticated>(&packet)) {
+            iovs[0].iov_base = const_cast<char*>("CK");
+            iovs[0].iov_len = 2;
+            iovs[1].iov_base = p->public_key.data();
+            iovs[1].iov_len = p->public_key.size();
+        } else if (auto* p = std::get_if<AdbdAuthPacketDisconnected>(&packet)) {
+            iovs[0].iov_base = const_cast<char*>("DC");
+            iovs[0].iov_len = 2;
+            iovs[1].iov_base = p->public_key.data();
+            iovs[1].iov_len = p->public_key.size();
+        } else if (auto* p = std::get_if<AdbdAuthPacketRequestAuthorization>(&packet)) {
+            iovs[0].iov_base = const_cast<char*>("PK");
+            iovs[0].iov_len = 2;
+            iovs[1].iov_base = p->public_key.data();
+            iovs[1].iov_len = p->public_key.size();
+        } else {
+            LOG(FATAL) << "unhandled packet type?";
+        }
+
+        output_queue_.pop_front();
+
+        ssize_t rc = writev(framework_fd_.get(), iovs, 2);
+        if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
+            PLOG(ERROR) << "failed to write to framework fd";
+            ReplaceFrameworkFd(unique_fd());
+            return false;
+        }
+
+        return true;
+    }
+
+    void Run() {
+        if (sock_fd_ == -1) {
+            LOG(ERROR) << "adbd authentication socket unavailable, disabling user prompts";
+        } else {
+            struct epoll_event event;
+            event.events = EPOLLIN;
+            event.data.u64 = kEpollConstSocket;
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, sock_fd_.get(), &event));
+        }
+
+        {
+            struct epoll_event event;
+            event.events = EPOLLIN;
+            event.data.u64 = kEpollConstEventFd;
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event));
+        }
+
+        while (true) {
+            struct epoll_event events[3];
+            int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 3, -1));
+            if (rc == -1) {
+                PLOG(FATAL) << "epoll_wait failed";
+            } else if (rc == 0) {
+                LOG(FATAL) << "epoll_wait returned 0";
+            }
+
+            bool restart = false;
+            for (int i = 0; i < rc; ++i) {
+                if (restart) {
+                    break;
+                }
+
+                struct epoll_event& event = events[i];
+                switch (event.data.u64) {
+                    case kEpollConstSocket: {
+                        unique_fd new_framework_fd(accept4(sock_fd_.get(), nullptr, nullptr,
+                                                           SOCK_CLOEXEC | SOCK_NONBLOCK));
+                        if (new_framework_fd == -1) {
+                            PLOG(FATAL) << "failed to accept framework fd";
+                        }
+
+                        LOG(INFO) << "adbd_auth: received a new framework connection";
+                        std::lock_guard<std::mutex> lock(mutex_);
+                        ReplaceFrameworkFd(std::move(new_framework_fd));
+
+                        // Stop iterating over events: one of the later ones might be the old
+                        // framework fd.
+                        restart = false;
+                        break;
+                    }
+
+                    case kEpollConstEventFd: {
+                        // We were woken up to write something.
+                        uint64_t dummy;
+                        int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
+                        if (rc != 8) {
+                            PLOG(FATAL) << "failed to read from eventfd (rc = " << rc << ")";
+                        }
+
+                        std::lock_guard<std::mutex> lock(mutex_);
+                        UpdateFrameworkWritable();
+                        break;
+                    }
+
+                    case kEpollConstFramework: {
+                        char buf[4096];
+                        if (event.events & EPOLLIN) {
+                            int rc = TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf)));
+                            if (rc == -1) {
+                                LOG(FATAL) << "failed to read from framework fd";
+                            } else if (rc == 0) {
+                                LOG(INFO) << "hit EOF on framework fd";
+                                std::lock_guard<std::mutex> lock(mutex_);
+                                ReplaceFrameworkFd(unique_fd());
+                            } else {
+                                std::lock_guard<std::mutex> lock(mutex_);
+                                HandlePacket(std::string_view(buf, rc));
+                            }
+                        }
+
+                        if (event.events & EPOLLOUT) {
+                            std::lock_guard<std::mutex> lock(mutex_);
+                            while (SendPacket()) {
+                                continue;
+                            }
+                            UpdateFrameworkWritable();
+                        }
+
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    static constexpr const char* key_paths[] = {"/adb_keys", "/data/misc/adb/adb_keys"};
+    void IteratePublicKeys(bool (*callback)(const char*, size_t, void*), void* arg) {
+        for (const auto& path : key_paths) {
+            if (access(path, R_OK) == 0) {
+                LOG(INFO) << "Loading keys from " << path;
+                std::string content;
+                if (!android::base::ReadFileToString(path, &content)) {
+                    PLOG(ERROR) << "Couldn't read " << path;
+                    continue;
+                }
+                for (const auto& line : android::base::Split(content, "\n")) {
+                    if (!callback(line.data(), line.size(), arg)) {
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    uint64_t PromptUser(std::string_view public_key, void* arg) EXCLUDES(mutex_) {
+        uint64_t id = NextId();
+
+        std::lock_guard<std::mutex> lock(mutex_);
+        pending_prompts_.emplace_back(id, public_key, arg);
+        DispatchPendingPrompt();
+        return id;
+    }
+
+    uint64_t NotifyAuthenticated(std::string_view public_key) EXCLUDES(mutex_) {
+        uint64_t id = NextId();
+        std::lock_guard<std::mutex> lock(mutex_);
+        keys_.emplace(id, public_key);
+        output_queue_.emplace_back(
+                AdbdAuthPacketDisconnected{.public_key = std::string(public_key)});
+        return id;
+    }
+
+    void NotifyDisconnected(uint64_t id) EXCLUDES(mutex_) {
+        std::lock_guard<std::mutex> lock(mutex_);
+        auto it = keys_.find(id);
+        if (it == keys_.end()) {
+            LOG(DEBUG) << "couldn't find public key to notify disconnection, skipping";
+            return;
+        }
+        output_queue_.emplace_back(AdbdAuthPacketDisconnected{.public_key = std::move(it->second)});
+        keys_.erase(it);
+    }
+
+    // Interrupt the worker thread to do some work.
+    void Interrupt() {
+        uint64_t value = 1;
+        ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
+        if (rc == -1) {
+            PLOG(FATAL) << "write to eventfd failed";
+        } else if (rc != sizeof(value)) {
+            LOG(FATAL) << "write to eventfd returned short (" << rc << ")";
+        }
+    }
+
+    unique_fd epoll_fd_;
+    unique_fd event_fd_;
+    unique_fd sock_fd_;
+    unique_fd framework_fd_;
+
+    std::atomic<uint64_t> next_id_;
+    AdbdAuthCallbacksV1 callbacks_;
+
+    std::mutex mutex_;
+    std::unordered_map<uint64_t, std::string> keys_ GUARDED_BY(mutex_);
+
+    // We keep two separate queues: one to handle backpressure from the socket (output_queue_)
+    // and one to make sure we only dispatch one authrequest at a time (pending_prompts_).
+    std::deque<AdbdAuthPacket> output_queue_;
+
+    std::optional<std::tuple<uint64_t, std::string, void*>> dispatched_prompt_ GUARDED_BY(mutex_);
+    std::deque<std::tuple<uint64_t, std::string, void*>> pending_prompts_ GUARDED_BY(mutex_);
+};
+
+AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) {
+    if (callbacks->version != 1) {
+      LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version;
+      return nullptr;
+    }
+
+    return new AdbdAuthContext(&callbacks->callbacks.v1);
+}
+
+void adbd_auth_delete(AdbdAuthContext* ctx) {
+    delete ctx;
+}
+
+void adbd_auth_run(AdbdAuthContext* ctx) {
+    return ctx->Run();
+}
+
+void adbd_auth_get_public_keys(AdbdAuthContext* ctx,
+                               bool (*callback)(const char* public_key, size_t len, void* arg),
+                               void* arg) {
+    ctx->IteratePublicKeys(callback, arg);
+}
+
+uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len) {
+    return ctx->NotifyAuthenticated(std::string_view(public_key, len));
+}
+
+void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) {
+    return ctx->NotifyDisconnected(id);
+}
+
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len,
+                               void* arg) {
+    ctx->PromptUser(std::string_view(public_key, len), arg);
+}
+
+bool adbd_auth_supports_feature(AdbdAuthFeature) {
+    return false;
+}
diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h
new file mode 100644
index 0000000..b7c1cb8
--- /dev/null
+++ b/libs/adbd_auth/include/adbd_auth.h
@@ -0,0 +1,65 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 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 <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+extern "C" {
+
+struct AdbdAuthCallbacksV1 {
+    // Callback for a successful user authorization.
+    void (*key_authorized)(void* arg, uint64_t id);
+};
+
+struct AdbdAuthCallbacks {
+    uint32_t version;
+    union {
+        AdbdAuthCallbacksV1 v1;
+    } callbacks;
+};
+
+struct AdbdAuthContext;
+
+AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks);
+void adbd_auth_delete(AdbdAuthContext* ctx);
+
+void adbd_auth_run(AdbdAuthContext* ctx);
+
+// Iterate through the list of authorized public keys.
+// Return false from the callback to stop iteration.
+void adbd_auth_get_public_keys(AdbdAuthContext* ctx,
+                               bool (*callback)(const char* public_key, size_t len, void* arg),
+                               void* arg);
+
+// Let system_server know that a key has been successfully used for authentication.
+uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len);
+
+// Let system_server know that a connection has been closed.
+void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id);
+
+// Prompt the user to authorize a public key.
+// When this happens, a callback will be run on the auth thread with the result.
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* arg);
+
+enum AdbdAuthFeature {
+};
+
+bool adbd_auth_supports_feature(AdbdAuthFeature f);
+
+}
diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt
new file mode 100644
index 0000000..d01233c
--- /dev/null
+++ b/libs/adbd_auth/libadbd_auth.map.txt
@@ -0,0 +1,13 @@
+LIBADBD_AUTH {
+  global:
+    adbd_auth_new; # apex
+    adbd_auth_delete; # apex
+    adbd_auth_run; # apex
+    adbd_auth_get_public_keys; #apex
+    adbd_auth_notify_auth; # apex
+    adbd_auth_notify_disconnect; # apex
+    adbd_auth_prompt_user; # apex
+    adbd_auth_supports_feature; # apex
+  local:
+    *;
+};
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index ad8287c..2518b14 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -13,13 +13,18 @@
 // limitations under the License.
 
 ndk_headers {
-    name: "libarect_headers",
+    name: "libarect_headers_for_ndk",
     from: "include/android",
     to: "android",
     srcs: ["include/android/*.h"],
     license: "NOTICE",
 }
 
+cc_library_headers {
+    name: "libarect_headers",
+    export_include_dirs: ["include"],
+}
+
 cc_library_static {
     name: "libarect",
     host_supported: true,
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 60f047f..4f0b7d3 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -115,23 +115,19 @@
 }
 
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
-    return noteOp(op, uid, callingPackage, String16(), String16());
+    return noteOp(op, uid, callingPackage, std::unique_ptr<String16>(),
+            String16("Legacy AppOpsManager.noteOp call"));
 }
 
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
-        const String16& featureId, const String16& message) {
+        const std::unique_ptr<String16>& featureId, const String16& message) {
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
-            ? service->noteOperation(op, uid, callingPackage)
+            ? service->noteOperation(op, uid, callingPackage, featureId)
             : APP_OPS_MANAGER_UNAVAILABLE_MODE;
 
     if (mode == AppOpsManager::MODE_ALLOWED) {
-        if (message.size() == 0) {
-            markAppOpNoted(uid, callingPackage, op, featureId,
-                    String16("noteOp from native code"));
-        } else {
-            markAppOpNoted(uid, callingPackage, op, featureId, message);
-        }
+        markAppOpNoted(uid, callingPackage, op, featureId, message);
     }
 
     return mode;
@@ -139,32 +135,34 @@
 
 int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
         bool startIfModeDefault) {
-    return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, String16(), String16());
+    return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, std::unique_ptr<String16>(),
+            String16("Legacy AppOpsManager.startOpNoThrow call"));
 }
 
 int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
-        bool startIfModeDefault, const String16& featureId, const String16& message) {
+        bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+        const String16& message) {
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
             ? service->startOperation(getToken(service), op, uid, callingPackage,
-                    startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+                    featureId, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
 
     if (mode == AppOpsManager::MODE_ALLOWED) {
-        if (message.size() == 0) {
-            markAppOpNoted(uid, callingPackage, op, featureId,
-                    String16("startOp from native code"));
-        } else {
-            markAppOpNoted(uid, callingPackage, op, featureId, message);
-        }
+        markAppOpNoted(uid, callingPackage, op, featureId, message);
     }
 
     return mode;
 }
 
 void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
+    finishOp(op, uid, callingPackage, std::unique_ptr<String16>());
+}
+
+void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
+        const std::unique_ptr<String16>& callingFeatureId) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
-        service->finishOperation(getToken(service), op, uid, callingPackage);
+        service->finishOperation(getToken(service), op, uid, callingPackage, callingFeatureId);
     }
 }
 
@@ -207,7 +205,7 @@
 }
 
 void AppOpsManager::markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode,
-         const String16& featureId, const String16& message) {
+         const std::unique_ptr<String16>& featureId, const String16& message) {
     // check it the appops needs to be collected and cache result
     if (appOpsToNote[opCode] == 0) {
         if (shouldCollectNotes(opCode)) {
@@ -221,11 +219,11 @@
         return;
     }
 
-    noteAsyncOp(String16(), uid, packageName, opCode, featureId, message);
+    noteAsyncOp(std::unique_ptr<String16>(), uid, packageName, opCode, featureId, message);
 }
 
-void AppOpsManager::noteAsyncOp(const String16& callingPackageName, int32_t uid,
-         const String16& packageName, int32_t opCode, const String16& featureId,
+void AppOpsManager::noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
+         const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
          const String16& message) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 9760e13..b85a5f2 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -46,12 +46,14 @@
         return reply.readInt32();
     }
 
-    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) {
+    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+                const std::unique_ptr<String16>& featureId) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
+        data.writeString16(featureId);
         remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -59,13 +61,15 @@
     }
 
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-                const String16& packageName, bool startIfModeDefault) {
+                const String16& packageName, const std::unique_ptr<String16>& featureId,
+                bool startIfModeDefault) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
+        data.writeString16(featureId);
         data.writeInt32(startIfModeDefault ? 1 : 0);
         remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
@@ -74,13 +78,14 @@
     }
 
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName) {
+            const String16& packageName, const std::unique_ptr<String16>& featureId) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
+        data.writeString16(featureId);
         remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
     }
 
@@ -144,37 +149,16 @@
         remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply);
     }
 
-    virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid,
-            const String16& packageName, int32_t opCode, const String16& featureId,
+    virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
+            const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
             const String16& message) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
-
-        // Convert empty callingPackage into null string
-        if (callingPackageName.size() != 0) {
-            data.writeString16(callingPackageName);
-        } else {
-            data.writeString16(nullptr, 0);
-        }
-
+        data.writeString16(callingPackageName);
         data.writeInt32(uid);
-
-        // Convert empty packageName into null string
-        if (packageName.size() != 0) {
-            data.writeString16(packageName);
-        } else {
-            data.writeString16(nullptr, 0);
-        }
-
+        data.writeString16(packageName);
         data.writeInt32(opCode);
-
-        // Convert empty featureId into null string
-        if (featureId.size() != 0) {
-            data.writeString16(featureId);
-        } else {
-            data.writeString16(nullptr, 0);
-        }
-
+        data.writeString16(featureId);
         data.writeString16(message);
         remote()->transact(NOTE_ASYNC_OP_TRANSACTION, data, &reply);
     }
@@ -217,7 +201,9 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            int32_t res = noteOperation(code, uid, packageName);
+            std::unique_ptr<String16> featureId;
+            data.readString16(&featureId);
+            int32_t res = noteOperation(code, uid, packageName, featureId);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
@@ -228,8 +214,11 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
+            std::unique_ptr<String16> featureId;
+            data.readString16(&featureId);
             bool startIfModeDefault = data.readInt32() == 1;
-            int32_t res = startOperation(token, code, uid, packageName, startIfModeDefault);
+            int32_t res = startOperation(token, code, uid, packageName, featureId,
+                    startIfModeDefault);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
@@ -240,7 +229,9 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            finishOperation(token, code, uid, packageName);
+            std::unique_ptr<String16> featureId;
+            data.readString16(&featureId);
+            finishOperation(token, code, uid, packageName, featureId);
             reply->writeNoException();
             return NO_ERROR;
         } break;
@@ -296,11 +287,13 @@
         } break;
         case NOTE_ASYNC_OP_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
-            String16 callingPackageName = data.readString16();
+            std::unique_ptr<String16> callingPackageName;
+            data.readString16(&callingPackageName);
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
             int32_t opCode = data.readInt32();
-            String16 featureId = data.readString16();
+            std::unique_ptr<String16> featureId;
+            data.readString16(&featureId);
             String16 message = data.readString16();
             noteAsyncOp(callingPackageName, uid, packageName, opCode, featureId, message);
             reply->writeNoException();
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 2744ce1..22a0179 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -134,21 +134,26 @@
     //              const String16&) instead
     int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
     int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
-            const String16& featureId, const String16& message);
+            const std::unique_ptr<String16>& featureId, const String16& message);
     // @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
     //              const String16&) instead
     int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
             bool startIfModeDefault);
     int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
-            bool startIfModeDefault, const String16& featureId, const String16& message);
+            bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+            const String16& message);
+    // @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
     void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
+    void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
+            const std::unique_ptr<String16>& featureId);
     void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback);
     void stopWatchingMode(const sp<IAppOpsCallback>& callback);
     int32_t permissionToOpCode(const String16& permission);
     void setCameraAudioRestriction(int32_t mode);
-    void noteAsyncOp(const String16& callingPackageName, int32_t uid, const String16& packageName,
-            int32_t opCode, const String16& featureId, const String16& message);
+    void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
+            const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
+            const String16& message);
 
 private:
     Mutex mLock;
@@ -156,7 +161,7 @@
 
     sp<IAppOpsService> getService();
     void markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode,
-            const String16& featureId, const String16& message);
+            const std::unique_ptr<String16>& featureId, const String16& message);
     bool shouldCollectNotes(int32_t opCode);
 };
 
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index ad34bc5..15ba005 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -35,11 +35,13 @@
     DECLARE_META_INTERFACE(AppOpsService)
 
     virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
-    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+            const std::unique_ptr<String16>& featureId) = 0;
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, bool startIfModeDefault) = 0;
+            const String16& packageName, const std::unique_ptr<String16>& featureId,
+            bool startIfModeDefault) = 0;
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName) = 0;
+            const String16& packageName, const std::unique_ptr<String16>& featureId) = 0;
     virtual void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback) = 0;
     virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
@@ -48,8 +50,8 @@
     virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
             const String16& packageName) = 0;
     virtual void setCameraAudioRestriction(int32_t mode) = 0;
-    virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid,
-            const String16& packageName, int32_t opCode, const String16& featureId,
+    virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
+            const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
             const String16& message) = 0;
     virtual bool shouldCollectNotes(int32_t opCode) = 0;
 
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index bd77567..2c43263 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -113,6 +113,13 @@
     return interface_cast<INTERFACE>(sm->waitForService(name));
 }
 
+template <typename INTERFACE>
+sp<INTERFACE> checkDeclaredService(const String16& name) {
+    const sp<IServiceManager> sm = defaultServiceManager();
+    if (!sm->isDeclared(name)) return nullptr;
+    return interface_cast<INTERFACE>(sm->checkService(name));
+}
+
 template<typename INTERFACE>
 sp<INTERFACE> waitForVintfService(
         const String16& instance = String16("default")) {
@@ -121,6 +128,13 @@
 }
 
 template<typename INTERFACE>
+sp<INTERFACE> checkVintfService(
+        const String16& instance = String16("default")) {
+    return checkDeclaredService<INTERFACE>(
+        INTERFACE::descriptor + String16("/") + instance);
+}
+
+template<typename INTERFACE>
 status_t getService(const String16& name, sp<INTERFACE>* outService)
 {
     const sp<IServiceManager> sm = defaultServiceManager();
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 662e56e..4635ad8 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -42,6 +42,24 @@
     android::status_t writeToParcel(android::Parcel* parcel) const override;
     android::status_t readFromParcel(const android::Parcel* parcel) override;
 
+    inline bool operator!=(const ParcelFileDescriptor& rhs) const {
+        return mFd != rhs.mFd;
+    }
+    inline bool operator<(const ParcelFileDescriptor& rhs) const {
+        return mFd < rhs.mFd;
+    }
+    inline bool operator<=(const ParcelFileDescriptor& rhs) const {
+        return mFd <= rhs.mFd;
+    }
+    inline bool operator==(const ParcelFileDescriptor& rhs) const {
+        return mFd == rhs.mFd;
+    }
+    inline bool operator>(const ParcelFileDescriptor& rhs) const {
+        return mFd > rhs.mFd;
+    }
+    inline bool operator>=(const ParcelFileDescriptor& rhs) const {
+        return mFd >= rhs.mFd;
+    }
 private:
     android::base::unique_fd mFd;
 };
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index 8f37c5e..946ccb7 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -159,13 +159,17 @@
      */
     T* getR() { return &mT; }
 
-    // copy-constructing, or move/copy assignment is disallowed
+    // copy-constructing/assignment is disallowed
     ScopedAResource(const ScopedAResource&) = delete;
     ScopedAResource& operator=(const ScopedAResource&) = delete;
-    ScopedAResource& operator=(ScopedAResource&&) = delete;
 
-    // move-constructing is okay
+    // move-constructing/assignment is okay
     ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) { other.mT = DEFAULT; }
+    ScopedAResource& operator=(ScopedAResource&& other) {
+        set(other.mT);
+        other.mT = DEFAULT;
+        return *this;
+    }
 
    private:
     T mT;
@@ -197,6 +201,7 @@
     explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {}
     ~ScopedAStatus() {}
     ScopedAStatus(ScopedAStatus&&) = default;
+    ScopedAStatus& operator=(ScopedAStatus&&) = default;
 
     /**
      * See AStatus_isOk.
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 635ea69..5a7f9a9 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -119,12 +119,7 @@
     srcs: ["binderSafeInterfaceTest.cpp"],
 
     cppflags: [
-        "-Weverything",
-        "-Wno-c++98-compat",
-        "-Wno-c++98-compat-pedantic",
-        "-Wno-global-constructors",
-        "-Wno-padded",
-        "-Wno-weak-vtables",
+        "-Wextra",
     ],
 
     cpp_std: "experimental",
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index b360a26..4f605e0 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -67,6 +67,9 @@
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
         "view/Surface.cpp",
+        "surfacetexture/SurfaceTexture.cpp",
+        "surfacetexture/ImageConsumer.cpp",
+        "surfacetexture/EGLConsumer.cpp",
     ],
 
     shared_libs: [
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 9a50175..3c31d74 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -33,6 +33,7 @@
     mBufferItemConsumer->setBufferFreedListener(this);
     mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
     mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
+    mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
@@ -41,6 +42,7 @@
     mWidth = width;
     mHeight = height;
     mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+    mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
 }
 
 static void transactionCallbackThunk(void* context, nsecs_t latchTime,
@@ -63,6 +65,7 @@
                                                    ? stats[0].previousReleaseFence
                                                    : Fence::NO_FENCE);
         mNextCallbackBufferItem = BufferItem();
+        mBufferItemConsumer->setTransformHint(stats[0].transformHint);
     }
     mDequeueWaitCV.notify_all();
     decStrong((void*)transactionCallbackThunk);
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index b98e48b..621cf59 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -49,25 +49,28 @@
 
     status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
                            uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
-                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override {
+                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
+                           uint32_t* outTransformHint) override {
         return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
                                                                             name, width, height,
                                                                             format, flags, parent,
                                                                             std::move(metadata),
-                                                                            handle, gbp);
+                                                                            handle, gbp,
+                                                                            outTransformHint);
     }
 
     status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height,
                                      PixelFormat format, uint32_t flags,
                                      const sp<IGraphicBufferProducer>& parent,
                                      LayerMetadata metadata, sp<IBinder>* handle,
-                                     sp<IGraphicBufferProducer>* gbp) override {
+                                     sp<IGraphicBufferProducer>* gbp,
+                                     uint32_t* outTransformHint) override {
         return callRemote<decltype(
                 &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
                                                                    name, width, height, format,
                                                                    flags, parent,
-                                                                   std::move(metadata), handle,
-                                                                   gbp);
+                                                                   std::move(metadata), handle, gbp,
+                                                                   outTransformHint);
     }
 
     status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e004e95..e392bc5 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -406,6 +406,12 @@
         what |= eMetadataChanged;
         metadata.merge(other.metadata);
     }
+
+    if (other.what & eShadowRadiusChanged) {
+        what |= eShadowRadiusChanged;
+        shadowRadius = other.shadowRadius;
+    }
+
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index e9079ef..a538e14 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -224,6 +224,8 @@
                                                  surfaceStats.acquireTime,
                                                  surfaceStats.previousReleaseFence,
                                                  surfaceStats.transformHint);
+                surfaceControls[surfaceStats.surfaceControl]->setTransformHint(
+                        surfaceStats.transformHint);
             }
 
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -1335,6 +1337,18 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setShadowRadius(
+        const sp<SurfaceControl>& sc, float shadowRadius) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eShadowRadiusChanged;
+    s->shadowRadius = shadowRadius;
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1451,16 +1465,19 @@
 sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
                                                         PixelFormat format, uint32_t flags,
                                                         SurfaceControl* parent,
-                                                        LayerMetadata metadata) {
+                                                        LayerMetadata metadata,
+                                                        uint32_t* outTransformHint) {
     sp<SurfaceControl> s;
-    createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata));
+    createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata),
+                         outTransformHint);
     return s;
 }
 
 sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w,
                                                                   uint32_t h, PixelFormat format,
                                                                   uint32_t flags, Surface* parent,
-                                                                  LayerMetadata metadata) {
+                                                                  LayerMetadata metadata,
+                                                                  uint32_t* outTransformHint) {
     sp<SurfaceControl> sur;
     status_t err = mStatus;
 
@@ -1469,11 +1486,15 @@
         sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer();
         sp<IGraphicBufferProducer> gbp;
 
+        uint32_t transformHint = 0;
         err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
-                                               std::move(metadata), &handle, &gbp);
+                                               std::move(metadata), &handle, &gbp, &transformHint);
+        if (outTransformHint) {
+            *outTransformHint = transformHint;
+        }
         ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
         if (err == NO_ERROR) {
-            return new SurfaceControl(this, handle, gbp, true /* owned */);
+            return new SurfaceControl(this, handle, gbp, true /* owned */, transformHint);
         }
     }
     return nullptr;
@@ -1482,8 +1503,8 @@
 status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                      PixelFormat format,
                                                      sp<SurfaceControl>* outSurface, uint32_t flags,
-                                                     SurfaceControl* parent,
-                                                     LayerMetadata metadata) {
+                                                     SurfaceControl* parent, LayerMetadata metadata,
+                                                     uint32_t* outTransformHint) {
     sp<SurfaceControl> sur;
     status_t err = mStatus;
 
@@ -1496,11 +1517,15 @@
             parentHandle = parent->getHandle();
         }
 
+        uint32_t transformHint = 0;
         err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
-                                     &handle, &gbp);
+                                     &handle, &gbp, &transformHint);
+        if (outTransformHint) {
+            *outTransformHint = transformHint;
+        }
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
-            *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
+            *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */, transformHint);
         }
     }
     return err;
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 071314f..6292388 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -45,20 +45,21 @@
 //  SurfaceControl
 // ============================================================================
 
-SurfaceControl::SurfaceControl(
-        const sp<SurfaceComposerClient>& client,
-        const sp<IBinder>& handle,
-        const sp<IGraphicBufferProducer>& gbp,
-        bool owned)
-    : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp), mOwned(owned)
-{
-}
+SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
+                               const sp<IGraphicBufferProducer>& gbp, bool owned,
+                               uint32_t transform)
+      : mClient(client),
+        mHandle(handle),
+        mGraphicBufferProducer(gbp),
+        mOwned(owned),
+        mTransformHint(transform) {}
 
 SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
     mClient = other->mClient;
     mHandle = other->mHandle;
     mGraphicBufferProducer = other->mGraphicBufferProducer;
     mOwned = false;
+    mTransformHint = other->mTransformHint;
 }
 
 SurfaceControl::~SurfaceControl()
@@ -171,11 +172,22 @@
     return mClient;
 }
 
+uint32_t SurfaceControl::getTransformHint() const {
+    Mutex::Autolock _l(mLock);
+    return mTransformHint;
+}
+
+void SurfaceControl::setTransformHint(uint32_t hint) {
+    Mutex::Autolock _l(mLock);
+    mTransformHint = hint;
+}
+
 void SurfaceControl::writeToParcel(Parcel* parcel)
 {
     parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient()));
     parcel->writeStrongBinder(mHandle);
     parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
+    parcel->writeUint32(mTransformHint);
 }
 
 sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
@@ -189,10 +201,12 @@
     sp<IBinder> gbp;
     parcel->readNullableStrongBinder(&gbp);
 
+    uint32_t transformHint = parcel->readUint32();
     // We aren't the original owner of the surface.
     return new SurfaceControl(new SurfaceComposerClient(
-                    interface_cast<ISurfaceComposerClient>(client)),
-            handle.get(), interface_cast<IGraphicBufferProducer>(gbp), false /* owned */);
+                                      interface_cast<ISurfaceComposerClient>(client)),
+                              handle.get(), interface_cast<IGraphicBufferProducer>(gbp),
+                              false /* owned */, transformHint);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 5fe7ca5..2b65d2f 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -56,7 +56,7 @@
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                    uint32_t flags, const sp<IBinder>& parent,
                                    LayerMetadata metadata, sp<IBinder>* handle,
-                                   sp<IGraphicBufferProducer>* gbp) = 0;
+                                   sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) = 0;
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -65,7 +65,8 @@
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
                                              LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp) = 0;
+                                             sp<IGraphicBufferProducer>* gbp,
+                                             uint32_t* outTransformHint) = 0;
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index a49ed52..c2b5119 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -72,7 +72,7 @@
         eCropChanged_legacy = 0x00000100,
         eDeferTransaction_legacy = 0x00000200,
         eOverrideScalingModeChanged = 0x00000400,
-        // AVAILABLE 0x00000800,
+        eShadowRadiusChanged = 0x00000800,
         eReparentChildren = 0x00001000,
         eDetachChildren = 0x00002000,
         eRelativeLayerChanged = 0x00004000,
@@ -126,7 +126,8 @@
             colorTransform(mat4()),
             bgColorAlpha(0),
             bgColorDataspace(ui::Dataspace::UNKNOWN),
-            colorSpaceAgnostic(false) {
+            colorSpaceAgnostic(false),
+            shadowRadius(0.0f) {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
         hdrMetadata.validTypes = 0;
@@ -204,6 +205,9 @@
     bool colorSpaceAgnostic;
 
     std::vector<ListenerCallbacks> listeners;
+
+    // Draws a shadow around the surface.
+    float shadowRadius;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 08f4e9e..37387ac 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -226,18 +226,18 @@
                                      PixelFormat format,               // pixel-format desired
                                      uint32_t flags = 0,               // usage flags
                                      SurfaceControl* parent = nullptr, // parent
-                                     LayerMetadata metadata = LayerMetadata() // metadata
-    );
+                                     LayerMetadata metadata = LayerMetadata(), // metadata
+                                     uint32_t* outTransformHint = nullptr);
 
     status_t createSurfaceChecked(const String8& name, // name of the surface
                                   uint32_t w,          // width in pixel
                                   uint32_t h,          // height in pixel
                                   PixelFormat format,  // pixel-format desired
                                   sp<SurfaceControl>* outSurface,
-                                  uint32_t flags = 0,                      // usage flags
-                                  SurfaceControl* parent = nullptr,        // parent
-                                  LayerMetadata metadata = LayerMetadata() // metadata
-    );
+                                  uint32_t flags = 0,                       // usage flags
+                                  SurfaceControl* parent = nullptr,         // parent
+                                  LayerMetadata metadata = LayerMetadata(), // metadata
+                                  uint32_t* outTransformHint = nullptr);
 
     //! Create a surface
     sp<SurfaceControl> createWithSurfaceParent(const String8& name,       // name of the surface
@@ -246,8 +246,8 @@
                                                PixelFormat format,        // pixel-format desired
                                                uint32_t flags = 0,        // usage flags
                                                Surface* parent = nullptr, // parent
-                                               LayerMetadata metadata = LayerMetadata() // metadata
-    );
+                                               LayerMetadata metadata = LayerMetadata(), // metadata
+                                               uint32_t* outTransformHint = nullptr);
 
     // Creates a mirrored hierarchy for the mirrorFromSurface. This returns a SurfaceControl
     // which is a parent of the root of the mirrored hierarchy.
@@ -474,6 +474,7 @@
 
         Transaction& setGeometry(const sp<SurfaceControl>& sc,
                 const Rect& source, const Rect& dst, int transform);
+        Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
 
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index ae4a146..7bc7c68 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -82,10 +82,14 @@
 
     sp<SurfaceComposerClient> getClient() const;
 
+    uint32_t getTransformHint() const;
+
+    void setTransformHint(uint32_t hint);
+
     explicit SurfaceControl(const sp<SurfaceControl>& other);
 
     SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
-                   const sp<IGraphicBufferProducer>& gbp, bool owned);
+                   const sp<IGraphicBufferProducer>& gbp, bool owned, uint32_t transformHint = 0);
 
 private:
     // can't be copied
@@ -106,6 +110,7 @@
     mutable Mutex               mLock;
     mutable sp<Surface>         mSurfaceData;
     bool                        mOwned;
+    uint32_t mTransformHint;
 };
 
 }; // namespace android
diff --git a/libs/gui/include/gui/surfacetexture/EGLConsumer.h b/libs/gui/include/gui/surfacetexture/EGLConsumer.h
new file mode 100644
index 0000000..444722b
--- /dev/null
+++ b/libs/gui/include/gui/surfacetexture/EGLConsumer.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 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 <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <gui/BufferQueueDefs.h>
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+class SurfaceTexture;
+
+/*
+ * EGLConsumer implements the parts of SurfaceTexture that deal with
+ * textures attached to an GL context.
+ */
+class EGLConsumer {
+public:
+    EGLConsumer();
+
+    /**
+     * updateTexImage acquires the most recently queued buffer, and sets the
+     * image contents of the target texture to it.
+     *
+     * This call may only be made while the OpenGL ES context to which the
+     * target texture belongs is bound to the calling thread.
+     *
+     * This calls doGLFenceWait to ensure proper synchronization.
+     */
+    status_t updateTexImage(SurfaceTexture& st);
+
+    /*
+     * releaseTexImage releases the texture acquired in updateTexImage().
+     * This is intended to be used in single buffer mode.
+     *
+     * This call may only be made while the OpenGL ES context to which the
+     * target texture belongs is bound to the calling thread.
+     */
+    status_t releaseTexImage(SurfaceTexture& st);
+
+    /**
+     * detachFromContext detaches the EGLConsumer from the calling thread's
+     * current OpenGL ES context.  This context must be the same as the context
+     * that was current for previous calls to updateTexImage.
+     *
+     * Detaching a EGLConsumer from an OpenGL ES context will result in the
+     * deletion of the OpenGL ES texture object into which the images were being
+     * streamed.  After a EGLConsumer has been detached from the OpenGL ES
+     * context calls to updateTexImage will fail returning INVALID_OPERATION
+     * until the EGLConsumer is attached to a new OpenGL ES context using the
+     * attachToContext method.
+     */
+    status_t detachFromContext(SurfaceTexture& st);
+
+    /**
+     * attachToContext attaches a EGLConsumer that is currently in the
+     * 'detached' state to the current OpenGL ES context.  A EGLConsumer is
+     * in the 'detached' state iff detachFromContext has successfully been
+     * called and no calls to attachToContext have succeeded since the last
+     * detachFromContext call.  Calls to attachToContext made on a
+     * EGLConsumer that is not in the 'detached' state will result in an
+     * INVALID_OPERATION error.
+     *
+     * The tex argument specifies the OpenGL ES texture object name in the
+     * new context into which the image contents will be streamed.  A successful
+     * call to attachToContext will result in this texture object being bound to
+     * the texture target and populated with the image contents that were
+     * current at the time of the last call to detachFromContext.
+     */
+    status_t attachToContext(uint32_t tex, SurfaceTexture& st);
+
+    /**
+     * onAcquireBufferLocked amends the ConsumerBase method to update the
+     * mEglSlots array in addition to the ConsumerBase behavior.
+     */
+    void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st);
+
+    /**
+     * onReleaseBufferLocked amends the ConsumerBase method to update the
+     * mEglSlots array in addition to the ConsumerBase.
+     */
+    void onReleaseBufferLocked(int slot);
+
+    /**
+     * onFreeBufferLocked frees up the given buffer slot. If the slot has been
+     * initialized this will release the reference to the GraphicBuffer in that
+     * slot and destroy the EGLImage in that slot.  Otherwise it has no effect.
+     */
+    void onFreeBufferLocked(int slotIndex);
+
+    /**
+     * onAbandonLocked amends the ConsumerBase method to clear
+     * mCurrentTextureImage in addition to the ConsumerBase behavior.
+     */
+    void onAbandonLocked();
+
+protected:
+    struct PendingRelease {
+        PendingRelease()
+              : isPending(false),
+                currentTexture(-1),
+                graphicBuffer(),
+                display(nullptr),
+                fence(nullptr) {}
+
+        bool isPending;
+        int currentTexture;
+        sp<GraphicBuffer> graphicBuffer;
+        EGLDisplay display;
+        EGLSyncKHR fence;
+    };
+
+    /**
+     * This releases the buffer in the slot referenced by mCurrentTexture,
+     * then updates state to refer to the BufferItem, which must be a
+     * newly-acquired buffer. If pendingRelease is not null, the parameters
+     * which would have been passed to releaseBufferLocked upon the successful
+     * completion of the method will instead be returned to the caller, so that
+     * it may call releaseBufferLocked itself later.
+     */
+    status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+                                    SurfaceTexture& st);
+
+    /**
+     * Binds mTexName and the current buffer to mTexTarget.  Uses
+     * mCurrentTexture if it's set, mCurrentTextureImage if not.  If the
+     * bind succeeds, this calls doGLFenceWait.
+     */
+    status_t bindTextureImageLocked(SurfaceTexture& st);
+
+    /**
+     * Gets the current EGLDisplay and EGLContext values, and compares them
+     * to mEglDisplay and mEglContext.  If the fields have been previously
+     * set, the values must match; if not, the fields are set to the current
+     * values.
+     * The contextCheck argument is used to ensure that a GL context is
+     * properly set; when set to false, the check is not performed.
+     */
+    status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false);
+
+    /**
+     * EglImage is a utility class for tracking and creating EGLImageKHRs. There
+     * is primarily just one image per slot, but there is also special cases:
+     *  - For releaseTexImage, we use a debug image (mReleasedTexImage)
+     *  - After freeBuffer, we must still keep the current image/buffer
+     * Reference counting EGLImages lets us handle all these cases easily while
+     * also only creating new EGLImages from buffers when required.
+     */
+    class EglImage : public LightRefBase<EglImage> {
+    public:
+        EglImage(sp<GraphicBuffer> graphicBuffer);
+
+        /**
+         * createIfNeeded creates an EGLImage if required (we haven't created
+         * one yet, or the EGLDisplay or crop-rect has changed).
+         */
+        status_t createIfNeeded(EGLDisplay display, bool forceCreate = false);
+
+        /**
+         * This calls glEGLImageTargetTexture2DOES to bind the image to the
+         * texture in the specified texture target.
+         */
+        void bindToTextureTarget(uint32_t texTarget);
+
+        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+        const native_handle* graphicBufferHandle() {
+            return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
+        }
+
+    private:
+        // Only allow instantiation using ref counting.
+        friend class LightRefBase<EglImage>;
+        virtual ~EglImage();
+
+        // createImage creates a new EGLImage from a GraphicBuffer.
+        EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer);
+
+        // Disallow copying
+        EglImage(const EglImage& rhs);
+        void operator=(const EglImage& rhs);
+
+        // mGraphicBuffer is the buffer that was used to create this image.
+        sp<GraphicBuffer> mGraphicBuffer;
+
+        // mEglImage is the EGLImage created from mGraphicBuffer.
+        EGLImageKHR mEglImage;
+
+        // mEGLDisplay is the EGLDisplay that was used to create mEglImage.
+        EGLDisplay mEglDisplay;
+
+        // mCropRect is the crop rectangle passed to EGL when mEglImage
+        // was created.
+        Rect mCropRect;
+    };
+
+    /**
+     * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
+     * stream to ensure that it is safe for future OpenGL ES commands to
+     * access the current texture buffer.
+     */
+    status_t doGLFenceWaitLocked(SurfaceTexture& st) const;
+
+    /**
+     * syncForReleaseLocked performs the synchronization needed to release the
+     * current slot from an OpenGL ES context.  If needed it will set the
+     * current slot's fence to guard against a producer accessing the buffer
+     * before the outstanding accesses have completed.
+     */
+    status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st);
+
+    /**
+     * returns a graphic buffer used when the texture image has been released
+     */
+    static sp<GraphicBuffer> getDebugTexImageBuffer();
+
+    /**
+     * The default consumer usage flags that EGLConsumer always sets on its
+     * BufferQueue instance; these will be OR:d with any additional flags passed
+     * from the EGLConsumer user. In particular, EGLConsumer will always
+     * consume buffers as hardware textures.
+     */
+    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+    /**
+     * mCurrentTextureImage is the EglImage/buffer of the current texture. It's
+     * possible that this buffer is not associated with any buffer slot, so we
+     * must track it separately in order to support the getCurrentBuffer method.
+     */
+    sp<EglImage> mCurrentTextureImage;
+
+    /**
+     * EGLSlot contains the information and object references that
+     * EGLConsumer maintains about a BufferQueue buffer slot.
+     */
+    struct EglSlot {
+        EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+        /**
+         * mEglImage is the EGLImage created from mGraphicBuffer.
+         */
+        sp<EglImage> mEglImage;
+
+        /**
+         * mFence is the EGL sync object that must signal before the buffer
+         * associated with this buffer slot may be dequeued. It is initialized
+         * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
+         * on a compile-time option) set to a new sync object in updateTexImage.
+         */
+        EGLSyncKHR mEglFence;
+    };
+
+    /**
+     * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently
+     * associated.  It is intialized to EGL_NO_DISPLAY and gets set to the
+     * current display when updateTexImage is called for the first time and when
+     * attachToContext is called.
+     */
+    EGLDisplay mEglDisplay;
+
+    /**
+     * mEglContext is the OpenGL ES context with which this EGLConsumer is
+     * currently associated.  It is initialized to EGL_NO_CONTEXT and gets set
+     * to the current GL context when updateTexImage is called for the first
+     * time and when attachToContext is called.
+     */
+    EGLContext mEglContext;
+
+    /**
+     * mEGLSlots stores the buffers that have been allocated by the BufferQueue
+     * for each buffer slot.  It is initialized to null pointers, and gets
+     * filled in with the result of BufferQueue::acquire when the
+     * client dequeues a buffer from a
+     * slot that has not yet been used. The buffer allocated to a slot will also
+     * be replaced if the requested buffer usage or geometry differs from that
+     * of the buffer allocated to a slot.
+     */
+    EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+    /**
+     * protects static initialization
+     */
+    static Mutex sStaticInitLock;
+
+    /**
+     * mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+     * mode and releaseTexImage() has been called
+     */
+    static sp<GraphicBuffer> sReleasedTexImageBuffer;
+    sp<EglImage> mReleasedTexImage;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/surfacetexture/ImageConsumer.h b/libs/gui/include/gui/surfacetexture/ImageConsumer.h
new file mode 100644
index 0000000..35ae3d2
--- /dev/null
+++ b/libs/gui/include/gui/surfacetexture/ImageConsumer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019 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 <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+
+namespace android {
+
+class SurfaceTexture;
+class DequeueBufferCallbacks;
+
+/*
+ * ImageConsumer implements the parts of SurfaceTexture that deal with
+ * images consumed by HWUI view system.
+ */
+class ImageConsumer {
+public:
+    typedef status_t (*SurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+                                                          EGLDisplay* display, int* releaseFence,
+                                                          void* fencePassThroughHandle);
+
+    typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
+
+    sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+                                    bool* outQueueEmpty, SurfaceTexture& cb,
+                                    SurfaceTexture_createReleaseFence createFence,
+                                    SurfaceTexture_fenceWait fenceWait,
+                                    void* fencePassThroughHandle);
+
+    /**
+     * onReleaseBufferLocked amends the ConsumerBase method to update the
+     * mImageSlots array in addition to the ConsumerBase.
+     */
+    void onReleaseBufferLocked(int slot);
+
+private:
+    /**
+     * ImageSlot contains the information and object references that
+     * ImageConsumer maintains about a BufferQueue buffer slot.
+     */
+    class ImageSlot {
+    public:
+        ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+        inline EGLSyncKHR& eglFence() { return mEglFence; }
+
+    private:
+        /**
+         * mEglFence is the EGL sync object that must signal before the buffer
+         * associated with this buffer slot may be dequeued.
+         */
+        EGLSyncKHR mEglFence;
+    };
+
+    /**
+     * ImageConsumer stores the SkImages that have been allocated by the BufferQueue
+     * for each buffer slot.  It is initialized to null pointers, and gets
+     * filled in with the result of BufferQueue::acquire when the
+     * client dequeues a buffer from a
+     * slot that has not yet been used. The buffer allocated to a slot will also
+     * be replaced if the requested buffer usage or geometry differs from that
+     * of the buffer allocated to a slot.
+     */
+    ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+} /* namespace android */
diff --git a/libs/gui/include/gui/surfacetexture/SurfaceTexture.h b/libs/gui/include/gui/surfacetexture/SurfaceTexture.h
new file mode 100644
index 0000000..6eaa84e
--- /dev/null
+++ b/libs/gui/include/gui/surfacetexture/SurfaceTexture.h
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2019 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 <android/hardware_buffer.h>
+#include <gui/BufferQueueDefs.h>
+#include <gui/ConsumerBase.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+
+#include "EGLConsumer.h"
+#include "ImageConsumer.h"
+
+namespace android {
+
+/*
+ * SurfaceTexture consumes buffers of graphics data from a BufferQueue,
+ * and makes them available to HWUI render thread as a SkImage and to
+ * an application GL render thread as an OpenGL texture.
+ *
+ * When attached to an application GL render thread, a typical usage
+ * pattern is to set up the SurfaceTexture with the
+ * desired options, and call updateTexImage() when a new frame is desired.
+ * If a new frame is available, the texture will be updated.  If not,
+ * the previous contents are retained.
+ *
+ * When attached to a HWUI render thread, the TextureView implementation
+ * calls dequeueBuffer, which either pulls a new buffer or returns the
+ * last cached buffer if BufferQueue is empty.
+ * When attached to HWUI render thread, SurfaceTexture is compatible to
+ * both Vulkan and GL drawing pipelines.
+ */
+class ANDROID_API SurfaceTexture : public ConsumerBase {
+public:
+    /**
+     * Callback function needed by dequeueBuffer. It creates a fence,
+     * that is signalled, when the previous buffer is no longer in use by HWUI
+     * and can be used by written by the producer.
+     */
+    typedef status_t (*SurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+                                                          EGLDisplay* display, int* releaseFence,
+                                                          void* passThroughHandle);
+
+    /**
+     * Callback function needed by dequeueBuffer. It waits for the new buffer
+     * fence to signal, before issuing any draw commands.
+     */
+    typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* passThroughHandle);
+
+    enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES
+    typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
+
+    /**
+     * SurfaceTexture constructs a new SurfaceTexture object. If the constructor
+     * with the tex parameter is used, tex indicates the name of the OpenGL ES
+     * texture to which images are to be streamed. texTarget specifies the
+     * OpenGL ES texture target to which the texture will be bound in
+     * updateTexImage. useFenceSync specifies whether fences should be used to
+     * synchronize access to buffers if that behavior is enabled at
+     * compile-time.
+     *
+     * A SurfaceTexture may be detached from one OpenGL ES context and then
+     * attached to a different context using the detachFromContext and
+     * attachToContext methods, respectively. The intention of these methods is
+     * purely to allow a SurfaceTexture to be transferred from one consumer
+     * context to another. If such a transfer is not needed there is no
+     * requirement that either of these methods be called.
+     *
+     * If the constructor with the tex parameter is used, the SurfaceTexture is
+     * created in a state where it is considered attached to an OpenGL ES
+     * context for the purposes of the attachToContext and detachFromContext
+     * methods. However, despite being considered "attached" to a context, the
+     * specific OpenGL ES context doesn't get latched until the first call to
+     * updateTexImage. After that point, all calls to updateTexImage must be
+     * made with the same OpenGL ES context current.
+     *
+     * If the constructor without the tex parameter is used, the SurfaceTexture
+     * is created in a detached state, and attachToContext must be called before
+     * calls to updateTexImage.
+     */
+    SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+                   bool useFenceSync, bool isControlledByApp);
+
+    SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+                   bool isControlledByApp);
+
+    /**
+     * updateTexImage acquires the most recently queued buffer, and sets the
+     * image contents of the target texture to it.
+     *
+     * This call may only be made while the OpenGL ES context to which the
+     * target texture belongs is bound to the calling thread.
+     *
+     * This calls doGLFenceWait to ensure proper synchronization.
+     */
+    status_t updateTexImage();
+
+    /**
+     * releaseTexImage releases the texture acquired in updateTexImage().
+     * This is intended to be used in single buffer mode.
+     *
+     * This call may only be made while the OpenGL ES context to which the
+     * target texture belongs is bound to the calling thread.
+     */
+    status_t releaseTexImage();
+
+    /**
+     * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
+     * associated with the texture image set by the most recent call to
+     * updateTexImage.
+     *
+     * This transform matrix maps 2D homogeneous texture coordinates of the form
+     * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture
+     * coordinate that should be used to sample that location from the texture.
+     * Sampling the texture outside of the range of this transform is undefined.
+     *
+     * This transform is necessary to compensate for transforms that the stream
+     * content producer may implicitly apply to the content. By forcing users of
+     * a SurfaceTexture to apply this transform we avoid performing an extra
+     * copy of the data that would be needed to hide the transform from the
+     * user.
+     *
+     * The matrix is stored in column-major order so that it may be passed
+     * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv
+     * functions.
+     */
+    void getTransformMatrix(float mtx[16]);
+
+    /**
+     * Computes the transform matrix documented by getTransformMatrix
+     * from the BufferItem sub parts.
+     */
+    static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+                                       const Rect& cropRect, uint32_t transform, bool filtering);
+
+    /**
+     * Scale the crop down horizontally or vertically such that it has the
+     * same aspect ratio as the buffer does.
+     */
+    static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight);
+
+    /**
+     * getTimestamp retrieves the timestamp associated with the texture image
+     * set by the most recent call to updateTexImage.
+     *
+     * The timestamp is in nanoseconds, and is monotonically increasing. Its
+     * other semantics (zero point, etc) are source-dependent and should be
+     * documented by the source.
+     */
+    int64_t getTimestamp();
+
+    /**
+     * getDataSpace retrieves the DataSpace associated with the texture image
+     * set by the most recent call to updateTexImage.
+     */
+    android_dataspace getCurrentDataSpace();
+
+    /**
+     * getFrameNumber retrieves the frame number associated with the texture
+     * image set by the most recent call to updateTexImage.
+     *
+     * The frame number is an incrementing counter set to 0 at the creation of
+     * the BufferQueue associated with this consumer.
+     */
+    uint64_t getFrameNumber();
+
+    /**
+     * setDefaultBufferSize is used to set the size of buffers returned by
+     * requestBuffers when a with and height of zero is requested.
+     * A call to setDefaultBufferSize() may trigger requestBuffers() to
+     * be called from the client.
+     * The width and height parameters must be no greater than the minimum of
+     * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+     * An error due to invalid dimensions might not be reported until
+     * updateTexImage() is called.
+     */
+    status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+    /**
+     * setFilteringEnabled sets whether the transform matrix should be computed
+     * for use with bilinear filtering.
+     */
+    void setFilteringEnabled(bool enabled);
+
+    /**
+     * getCurrentTextureTarget returns the texture target of the current
+     * texture as returned by updateTexImage().
+     */
+    uint32_t getCurrentTextureTarget() const;
+
+    /**
+     * getCurrentCrop returns the cropping rectangle of the current buffer.
+     */
+    Rect getCurrentCrop() const;
+
+    /**
+     * getCurrentTransform returns the transform of the current buffer.
+     */
+    uint32_t getCurrentTransform() const;
+
+    /**
+     * getCurrentScalingMode returns the scaling mode of the current buffer.
+     */
+    uint32_t getCurrentScalingMode() const;
+
+    /**
+     * getCurrentFence returns the fence indicating when the current buffer is
+     * ready to be read from.
+     */
+    sp<Fence> getCurrentFence() const;
+
+    /**
+     * getCurrentFence returns the FenceTime indicating when the current
+     * buffer is ready to be read from.
+     */
+    std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
+    /**
+     * setConsumerUsageBits overrides the ConsumerBase method to OR
+     * DEFAULT_USAGE_FLAGS to usage.
+     */
+    status_t setConsumerUsageBits(uint64_t usage);
+
+    /**
+     * detachFromContext detaches the SurfaceTexture from the calling thread's
+     * current OpenGL ES context.  This context must be the same as the context
+     * that was current for previous calls to updateTexImage.
+     *
+     * Detaching a SurfaceTexture from an OpenGL ES context will result in the
+     * deletion of the OpenGL ES texture object into which the images were being
+     * streamed.  After a SurfaceTexture has been detached from the OpenGL ES
+     * context calls to updateTexImage will fail returning INVALID_OPERATION
+     * until the SurfaceTexture is attached to a new OpenGL ES context using the
+     * attachToContext method.
+     */
+    status_t detachFromContext();
+
+    /**
+     * attachToContext attaches a SurfaceTexture that is currently in the
+     * 'detached' state to the current OpenGL ES context.  A SurfaceTexture is
+     * in the 'detached' state iff detachFromContext has successfully been
+     * called and no calls to attachToContext have succeeded since the last
+     * detachFromContext call.  Calls to attachToContext made on a
+     * SurfaceTexture that is not in the 'detached' state will result in an
+     * INVALID_OPERATION error.
+     *
+     * The tex argument specifies the OpenGL ES texture object name in the
+     * new context into which the image contents will be streamed.  A successful
+     * call to attachToContext will result in this texture object being bound to
+     * the texture target and populated with the image contents that were
+     * current at the time of the last call to detachFromContext.
+     */
+    status_t attachToContext(uint32_t tex);
+
+    sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+                                    float* outTransformMatrix, bool* outQueueEmpty,
+                                    SurfaceTexture_createReleaseFence createFence,
+                                    SurfaceTexture_fenceWait fenceWait,
+                                    void* fencePassThroughHandle);
+
+    /**
+     * takeConsumerOwnership attaches a SurfaceTexture that is currently in the
+     * 'detached' state to a consumer context (usually HWUI RenderThread).
+     */
+    void takeConsumerOwnership();
+
+    /**
+     * releaseConsumerOwnership detaches a SurfaceTexture from a consumer
+     * context (usually HWUI RenderThread).
+     */
+    void releaseConsumerOwnership();
+
+protected:
+    /**
+     * abandonLocked overrides the ConsumerBase method to clear
+     * mCurrentTextureImage in addition to the ConsumerBase behavior.
+     */
+    virtual void abandonLocked();
+
+    /**
+     * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
+     * specific info in addition to the ConsumerBase behavior.
+     */
+    virtual void dumpLocked(String8& result, const char* prefix) const override;
+
+    /**
+     * acquireBufferLocked overrides the ConsumerBase method to update the
+     * mEglSlots array in addition to the ConsumerBase behavior.
+     */
+    virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+                                         uint64_t maxFrameNumber = 0) override;
+
+    /**
+     * releaseBufferLocked overrides the ConsumerBase method to update the
+     * mEglSlots array in addition to the ConsumerBase.
+     */
+    virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
+                                         EGLDisplay display, EGLSyncKHR eglFence) override;
+
+    /**
+     * freeBufferLocked frees up the given buffer slot. If the slot has been
+     * initialized this will release the reference to the GraphicBuffer in that
+     * slot and destroy the EGLImage in that slot.  Otherwise it has no effect.
+     *
+     * This method must be called with mMutex locked.
+     */
+    virtual void freeBufferLocked(int slotIndex);
+
+    /**
+     * computeCurrentTransformMatrixLocked computes the transform matrix for the
+     * current texture.  It uses mCurrentTransform and the current GraphicBuffer
+     * to compute this matrix and stores it in mCurrentTransformMatrix.
+     * mCurrentTextureImage must not be NULL.
+     */
+    void computeCurrentTransformMatrixLocked();
+
+    /**
+     * The default consumer usage flags that SurfaceTexture always sets on its
+     * BufferQueue instance; these will be OR:d with any additional flags passed
+     * from the SurfaceTexture user. In particular, SurfaceTexture will always
+     * consume buffers as hardware textures.
+     */
+    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+    /**
+     * mCurrentCrop is the crop rectangle that applies to the current texture.
+     * It gets set each time updateTexImage is called.
+     */
+    Rect mCurrentCrop;
+
+    /**
+     * mCurrentTransform is the transform identifier for the current texture. It
+     * gets set each time updateTexImage is called.
+     */
+    uint32_t mCurrentTransform;
+
+    /**
+     * mCurrentScalingMode is the scaling mode for the current texture. It gets
+     * set each time updateTexImage is called.
+     */
+    uint32_t mCurrentScalingMode;
+
+    /**
+     * mCurrentFence is the fence received from BufferQueue in updateTexImage.
+     */
+    sp<Fence> mCurrentFence;
+
+    /**
+     * The FenceTime wrapper around mCurrentFence.
+     */
+    std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
+    /**
+     * mCurrentTransformMatrix is the transform matrix for the current texture.
+     * It gets computed by computeTransformMatrix each time updateTexImage is
+     * called.
+     */
+    float mCurrentTransformMatrix[16];
+
+    /**
+     * mCurrentTimestamp is the timestamp for the current texture. It
+     * gets set each time updateTexImage is called.
+     */
+    int64_t mCurrentTimestamp;
+
+    /**
+     * mCurrentDataSpace is the dataspace for the current texture. It
+     * gets set each time updateTexImage is called.
+     */
+    android_dataspace mCurrentDataSpace;
+
+    /**
+     * mCurrentFrameNumber is the frame counter for the current texture.
+     * It gets set each time updateTexImage is called.
+     */
+    uint64_t mCurrentFrameNumber;
+
+    uint32_t mDefaultWidth, mDefaultHeight;
+
+    /**
+     * mFilteringEnabled indicates whether the transform matrix is computed for
+     * use with bilinear filtering. It defaults to true and is changed by
+     * setFilteringEnabled().
+     */
+    bool mFilteringEnabled;
+
+    /**
+     * mTexName is the name of the OpenGL texture to which streamed images will
+     * be bound when updateTexImage is called. It is set at construction time
+     * and can be changed with a call to attachToContext.
+     */
+    uint32_t mTexName;
+
+    /**
+     * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
+     * extension should be used to prevent buffers from being dequeued before
+     * it's safe for them to be written. It gets set at construction time and
+     * never changes.
+     */
+    const bool mUseFenceSync;
+
+    /**
+     * mTexTarget is the GL texture target with which the GL texture object is
+     * associated.  It is set in the constructor and never changed.  It is
+     * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android
+     * Browser.  In that case it is set to GL_TEXTURE_2D to allow
+     * glCopyTexSubImage to read from the texture.  This is a hack to work
+     * around a GL driver limitation on the number of FBO attachments, which the
+     * browser's tile cache exceeds.
+     */
+    const uint32_t mTexTarget;
+
+    /**
+     * mCurrentTexture is the buffer slot index of the buffer that is currently
+     * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
+     * indicating that no buffer slot is currently bound to the texture. Note,
+     * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+     * that no buffer is bound to the texture. A call to setBufferCount will
+     * reset mCurrentTexture to INVALID_BUFFER_SLOT.
+     */
+    int mCurrentTexture;
+
+    enum class OpMode { detached, attachedToConsumer, attachedToGL };
+    /**
+     * mOpMode indicates whether the SurfaceTexture is currently attached to
+     * an OpenGL ES context or the consumer context.  For legacy reasons, this
+     * is initialized to, "attachedToGL" indicating that the SurfaceTexture is
+     * considered to be attached to whatever GL context is current at the time
+     * of the first updateTexImage call.
+     * It is set to "detached" by detachFromContext, and then set to
+     * "attachedToGL" again by attachToContext.
+     * takeConsumerOwnership/releaseConsumerOwnership are used to attach/detach
+     * from a consumer context - usually HWUI RenderThread.
+     */
+    OpMode mOpMode;
+
+    /**
+     * mEGLConsumer has SurfaceTexture logic used when attached to GL context.
+     */
+    EGLConsumer mEGLConsumer;
+
+    /**
+     * mImageConsumer has SurfaceTexture logic used when attached to a consumer
+     * context (usually HWUI RenderThread).
+     */
+    ImageConsumer mImageConsumer;
+
+    friend class ImageConsumer;
+    friend class EGLConsumer;
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/gui/include/gui/surfacetexture/surface_texture_platform.h b/libs/gui/include/gui/surfacetexture/surface_texture_platform.h
new file mode 100644
index 0000000..72f98ad
--- /dev/null
+++ b/libs/gui/include/gui/surfacetexture/surface_texture_platform.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#ifndef _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
+#define _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <system/graphics.h>
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/surfacetexture/SurfaceTexture.h>
+
+// This file provides a facade API on top of SurfaceTexture, which avoids using
+// C++ types. This is still a C++ unstable API though. Ideally features here
+// will be exposed via public NDK API and this file will be deleted.
+
+struct ASurfaceTexture {
+    android::sp<android::SurfaceTexture> consumer;
+    android::sp<android::IGraphicBufferProducer> producer;
+};
+
+namespace android {
+
+/**
+ * ASurfaceTexture_getCurrentTextureTarget returns the texture target of the
+ * current texture.
+ */
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st);
+
+/**
+ * ASurfaceTexture_takeConsumerOwnership attaches an ASurfaceTexture that is
+ * currently in the 'detached' state to a consumer context.
+ */
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* st);
+
+/**
+ * ASurfaceTexture_releaseConsumerOwnership detaches a SurfaceTexture from
+ * a consumer context.
+ */
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* st);
+
+/**
+ * Callback function needed by ASurfaceTexture_dequeueBuffer. It creates a
+ * fence that is signalled when the previous buffer is no longer in use by the
+ * consumer (usually HWUI RenderThread) and can be written to by the producer.
+ */
+typedef int (*ASurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+                                                  EGLDisplay* display, int* releaseFence,
+                                                  void* fencePassThroughHandle);
+
+/**
+ * Callback function needed by ASurfaceTexture_dequeueBuffer. It waits for the
+ * new buffer fence to signal before issuing any draw commands.
+ */
+typedef int (*ASurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
+
+/**
+ * ASurfaceTexture_dequeueBuffer returns the next available AHardwareBuffer.
+ */
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+                                               android_dataspace* outDataspace,
+                                               float* outTransformMatrix, bool* outNewContent,
+                                               ASurfaceTexture_createReleaseFence createFence,
+                                               ASurfaceTexture_fenceWait fenceWait,
+                                               void* fencePassThroughHandle);
+
+/**
+ * ASurfaceTexture_create creates an ASurfaceTexture, which is
+ * a simple struct containing a SurfaceTexture and an IGraphicBufferProducer.
+ */
+ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer,
+                                        sp<IGraphicBufferProducer> producer);
+
+} // namespace android
+
+#endif // _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
diff --git a/libs/gui/surfacetexture/EGLConsumer.cpp b/libs/gui/surfacetexture/EGLConsumer.cpp
new file mode 100644
index 0000000..1f0f52c
--- /dev/null
+++ b/libs/gui/surfacetexture/EGLConsumer.cpp
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <gui/surfacetexture/EGLConsumer.h>
+#include <gui/surfacetexture/SurfaceTexture.h>
+#include <inttypes.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
+#define EGL_PROTECTED_CONTENT_EXT 0x32C0
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+static const struct {
+    uint32_t width, height;
+    char const* bits;
+} kDebugData = {15, 12,
+                "_______________"
+                "_______________"
+                "_____XX_XX_____"
+                "__X_X_____X_X__"
+                "__X_XXXXXXX_X__"
+                "__XXXXXXXXXXX__"
+                "___XX_XXX_XX___"
+                "____XXXXXXX____"
+                "_____X___X_____"
+                "____X_____X____"
+                "_______________"
+                "_______________"};
+
+Mutex EGLConsumer::sStaticInitLock;
+sp<GraphicBuffer> EGLConsumer::sReleasedTexImageBuffer;
+
+static bool hasEglProtectedContentImpl() {
+    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+    size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR);
+    size_t extsLen = strlen(exts);
+    bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts);
+    bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1);
+    bool atEnd = (cropExtLen + 1) < extsLen &&
+            !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1));
+    bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " ");
+    return equal || atStart || atEnd || inMiddle;
+}
+
+static bool hasEglProtectedContent() {
+    // Only compute whether the extension is present once the first time this
+    // function is called.
+    static bool hasIt = hasEglProtectedContentImpl();
+    return hasIt;
+}
+
+EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {}
+
+status_t EGLConsumer::updateTexImage(SurfaceTexture& st) {
+    // Make sure the EGL state is the same as in previous calls.
+    status_t err = checkAndUpdateEglStateLocked(st);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    BufferItem item;
+
+    // Acquire the next buffer.
+    // In asynchronous mode the list is guaranteed to be one buffer
+    // deep, while in synchronous mode we use the oldest buffer.
+    err = st.acquireBufferLocked(&item, 0);
+    if (err != NO_ERROR) {
+        if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+            // We always bind the texture even if we don't update its contents.
+            EGC_LOGV("updateTexImage: no buffers were available");
+            glBindTexture(st.mTexTarget, st.mTexName);
+            err = NO_ERROR;
+        } else {
+            EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
+        }
+        return err;
+    }
+
+    // Release the previous buffer.
+    err = updateAndReleaseLocked(item, nullptr, st);
+    if (err != NO_ERROR) {
+        // We always bind the texture.
+        glBindTexture(st.mTexTarget, st.mTexName);
+        return err;
+    }
+
+    // Bind the new buffer to the GL texture, and wait until it's ready.
+    return bindTextureImageLocked(st);
+}
+
+status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) {
+    // Make sure the EGL state is the same as in previous calls.
+    status_t err = NO_ERROR;
+
+    // if we're detached, no need to validate EGL's state -- we won't use it.
+    if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+        err = checkAndUpdateEglStateLocked(st, true);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    // Update the EGLConsumer state.
+    int buf = st.mCurrentTexture;
+    if (buf != BufferQueue::INVALID_BUFFER_SLOT) {
+        EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode);
+
+        // if we're detached, we just use the fence that was created in
+        // detachFromContext() so... basically, nothing more to do here.
+        if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+            // Do whatever sync ops we need to do before releasing the slot.
+            err = syncForReleaseLocked(mEglDisplay, st);
+            if (err != NO_ERROR) {
+                EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
+                return err;
+            }
+        }
+
+        err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay,
+                                     EGL_NO_SYNC_KHR);
+        if (err < NO_ERROR) {
+            EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err);
+            return err;
+        }
+
+        if (mReleasedTexImage == nullptr) {
+            mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+        }
+
+        st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+        mCurrentTextureImage = mReleasedTexImage;
+        st.mCurrentCrop.makeInvalid();
+        st.mCurrentTransform = 0;
+        st.mCurrentTimestamp = 0;
+        st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
+        st.mCurrentFence = Fence::NO_FENCE;
+        st.mCurrentFenceTime = FenceTime::NO_FENCE;
+
+        // detached, don't touch the texture (and we may not even have an
+        // EGLDisplay here.
+        if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+            // This binds a dummy buffer (mReleasedTexImage).
+            status_t result = bindTextureImageLocked(st);
+            if (result != NO_ERROR) {
+                return result;
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() {
+    Mutex::Autolock _l(sStaticInitLock);
+    if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
+        // The first time, create the debug texture in case the application
+        // continues to use it.
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
+                                  GraphicBuffer::USAGE_SW_WRITE_RARELY,
+                                  "[EGLConsumer debug texture]");
+        uint32_t* bits;
+        buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
+        uint32_t stride = buffer->getStride();
+        uint32_t height = buffer->getHeight();
+        memset(bits, 0, stride * height * 4);
+        for (uint32_t y = 0; y < kDebugData.height; y++) {
+            for (uint32_t x = 0; x < kDebugData.width; x++) {
+                bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000
+                                                                             : 0xFFFFFFFF;
+            }
+            bits += stride;
+        }
+        buffer->unlock();
+        sReleasedTexImageBuffer = buffer;
+    }
+    return sReleasedTexImageBuffer;
+}
+
+void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) {
+    // If item->mGraphicBuffer is not null, this buffer has not been acquired
+    // before, so any prior EglImage created is using a stale buffer. This
+    // replaces any old EglImage with a new one (using the new buffer).
+    int slot = item->mSlot;
+    if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) {
+        mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+    }
+}
+
+void EGLConsumer::onReleaseBufferLocked(int buf) {
+    mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
+}
+
+status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+                                             SurfaceTexture& st) {
+    status_t err = NO_ERROR;
+
+    int slot = item.mSlot;
+
+    if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) {
+        EGC_LOGE("updateAndRelease: EGLConsumer is not attached to an OpenGL "
+                 "ES context");
+        st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+        return INVALID_OPERATION;
+    }
+
+    // Confirm state.
+    err = checkAndUpdateEglStateLocked(st);
+    if (err != NO_ERROR) {
+        st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+        return err;
+    }
+
+    // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
+    // if nessessary, for the gralloc buffer currently in the slot in
+    // ConsumerBase.
+    // We may have to do this even when item.mGraphicBuffer == NULL (which
+    // means the buffer was previously acquired).
+    err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay);
+    if (err != NO_ERROR) {
+        EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay,
+                 slot);
+        st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+        return UNKNOWN_ERROR;
+    }
+
+    // Do whatever sync ops we need to do before releasing the old slot.
+    if (slot != st.mCurrentTexture) {
+        err = syncForReleaseLocked(mEglDisplay, st);
+        if (err != NO_ERROR) {
+            // Release the buffer we just acquired.  It's not safe to
+            // release the old buffer, so instead we just drop the new frame.
+            // As we are still under lock since acquireBuffer, it is safe to
+            // release by slot.
+            st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay,
+                                   EGL_NO_SYNC_KHR);
+            return err;
+        }
+    }
+
+    EGC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture,
+             mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle()
+                                             : nullptr,
+             slot, st.mSlots[slot].mGraphicBuffer->handle);
+
+    // Hang onto the pointer so that it isn't freed in the call to
+    // releaseBufferLocked() if we're in shared buffer mode and both buffers are
+    // the same.
+    sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage;
+
+    // release old buffer
+    if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+        if (pendingRelease == nullptr) {
+            status_t status =
+                    st.releaseBufferLocked(st.mCurrentTexture,
+                                           mCurrentTextureImage->graphicBuffer(), mEglDisplay,
+                                           mEglSlots[st.mCurrentTexture].mEglFence);
+            if (status < NO_ERROR) {
+                EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
+                         status);
+                err = status;
+                // keep going, with error raised [?]
+            }
+        } else {
+            pendingRelease->currentTexture = st.mCurrentTexture;
+            pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+            pendingRelease->display = mEglDisplay;
+            pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence;
+            pendingRelease->isPending = true;
+        }
+    }
+
+    // Update the EGLConsumer state.
+    st.mCurrentTexture = slot;
+    mCurrentTextureImage = nextTextureImage;
+    st.mCurrentCrop = item.mCrop;
+    st.mCurrentTransform = item.mTransform;
+    st.mCurrentScalingMode = item.mScalingMode;
+    st.mCurrentTimestamp = item.mTimestamp;
+    st.mCurrentDataSpace = item.mDataSpace;
+    st.mCurrentFence = item.mFence;
+    st.mCurrentFenceTime = item.mFenceTime;
+    st.mCurrentFrameNumber = item.mFrameNumber;
+
+    st.computeCurrentTransformMatrixLocked();
+
+    return err;
+}
+
+status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) {
+    if (mEglDisplay == EGL_NO_DISPLAY) {
+        ALOGE("bindTextureImage: invalid display");
+        return INVALID_OPERATION;
+    }
+
+    GLenum error;
+    while ((error = glGetError()) != GL_NO_ERROR) {
+        EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+    }
+
+    glBindTexture(st.mTexTarget, st.mTexName);
+    if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
+        EGC_LOGE("bindTextureImage: no currently-bound texture");
+        return NO_INIT;
+    }
+
+    status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay);
+    if (err != NO_ERROR) {
+        EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+                 st.mCurrentTexture);
+        return UNKNOWN_ERROR;
+    }
+    mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+
+    // In the rare case that the display is terminated and then initialized
+    // again, we can't detect that the display changed (it didn't), but the
+    // image is invalid. In this case, repeat the exact same steps while
+    // forcing the creation of a new image.
+    if ((error = glGetError()) != GL_NO_ERROR) {
+        glBindTexture(st.mTexTarget, st.mTexName);
+        status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true);
+        if (result != NO_ERROR) {
+            EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+                     st.mCurrentTexture);
+            return UNKNOWN_ERROR;
+        }
+        mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+        if ((error = glGetError()) != GL_NO_ERROR) {
+            EGC_LOGE("bindTextureImage: error binding external image: %#04x", error);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    // Wait for the new buffer to be ready.
+    return doGLFenceWaitLocked(st);
+}
+
+status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) {
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    EGLContext ctx = eglGetCurrentContext();
+
+    if (!contextCheck) {
+        // if this is the first time we're called, mEglDisplay/mEglContext have
+        // never been set, so don't error out (below).
+        if (mEglDisplay == EGL_NO_DISPLAY) {
+            mEglDisplay = dpy;
+        }
+        if (mEglContext == EGL_NO_CONTEXT) {
+            mEglContext = ctx;
+        }
+    }
+
+    if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) {
+        EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
+        return INVALID_OPERATION;
+    }
+
+    if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) {
+        EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext");
+        return INVALID_OPERATION;
+    }
+
+    mEglDisplay = dpy;
+    mEglContext = ctx;
+    return NO_ERROR;
+}
+
+status_t EGLConsumer::detachFromContext(SurfaceTexture& st) {
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    EGLContext ctx = eglGetCurrentContext();
+
+    if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
+        EGC_LOGE("detachFromContext: invalid current EGLDisplay");
+        return INVALID_OPERATION;
+    }
+
+    if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
+        EGC_LOGE("detachFromContext: invalid current EGLContext");
+        return INVALID_OPERATION;
+    }
+
+    if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) {
+        status_t err = syncForReleaseLocked(dpy, st);
+        if (err != OK) {
+            return err;
+        }
+
+        glDeleteTextures(1, &st.mTexName);
+    }
+
+    mEglDisplay = EGL_NO_DISPLAY;
+    mEglContext = EGL_NO_CONTEXT;
+
+    return OK;
+}
+
+status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) {
+    // Initialize mCurrentTextureImage if there is a current buffer from past
+    // attached state.
+    int slot = st.mCurrentTexture;
+    if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+        if (!mEglSlots[slot].mEglImage.get()) {
+            mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+        }
+        mCurrentTextureImage = mEglSlots[slot].mEglImage;
+    }
+
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    EGLContext ctx = eglGetCurrentContext();
+
+    if (dpy == EGL_NO_DISPLAY) {
+        EGC_LOGE("attachToContext: invalid current EGLDisplay");
+        return INVALID_OPERATION;
+    }
+
+    if (ctx == EGL_NO_CONTEXT) {
+        EGC_LOGE("attachToContext: invalid current EGLContext");
+        return INVALID_OPERATION;
+    }
+
+    // We need to bind the texture regardless of whether there's a current
+    // buffer.
+    glBindTexture(st.mTexTarget, GLuint(tex));
+
+    mEglDisplay = dpy;
+    mEglContext = ctx;
+    st.mTexName = tex;
+    st.mOpMode = SurfaceTexture::OpMode::attachedToGL;
+
+    if (mCurrentTextureImage != nullptr) {
+        // This may wait for a buffer a second time. This is likely required if
+        // this is a different context, since otherwise the wait could be skipped
+        // by bouncing through another context. For the same context the extra
+        // wait is redundant.
+        status_t err = bindTextureImageLocked(st);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return OK;
+}
+
+status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) {
+    EGC_LOGV("syncForReleaseLocked");
+
+    if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+        if (SyncFeatures::getInstance().useNativeFenceSync()) {
+            EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+            if (sync == EGL_NO_SYNC_KHR) {
+                EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError());
+                return UNKNOWN_ERROR;
+            }
+            glFlush();
+            int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync);
+            eglDestroySyncKHR(dpy, sync);
+            if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+                EGC_LOGE("syncForReleaseLocked: error dup'ing native fence "
+                         "fd: %#x",
+                         eglGetError());
+                return UNKNOWN_ERROR;
+            }
+            sp<Fence> fence(new Fence(fenceFd));
+            status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+                                                    mCurrentTextureImage->graphicBuffer(), fence);
+            if (err != OK) {
+                EGC_LOGE("syncForReleaseLocked: error adding release fence: "
+                         "%s (%d)",
+                         strerror(-err), err);
+                return err;
+            }
+        } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+            EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence;
+            if (fence != EGL_NO_SYNC_KHR) {
+                // There is already a fence for the current slot.  We need to
+                // wait on that before replacing it with another fence to
+                // ensure that all outstanding buffer accesses have completed
+                // before the producer accesses it.
+                EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
+                if (result == EGL_FALSE) {
+                    EGC_LOGE("syncForReleaseLocked: error waiting for previous "
+                             "fence: %#x",
+                             eglGetError());
+                    return UNKNOWN_ERROR;
+                } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+                    EGC_LOGE("syncForReleaseLocked: timeout waiting for previous "
+                             "fence");
+                    return TIMED_OUT;
+                }
+                eglDestroySyncKHR(dpy, fence);
+            }
+
+            // Create a fence for the outstanding accesses in the current
+            // OpenGL ES context.
+            fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+            if (fence == EGL_NO_SYNC_KHR) {
+                EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError());
+                return UNKNOWN_ERROR;
+            }
+            glFlush();
+            mEglSlots[st.mCurrentTexture].mEglFence = fence;
+        }
+    }
+
+    return OK;
+}
+
+status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const {
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    EGLContext ctx = eglGetCurrentContext();
+
+    if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) {
+        EGC_LOGE("doGLFenceWait: invalid current EGLDisplay");
+        return INVALID_OPERATION;
+    }
+
+    if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) {
+        EGC_LOGE("doGLFenceWait: invalid current EGLContext");
+        return INVALID_OPERATION;
+    }
+
+    if (st.mCurrentFence->isValid()) {
+        if (SyncFeatures::getInstance().useWaitSync() &&
+            SyncFeatures::getInstance().useNativeFenceSync()) {
+            // Create an EGLSyncKHR from the current fence.
+            int fenceFd = st.mCurrentFence->dup();
+            if (fenceFd == -1) {
+                EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno);
+                return -errno;
+            }
+            EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+            EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+            if (sync == EGL_NO_SYNC_KHR) {
+                close(fenceFd);
+                EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError());
+                return UNKNOWN_ERROR;
+            }
+
+            // XXX: The spec draft is inconsistent as to whether this should
+            // return an EGLint or void.  Ignore the return value for now, as
+            // it's not strictly needed.
+            eglWaitSyncKHR(dpy, sync, 0);
+            EGLint eglErr = eglGetError();
+            eglDestroySyncKHR(dpy, sync);
+            if (eglErr != EGL_SUCCESS) {
+                EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr);
+                return UNKNOWN_ERROR;
+            }
+        } else {
+            status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked");
+            if (err != NO_ERROR) {
+                EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err);
+                return err;
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+void EGLConsumer::onFreeBufferLocked(int slotIndex) {
+    mEglSlots[slotIndex].mEglImage.clear();
+}
+
+void EGLConsumer::onAbandonLocked() {
+    mCurrentTextureImage.clear();
+}
+
+EGLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer)
+      : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {}
+
+EGLConsumer::EglImage::~EglImage() {
+    if (mEglImage != EGL_NO_IMAGE_KHR) {
+        if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+            ALOGE("~EglImage: eglDestroyImageKHR failed");
+        }
+        eglTerminate(mEglDisplay);
+    }
+}
+
+status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) {
+    // If there's an image and it's no longer valid, destroy it.
+    bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
+    bool displayInvalid = mEglDisplay != eglDisplay;
+    if (haveImage && (displayInvalid || forceCreation)) {
+        if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+            ALOGE("createIfNeeded: eglDestroyImageKHR failed");
+        }
+        eglTerminate(mEglDisplay);
+        mEglImage = EGL_NO_IMAGE_KHR;
+        mEglDisplay = EGL_NO_DISPLAY;
+    }
+
+    // If there's no image, create one.
+    if (mEglImage == EGL_NO_IMAGE_KHR) {
+        mEglDisplay = eglDisplay;
+        mEglImage = createImage(mEglDisplay, mGraphicBuffer);
+    }
+
+    // Fail if we can't create a valid image.
+    if (mEglImage == EGL_NO_IMAGE_KHR) {
+        mEglDisplay = EGL_NO_DISPLAY;
+        const sp<GraphicBuffer>& buffer = mGraphicBuffer;
+        ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+              buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+              buffer->getPixelFormat());
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
+    glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage));
+}
+
+EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy,
+                                               const sp<GraphicBuffer>& graphicBuffer) {
+    EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
+    const bool createProtectedImage =
+            (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent();
+    EGLint attrs[] = {
+            EGL_IMAGE_PRESERVED_KHR,
+            EGL_TRUE,
+            createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+            createProtectedImage ? EGL_TRUE : EGL_NONE,
+            EGL_NONE,
+    };
+    eglInitialize(dpy, nullptr, nullptr);
+    EGLImageKHR image =
+            eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
+    if (image == EGL_NO_IMAGE_KHR) {
+        EGLint error = eglGetError();
+        ALOGE("error creating EGLImage: %#x", error);
+        eglTerminate(dpy);
+    }
+    return image;
+}
+
+} // namespace android
diff --git a/libs/gui/surfacetexture/ImageConsumer.cpp b/libs/gui/surfacetexture/ImageConsumer.cpp
new file mode 100644
index 0000000..4bc4a7b
--- /dev/null
+++ b/libs/gui/surfacetexture/ImageConsumer.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2019 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 <gui/BufferQueue.h>
+#include <gui/surfacetexture/ImageConsumer.h>
+#include <gui/surfacetexture/SurfaceTexture.h>
+
+// Macro for including the SurfaceTexture name in log messages
+#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+void ImageConsumer::onReleaseBufferLocked(int buf) {
+    mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR;
+}
+
+sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+                                               bool* outQueueEmpty, SurfaceTexture& st,
+                                               SurfaceTexture_createReleaseFence createFence,
+                                               SurfaceTexture_fenceWait fenceWait,
+                                               void* fencePassThroughHandle) {
+    BufferItem item;
+    status_t err;
+    err = st.acquireBufferLocked(&item, 0);
+    if (err != OK) {
+        if (err != BufferQueue::NO_BUFFER_AVAILABLE) {
+            IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+        } else {
+            int slot = st.mCurrentTexture;
+            if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+                *outQueueEmpty = true;
+                *outDataspace = st.mCurrentDataSpace;
+                *outSlotid = slot;
+                return st.mSlots[slot].mGraphicBuffer;
+            }
+        }
+        return nullptr;
+    }
+
+    int slot = item.mSlot;
+    if (item.mFence->isValid()) {
+        // Wait on the producer fence for the buffer to be ready.
+        err = fenceWait(item.mFence->get(), fencePassThroughHandle);
+        if (err != OK) {
+            st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+                                   EGL_NO_SYNC_KHR);
+            return nullptr;
+        }
+    }
+
+    // Release old buffer.
+    if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
+        // If needed, set the released slot's fence to guard against a producer
+        // accessing the buffer before the outstanding accesses have completed.
+        int releaseFenceId = -1;
+        EGLDisplay display = EGL_NO_DISPLAY;
+        err = createFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), &display,
+                          &releaseFenceId, fencePassThroughHandle);
+        if (OK != err) {
+            st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+                                   EGL_NO_SYNC_KHR);
+            return nullptr;
+        }
+
+        if (releaseFenceId != -1) {
+            sp<Fence> releaseFence(new Fence(releaseFenceId));
+            status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+                                                    st.mSlots[st.mCurrentTexture].mGraphicBuffer,
+                                                    releaseFence);
+            if (err != OK) {
+                IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
+                st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+                                       EGL_NO_SYNC_KHR);
+                return nullptr;
+            }
+        }
+
+        // Finally release the old buffer.
+        status_t status =
+                st.releaseBufferLocked(st.mCurrentTexture,
+                                       st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
+                                       mImageSlots[st.mCurrentTexture].eglFence());
+        if (status < NO_ERROR) {
+            IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
+            err = status;
+            // Keep going, with error raised.
+        }
+    }
+
+    // Update the state.
+    st.mCurrentTexture = slot;
+    st.mCurrentCrop = item.mCrop;
+    st.mCurrentTransform = item.mTransform;
+    st.mCurrentScalingMode = item.mScalingMode;
+    st.mCurrentTimestamp = item.mTimestamp;
+    st.mCurrentDataSpace = item.mDataSpace;
+    st.mCurrentFence = item.mFence;
+    st.mCurrentFenceTime = item.mFenceTime;
+    st.mCurrentFrameNumber = item.mFrameNumber;
+    st.computeCurrentTransformMatrixLocked();
+
+    *outQueueEmpty = false;
+    *outDataspace = item.mDataSpace;
+    *outSlotid = slot;
+    return st.mSlots[slot].mGraphicBuffer;
+}
+
+} /* namespace android */
diff --git a/libs/gui/surfacetexture/SurfaceTexture.cpp b/libs/gui/surfacetexture/SurfaceTexture.cpp
new file mode 100644
index 0000000..25e5618
--- /dev/null
+++ b/libs/gui/surfacetexture/SurfaceTexture.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2019 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 <cutils/compiler.h>
+#include <gui/BufferQueue.h>
+#include <gui/surfacetexture/ImageConsumer.h>
+#include <gui/surfacetexture/SurfaceTexture.h>
+#include <gui/surfacetexture/surface_texture_platform.h>
+#include <math/mat4.h>
+#include <system/window.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+
+static const mat4 mtxIdentity;
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+                               uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+      : ConsumerBase(bq, isControlledByApp),
+        mCurrentCrop(Rect::EMPTY_RECT),
+        mCurrentTransform(0),
+        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+        mCurrentFence(Fence::NO_FENCE),
+        mCurrentTimestamp(0),
+        mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+        mCurrentFrameNumber(0),
+        mDefaultWidth(1),
+        mDefaultHeight(1),
+        mFilteringEnabled(true),
+        mTexName(tex),
+        mUseFenceSync(useFenceSync),
+        mTexTarget(texTarget),
+        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+        mOpMode(OpMode::attachedToGL) {
+    SFT_LOGV("SurfaceTexture");
+
+    memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+    mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
+                               bool useFenceSync, bool isControlledByApp)
+      : ConsumerBase(bq, isControlledByApp),
+        mCurrentCrop(Rect::EMPTY_RECT),
+        mCurrentTransform(0),
+        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+        mCurrentFence(Fence::NO_FENCE),
+        mCurrentTimestamp(0),
+        mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+        mCurrentFrameNumber(0),
+        mDefaultWidth(1),
+        mDefaultHeight(1),
+        mFilteringEnabled(true),
+        mTexName(0),
+        mUseFenceSync(useFenceSync),
+        mTexTarget(texTarget),
+        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+        mOpMode(OpMode::detached) {
+    SFT_LOGV("SurfaceTexture");
+
+    memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+    mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) {
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!");
+        return NO_INIT;
+    }
+    mDefaultWidth = w;
+    mDefaultHeight = h;
+    return mConsumer->setDefaultBufferSize(w, h);
+}
+
+status_t SurfaceTexture::updateTexImage() {
+    ATRACE_CALL();
+    SFT_LOGV("updateTexImage");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!");
+        return NO_INIT;
+    }
+
+    return mEGLConsumer.updateTexImage(*this);
+}
+
+status_t SurfaceTexture::releaseTexImage() {
+    // releaseTexImage can be invoked even when not attached to a GL context.
+    ATRACE_CALL();
+    SFT_LOGV("releaseTexImage");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!");
+        return NO_INIT;
+    }
+
+    return mEGLConsumer.releaseTexImage(*this);
+}
+
+status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+                                             uint64_t maxFrameNumber) {
+    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    switch (mOpMode) {
+        case OpMode::attachedToConsumer:
+            break;
+        case OpMode::attachedToGL:
+            mEGLConsumer.onAcquireBufferLocked(item, *this);
+            break;
+        case OpMode::detached:
+            break;
+    }
+
+    return NO_ERROR;
+}
+
+status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer,
+                                             EGLDisplay display, EGLSyncKHR eglFence) {
+    // release the buffer if it hasn't already been discarded by the
+    // BufferQueue. This can happen, for example, when the producer of this
+    // buffer has reallocated the original buffer slot after this buffer
+    // was acquired.
+    status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence);
+    // We could be releasing an EGL/Vulkan buffer, even if not currently
+    // attached to a GL context.
+    mImageConsumer.onReleaseBufferLocked(buf);
+    mEGLConsumer.onReleaseBufferLocked(buf);
+    return err;
+}
+
+status_t SurfaceTexture::detachFromContext() {
+    ATRACE_CALL();
+    SFT_LOGV("detachFromContext");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("detachFromContext: abandoned SurfaceTexture");
+        return NO_INIT;
+    }
+
+    if (mOpMode != OpMode::attachedToGL) {
+        SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context");
+        return INVALID_OPERATION;
+    }
+
+    status_t err = mEGLConsumer.detachFromContext(*this);
+    if (err == OK) {
+        mOpMode = OpMode::detached;
+    }
+
+    return err;
+}
+
+status_t SurfaceTexture::attachToContext(uint32_t tex) {
+    ATRACE_CALL();
+    SFT_LOGV("attachToContext");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("attachToContext: abandoned SurfaceTexture");
+        return NO_INIT;
+    }
+
+    if (mOpMode != OpMode::detached) {
+        SFT_LOGE("attachToContext: SurfaceTexture is already attached to a "
+                 "context");
+        return INVALID_OPERATION;
+    }
+
+    return mEGLConsumer.attachToContext(tex, *this);
+}
+
+void SurfaceTexture::takeConsumerOwnership() {
+    ATRACE_CALL();
+    Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        SFT_LOGE("attachToView: abandoned SurfaceTexture");
+        return;
+    }
+    if (mOpMode == OpMode::detached) {
+        mOpMode = OpMode::attachedToConsumer;
+
+        if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+            // release possible EGLConsumer texture cache
+            mEGLConsumer.onFreeBufferLocked(mCurrentTexture);
+            mEGLConsumer.onAbandonLocked();
+        }
+    } else {
+        SFT_LOGE("attachToView: already attached");
+    }
+}
+
+void SurfaceTexture::releaseConsumerOwnership() {
+    ATRACE_CALL();
+    Mutex::Autolock _l(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("detachFromView: abandoned SurfaceTexture");
+        return;
+    }
+
+    if (mOpMode == OpMode::attachedToConsumer) {
+        mOpMode = OpMode::detached;
+    } else {
+        SFT_LOGE("detachFromView: not attached to View");
+    }
+}
+
+uint32_t SurfaceTexture::getCurrentTextureTarget() const {
+    return mTexTarget;
+}
+
+void SurfaceTexture::getTransformMatrix(float mtx[16]) {
+    Mutex::Autolock lock(mMutex);
+    memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::setFilteringEnabled(bool enabled) {
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
+        return;
+    }
+    bool needsRecompute = mFilteringEnabled != enabled;
+    mFilteringEnabled = enabled;
+
+    if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+        SFT_LOGD("setFilteringEnabled called with no current item");
+    }
+
+    if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+        computeCurrentTransformMatrixLocked();
+    }
+}
+
+void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+    SFT_LOGV("computeCurrentTransformMatrixLocked");
+    sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
+            ? nullptr
+            : mSlots[mCurrentTexture].mGraphicBuffer;
+    if (buf == nullptr) {
+        SFT_LOGD("computeCurrentTransformMatrixLocked: no current item");
+    }
+    computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform,
+                           mFilteringEnabled);
+}
+
+void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+                                            const Rect& cropRect, uint32_t transform,
+                                            bool filtering) {
+    // Transform matrices
+    static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+    static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+    static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+
+    mat4 xform;
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+        xform *= mtxFlipH;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+        xform *= mtxFlipV;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+        xform *= mtxRot90;
+    }
+
+    if (!cropRect.isEmpty() && buf.get()) {
+        float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+        float bufferWidth = buf->getWidth();
+        float bufferHeight = buf->getHeight();
+        float shrinkAmount = 0.0f;
+        if (filtering) {
+            // In order to prevent bilinear sampling beyond the edge of the
+            // crop rectangle we may need to shrink it by 2 texels in each
+            // dimension.  Normally this would just need to take 1/2 a texel
+            // off each end, but because the chroma channels of YUV420 images
+            // are subsampled we may need to shrink the crop region by a whole
+            // texel on each side.
+            switch (buf->getPixelFormat()) {
+                case PIXEL_FORMAT_RGBA_8888:
+                case PIXEL_FORMAT_RGBX_8888:
+                case PIXEL_FORMAT_RGBA_FP16:
+                case PIXEL_FORMAT_RGBA_1010102:
+                case PIXEL_FORMAT_RGB_888:
+                case PIXEL_FORMAT_RGB_565:
+                case PIXEL_FORMAT_BGRA_8888:
+                    // We know there's no subsampling of any channels, so we
+                    // only need to shrink by a half a pixel.
+                    shrinkAmount = 0.5;
+                    break;
+
+                default:
+                    // If we don't recognize the format, we must assume the
+                    // worst case (that we care about), which is YUV420.
+                    shrinkAmount = 1.0;
+                    break;
+            }
+        }
+
+        // Only shrink the dimensions that are not the size of the buffer.
+        if (cropRect.width() < bufferWidth) {
+            tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+            sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth;
+        }
+        if (cropRect.height() < bufferHeight) {
+            ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight;
+            sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight;
+        }
+
+        mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1);
+        xform = crop * xform;
+    }
+
+    // SurfaceFlinger expects the top of its window textures to be at a Y
+    // coordinate of 0, so SurfaceTexture must behave the same way.  We don't
+    // want to expose this to applications, however, so we must add an
+    // additional vertical flip to the transform after all the other transforms.
+    xform = mtxFlipV * xform;
+
+    memcpy(outTransform, xform.asArray(), sizeof(xform));
+}
+
+Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) {
+    Rect outCrop = crop;
+
+    uint32_t newWidth = static_cast<uint32_t>(crop.width());
+    uint32_t newHeight = static_cast<uint32_t>(crop.height());
+
+    if (newWidth * bufferHeight > newHeight * bufferWidth) {
+        newWidth = newHeight * bufferWidth / bufferHeight;
+        ALOGV("too wide: newWidth = %d", newWidth);
+    } else if (newWidth * bufferHeight < newHeight * bufferWidth) {
+        newHeight = newWidth * bufferHeight / bufferWidth;
+        ALOGV("too tall: newHeight = %d", newHeight);
+    }
+
+    uint32_t currentWidth = static_cast<uint32_t>(crop.width());
+    uint32_t currentHeight = static_cast<uint32_t>(crop.height());
+
+    // The crop is too wide
+    if (newWidth < currentWidth) {
+        uint32_t dw = currentWidth - newWidth;
+        auto halfdw = dw / 2;
+        outCrop.left += halfdw;
+        // Not halfdw because it would subtract 1 too few when dw is odd
+        outCrop.right -= (dw - halfdw);
+        // The crop is too tall
+    } else if (newHeight < currentHeight) {
+        uint32_t dh = currentHeight - newHeight;
+        auto halfdh = dh / 2;
+        outCrop.top += halfdh;
+        // Not halfdh because it would subtract 1 too few when dh is odd
+        outCrop.bottom -= (dh - halfdh);
+    }
+
+    ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,
+          outCrop.bottom);
+
+    return outCrop;
+}
+
+nsecs_t SurfaceTexture::getTimestamp() {
+    SFT_LOGV("getTimestamp");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTimestamp;
+}
+
+android_dataspace SurfaceTexture::getCurrentDataSpace() {
+    SFT_LOGV("getCurrentDataSpace");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentDataSpace;
+}
+
+uint64_t SurfaceTexture::getFrameNumber() {
+    SFT_LOGV("getFrameNumber");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFrameNumber;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+    Mutex::Autolock lock(mMutex);
+    return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
+            ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
+            : mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTransform;
+}
+
+uint32_t SurfaceTexture::getCurrentScalingMode() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentScalingMode;
+}
+
+sp<Fence> SurfaceTexture::getCurrentFence() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFence;
+}
+
+std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFenceTime;
+}
+
+void SurfaceTexture::freeBufferLocked(int slotIndex) {
+    SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+    if (slotIndex == mCurrentTexture) {
+        mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+    }
+    // The slotIndex buffer could have EGL cache, but there is no way to tell
+    // for sure. Buffers can be freed after SurfaceTexture has detached from GL
+    // context or View.
+    mEGLConsumer.onFreeBufferLocked(slotIndex);
+    ConsumerBase::freeBufferLocked(slotIndex);
+}
+
+void SurfaceTexture::abandonLocked() {
+    SFT_LOGV("abandonLocked");
+    mEGLConsumer.onAbandonLocked();
+    ConsumerBase::abandonLocked();
+}
+
+status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) {
+    return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
+}
+
+void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {
+    result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
+                        "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
+                        prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
+                        mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+                        mCurrentTransform);
+
+    ConsumerBase::dumpLocked(result, prefix);
+}
+
+sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+                                                float* outTransformMatrix, bool* outQueueEmpty,
+                                                SurfaceTexture_createReleaseFence createFence,
+                                                SurfaceTexture_fenceWait fenceWait,
+                                                void* fencePassThroughHandle) {
+    Mutex::Autolock _l(mMutex);
+    sp<GraphicBuffer> buffer;
+
+    if (mAbandoned) {
+        SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!");
+        return buffer;
+    }
+
+    if (mOpMode != OpMode::attachedToConsumer) {
+        SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View");
+        return buffer;
+    }
+
+    buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
+                                          createFence, fenceWait, fencePassThroughHandle);
+    memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+    return buffer;
+}
+
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
+    return st->consumer->getCurrentTextureTarget();
+}
+
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* texture) {
+    texture->consumer->takeConsumerOwnership();
+}
+
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
+    texture->consumer->releaseConsumerOwnership();
+}
+
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+                                               android_dataspace* outDataspace,
+                                               float* outTransformMatrix, bool* outNewContent,
+                                               ASurfaceTexture_createReleaseFence createFence,
+                                               ASurfaceTexture_fenceWait fenceWait, void* handle) {
+    sp<GraphicBuffer> buffer;
+    *outNewContent = false;
+    bool queueEmpty;
+    do {
+        buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
+                                             &queueEmpty, createFence, fenceWait, handle);
+        if (!queueEmpty) {
+            *outNewContent = true;
+        }
+    } while (buffer.get() && (!queueEmpty));
+    return reinterpret_cast<AHardwareBuffer*>(buffer.get());
+}
+
+ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer,
+                                        sp<IGraphicBufferProducer> producer) {
+    ASurfaceTexture* ast = new ASurfaceTexture;
+    ast->consumer = consumer;
+    ast->producer = producer;
+    return ast;
+}
+
+} // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index db1ac24..ff22913 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -26,6 +26,7 @@
 #include <ui/DisplayInfo.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
 
 #include <gtest/gtest.h>
 
@@ -201,6 +202,7 @@
               igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
                                    &qbOutput));
     ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(3));
+    ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
 
     int slot;
     sp<Fence> fence;
@@ -222,6 +224,7 @@
                                                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                    Fence::NO_FENCE);
     igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
 
     adapter.waitForCallback();
 
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
new file mode 100644
index 0000000..6665635
--- /dev/null
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2019 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 <apex/display.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayInfo.h>
+#include <ui/GraphicTypes.h>
+
+#include <algorithm>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+namespace android::display::impl {
+
+/**
+ * Implementation of ADisplayConfig
+ */
+struct DisplayConfigImpl {
+    /**
+     * The width in pixels of the display configuration.
+     */
+    int32_t width{0};
+
+    /**
+     * The height in pixels of the display configuration.
+     */
+
+    int32_t height{0};
+
+    /**
+     * The display density.
+     */
+    float density{0};
+
+    /**
+     * The refresh rate of the display configuration, in frames per second.
+     */
+    float fps{0.0};
+
+    /**
+     * The vsync offset at which surfaceflinger runs, in nanoseconds.
+     */
+    int64_t sfOffset{0};
+
+    /**
+     * The vsync offset at which applications run, in nanoseconds.
+     */
+    int64_t appOffset{0};
+};
+
+// DisplayConfigImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value);
+
+/**
+ * Implementation of ADisplay
+ */
+struct DisplayImpl {
+    /**
+     * A physical display ID, unique to this display.
+     */
+    PhysicalDisplayId id;
+
+    /**
+     * The type of the display, i.e. whether it is an internal or external
+     * display.
+     */
+    ADisplayType type;
+
+    /**
+     * Number of supported configs
+     */
+    size_t numConfigs;
+
+    /**
+     * Set of supported configs by this display.
+     */
+    DisplayConfigImpl* configs;
+};
+
+// DisplayImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayImpl>::value);
+
+} // namespace android::display::impl
+
+using namespace android;
+using namespace android::display::impl;
+
+#define CHECK_NOT_NULL(name) \
+    LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+namespace {
+sp<IBinder> getToken(ADisplay* display) {
+    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+    return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
+}
+
+int64_t computeSfOffset(const DisplayInfo& info) {
+    // This should probably be part of the config instead of extrapolated from
+    // the presentation deadline and fudged here, but the way the math works out
+    // here we do get the right offset.
+    return static_cast<int64_t>((1000000000 / info.fps) - info.presentationDeadline + 1000000);
+}
+} // namespace
+
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
+    const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    const size_t size = ids.size();
+    if (size == 0) {
+        return NO_INIT;
+    }
+
+    std::vector<DisplayConfigImpl> configsPerDisplay[size];
+    int numConfigs = 0;
+    for (int i = 0; i < size; ++i) {
+        const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
+        Vector<DisplayInfo> configs;
+        const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+        if (status != OK) {
+            return status;
+        }
+        if (configs.empty()) {
+            return NO_INIT;
+        }
+
+        numConfigs += configs.size();
+        configsPerDisplay[i].reserve(configs.size());
+        for (int j = 0; j < configs.size(); ++j) {
+            const DisplayInfo config = configs[j];
+            configsPerDisplay[i].emplace_back(
+                    DisplayConfigImpl{static_cast<int32_t>(config.w),
+                                      static_cast<int32_t>(config.h), config.density, config.fps,
+                                      computeSfOffset(config), config.appVsyncOffset});
+        }
+    }
+
+    const std::optional<PhysicalDisplayId> internalId =
+            SurfaceComposerClient::getInternalDisplayId();
+
+    // Here we allocate all our required memory in one block. The layout is as
+    // follows:
+    // ------------------------------------------------------------
+    // | DisplayImpl pointers | DisplayImpls | DisplayConfigImpls |
+    // ------------------------------------------------------------
+    //
+    // The caller will be given a DisplayImpl** which points to the beginning of
+    // the block of DisplayImpl pointers.
+    // Each DisplayImpl* points to a DisplayImpl in the second block.
+    // Each DisplayImpl contains a DisplayConfigImpl*, which points to a
+    // contiguous block of DisplayConfigImpls specific to that display.
+    DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
+            malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size +
+                   sizeof(DisplayConfigImpl) * numConfigs));
+    DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size);
+    DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size);
+
+    for (size_t i = 0; i < size; ++i) {
+        const PhysicalDisplayId id = ids[i];
+        const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
+                                                     : ADisplayType::DISPLAY_TYPE_EXTERNAL;
+        const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
+        memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
+
+        displayData[i] = DisplayImpl{id, type, configs.size(), configData};
+        impls[i] = displayData + i;
+        // Advance the configData pointer so that future configs are written to
+        // the correct display.
+        configData += configs.size();
+    }
+
+    *outDisplays = reinterpret_cast<ADisplay**>(impls);
+    return size;
+}
+
+void ADisplay_release(ADisplay** displays) {
+    if (displays == nullptr) {
+        return;
+    }
+    free(displays);
+}
+
+float ADisplay_getMaxSupportedFps(ADisplay* display) {
+    CHECK_NOT_NULL(display);
+    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+    float maxFps = 0.0;
+    for (int i = 0; i < impl->numConfigs; ++i) {
+        maxFps = std::max(maxFps, impl->configs[i].fps);
+    }
+    return maxFps;
+}
+
+ADisplayType ADisplay_getDisplayType(ADisplay* display) {
+    CHECK_NOT_NULL(display);
+
+    return reinterpret_cast<DisplayImpl*>(display)->type;
+}
+
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
+    CHECK_NOT_NULL(display);
+
+    sp<IBinder> token = getToken(display);
+    const int index = SurfaceComposerClient::getActiveConfig(token);
+    if (index < 0) {
+        return index;
+    }
+
+    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+    *outConfig = reinterpret_cast<ADisplayConfig*>(impl->configs + index);
+    return OK;
+}
+
+float ADisplayConfig_getDensity(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->density;
+}
+
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->width;
+}
+
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->height;
+}
+
+float ADisplayConfig_getFps(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->fps;
+}
+
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset;
+}
+
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset;
+}
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
new file mode 100644
index 0000000..66ebdfd
--- /dev/null
+++ b/libs/nativedisplay/Android.bp
@@ -0,0 +1,50 @@
+// Copyright 2019 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.
+
+cc_library_headers {
+    name: "libnativedisplay_headers",
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libnativedisplay",
+    export_include_dirs: [
+        "include",
+    ],
+
+    clang: true,
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-enum-compare",
+        "-Wno-unused-function",
+    ],
+
+    srcs: [
+        "ADisplay.cpp",
+    ],
+
+    shared_libs: [
+        "libgui",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+
+    header_libs: [
+        "libnativedisplay_headers",
+    ],
+
+}
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
new file mode 100644
index 0000000..7af452a
--- /dev/null
+++ b/libs/nativedisplay/include/apex/display.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2019 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 <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native display
+ */
+typedef struct ADisplay ADisplay;
+
+/**
+ * Enum describing the possible types of a display
+ */
+enum ADisplayType {
+    /**
+     * A display that is the internal, or "primary" display for a device.
+     */
+    DISPLAY_TYPE_INTERNAL = 0,
+
+    /**
+     * A display that is externally connected for a device.
+     */
+    DISPLAY_TYPE_EXTERNAL = 1,
+};
+
+/**
+ * Opaque handle for display metadata
+ */
+typedef struct ADisplayConfig ADisplayConfig;
+
+/**
+ * Acquires a list of display handles. Memory is allocated for the list and is
+ * owned by the caller. The caller is responsible for freeing this memory by
+ * calling ADisplayList_release.
+ *
+ * Returns the size of the returned list on success.
+ * Returns -errno on error.
+ */
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays);
+
+/**
+ * Releases a list of display handles created by
+ * ADisplayList_acquirePhysicalDisplays.
+ */
+void ADisplay_release(ADisplay** displays);
+
+/**
+ * Queries the maximum supported fps for the given display.
+ */
+float ADisplay_getMaxSupportedFps(ADisplay* display);
+
+/**
+ * Queries the display's type.
+ */
+ADisplayType ADisplay_getDisplayType(ADisplay* display);
+
+/**
+ * Gets the current display configuration for the given display.
+ *
+ * Memory is *not* allocated for the caller. As such, the returned output
+ * configuration's lifetime will not be longer than the ADisplay* passed to this
+ * function - if ADisplay_release is called destroying the ADisplay object then
+ * it is invalid to access the ADisplayConfig returned here.
+ *
+ * Note that the current display configuration can change. Listening to updates
+ * to the current display configuration should be done via Choreographer. If
+ * such an update is observed, then this method should be recalled to get the
+ * new current configuration.
+ *
+ * Returns OK on success, -errno on failure.
+ */
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);
+
+/**
+ * Queries the density for a given display configuration.
+ */
+float ADisplayConfig_getDensity(ADisplayConfig* config);
+
+/**
+ * Queries the width in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config);
+
+/**
+ * Queries the height in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config);
+
+/**
+ * Queries the display refresh rate for a given display configuration.
+ */
+float ADisplayConfig_getFps(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which the system compositor is scheduled to
+ * run. If a vsync occurs at time T, and the compositor runs at time T + S, then
+ * this returns S in nanoseconds.
+ */
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which applications are scheduled to run. If a
+ * vsync occurs at time T, and applications run at time T + S, then this returns
+ * S in nanoseconds.
+ */
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config);
+
+__END_DECLS
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 940ff5a..e8154a6 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -21,22 +21,7 @@
         "-Werror",
     ],
     cppflags: [
-        "-Weverything",
-
-        // The static constructors and destructors in this library have not been noted to
-        // introduce significant overheads
-        "-Wno-exit-time-destructors",
-        "-Wno-global-constructors",
-
-        // We only care about compiling as C++14
-        "-Wno-c++98-compat-pedantic",
-
-        // android/sensors.h uses nested anonymous unions and anonymous structs
-        "-Wno-nested-anon-types",
-        "-Wno-gnu-anonymous-struct",
-
-        // Don't warn about struct padding
-        "-Wno-padded",
+        "-Wextra",
     ],
 
     srcs: [
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 47137f1..afa6a2b 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -26,32 +26,18 @@
         "-Werror",
     ],
     cppflags: [
-        "-Weverything",
-
-        // The static constructors and destructors in this library have not been noted to
-        // introduce significant overheads
-        "-Wno-exit-time-destructors",
-        "-Wno-global-constructors",
-
-        // We only care about compiling as C++14
-        "-Wno-c++98-compat-pedantic",
-
-        // We are aware of the risks inherent in comparing floats for equality
-        "-Wno-float-equal",
-
-        // We use four-character constants for the GraphicBuffer header, and don't care
-        // that they're non-portable as long as they're consistent within one execution
-        "-Wno-four-char-constants",
-
-        // Don't warn about struct padding
-        "-Wno-padded",
-
-        "-Wno-switch-enum",
-        "-Wno-format-pedantic",
+        "-Wextra",
     ],
 
     sanitize: {
         integer_overflow: true,
+        misc_undefined: ["bounds"],
+        diag: {
+            misc_undefined: ["bounds"],
+            no_recover: [
+                "bounds",
+            ],
+        },
     },
 
     srcs: [
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index 1176abf..1ce9c99 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -39,7 +39,6 @@
         "libcutils",
         "liblog",
         "libutils",
-        "libcrypto",
         "libselinux",
     ],
 }
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index d1b4a4e..1fc7927 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -61,7 +61,7 @@
 
 using nsecs_t = int64_t;
 
-struct extention_map_t {
+struct extension_map_t {
     const char* name;
     __eglMustCastToProperFunctionPointerType address;
 };
@@ -154,7 +154,7 @@
  * (keep in sync with gExtensionString above)
  *
  */
-static const extention_map_t sExtensionMap[] = {
+static const extension_map_t sExtensionMap[] = {
     // EGL_KHR_lock_surface
     { "eglLockSurfaceKHR",
             (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
@@ -257,13 +257,14 @@
          !strcmp((procname), "eglAwakenProcessIMG"))
 
 // accesses protected by sExtensionMapMutex
-static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap;
+static std::unordered_map<std::string, int> sGLExtensionSlotMap;
 
-static int sGLExtentionSlot = 0;
+static int sGLExtensionSlot = 0;
 static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
 
 static void(*findProcAddress(const char* name,
-        const extention_map_t* map, size_t n))() {
+        const extension_map_t* map, size_t n))() {
     for (uint32_t i=0 ; i<n ; i++) {
         if (!strcmp(name, map[i].name)) {
             return map[i].address;
@@ -1223,7 +1224,7 @@
     addr = findBuiltinWrapper(procname);
     if (addr) return addr;
 
-    // this protects accesses to sGLExtentionMap and sGLExtentionSlot
+    // this protects accesses to sGLExtensionMap, sGLExtensionSlot, and sGLExtensionSlotMap
     pthread_mutex_lock(&sExtensionMapMutex);
 
     /*
@@ -1244,51 +1245,69 @@
      */
 
     const std::string name(procname);
-
-    auto& extentionMap = sGLExtentionMap;
-    auto pos = extentionMap.find(name);
-    addr = (pos != extentionMap.end()) ? pos->second : nullptr;
-    const int slot = sGLExtentionSlot;
-
-    ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
-             "no more slots for eglGetProcAddress(\"%s\")",
-             procname);
-
+    auto& extensionMap = sGLExtensionMap;
+    auto& extensionSlotMap = sGLExtensionSlotMap;
     egl_connection_t* const cnx = &gEGLImpl;
     LayerLoader& layer_loader(LayerLoader::getInstance());
 
-    if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+    // See if we've already looked up this extension
+    auto pos = extensionMap.find(name);
+    addr = (pos != extensionMap.end()) ? pos->second : nullptr;
 
-        if (cnx->dso && cnx->egl.eglGetProcAddress) {
+    if (!addr) {
+        // This is the first time we've looked this function up
+        // Ensure we have room to track it
+        const int slot = sGLExtensionSlot;
+        if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
 
-            // Extensions are independent of the bound context
-            addr = cnx->egl.eglGetProcAddress(procname);
-            if (addr) {
+            if (cnx->dso && cnx->egl.eglGetProcAddress) {
 
-                // purposefully track the bottom of the stack in extensionMap
-                extentionMap[name] = addr;
+                // Extensions are independent of the bound context
+                addr = cnx->egl.eglGetProcAddress(procname);
+                if (addr) {
 
-                // Apply layers
-                addr = layer_loader.ApplyLayers(procname, addr);
+                    // purposefully track the bottom of the stack in extensionMap
+                    extensionMap[name] = addr;
 
-                // Track the top most entry point
-                cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
-                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
-                addr = gExtensionForwarders[slot];
-                sGLExtentionSlot++;
+                    // Apply layers
+                    addr = layer_loader.ApplyLayers(procname, addr);
+
+                    // Track the top most entry point return the extension forwarder
+                    cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+                    cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+                    addr = gExtensionForwarders[slot];
+
+                    // Remember the slot for this extension
+                    extensionSlotMap[name] = slot;
+
+                    // Increment the global extension index
+                    sGLExtensionSlot++;
+                }
             }
+        } else {
+            // The extension forwarder has a fixed number of slots
+            ALOGE("no more slots for eglGetProcAddress(\"%s\")", procname);
         }
 
-    } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
+    } else {
+        // We tracked an address, so we've seen this func before
+        // Look up the slot for this extension
+        auto slot_pos = extensionSlotMap.find(name);
+        int ext_slot = (slot_pos != extensionSlotMap.end()) ? slot_pos->second : -1;
+        if (ext_slot < 0) {
+            // Something has gone wrong, this should not happen
+            ALOGE("No extension slot found for %s", procname);
+            return nullptr;
+        }
 
-        // We've seen this func before, but we tracked the bottom, so re-apply layers
-        // More layers might have been enabled
+        // We tracked the bottom of the stack, so re-apply layers since
+        // more layers might have been enabled
         addr = layer_loader.ApplyLayers(procname, addr);
 
-        // Track the top most entry point
-        cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
-        cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
-        addr = gExtensionForwarders[slot];
+        // Track the top most entry point and return the extension forwarder
+        cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] =
+        cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
+        addr = gExtensionForwarders[ext_slot];
     }
 
     pthread_mutex_unlock(&sExtensionMapMutex);
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e7640dd..1043390 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -46,7 +46,6 @@
 }
 
 void InputManager::initialize() {
-    mReaderThread = new InputReaderThread(mReader);
     mDispatcherThread = new InputDispatcherThread(mDispatcher);
 }
 
@@ -57,9 +56,9 @@
         return result;
     }
 
-    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+    result = mReader->start();
     if (result) {
-        ALOGE("Could not start InputReader thread due to error %d.", result);
+        ALOGE("Could not start InputReader due to error %d.", result);
 
         mDispatcherThread->requestExit();
         return result;
@@ -69,9 +68,9 @@
 }
 
 status_t InputManager::stop() {
-    status_t result = mReaderThread->requestExitAndWait();
+    status_t result = mReader->stop();
     if (result) {
-        ALOGW("Could not stop InputReader thread due to error %d.", result);
+        ALOGW("Could not stop InputReader due to error %d.", result);
     }
 
     result = mDispatcherThread->requestExitAndWait();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 40f66d8..2a7ed0f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -43,15 +43,15 @@
 /*
  * The input manager is the core of the system event processing.
  *
- * The input manager uses two threads.
+ * The input manager has two components.
  *
- * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
- *    applies policy, and posts messages to a queue managed by the DispatcherThread.
+ * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
+ *    policy, and posts messages to a queue managed by the InputDispatcherThread.
  * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
  *    queue and asynchronously dispatches them to applications.
  *
- * By design, the InputReaderThread class and InputDispatcherThread class do not share any
- * internal state.  Moreover, all communication is done one way from the InputReaderThread
+ * By design, the InputReader class and InputDispatcherThread class do not share any
+ * internal state.  Moreover, all communication is done one way from the InputReader
  * into the InputDispatcherThread and never the reverse.  Both classes may interact with the
  * InputDispatchPolicy, however.
  *
@@ -102,7 +102,6 @@
 
 private:
     sp<InputReaderInterface> mReader;
-    sp<InputReaderThread> mReaderThread;
 
     sp<InputClassifierInterface> mClassifier;
 
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 0422d83..2d6f2c1 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,20 +33,6 @@
 
 namespace android {
 
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
-        Thread(/*canCallJava*/ true), mReader(reader) {
-}
-
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
-    mReader->loopOnce();
-    return true;
-}
-
 // --- InputReaderConfiguration ---
 
 std::string InputReaderConfiguration::changesToString(uint32_t changes) {
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 5d576b9..56c0a73 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -19,12 +19,12 @@
 
 #include "PointerControllerInterface.h"
 
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
-#include <input/DisplayViewport.h>
 #include <input/VelocityControl.h>
 #include <input/VelocityTracker.h>
-#include <utils/Thread.h>
+#include <utils/Errors.h>
 #include <utils/RefBase.h>
 
 #include <stddef.h>
@@ -44,7 +44,16 @@
 
 namespace android {
 
-/* Processes raw input events and sends cooked event data to an input listener. */
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
 class InputReaderInterface : public virtual RefBase {
 protected:
     InputReaderInterface() { }
@@ -56,18 +65,17 @@
      * This method may be called on any thread (usually by the input manager). */
     virtual void dump(std::string& dump) = 0;
 
-    /* Called by the heatbeat to ensures that the reader has not deadlocked. */
+    /* Called by the heartbeat to ensures that the reader has not deadlocked. */
     virtual void monitor() = 0;
 
     /* Returns true if the input device is enabled. */
     virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
 
-    /* Runs a single iteration of the processing loop.
-     * Nominally reads and processes one incoming message from the EventHub.
-     *
-     * This method should be called on the input reader thread.
-     */
-    virtual void loopOnce() = 0;
+    /* Makes the reader start processing events from the kernel. */
+    virtual status_t start() = 0;
+
+    /* Makes the reader stop processing any more events. */
+    virtual status_t stop() = 0;
 
     /* Gets information about all input devices.
      *
@@ -104,17 +112,7 @@
     virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
 };
 
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
-    explicit InputReaderThread(const sp<InputReaderInterface>& reader);
-    virtual ~InputReaderThread();
-
-private:
-    sp<InputReaderInterface> mReader;
-
-    virtual bool threadLoop();
-};
+// --- InputReaderConfiguration ---
 
 /*
  * Input reader configuration.
@@ -285,6 +283,8 @@
     std::vector<DisplayViewport> mDisplays;
 };
 
+// --- TouchAffineTransformation ---
+
 struct TouchAffineTransformation {
     float x_scale;
     float x_ymix;
@@ -307,6 +307,8 @@
     void applyTo(float& x, float& y) const;
 };
 
+// --- InputReaderPolicyInterface ---
+
 /*
  * Input reader policy interface.
  *
@@ -316,8 +318,8 @@
  * The actual implementation is partially supported by callbacks into the DVM
  * via JNI.  This interface is also mocked in the unit tests.
  *
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
+ * These methods will NOT re-enter the input reader interface, so they may be called from
+ * any method in the input reader interface.
  */
 class InputReaderPolicyInterface : public virtual RefBase {
 protected:
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index e57604c..05f0db1 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,16 +38,38 @@
 #include <unistd.h>
 
 #include <log/log.h>
+#include <utils/Errors.h>
 
 #include <android-base/stringprintf.h>
 #include <input/Keyboard.h>
 #include <input/VirtualKeyMap.h>
-
+#include <utils/Thread.h>
 
 using android::base::StringPrintf;
 
 namespace android {
 
+// --- InputReader::InputReaderThread ---
+
+/* Thread that reads raw events from the event hub and processes them, endlessly. */
+class InputReader::InputReaderThread : public Thread {
+public:
+    explicit InputReaderThread(InputReader* reader)
+          : Thread(/* canCallJava */ true), mReader(reader) {}
+
+    ~InputReaderThread() {}
+
+private:
+    InputReader* mReader;
+
+    bool threadLoop() override {
+        mReader->loopOnce();
+        return true;
+    }
+};
+
+// --- InputReader ---
+
 InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                          const sp<InputReaderPolicyInterface>& policy,
                          const sp<InputListenerInterface>& listener)
@@ -61,6 +83,7 @@
         mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
     mQueuedListener = new QueuedInputListener(listener);
+    mThread = new InputReaderThread(this);
 
     { // acquire lock
         AutoMutex _l(mLock);
@@ -71,11 +94,33 @@
 }
 
 InputReader::~InputReader() {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        delete mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        delete devicePair.second;
     }
 }
 
+status_t InputReader::start() {
+    if (mThread->isRunning()) {
+        return ALREADY_EXISTS;
+    }
+    return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+}
+
+status_t InputReader::stop() {
+    if (!mThread->isRunning()) {
+        return OK;
+    }
+    if (gettid() == mThread->getTid()) {
+        ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+        return INVALID_OPERATION;
+    }
+    // Directly calling requestExitAndWait() causes the thread to not exit
+    // if mEventHub is waiting for a long timeout.
+    mThread->requestExit();
+    mEventHub->wake();
+    return mThread->requestExitAndWait();
+}
+
 void InputReader::loopOnce() {
     int32_t oldGeneration;
     int32_t timeoutMillis;
@@ -179,8 +224,7 @@
 }
 
 void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
+    if (mDevices.find(deviceId) != mDevices.end()) {
         ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
         return;
     }
@@ -201,7 +245,7 @@
               device->getSources());
     }
 
-    mDevices.add(deviceId, device);
+    mDevices.insert({deviceId, device});
     bumpGenerationLocked();
 
     if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
@@ -210,15 +254,14 @@
 }
 
 void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
-    InputDevice* device = nullptr;
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt == mDevices.end()) {
         ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
         return;
     }
 
-    device = mDevices.valueAt(deviceIndex);
-    mDevices.removeItemsAt(deviceIndex, 1);
+    InputDevice* device = deviceIt->second;
+    mDevices.erase(deviceIt);
     bumpGenerationLocked();
 
     if (device->isIgnored()) {
@@ -315,13 +358,13 @@
 
 void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents,
                                                size_t count) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt == mDevices.end()) {
         ALOGW("Discarding event for unknown deviceId %d.", deviceId);
         return;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
+    InputDevice* device = deviceIt->second;
     if (device->isIgnored()) {
         // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
         return;
@@ -331,8 +374,8 @@
 }
 
 void InputReader::timeoutExpiredLocked(nsecs_t when) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         if (!device->isIgnored()) {
             device->timeoutExpired(when);
         }
@@ -360,8 +403,8 @@
         if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
             mEventHub->requestReopenDevices();
         } else {
-            for (size_t i = 0; i < mDevices.size(); i++) {
-                InputDevice* device = mDevices.valueAt(i);
+            for (auto& devicePair : mDevices) {
+                InputDevice* device = devicePair.second;
                 device->configure(now, &mConfig, changes);
             }
         }
@@ -371,8 +414,8 @@
 void InputReader::updateGlobalMetaStateLocked() {
     mGlobalMetaState = 0;
 
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         mGlobalMetaState |= device->getMetaState();
     }
 }
@@ -386,8 +429,8 @@
 }
 
 void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
@@ -397,8 +440,8 @@
 }
 
 void InputReader::dispatchExternalStylusState(const StylusState& state) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         device->updateExternalStylusState(state);
     }
 }
@@ -421,8 +464,8 @@
 }
 
 void InputReader::fadePointerLocked() {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         device->fadePointer();
     }
 }
@@ -446,9 +489,8 @@
 void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
     outInputDevices.clear();
 
-    size_t numDevices = mDevices.size();
-    for (size_t i = 0; i < numDevices; i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         if (!device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
@@ -479,17 +521,16 @@
                                     GetStateFunc getStateFunc) {
     int32_t result = AKEY_STATE_UNKNOWN;
     if (deviceId >= 0) {
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            InputDevice* device = mDevices.valueAt(deviceIndex);
+        auto deviceIt = mDevices.find(deviceId);
+        if (deviceIt != mDevices.end()) {
+            InputDevice* device = deviceIt->second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 result = (device->*getStateFunc)(sourceMask, code);
             }
         }
     } else {
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
+        for (auto& devicePair : mDevices) {
+            InputDevice* device = devicePair.second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
                 // value.  Otherwise, return AKEY_STATE_UP as long as one device reports it.
@@ -506,13 +547,13 @@
 }
 
 void InputReader::toggleCapsLockState(int32_t deviceId) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt == mDevices.end()) {
         ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
         return;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
+    InputDevice* device = deviceIt->second;
     if (device->isIgnored()) {
         return;
     }
@@ -533,17 +574,16 @@
                                               uint8_t* outFlags) {
     bool result = false;
     if (deviceId >= 0) {
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            InputDevice* device = mDevices.valueAt(deviceIndex);
+        auto deviceIt = mDevices.find(deviceId);
+        if (deviceIt != mDevices.end()) {
+            InputDevice* device = deviceIt->second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
             }
         }
     } else {
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
+        for (auto& devicePair : mDevices) {
+            InputDevice* device = devicePair.second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
             }
@@ -568,10 +608,9 @@
 void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
                           ssize_t repeat, int32_t token) {
     AutoMutex _l(mLock);
-
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt != mDevices.end()) {
+        InputDevice* device = deviceIt->second;
         device->vibrate(pattern, patternSize, repeat, token);
     }
 }
@@ -579,9 +618,9 @@
 void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt != mDevices.end()) {
+        InputDevice* device = deviceIt->second;
         device->cancelVibrate(token);
     }
 }
@@ -589,9 +628,9 @@
 bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt != mDevices.end()) {
+        InputDevice* device = deviceIt->second;
         return device->isEnabled();
     }
     ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
@@ -601,13 +640,13 @@
 bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt == mDevices.end()) {
         ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
         return false;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
+    InputDevice* device = deviceIt->second;
     if (!device->isEnabled()) {
         ALOGW("Ignoring disabled device %s", device->getName().c_str());
         return false;
@@ -635,8 +674,9 @@
 
     dump += "Input Reader State:\n";
 
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        mDevices.valueAt(i)->dump(dump);
+    for (const auto& devicePair : mDevices) {
+        InputDevice* const device = devicePair.second;
+        device->dump(dump);
     }
 
     dump += INDENT "Configuration:\n";
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 7b4321e..0a4e808 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -23,9 +23,9 @@
 #include "InputReaderContext.h"
 
 #include <utils/Condition.h>
-#include <utils/KeyedVector.h>
 #include <utils/Mutex.h>
 
+#include <unordered_map>
 #include <vector>
 
 namespace android {
@@ -38,12 +38,12 @@
  * that it sends to the input listener.  Some functions of the input reader, such as early
  * event filtering in low power states, are controlled by a separate policy object.
  *
- * The InputReader owns a collection of InputMappers.  Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
+ * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where
+ * most of the work happens, but the InputReader can receive queries from other system
  * components running on arbitrary threads.  To keep things manageable, the InputReader
  * uses a single Mutex to guard its state.  The Mutex may be held while calling into the
  * EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
+ * InputListener. All calls to InputListener must happen from InputReader's thread.
  */
 class InputReader : public InputReaderInterface {
 public:
@@ -55,7 +55,8 @@
     virtual void dump(std::string& dump) override;
     virtual void monitor() override;
 
-    virtual void loopOnce() override;
+    virtual status_t start() override;
+    virtual status_t stop() override;
 
     virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
 
@@ -111,6 +112,9 @@
     friend class ContextImpl;
 
 private:
+    class InputReaderThread;
+    sp<InputReaderThread> mThread;
+
     Mutex mLock;
 
     Condition mReaderIsAliveCondition;
@@ -131,7 +135,11 @@
     static const int EVENT_BUFFER_SIZE = 256;
     RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
 
-    KeyedVector<int32_t, InputDevice*> mDevices;
+    std::unordered_map<int32_t /*deviceId*/, InputDevice*> mDevices;
+
+    // With each iteration of the loop, InputReader reads and processes one incoming message from
+    // the EventHub.
+    void loopOnce();
 
     // low-level input event decoding and device management
     void processEventsLocked(const RawEvent* rawEvents, size_t count);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c1c9122..8d4ab6a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1133,12 +1133,8 @@
 protected:
     sp<FakeInputReaderPolicy> mFakePolicy;
 
-    virtual void SetUp() {
-        mFakePolicy = new FakeInputReaderPolicy();
-    }
-    virtual void TearDown() {
-        mFakePolicy.clear();
-    }
+    virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+    virtual void TearDown() override { mFakePolicy.clear(); }
 };
 
 /**
@@ -1321,18 +1317,20 @@
     sp<TestInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
     std::shared_ptr<FakeEventHub> mFakeEventHub;
-    sp<InstrumentedInputReader> mReader;
+    std::unique_ptr<InstrumentedInputReader> mReader;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
 
-        mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
+        ASSERT_EQ(OK, mReader->start());
     }
 
-    virtual void TearDown() {
-        mReader.clear();
+    virtual void TearDown() override {
+        ASSERT_EQ(OK, mReader->stop());
 
         mFakeListener.clear();
         mFakePolicy.clear();
@@ -1346,24 +1344,18 @@
             mFakeEventHub->addConfigurationMap(deviceId, configuration);
         }
         mFakeEventHub->finishDeviceScan();
-        mReader->loopOnce();
-        mReader->loopOnce();
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
         ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
     }
 
     void disableDevice(int32_t deviceId, InputDevice* device) {
         mFakePolicy->addDisabledDevice(deviceId);
-        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
     void enableDevice(int32_t deviceId, InputDevice* device) {
         mFakePolicy->removeDisabledDevice(deviceId);
-        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
-    }
-
-    void configureDevice(uint32_t changes, InputDevice* device) {
-        device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
     FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
@@ -1417,28 +1409,22 @@
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
 
     ASSERT_EQ(device->isEnabled(), true);
     disableDevice(deviceId, device);
-    mReader->loopOnce();
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), false);
 
     disableDevice(deviceId, device);
-    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
     ASSERT_EQ(device->isEnabled(), false);
 
     enableDevice(deviceId, device);
-    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), true);
 }
@@ -1560,7 +1546,7 @@
     ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
 }
 
-TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
+TEST_F(InputReaderTest, WhenDeviceScanFinished_SendsConfigurationChanged) {
     addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
 
     NotifyConfigurationChangedArgs args;
@@ -1569,13 +1555,12 @@
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
 }
 
-TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
+TEST_F(InputReaderTest, ForwardsRawEventsToMappers) {
     FakeInputMapper* mapper = nullptr;
     ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
             INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
 
     mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
-    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
 
     RawEvent event;
@@ -1602,19 +1587,16 @@
     uint32_t prevSequenceNum = resetArgs.sequenceNum;
 
     disableDevice(deviceId, device);
-    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 
     enableDevice(deviceId, device);
-    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 
     disableDevice(deviceId, device);
-    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
@@ -1629,7 +1611,6 @@
     FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
     device->addMapper(mapper);
     mReader->setNextDevice(device);
-    addDevice(deviceId, "fake", deviceClass, nullptr);
 
     const uint8_t hdmi1 = 1;
 
@@ -1637,13 +1618,20 @@
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
 
     // Add default and second display.
+    mFakePolicy->clearViewports();
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
             DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
             DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
-    mReader->loopOnce();
+
+    // Add the device, and make sure all of the callbacks are triggered.
+    // The device is added after the input port associations are processed since
+    // we do not yet support dynamic device-to-display associations.
+    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
 
     // Device should only dispatch to the specified display.
     ASSERT_EQ(deviceId, device->getId());
@@ -1652,6 +1640,8 @@
 
     // Can't dispatch event from a disabled device.
     disableDevice(deviceId, device);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
@@ -1674,7 +1664,7 @@
 
     InputDevice* mDevice;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1688,7 +1678,7 @@
                 DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         delete mDevice;
 
         delete mFakeContext;
@@ -1912,7 +1902,7 @@
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1926,7 +1916,7 @@
         mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         delete mDevice;
         delete mFakeContext;
         mFakeListener.clear();
@@ -2589,7 +2579,7 @@
 
     sp<FakePointerController> mFakePointerController;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         InputMapperTest::SetUp();
 
         mFakePointerController = new FakePointerController();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index e5d23d0..4226e9a 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -165,6 +165,7 @@
         "Scheduler/PhaseOffsets.cpp",
         "Scheduler/Scheduler.cpp",
         "Scheduler/SchedulerUtils.cpp",
+        "Scheduler/VSyncDispatch.cpp",
         "Scheduler/VSyncModulator.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 289bb17..8e6a70c 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -63,6 +63,7 @@
     }
     Rect getCrop(const Layer::State& s) const;
 
+    uint32_t getTransformHint() const { return mTransformHint; }
     bool setTransform(uint32_t transform) override;
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index c7ed9b0..f331364 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -75,17 +75,18 @@
 status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                uint32_t flags, const sp<IBinder>& parentHandle,
                                LayerMetadata metadata, sp<IBinder>* handle,
-                               sp<IGraphicBufferProducer>* gbp) {
+                               sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) {
     // We rely on createLayer to check permissions.
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 parentHandle);
+                                 parentHandle, nullptr, outTransformHint);
 }
 
 status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                          PixelFormat format, uint32_t flags,
                                          const sp<IGraphicBufferProducer>& parent,
                                          LayerMetadata metadata, sp<IBinder>* handle,
-                                         sp<IGraphicBufferProducer>* gbp) {
+                                         sp<IGraphicBufferProducer>* gbp,
+                                         uint32_t* outTransformHint) {
     if (mFlinger->authenticateSurfaceTexture(parent) == false) {
         ALOGE("failed to authenticate surface texture");
         // The extra parent layer check below before returning is to help with debugging
@@ -103,7 +104,7 @@
     }
 
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 nullptr, layer);
+                                 nullptr, layer, outTransformHint);
 }
 
 status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 7d7cef8..e9063e5 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -54,13 +54,15 @@
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                    uint32_t flags, const sp<IBinder>& parent,
                                    LayerMetadata metadata, sp<IBinder>* handle,
-                                   sp<IGraphicBufferProducer>* gbp);
+                                   sp<IGraphicBufferProducer>* gbp,
+                                   uint32_t* outTransformHint = nullptr);
 
     virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
                                              LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp);
+                                             sp<IGraphicBufferProducer>* gbp,
+                                             uint32_t* outTransformHint = nullptr);
 
     status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle);
 
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index b48d471..9b7bab1 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -34,6 +34,7 @@
     bool isCreatedFromMainThread() const override { return true; }
 
 protected:
+    bool canDrawShadows() const override { return false; }
     sp<Layer> createClone() override;
 };
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e19b79b..d5a9ae1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -339,7 +339,8 @@
     return bufferScaleTransform.inverse().transform(mBounds);
 }
 
-void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
+void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
+                          float parentShadowRadius) {
     const State& s(getDrawingState());
 
     // Calculate effective layer transform
@@ -362,11 +363,23 @@
     mBounds = bounds;
     mScreenBounds = mEffectiveTransform.transform(mBounds);
 
+    // Use the layer's own shadow radius if set. Otherwise get the radius from
+    // parent.
+    if (s.shadowRadius > 0.f) {
+        mEffectiveShadowRadius = s.shadowRadius;
+    } else {
+        mEffectiveShadowRadius = parentShadowRadius;
+    }
+
+    // Shadow radius is passed down to only one layer so if the layer can draw shadows,
+    // don't pass it to its children.
+    const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
+
     // Add any buffer scaling to the layer's children.
     ui::Transform bufferScaleTransform = getBufferScaleTransform();
     for (const sp<Layer>& child : mDrawingChildren) {
         child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
-                             getTransformWithScale(bufferScaleTransform));
+                             getTransformWithScale(bufferScaleTransform), childShadowRadius);
     }
 }
 
@@ -466,11 +479,13 @@
     compositionState.hasProtectedContent = isProtected();
 
     const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+    const bool drawsShadows = mEffectiveShadowRadius != 0.f;
+
     compositionState.isOpaque =
             isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
-    if (isHdrY410() || usesRoundedCorners) {
+    if (isHdrY410() || usesRoundedCorners || drawsShadows) {
         compositionState.forceClientComposition = true;
     }
 }
@@ -1116,6 +1131,18 @@
     return p->getLayerStack();
 }
 
+bool Layer::setShadowRadius(float shadowRadius) {
+    if (mCurrentState.shadowRadius == shadowRadius) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.shadowRadius = shadowRadius;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
     ATRACE_CALL();
     mCurrentState.barrierLayer_legacy = barrierLayer;
@@ -1410,8 +1437,8 @@
     for (const sp<Layer>& child : mDrawingChildren) {
         child->mDrawingParent = newParent;
         child->computeBounds(newParent->mBounds,
-                             newParent->getTransformWithScale(
-                                     newParent->getBufferScaleTransform()));
+                             newParent->getTransformWithScale(newParent->getBufferScaleTransform()),
+                             newParent->mEffectiveShadowRadius);
     }
 }
 
@@ -1540,8 +1567,10 @@
     mCurrentParent = layer;
 }
 
-int32_t Layer::getZ() const {
-    return mDrawingState.z;
+int32_t Layer::getZ(LayerVector::StateSet stateSet) const {
+    const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
+    const State& state = useDrawing ? mDrawingState : mCurrentState;
+    return state.z;
 }
 
 bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const {
@@ -1601,7 +1630,7 @@
             continue;
         }
 
-        if (relative->getZ() >= 0) {
+        if (relative->getZ(stateSet) >= 0) {
             break;
         }
         relative->traverseInZOrder(stateSet, visitor);
@@ -1635,7 +1664,7 @@
             continue;
         }
 
-        if (relative->getZ() < 0) {
+        if (relative->getZ(stateSet) < 0) {
             break;
         }
         relative->traverseInReverseZOrder(stateSet, visitor);
@@ -1693,7 +1722,7 @@
     size_t i = 0;
     for (; i < list.size(); i++) {
         const auto& relative = list[i];
-        if (relative->getZ() >= 0) {
+        if (relative->getZ(stateSet) >= 0) {
             break;
         }
         relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index fdac98f..fb72391 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -216,6 +216,7 @@
         std::deque<sp<CallbackHandle>> callbackHandles;
         bool colorSpaceAgnostic;
         nsecs_t desiredPresentTime = -1;
+        float shadowRadius;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -329,6 +330,7 @@
     };
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
     virtual bool setColorSpaceAgnostic(const bool agnostic);
+    bool setShadowRadius(float shadowRadius);
 
     virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
 
@@ -362,7 +364,7 @@
     FloatRect getBounds() const;
 
     // Compute bounds for the layer and cache the results.
-    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
+    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
 
     // Returns the buffer scale transform if a scaling mode is set.
     ui::Transform getBufferScaleTransform() const;
@@ -672,7 +674,7 @@
     // Copy the current list of children to the drawing state. Called by
     // SurfaceFlinger to complete a transaction.
     void commitChildList();
-    int32_t getZ() const;
+    int32_t getZ(LayerVector::StateSet stateSet) const;
     virtual void pushPendingState();
 
     /**
@@ -942,6 +944,14 @@
     // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
     // and relatives, this layer will update as well.
     wp<Layer> mClonedFrom;
+
+    // The inherited shadow radius after taking into account the layer hierarchy. This is the
+    // final shadow radius for this layer. If a shadow is specified for a layer, then effective
+    // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
+    float mEffectiveShadowRadius;
+
+    // Returns true if the layer can draw shadows on its border.
+    virtual bool canDrawShadows() const { return true; }
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
new file mode 100644
index 0000000..699cd50
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 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 <utils/Timers.h>
+#include <functional>
+
+namespace android::scheduler {
+
+/*
+ * TimeKeeper is the interface for a single-shot timer primitive.
+ */
+class TimeKeeper {
+public:
+    virtual ~TimeKeeper();
+
+    /*
+     * Arms callback to fired in time nanoseconds.
+     * There is only one timer, and subsequent calls will reset the callback function and the time.
+     */
+    virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0;
+
+    /*
+     * Cancels an existing pending callback
+     */
+    virtual void alarmCancel() = 0;
+
+    /*
+     * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
+     */
+    virtual nsecs_t now() const = 0;
+
+protected:
+    TimeKeeper(TimeKeeper const&) = delete;
+    TimeKeeper& operator=(TimeKeeper const&) = delete;
+    TimeKeeper() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.cpp b/services/surfaceflinger/Scheduler/VSyncDispatch.cpp
new file mode 100644
index 0000000..c9b2e77
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+#include <vector>
+
+#include "TimeKeeper.h"
+#include "VSyncDispatch.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+VSyncTracker::~VSyncTracker() = default;
+TimeKeeper::~TimeKeeper() = default;
+
+impl::VSyncDispatchEntry::VSyncDispatchEntry(std::string const& name,
+                                             std::function<void(nsecs_t)> const& cb)
+      : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
+
+std::optional<nsecs_t> impl::VSyncDispatchEntry::lastExecutedVsyncTarget() const {
+    return mLastDispatchTime;
+}
+
+std::string_view impl::VSyncDispatchEntry::name() const {
+    return mName;
+}
+
+std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const {
+    if (!mArmedInfo) {
+        return {};
+    }
+    return {mArmedInfo->mActualWakeupTime};
+}
+
+nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+                                           VSyncTracker& tracker, nsecs_t now) {
+    mWorkDuration = workDuration;
+    mEarliestVsync = earliestVsync;
+    arm(tracker, now);
+    return mArmedInfo->mActualWakeupTime;
+}
+
+void impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) {
+    if (!mArmedInfo) {
+        return;
+    }
+    arm(tracker, now);
+}
+
+void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) {
+    auto const nextVsyncTime =
+            tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
+    mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
+}
+
+void impl::VSyncDispatchEntry::disarm() {
+    mArmedInfo.reset();
+}
+
+nsecs_t impl::VSyncDispatchEntry::executing() {
+    mLastDispatchTime = mArmedInfo->mActualVsyncTime;
+    disarm();
+    return *mLastDispatchTime;
+}
+
+void impl::VSyncDispatchEntry::callback(nsecs_t t) {
+    {
+        std::lock_guard<std::mutex> lk(mRunningMutex);
+        mRunning = true;
+    }
+
+    mCallback(t);
+
+    std::lock_guard<std::mutex> lk(mRunningMutex);
+    mRunning = false;
+    mCv.notify_all();
+}
+
+void impl::VSyncDispatchEntry::ensureNotRunning() {
+    std::unique_lock<std::mutex> lk(mRunningMutex);
+    mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
+}
+
+VSyncDispatch::VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
+                             nsecs_t timerSlack)
+      : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
+
+VSyncDispatch::~VSyncDispatch() {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    cancelTimer();
+}
+
+void VSyncDispatch::cancelTimer() {
+    mIntendedWakeupTime = kInvalidTime;
+    mTimeKeeper->alarmCancel();
+}
+
+void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) {
+    mIntendedWakeupTime = targetTime;
+    mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now);
+}
+
+void VSyncDispatch::rearmTimer(nsecs_t now) {
+    rearmTimerSkippingUpdateFor(now, mCallbacks.end());
+}
+
+void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now,
+                                                CallbackMap::iterator const& skipUpdateIt) {
+    std::optional<nsecs_t> min;
+    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+        auto& callback = it->second;
+        if (!callback->wakeupTime()) {
+            continue;
+        }
+
+        if (it != skipUpdateIt) {
+            callback->update(mTracker, now);
+        }
+        auto const wakeupTime = *callback->wakeupTime();
+        if (!min || (min && *min > wakeupTime)) {
+            min = wakeupTime;
+        }
+    }
+
+    if (min && (min < mIntendedWakeupTime)) {
+        setTimer(*min, now);
+    } else {
+        cancelTimer();
+    }
+}
+
+void VSyncDispatch::timerCallback() {
+    struct Invocation {
+        std::shared_ptr<impl::VSyncDispatchEntry> callback;
+        nsecs_t timestamp;
+    };
+    std::vector<Invocation> invocations;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+            auto& callback = it->second;
+            auto const wakeupTime = callback->wakeupTime();
+            if (!wakeupTime) {
+                continue;
+            }
+
+            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
+                callback->executing();
+                invocations.emplace_back(
+                        Invocation{callback, *callback->lastExecutedVsyncTarget()});
+            }
+        }
+
+        mIntendedWakeupTime = kInvalidTime;
+        rearmTimer(mTimeKeeper->now());
+    }
+
+    for (auto const& invocation : invocations) {
+        invocation.callback->callback(invocation.timestamp);
+    }
+}
+
+VSyncDispatch::CallbackToken VSyncDispatch::registerCallback(
+        std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    return CallbackToken{
+            mCallbacks
+                    .emplace(++mCallbackToken,
+                             std::make_shared<impl::VSyncDispatchEntry>(callbackName, callbackFn))
+                    .first->first};
+}
+
+void VSyncDispatch::unregisterCallback(CallbackToken token) {
+    std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        auto it = mCallbacks.find(token);
+        if (it != mCallbacks.end()) {
+            entry = it->second;
+            mCallbacks.erase(it);
+        }
+    }
+
+    if (entry) {
+        entry->ensureNotRunning();
+    }
+}
+
+ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration,
+                                       nsecs_t earliestVsync) {
+    auto result = ScheduleResult::Error;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+        auto it = mCallbacks.find(token);
+        if (it == mCallbacks.end()) {
+            return result;
+        }
+        auto& callback = it->second;
+        result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
+
+        auto const now = mTimeKeeper->now();
+        auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now);
+
+        if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) {
+            return ScheduleResult::CannotSchedule;
+        }
+
+        if (wakeupTime < mIntendedWakeupTime - mTimerSlack) {
+            rearmTimerSkippingUpdateFor(now, it);
+        }
+    }
+
+    return result;
+}
+
+CancelResult VSyncDispatch::cancel(CallbackToken token) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+    auto it = mCallbacks.find(token);
+    if (it == mCallbacks.end()) {
+        return CancelResult::Error;
+    }
+    auto& callback = it->second;
+
+    if (callback->wakeupTime()) {
+        callback->disarm();
+        mIntendedWakeupTime = kInvalidTime;
+        rearmTimer(mTimeKeeper->now());
+        return CancelResult::Cancelled;
+    }
+    return CancelResult::TooLate;
+}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
+                                                     std::function<void(nsecs_t)> const& callbackFn,
+                                                     std::string const& callbackName)
+      : mDispatch(dispatch),
+        mToken(dispatch.registerCallback(callbackFn, callbackName)),
+        mValidToken(true) {}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
+      : mDispatch(other.mDispatch),
+        mToken(std::move(other.mToken)),
+        mValidToken(std::move(other.mValidToken)) {
+    other.mValidToken = false;
+}
+
+VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
+    mDispatch = std::move(other.mDispatch);
+    mToken = std::move(other.mToken);
+    mValidToken = std::move(other.mValidToken);
+    other.mValidToken = false;
+    return *this;
+}
+
+VSyncCallbackRegistration::~VSyncCallbackRegistration() {
+    if (mValidToken) mDispatch.get().unregisterCallback(mToken);
+}
+
+ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
+    if (!mValidToken) return ScheduleResult::Error;
+    return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
+}
+
+CancelResult VSyncCallbackRegistration::cancel() {
+    if (!mValidToken) return CancelResult::Error;
+    return mDispatch.get().cancel(mToken);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
new file mode 100644
index 0000000..0050495
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2019 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 <android-base/thread_annotations.h>
+#include <utils/Timers.h>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include "StrongTyping.h"
+
+namespace android::scheduler {
+class TimeKeeper;
+class VSyncTracker;
+
+enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
+enum class CancelResult { Cancelled, TooLate, Error };
+
+namespace impl {
+
+// VSyncDispatchEntry is a helper class representing internal state for each entry in VSyncDispatch
+// hoisted to public for unit testing.
+class VSyncDispatchEntry {
+public:
+    // This is the state of the entry. There are 3 states, armed, running, disarmed.
+    // Valid transition: disarmed -> armed ( when scheduled )
+    // Valid transition: armed -> running -> disarmed ( when timer is called)
+    // Valid transition: armed -> disarmed ( when cancelled )
+    VSyncDispatchEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
+    std::string_view name() const;
+
+    // Start: functions that are not threadsafe.
+    // Return the last vsync time this callback was invoked.
+    std::optional<nsecs_t> lastExecutedVsyncTarget() const;
+
+    // This moves the state from disarmed->armed and will calculate the wakeupTime.
+    nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+                     nsecs_t now);
+    // This will update armed entries with the latest vsync information. Entry remains armed.
+    void update(VSyncTracker& tracker, nsecs_t now);
+
+    // This will return empty if not armed, or the next calculated wakeup time if armed.
+    // It will not update the wakeupTime.
+    std::optional<nsecs_t> wakeupTime() const;
+
+    // This moves state from armed->disarmed.
+    void disarm();
+
+    // This moves the state from armed->running.
+    // Store the timestamp that this was intended for as the last called timestamp.
+    nsecs_t executing();
+    // End: functions that are not threadsafe.
+
+    // Invoke the callback with the timestamp, moving the state from running->disarmed.
+    void callback(nsecs_t timestamp);
+    // Block calling thread while the callback is executing.
+    void ensureNotRunning();
+
+private:
+    void arm(VSyncTracker& tracker, nsecs_t now);
+    std::string const mName;
+    std::function<void(nsecs_t)> const mCallback;
+
+    nsecs_t mWorkDuration;
+    nsecs_t mEarliestVsync;
+
+    struct ArmingInfo {
+        nsecs_t mActualWakeupTime;
+        nsecs_t mActualVsyncTime;
+    };
+    std::optional<ArmingInfo> mArmedInfo;
+    std::optional<nsecs_t> mLastDispatchTime;
+
+    std::mutex mRunningMutex;
+    std::condition_variable mCv;
+    bool mRunning GUARDED_BY(mRunningMutex) = false;
+};
+
+} // namespace impl
+
+/*
+ * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events.
+ */
+class VSyncDispatch {
+public:
+    using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
+
+    /* creates a VsyncDispatch.
+     * \param [in]  a timekeeper object for dispatching events.
+     * \param [in]  a tracker object that is monitoring expected vsync events.
+     * \param [in]  a tunable in nanoseconds that indicates when events that fall close together
+     *              should be dispatched in one timer wakeup.
+     */
+    explicit VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
+                           nsecs_t timerSlack);
+    ~VSyncDispatch();
+
+    /*
+     * Registers a callback that will be called at designated points on the vsync timeline.
+     * The callback can be scheduled, rescheduled targeting vsync times, or cancelled.
+     * The token returned must be cleaned up via unregisterCallback.
+     *
+     * \param [in] callbackFn   A function to schedule for callback. The resources needed to invoke
+     *                          callbackFn must have lifetimes encompassing the lifetime of the
+     *                          CallbackToken returned.
+     * \param [in] callbackName A human-readable, unique name to identify the callback.
+     * \return                  A token that can be used to schedule, reschedule, or cancel the
+     *                          invocation of callbackFn.
+     *
+     */
+    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+                                   std::string callbackName);
+
+    /*
+     * Unregisters a callback.
+     *
+     * \param [in] token        The callback to unregister.
+     *
+     */
+    void unregisterCallback(CallbackToken token);
+
+    /*
+     * Schedules the registered callback to be dispatched.
+     *
+     * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event.
+     *
+     * The caller designates the earliest vsync event that should be targeted by the earliestVsync
+     * parameter.
+     * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync
+     * is the first vsync event time where ( predictedVsync >= earliestVsync ).
+     *
+     * If (workDuration - earliestVsync) is in the past, or if a callback has already been
+     * dispatched for the predictedVsync, an error will be returned.
+     *
+     * It is valid to reschedule a callback to a different time.
+     *
+     * \param [in] token           The callback to schedule.
+     * \param [in] workDuration    The time before the actual vsync time to invoke the callback
+     *                             associated with token.
+     * \param [in] earliestVsync   The targeted display time. This will be snapped to the closest
+     *                             predicted vsync time after earliestVsync.
+     * \return                     A ScheduleResult::Scheduled if callback was scheduled.
+     *                             A ScheduleResult::ReScheduled if callback was rescheduled.
+     *                             A ScheduleResult::CannotSchedule
+     *                             if (workDuration - earliestVsync) is in the past, or
+     *                             if a callback was dispatched for the predictedVsync already.
+     *                             A ScheduleResult::Error if there was another error.
+     */
+    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync);
+
+    /* Cancels a scheduled callback, if possible.
+     *
+     * \param [in] token    The callback to cancel.
+     * \return              A CancelResult::TooLate if the callback was already dispatched.
+     *                      A CancelResult::Cancelled if the callback was successfully cancelled.
+     *                      A CancelResult::Error if there was an pre-condition violation.
+     */
+    CancelResult cancel(CallbackToken token);
+
+private:
+    VSyncDispatch(VSyncDispatch const&) = delete;
+    VSyncDispatch& operator=(VSyncDispatch const&) = delete;
+
+    using CallbackMap = std::unordered_map<size_t, std::shared_ptr<impl::VSyncDispatchEntry>>;
+
+    void timerCallback();
+    void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
+    void rearmTimer(nsecs_t now) REQUIRES(mMutex);
+    void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
+            REQUIRES(mMutex);
+    void cancelTimer() REQUIRES(mMutex);
+
+    static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
+    std::unique_ptr<TimeKeeper> const mTimeKeeper;
+    VSyncTracker& mTracker;
+    nsecs_t const mTimerSlack;
+
+    std::mutex mutable mMutex;
+    size_t mCallbackToken GUARDED_BY(mMutex) = 0;
+
+    CallbackMap mCallbacks GUARDED_BY(mMutex);
+    nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
+};
+
+/*
+ * Helper class to operate on registered callbacks. It is up to user of the class to ensure
+ * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation.
+ */
+class VSyncCallbackRegistration {
+public:
+    VSyncCallbackRegistration(VSyncDispatch&, std::function<void(nsecs_t)> const& callbackFn,
+                              std::string const& callbackName);
+    VSyncCallbackRegistration(VSyncCallbackRegistration&&);
+    VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
+    ~VSyncCallbackRegistration();
+
+    // See documentation for VSyncDispatch::schedule.
+    ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync);
+
+    // See documentation for VSyncDispatch::cancel.
+    CancelResult cancel();
+
+private:
+    VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete;
+    VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete;
+
+    std::reference_wrapper<VSyncDispatch> mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    bool mValidToken;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
new file mode 100644
index 0000000..97b9620
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 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 <utils/Timers.h>
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+/*
+ * VSyncTracker is an interface for providing estimates on future Vsync signal times based on
+ * historical vsync timing data.
+ */
+class VSyncTracker {
+public:
+    virtual ~VSyncTracker();
+
+    /*
+     * Adds a known timestamp from a vsync timing source (HWVsync signal, present fence)
+     * to the model.
+     *
+     * \param [in] timestamp    The timestamp when the vsync signal was.
+     */
+    virtual void addVsyncTimestamp(nsecs_t timestamp) = 0;
+
+    /*
+     * Access the next anticipated vsync time such that the anticipated time >= timePoint.
+     * This will always give the best accurate at the time of calling; multiple
+     * calls with the same timePoint might give differing values if the internal model
+     * is updated.
+     *
+     * \param [in] timePoint    The point in time after which to estimate a vsync event.
+     * \return                  A prediction of the timestamp of a vsync event.
+     */
+    virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+
+protected:
+    VSyncTracker(VSyncTracker const&) = delete;
+    VSyncTracker& operator=(VSyncTracker const&) = delete;
+    VSyncTracker() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1acb2da..76fd51f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2011,7 +2011,8 @@
                 continue;
             }
 
-            layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform());
+            layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform(),
+                                 0.f /* shadowRadius */);
         }
     }
 }
@@ -3335,6 +3336,9 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eShadowRadiusChanged) {
+        if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
+    }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
     // the same transaction, then we have to make sure we reparent the children first so we do not
@@ -3432,8 +3436,8 @@
                                      uint32_t h, PixelFormat format, uint32_t flags,
                                      LayerMetadata metadata, sp<IBinder>* handle,
                                      sp<IGraphicBufferProducer>* gbp,
-                                     const sp<IBinder>& parentHandle,
-                                     const sp<Layer>& parentLayer) {
+                                     const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
+                                     uint32_t* outTransformHint) {
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -3470,7 +3474,7 @@
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
-                                            std::move(metadata), handle, &layer);
+                                            std::move(metadata), handle, outTransformHint, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceColor:
             // check if buffer size is set for color layer.
@@ -3585,11 +3589,14 @@
 status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name,
                                                 uint32_t w, uint32_t h, uint32_t flags,
                                                 LayerMetadata metadata, sp<IBinder>* handle,
-                                                sp<Layer>* outLayer) {
+                                                uint32_t* outTransformHint, sp<Layer>* outLayer) {
     LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
     args.displayDevice = getDefaultDisplayDevice();
     args.textureName = getNewTexture();
     sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
+    if (outTransformHint) {
+        *outTransformHint = layer->getTransformHint();
+    }
     *handle = layer->getHandle();
     *outLayer = layer;
 
@@ -4438,18 +4445,7 @@
         case SET_DISPLAY_BRIGHTNESS: {
             return OK;
         }
-        case CAPTURE_LAYERS: {
-            IPCThreadState* ipc = IPCThreadState::self();
-            const int pid = ipc->getCallingPid();
-            const int uid = ipc->getCallingUid();
-            // allow media to capture layer for video thumbnails
-            if ((uid != AID_GRAPHICS && uid != AID_MEDIA) &&
-                !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
-                ALOGE("Permission Denial: can't capture layer pid=%d, uid=%d", pid, uid);
-                return PERMISSION_DENIED;
-            }
-            return OK;
-        }
+        case CAPTURE_LAYERS:
         case CAPTURE_SCREEN:
         case ADD_REGION_SAMPLING_LISTENER:
         case REMOVE_REGION_SAMPLING_LISTENER: {
@@ -5010,7 +5006,8 @@
                                const Rect& drawingBounds)
                   : oldParent(oldParent), newParent(newParent) {
                 // Compute and cache the bounds for the new parent layer.
-                newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform());
+                newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
+                                         0.f /* shadowRadius */);
                 oldParent->setChildrenDrawingParent(newParent);
             }
             ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index abb8b82..e7ad295 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -602,7 +602,8 @@
     status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
                          PixelFormat format, uint32_t flags, LayerMetadata metadata,
                          sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
-                         const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr);
+                         const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr,
+                         uint32_t* outTransformHint = nullptr);
 
     status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
                                     uint32_t h, uint32_t flags, LayerMetadata metadata,
@@ -611,7 +612,8 @@
 
     status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
                                     uint32_t h, uint32_t flags, LayerMetadata metadata,
-                                    sp<IBinder>* outHandle, sp<Layer>* outLayer);
+                                    sp<IBinder>* outHandle, uint32_t* outTransformHint,
+                                    sp<Layer>* outLayer);
 
     status_t createColorLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h,
                               uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index a48f553..999e82d 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -15,9 +15,9 @@
  */
 
 #include <gui/BufferItemConsumer.h>
+#include <ui/Transform.h>
 #include <thread>
 #include "TransactionTestHarnesses.h"
-
 namespace android {
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -188,6 +188,15 @@
     getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
 }
 
+TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) {
+    uint32_t transformHint = ui::Transform::orientation_flags::ROT_INVALID;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32,
+                                                ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                /*parent*/ nullptr, &transformHint));
+    ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, transformHint);
+}
+
 void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 7edddb6..f7a6d96 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -53,9 +53,10 @@
 
     virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
                                            const char* name, uint32_t width, uint32_t height,
-                                           uint32_t flags = 0, SurfaceControl* parent = nullptr) {
-        auto layer =
-                createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent);
+                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
+                                           uint32_t* outTransformHint = nullptr) {
+        auto layer = createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags,
+                                   parent, outTransformHint);
 
         Transaction t;
         t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
@@ -72,15 +73,18 @@
     virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
                                              const char* name, uint32_t width, uint32_t height,
                                              PixelFormat format, uint32_t flags,
-                                             SurfaceControl* parent = nullptr) {
-        auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
+                                             SurfaceControl* parent = nullptr,
+                                             uint32_t* outTransformHint = nullptr) {
+        auto layer = client->createSurface(String8(name), width, height, format, flags, parent,
+                                           LayerMetadata(), outTransformHint);
         EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
         return layer;
     }
 
     virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
-                                           uint32_t flags = 0, SurfaceControl* parent = nullptr) {
-        return createLayer(mClient, name, width, height, flags, parent);
+                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
+                                           uint32_t* outTransformHint = nullptr) {
+        return createLayer(mClient, name, width, height, flags, parent, outTransformHint);
     }
 
     sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 8fdcde4..5612bb2 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -114,12 +114,14 @@
     LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
 
     sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
-                                   uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+                                   uint32_t flags = 0, SurfaceControl* parent = nullptr,
+                                   uint32_t* outTransformHint = nullptr) {
         // if the flags already have a layer type specified, return an error
         if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
             return nullptr;
         }
-        return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent);
+        return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent,
+                                                 outTransformHint);
     }
 
     void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f85da20..246a62f 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -54,6 +54,7 @@
         "FrameTracerTest.cpp",
         "TransactionApplicationTest.cpp",
         "StrongTypingTest.cpp",
+        "VSyncDispatchTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 60da70f..143a7a0 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -803,7 +803,7 @@
         layerDrawingState.active.h = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
-        layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform());
+        layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
 
         return layer;
     }
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp
new file mode 100644
index 0000000..d1ed7e3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp
@@ -0,0 +1,680 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+    MockVSyncTracker(nsecs_t period) : mPeriod{period} {
+        ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
+                .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
+    }
+
+    MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+
+    nsecs_t nextVSyncTime(nsecs_t timePoint) const {
+        if (timePoint % mPeriod == 0) {
+            return timePoint;
+        }
+        return (timePoint - (timePoint % mPeriod) + mPeriod);
+    }
+
+protected:
+    nsecs_t const mPeriod;
+};
+
+class ControllableClock : public TimeKeeper {
+public:
+    ControllableClock() {
+        ON_CALL(*this, alarmIn(_, _))
+                .WillByDefault(Invoke(this, &ControllableClock::alarmInDefaultBehavior));
+        ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
+    }
+
+    MOCK_CONST_METHOD0(now, nsecs_t());
+    MOCK_METHOD2(alarmIn, void(std::function<void()> const&, nsecs_t time));
+    MOCK_METHOD0(alarmCancel, void());
+
+    void alarmInDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
+        mCallback = callback;
+        mNextCallbackTime = time + mCurrentTime;
+    }
+
+    nsecs_t fakeTime() const { return mCurrentTime; }
+
+    void advanceToNextCallback() {
+        mCurrentTime = mNextCallbackTime;
+        if (mCallback) {
+            mCallback();
+        }
+    }
+
+    void advanceBy(nsecs_t advancement) {
+        mCurrentTime += advancement;
+        if (mCurrentTime >= mNextCallbackTime && mCallback) {
+            mCallback();
+        }
+    };
+
+private:
+    std::function<void()> mCallback;
+    nsecs_t mNextCallbackTime = 0;
+    nsecs_t mCurrentTime = 0;
+};
+
+class CountingCallback {
+public:
+    CountingCallback(VSyncDispatch& dispatch)
+          : mDispatch(dispatch),
+            mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
+                                                       std::placeholders::_1),
+                                             "test")) {}
+    ~CountingCallback() { mDispatch.unregisterCallback(mToken); }
+
+    operator VSyncDispatch::CallbackToken() const { return mToken; }
+
+    void counter(nsecs_t time) { mCalls.push_back(time); }
+
+    VSyncDispatch& mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    std::vector<nsecs_t> mCalls;
+};
+
+class PausingCallback {
+public:
+    PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount)
+          : mDispatch(dispatch),
+            mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this,
+                                                       std::placeholders::_1),
+                                             "test")),
+            mRegistered(true),
+            mPauseAmount(pauseAmount) {}
+    ~PausingCallback() { unregister(); }
+
+    operator VSyncDispatch::CallbackToken() const { return mToken; }
+
+    void pause(nsecs_t) {
+        std::unique_lock<std::mutex> lk(mMutex);
+        mPause = true;
+        mCv.notify_all();
+
+        mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; });
+
+        mResourcePresent = (mResource.lock() != nullptr);
+    }
+
+    bool waitForPause() {
+        std::unique_lock<std::mutex> lk(mMutex);
+        auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; });
+        return waiting;
+    }
+
+    void stashResource(std::weak_ptr<void> const& resource) { mResource = resource; }
+
+    bool resourcePresent() { return mResourcePresent; }
+
+    void unpause() {
+        std::unique_lock<std::mutex> lk(mMutex);
+        mPause = false;
+        mCv.notify_all();
+    }
+
+    void unregister() {
+        if (mRegistered) {
+            mDispatch.unregisterCallback(mToken);
+            mRegistered = false;
+        }
+    }
+
+    VSyncDispatch& mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    bool mRegistered = true;
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    bool mPause = false;
+    std::weak_ptr<void> mResource;
+    bool mResourcePresent = false;
+    std::chrono::milliseconds const mPauseAmount;
+};
+
+class VSyncDispatchTest : public testing::Test {
+protected:
+    std::unique_ptr<TimeKeeper> createTimeKeeper() {
+        class TimeKeeperWrapper : public TimeKeeper {
+        public:
+            TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
+            void alarmIn(std::function<void()> const& callback, nsecs_t time) final {
+                mControllableClock.alarmIn(callback, time);
+            }
+            void alarmCancel() final { mControllableClock.alarmCancel(); }
+            nsecs_t now() const final { return mControllableClock.now(); }
+
+        private:
+            TimeKeeper& mControllableClock;
+        };
+        return std::make_unique<TimeKeeperWrapper>(mMockClock);
+    }
+
+    ~VSyncDispatchTest() {
+        // destructor of  dispatch will cancelAlarm(). Ignore final cancel in common test.
+        Mock::VerifyAndClearExpectations(&mMockClock);
+    }
+
+    void advanceToNextCallback() { mMockClock.advanceToNextCallback(); }
+
+    NiceMock<ControllableClock> mMockClock;
+    static nsecs_t constexpr mDispatchGroupThreshold = 5;
+    nsecs_t const mPeriod = 1000;
+    NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+    VSyncDispatch mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
+};
+
+TEST_F(VSyncDispatchTest, unregistersSetAlarmOnDestruction) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+    {
+        VSyncDispatch mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
+        CountingCallback cb(mDispatch);
+        EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
+    }
+}
+
+TEST_F(VSyncDispatchTest, basicAlarmSettingFuture) {
+    auto intended = mPeriod - 230;
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+}
+
+TEST_F(VSyncDispatchTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
+    EXPECT_CALL(mMockClock, alarmIn(_, 1050));
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 100, mPeriod);
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(1150));
+}
+
+TEST_F(VSyncDispatchTest, basicAlarmSettingAdjustmentPast) {
+    auto const now = 234;
+    mMockClock.advanceBy(234);
+    auto const workDuration = 10 * mPeriod;
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
+            .WillOnce(Return(mPeriod * 11));
+    EXPECT_CALL(mMockClock, alarmIn(_, mPeriod - now));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTest, basicAlarmCancel) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
+}
+
+TEST_F(VSyncDispatchTest, basicAlarmCancelTooLate) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(950);
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+}
+
+TEST_F(VSyncDispatchTest, basicAlarmCancelTooLateWhenRunning) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+
+    std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
+    EXPECT_TRUE(cb.waitForPause());
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+    cb.unpause();
+    pausingThread.join();
+}
+
+TEST_F(VSyncDispatchTest, unregisterSynchronizes) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    auto resource = std::make_shared<int>(110);
+
+    PausingCallback cb(mDispatch, 50ms);
+    cb.stashResource(resource);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+
+    std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
+    EXPECT_TRUE(cb.waitForPause());
+
+    cb.unregister();
+    resource.reset();
+
+    cb.unpause();
+    pausingThread.join();
+
+    EXPECT_TRUE(cb.resourcePresent());
+}
+
+TEST_F(VSyncDispatchTest, basicTwoAlarmSetting) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+            .Times(4)
+            .WillOnce(Return(1055))
+            .WillOnce(Return(1063))
+            .WillOnce(Return(1063))
+            .WillOnce(Return(1075));
+
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 955)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 813)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 162)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, mPeriod);
+    mDispatch.schedule(cb1, 250, mPeriod);
+
+    advanceToNextCallback();
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb0.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb0.mCalls[0], Eq(1075));
+    ASSERT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb1.mCalls[0], Eq(1063));
+}
+
+TEST_F(VSyncDispatchTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(4)
+            .WillOnce(Return(10000))
+            .WillOnce(Return(1000))
+            .WillOnce(Return(10000))
+            .WillOnce(Return(10000));
+
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 750)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, mPeriod * 10);
+    mDispatch.schedule(cb1, 250, mPeriod);
+    mDispatch.cancel(cb1);
+}
+
+TEST_F(VSyncDispatchTest, noUnnecessaryRearmsWhenRescheduling) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, 300, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTest, necessaryRearmsWhenModifying) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, 500, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTest, modifyIntoGroup) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 990)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 10)).InSequence(seq);
+
+    auto offset = 400;
+    auto closeOffset = offset + mDispatchGroupThreshold - 1;
+    auto notCloseOffset = offset + 2 * mDispatchGroupThreshold;
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, closeOffset, 1000);
+
+    advanceToNextCallback();
+    ASSERT_THAT(cb0.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb0.mCalls[0], Eq(mPeriod));
+    ASSERT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
+
+    mDispatch.schedule(cb0, 400, 2000);
+    mDispatch.schedule(cb1, notCloseOffset, 2000);
+    advanceToNextCallback();
+    ASSERT_THAT(cb1.mCalls.size(), Eq(2));
+    EXPECT_THAT(cb1.mCalls[1], Eq(2000));
+
+    advanceToNextCallback();
+    ASSERT_THAT(cb0.mCalls.size(), Eq(2));
+    EXPECT_THAT(cb0.mCalls[1], Eq(2000));
+}
+
+TEST_F(VSyncDispatchTest, rearmsWhenEndingAndDoesntCancel) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmIn(_, 800));
+    EXPECT_CALL(mMockClock, alarmIn(_, 100));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
+}
+
+TEST_F(VSyncDispatchTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(3)
+            .WillOnce(Return(950))
+            .WillOnce(Return(1975))
+            .WillOnce(Return(2950));
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 100, 920);
+
+    mMockClock.advanceBy(850);
+    EXPECT_THAT(cb.mCalls.size(), Eq(1));
+
+    mDispatch.schedule(cb, 100, 1900);
+    mMockClock.advanceBy(900);
+    EXPECT_THAT(cb.mCalls.size(), Eq(1));
+    mMockClock.advanceBy(125);
+    EXPECT_THAT(cb.mCalls.size(), Eq(2));
+
+    mDispatch.schedule(cb, 100, 2900);
+    mMockClock.advanceBy(975);
+    EXPECT_THAT(cb.mCalls.size(), Eq(3));
+}
+
+TEST_F(VSyncDispatchTest, callbackReentrancy) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+
+    VSyncDispatch::CallbackToken tmp;
+    tmp = mDispatch.registerCallback([&](auto) { mDispatch.schedule(tmp, 100, 2000); }, "o.o");
+
+    mDispatch.schedule(tmp, 100, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTest, callbackReentrantWithPastWakeup) {
+    VSyncDispatch::CallbackToken tmp;
+    tmp = mDispatch.registerCallback(
+            [&](auto) {
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, 1000), ScheduleResult::CannotSchedule);
+            },
+            "oo");
+
+    mDispatch.schedule(tmp, 999, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTest, modificationsAroundVsyncTime) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 150)).InSequence(seq);
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 0, 1000);
+
+    mMockClock.advanceBy(750);
+    mDispatch.schedule(cb, 50, 1000);
+
+    advanceToNextCallback();
+    mDispatch.schedule(cb, 50, 2000);
+
+    mMockClock.advanceBy(800);
+    mDispatch.schedule(cb, 100, 2000);
+}
+
+TEST_F(VSyncDispatchTest, lateModifications) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 350)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 950)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.schedule(cb1, 100, 1000);
+
+    advanceToNextCallback();
+    mDispatch.schedule(cb0, 200, 2000);
+    mDispatch.schedule(cb1, 150, 1000);
+
+    advanceToNextCallback();
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTest, doesntCancelPriorValidTimerForFutureMod) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.schedule(cb1, 500, 20000);
+}
+
+TEST_F(VSyncDispatchTest, setsTimerAfterCancellation) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.cancel(cb0);
+    mDispatch.schedule(cb0, 100, 1000);
+}
+
+TEST_F(VSyncDispatchTest, makingUpIdsError) {
+    VSyncDispatch::CallbackToken token(100);
+    EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error));
+    EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
+}
+
+TEST_F(VSyncDispatchTest, distinguishesScheduleAndReschedule) {
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
+}
+
+TEST_F(VSyncDispatchTest, helperMove) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+    EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
+
+    VSyncCallbackRegistration cb(
+            mDispatch, [](auto) {}, "");
+    VSyncCallbackRegistration cb1(std::move(cb));
+    cb.schedule(100, 1000);
+    cb.cancel();
+
+    cb1.schedule(500, 1000);
+    cb1.cancel();
+}
+
+TEST_F(VSyncDispatchTest, helperMoveAssign) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+    EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
+
+    VSyncCallbackRegistration cb(
+            mDispatch, [](auto) {}, "");
+    VSyncCallbackRegistration cb1(
+            mDispatch, [](auto) {}, "");
+    cb1 = std::move(cb);
+    cb.schedule(100, 1000);
+    cb.cancel();
+
+    cb1.schedule(500, 1000);
+    cb1.cancel();
+}
+
+class VSyncDispatchEntryTest : public testing::Test {
+protected:
+    nsecs_t const mPeriod = 1000;
+    NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+};
+
+TEST_F(VSyncDispatchEntryTest, stateAfterInitialization) {
+    std::string name("basicname");
+    impl::VSyncDispatchEntry entry(name, [](auto) {});
+    EXPECT_THAT(entry.name(), Eq(name));
+    EXPECT_FALSE(entry.lastExecutedVsyncTarget());
+    EXPECT_FALSE(entry.wakeupTime());
+}
+
+TEST_F(VSyncDispatchEntryTest, stateScheduling) {
+    impl::VSyncDispatchEntry entry("test", [](auto) {});
+
+    EXPECT_FALSE(entry.wakeupTime());
+    auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+    auto const queried = entry.wakeupTime();
+    ASSERT_TRUE(queried);
+    EXPECT_THAT(*queried, Eq(wakeup));
+    EXPECT_THAT(*queried, Eq(900));
+
+    entry.disarm();
+    EXPECT_FALSE(entry.wakeupTime());
+}
+
+TEST_F(VSyncDispatchEntryTest, stateSchedulingReallyLongWakeupLatency) {
+    auto const duration = 500;
+    auto const now = 8750;
+
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
+            .Times(1)
+            .WillOnce(Return(10000));
+    impl::VSyncDispatchEntry entry("test", [](auto) {});
+
+    EXPECT_FALSE(entry.wakeupTime());
+    auto const wakeup = entry.schedule(500, 994, mStubTracker, now);
+    auto const queried = entry.wakeupTime();
+    ASSERT_TRUE(queried);
+    EXPECT_THAT(*queried, Eq(wakeup));
+    EXPECT_THAT(*queried, Eq(9500));
+}
+
+TEST_F(VSyncDispatchEntryTest, runCallback) {
+    auto callCount = 0;
+    auto calledTime = 0;
+    impl::VSyncDispatchEntry entry("test", [&](auto time) {
+        callCount++;
+        calledTime = time;
+    });
+
+    auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+    EXPECT_THAT(wakeup, Eq(900));
+
+    entry.callback(entry.executing());
+
+    EXPECT_THAT(callCount, Eq(1));
+    EXPECT_THAT(calledTime, Eq(mPeriod));
+    EXPECT_FALSE(entry.wakeupTime());
+    auto lastCalledTarget = entry.lastExecutedVsyncTarget();
+    ASSERT_TRUE(lastCalledTarget);
+    EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
+}
+
+TEST_F(VSyncDispatchEntryTest, updateCallback) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(2)
+            .WillOnce(Return(1000))
+            .WillOnce(Return(1020));
+
+    impl::VSyncDispatchEntry entry("test", [](auto) {});
+
+    EXPECT_FALSE(entry.wakeupTime());
+    entry.update(mStubTracker, 0);
+    EXPECT_FALSE(entry.wakeupTime());
+
+    auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+    EXPECT_THAT(wakeup, Eq(900));
+
+    entry.update(mStubTracker, 0);
+    auto const queried = entry.wakeupTime();
+    ASSERT_TRUE(queried);
+    EXPECT_THAT(*queried, Eq(920));
+}
+
+TEST_F(VSyncDispatchEntryTest, skipsUpdateIfJustScheduled) {
+    impl::VSyncDispatchEntry entry("test", [](auto) {});
+    auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+    entry.update(mStubTracker, 0);
+
+    auto const queried = entry.wakeupTime();
+    ASSERT_TRUE(queried);
+    EXPECT_THAT(*queried, Eq(wakeup));
+}
+
+} // namespace android::scheduler
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 5686891..018f200 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -41,12 +41,13 @@
         "-DVK_NO_PROTOTYPES",
         "-fvisibility=hidden",
         "-fstrict-aliasing",
-        "-Weverything",
+        "-Wextra",
         "-Werror",
         "-Wno-padded",
+        "-Wno-sign-compare",
         "-Wno-switch-enum",
-        "-Wno-undef",
-        "-Wno-format-pedantic",
+        "-Wno-unused-variable",
+        "-Wno-unused-function",
 
         // Have clang emit complete debug_info.
         "-fstandalone-debug",
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 24039b1..4725344 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -144,7 +144,7 @@
     }
 
     void GetLayersFromSettings() {
-        // These will only be available if conditions are met in GraphicsEnvironemnt
+        // These will only be available if conditions are met in GraphicsEnvironment
         // gpu_debug_layers = layer1:layer2:layerN
         const std::string layers = android::GraphicsEnv::getInstance().getDebugLayers();
         if (!layers.empty()) {
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index d92f35a..0fb3a29 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -754,7 +754,7 @@
 }  // anonymous namespace
 
 bool Debuggable() {
-    return (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) >= 0);
+    return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
 }
 
 bool OpenHAL() {
diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp
index dedf419..ba02504 100644
--- a/vulkan/nulldrv/Android.bp
+++ b/vulkan/nulldrv/Android.bp
@@ -23,18 +23,11 @@
         "-fvisibility=hidden",
         "-fstrict-aliasing",
         "-DLOG_TAG=\"vknulldrv\"",
-        "-Weverything",
+        "-Wextra",
         "-Werror",
-        "-Wno-padded",
-        "-Wno-undef",
-        "-Wno-zero-length-array",
 
         "-DLOG_NDEBUG=0",
     ],
-    cppflags: [
-        "-Wno-c++98-compat-pedantic",
-        "-Wno-c99-extensions",
-    ],
 
     srcs: [
         "null_driver.cpp",