Merge "Delete some unused code."
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 35cff5f..562898d 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 cc_defaults {
-    name: "dumpstate_defaults",
+    name: "dumpstate_cflag_defaults",
     cflags: [
         "-Wall",
         "-Werror",
@@ -26,7 +26,7 @@
 
 cc_library_shared {
     name: "libdumpstateutil",
-    defaults: ["dumpstate_defaults"],
+    defaults: ["dumpstate_cflag_defaults"],
     vendor_available: true,
     vndk: {
         enabled: true,
@@ -47,7 +47,7 @@
 
 cc_library_shared {
     name: "libdumpstateaidl",
-    defaults: ["dumpstate_defaults"],
+    defaults: ["dumpstate_cflag_defaults"],
     shared_libs: [
         "libbinder",
         "libutils",
@@ -63,9 +63,9 @@
     ],
 }
 
-cc_binary {
-    name: "dumpstate",
-    defaults: ["dumpstate_defaults"],
+cc_defaults {
+    name: "dumpstate_defaults",
+    defaults: ["dumpstate_cflag_defaults"],
     shared_libs: [
         "android.hardware.dumpstate@1.0",
         "libziparchive",
@@ -82,12 +82,22 @@
         "libutils",
     ],
     srcs: [
-        "DumpstateInternal.cpp",
         "DumpstateSectionReporter.cpp",
         "DumpstateService.cpp",
-        "main.cpp",
         "utils.cpp",
+    ],
+    static_libs: [
+        "libdumpsys",
+        "libserviceutils"
+    ],
+}
+
+cc_binary {
+    name: "dumpstate",
+    defaults: ["dumpstate_defaults"],
+    srcs: [
         "dumpstate.cpp",
+        "main.cpp",
     ],
     init_rc: ["dumpstate.rc"],
 }
@@ -95,24 +105,18 @@
 cc_test {
     name: "dumpstate_test",
     defaults: ["dumpstate_defaults"],
-    shared_libs: [
-        "libziparchive",
-        "libbase",
-        "libbinder",
-        "libcutils",
-        "libdebuggerd_client",
-        "libdumpstateaidl",
-        "libdumpstateutil",
-        "libhidlbase",
-        "libhidltransport",
-        "liblog",
-        "libutils",
-    ],
     srcs: [
-        "DumpstateInternal.cpp",
-        "DumpstateService.cpp",
-        "utils.cpp",
         "tests/dumpstate_test.cpp",
     ],
     static_libs: ["libgmock"],
 }
+
+cc_test {
+    name: "dumpstate_smoke_test",
+    defaults: ["dumpstate_defaults"],
+    srcs: [
+        "dumpstate.cpp",
+        "tests/dumpstate_smoke_test.cpp",
+    ],
+    static_libs: ["libgmock"],
+}
\ No newline at end of file
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2eb11cb..ef5064c 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/poll.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
@@ -46,22 +47,39 @@
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
+#include <dumpsys.h>
 #include <hidl/ServiceManagement.h>
 #include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
-
+#include <serviceutils/PriorityDumper.h>
 #include "DumpstateInternal.h"
+#include "DumpstateSectionReporter.h"
 #include "DumpstateService.h"
 #include "dumpstate.h"
 
 using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using ::std::literals::chrono_literals::operator""ms;
+using ::std::literals::chrono_literals::operator""s;
 
 // TODO: remove once moved to namespace
+using android::defaultServiceManager;
+using android::Dumpsys;
+using android::INVALID_OPERATION;
+using android::IServiceManager;
+using android::OK;
+using android::sp;
+using android::status_t;
+using android::String16;
+using android::String8;
+using android::TIMED_OUT;
+using android::UNKNOWN_ERROR;
+using android::Vector;
 using android::os::dumpstate::CommandOptions;
 using android::os::dumpstate::DumpFileToFd;
-using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::DumpstateSectionReporter;
 using android::os::dumpstate::GetPidByName;
+using android::os::dumpstate::PropertiesHelper;
 
 /* read before root is shed */
 static char cmdline_buf[16384] = "(unknown)";
@@ -127,6 +145,8 @@
 
 // Must be hardcoded because dumpstate HAL implementation need SELinux access to it
 static const std::string kDumpstateBoardPath = "/bugreports/";
+static const std::string kProtoPath = "proto/";
+static const std::string kProtoExt = ".proto";
 static const std::string kDumpstateBoardFiles[] = {
     "dumpstate_board.txt",
     "dumpstate_board.bin"
@@ -221,7 +241,7 @@
         }
 
         if (ds.IsZipping() && add_to_zip) {
-            if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
+            if (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd, /* timeout = */ 0ms) != OK) {
                 MYLOGE("Unable to add %s to zip file, addZipEntryFromFd failed\n", name.c_str());
             }
         } else {
@@ -714,11 +734,12 @@
       ".shb", ".sys", ".vb",  ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
 };
 
-bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) {
+status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd,
+                                      std::chrono::milliseconds timeout = 0ms) {
     if (!IsZipping()) {
         MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n",
                entry_name.c_str());
-        return false;
+        return INVALID_OPERATION;
     }
     std::string valid_name = entry_name;
 
@@ -740,32 +761,55 @@
     if (err != 0) {
         MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
                ZipWriter::ErrorCodeString(err));
-        return false;
+        return UNKNOWN_ERROR;
     }
+    auto start = std::chrono::steady_clock::now();
+    auto end = start + timeout;
+    struct pollfd pfd = {fd, POLLIN};
 
     std::vector<uint8_t> buffer(65536);
     while (1) {
+        if (timeout.count() > 0) {
+            // lambda to recalculate the timeout.
+            auto time_left_ms = [end]() {
+                auto now = std::chrono::steady_clock::now();
+                auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
+                return std::max(diff.count(), 0LL);
+            };
+
+            int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
+            if (rc < 0) {
+                MYLOGE("Error in poll while adding from fd to zip entry %s:%s", entry_name.c_str(),
+                       strerror(errno));
+                return -errno;
+            } else if (rc == 0) {
+                MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms",
+                       entry_name.c_str(), strerror(errno), timeout.count());
+                return TIMED_OUT;
+            }
+        }
+
         ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size()));
         if (bytes_read == 0) {
             break;
         } else if (bytes_read == -1) {
             MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
-            return false;
+            return -errno;
         }
         err = zip_writer_->WriteBytes(buffer.data(), bytes_read);
         if (err) {
             MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
-            return false;
+            return UNKNOWN_ERROR;
         }
     }
 
     err = zip_writer_->FinishEntry();
     if (err != 0) {
         MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
-        return false;
+        return UNKNOWN_ERROR;
     }
 
-    return true;
+    return OK;
 }
 
 bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) {
@@ -776,12 +820,12 @@
         return false;
     }
 
-    return AddZipEntryFromFd(entry_name, fd.get());
+    return (AddZipEntryFromFd(entry_name, fd.get()) == OK);
 }
 
 /* adds a file to the existing zipped bugreport */
 static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
-    return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
+    return (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) == OK) ? 0 : 1;
 }
 
 void Dumpstate::AddDir(const std::string& dir, bool recursive) {
@@ -1066,11 +1110,97 @@
     RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
 }
 
+void RunDumpsysText(const std::string& title, int priority, std::chrono::milliseconds timeout,
+                    std::chrono::milliseconds service_timeout) {
+    sp<android::IServiceManager> sm = defaultServiceManager();
+    Dumpsys dumpsys(sm.get());
+    DurationReporter duration_reporter(title);
+    Vector<String16> args;
+    Dumpsys::setServiceArgs(args, /* asProto = */ false, priority);
+
+    if (!title.empty()) {
+        dprintf(STDOUT_FILENO, "------ %s (%s) ------\n", title.c_str(), "/system/bin/dumpsys");
+        fsync(STDOUT_FILENO);
+    }
+
+    auto start = std::chrono::steady_clock::now();
+    Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ false);
+    for (const String16& service : services) {
+        std::string path(title);
+        path.append(" - ").append(String8(service).c_str());
+        DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_);
+        size_t bytes_written = 0;
+        status_t status = dumpsys.startDumpThread(service, args);
+        if (status == OK) {
+            dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
+            std::chrono::duration<double> elapsed_seconds;
+            status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout,
+                                       /* as_proto = */ false, elapsed_seconds, bytes_written);
+            section_reporter.setSize(bytes_written);
+            dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);
+            bool dump_complete = (status == OK);
+            dumpsys.stopDumpThread(dump_complete);
+        }
+        section_reporter.setStatus(status);
+
+        auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+            std::chrono::steady_clock::now() - start);
+        if (elapsed_duration > timeout) {
+            MYLOGE("*** command '%s' timed out after %llums\n", title.c_str(),
+                   elapsed_duration.count());
+            break;
+        }
+    }
+}
+
+void RunDumpsysProto(const std::string& title, int priority, std::chrono::milliseconds timeout,
+                     std::chrono::milliseconds service_timeout) {
+    sp<android::IServiceManager> sm = defaultServiceManager();
+    Dumpsys dumpsys(sm.get());
+    Vector<String16> args;
+    Dumpsys::setServiceArgs(args, /* asProto = */ true, priority);
+    DurationReporter duration_reporter(title);
+
+    auto start = std::chrono::steady_clock::now();
+    Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ true);
+    for (const String16& service : services) {
+        std::string path(kProtoPath);
+        path.append(String8(service).c_str());
+        if (priority == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) {
+            path.append("_CRITICAL");
+        } else if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) {
+            path.append("_HIGH");
+        }
+        path.append(kProtoExt);
+        DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_);
+        status_t status = dumpsys.startDumpThread(service, args);
+        if (status == OK) {
+            status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout);
+            bool dumpTerminated = (status == OK);
+            dumpsys.stopDumpThread(dumpTerminated);
+        }
+        ZipWriter::FileEntry file_entry;
+        ds.zip_writer_->GetLastEntry(&file_entry);
+        section_reporter.setSize(file_entry.compressed_size);
+        section_reporter.setStatus(status);
+
+        auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+            std::chrono::steady_clock::now() - start);
+        if (elapsed_duration > timeout) {
+            MYLOGE("*** command '%s' timed out after %llums\n", title.c_str(),
+                   elapsed_duration.count());
+            break;
+        }
+    }
+}
+
 // Runs dumpsys on services that must dump first and and will take less than 100ms to dump.
 static void RunDumpsysCritical() {
     if (ds.CurrentVersionSupportsPriorityDumps()) {
-        RunDumpsys("DUMPSYS CRITICAL", {"--priority", "CRITICAL"},
-                   CommandOptions::WithTimeout(5).DropRoot().Build());
+        RunDumpsysText("DUMPSYS CRITICAL", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL,
+                       /* timeout= */ 5s, /* service_timeout= */ 500ms);
+        RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL,
+                        /* timeout= */ 5s, /* service_timeout= */ 500ms);
     } else {
         RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
                    CommandOptions::WithTimeout(90).DropRoot().Build());
@@ -1082,8 +1212,13 @@
 // Runs dumpsys on services that must dump first but can take up to 250ms to dump.
 static void RunDumpsysHigh() {
     if (ds.CurrentVersionSupportsPriorityDumps()) {
-        RunDumpsys("DUMPSYS HIGH", {"--priority", "HIGH"},
-                   CommandOptions::WithTimeout(20).DropRoot().Build());
+        // TODO meminfo takes ~10s, connectivity takes ~5sec to dump. They are both
+        // high priority. Reduce timeout once they are able to dump in a shorter time or
+        // moved to a parallel task.
+        RunDumpsysText("DUMPSYS HIGH", IServiceManager::DUMP_FLAG_PRIORITY_HIGH,
+                       /* timeout= */ 90s, /* service_timeout= */ 30s);
+        RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH,
+                        /* timeout= */ 5s, /* service_timeout= */ 1s);
     } else {
         RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"});
     }
@@ -1092,8 +1227,10 @@
 // Runs dumpsys on services that must dump but can take up to 10s to dump.
 static void RunDumpsysNormal() {
     if (ds.CurrentVersionSupportsPriorityDumps()) {
-        RunDumpsys("DUMPSYS NORMAL", {"--priority", "NORMAL"},
-                   CommandOptions::WithTimeout(90).DropRoot().Build());
+        RunDumpsysText("DUMPSYS", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL,
+                       /* timeout= */ 90s, /* service_timeout= */ 10s);
+        RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL,
+                        /* timeout= */ 90s, /* service_timeout= */ 10s);
     } else {
         RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"},
                    CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 843c545..2554b63 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -226,8 +226,14 @@
 
     /*
      * Adds a new entry to the existing zip file.
+     *
+     * |entry_name| destination path of the new entry.
+     * |fd| file descriptor to read from.
+     * |timeout| timeout to terminate the read if not completed. Set
+     * value of 0s (default) to disable timeout.
      */
-    bool AddZipEntryFromFd(const std::string& entry_name, int fd);
+    android::status_t AddZipEntryFromFd(const std::string& entry_name, int fd,
+                                        std::chrono::milliseconds timeout);
 
     /*
      * Adds a text entry entry to the existing zip file.
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
new file mode 100644
index 0000000..61a5ef5
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <libgen.h>
+
+#include <android-base/file.h>
+#include <cutils/properties.h>
+#include <ziparchive/zip_archive.h>
+
+#include "dumpstate.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+using ::testing::Test;
+using ::std::literals::chrono_literals::operator""s;
+
+struct SectionInfo {
+    std::string name;
+    status_t status;
+    int32_t size_bytes;
+    int32_t duration_ms;
+};
+
+/**
+ * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
+ * section details generated by dumpstate are added to a vector to be used by Tests later.
+ */
+class DumpstateListener : public IDumpstateListener {
+  public:
+    int outFd_, max_progress_;
+    std::shared_ptr<std::vector<SectionInfo>> sections_;
+    DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
+        : outFd_(fd), max_progress_(5000), sections_(sections) {
+    }
+    binder::Status onProgressUpdated(int32_t progress) override {
+        dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_);
+        return binder::Status::ok();
+    }
+    binder::Status onMaxProgressUpdated(int32_t max_progress) override {
+        max_progress_ = max_progress;
+        return binder::Status::ok();
+    }
+    binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
+                                     int32_t duration_ms) override {
+        sections_->push_back({name, status, size_bytes, duration_ms});
+        return binder::Status::ok();
+    }
+    IBinder* onAsBinder() override {
+        return nullptr;
+    }
+};
+
+/**
+ * Generates bug report and provide access to the bug report file and other info for other tests.
+ * Since bug report generation is slow, the bugreport is only generated once.
+ */
+class ZippedBugreportGenerationTest : public Test {
+  public:
+    static std::shared_ptr<std::vector<SectionInfo>> sections;
+    static Dumpstate& ds;
+    static std::chrono::milliseconds duration;
+    static void SetUpTestCase() {
+        property_set("dumpstate.options", "bugreportplus");
+        // clang-format off
+        char* argv[] = {
+            (char*)"dumpstate",
+            (char*)"-d",
+            (char*)"-z",
+            (char*)"-B",
+            (char*)"-o",
+            (char*)dirname(android::base::GetExecutablePath().c_str())
+        };
+        // clang-format on
+        sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
+        ds.listener_ = listener;
+        ds.listener_name_ = "Smokey";
+        ds.report_section_ = true;
+        auto start = std::chrono::steady_clock::now();
+        run_main(ARRAY_SIZE(argv), argv);
+        auto end = std::chrono::steady_clock::now();
+        duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+    }
+
+    static const char* getZipFilePath() {
+        return ds.GetPath(".zip").c_str();
+    }
+};
+std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections =
+    std::make_shared<std::vector<SectionInfo>>();
+Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance();
+std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
+
+TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
+    EXPECT_EQ(access(getZipFilePath(), F_OK), 0);
+}
+
+TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
+    struct stat st;
+    EXPECT_EQ(stat(getZipFilePath(), &st), 0);
+    EXPECT_GE(st.st_size, 3000000 /* 3MB */);
+    EXPECT_LE(st.st_size, 30000000 /* 30MB */);
+}
+
+TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
+    EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
+                             << duration.count() << " s.";
+    EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
+                              << duration.count() << " s.";
+}
+
+/**
+ * Run tests on contents of zipped bug report.
+ */
+class ZippedBugReportContentsTest : public Test {
+  public:
+    ZipArchiveHandle handle;
+    void SetUp() {
+        ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0);
+    }
+    void TearDown() {
+        CloseArchive(handle);
+    }
+
+    void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
+        ZipEntry entry;
+        EXPECT_EQ(FindEntry(handle, ZipString(filename), &entry), 0);
+        EXPECT_GT(entry.uncompressed_length, minsize);
+        EXPECT_LT(entry.uncompressed_length, maxsize);
+    }
+};
+
+TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
+    ZipEntry mainEntryLoc;
+    // contains main entry name file
+    EXPECT_EQ(FindEntry(handle, ZipString("main_entry.txt"), &mainEntryLoc), 0);
+
+    char* buf = new char[mainEntryLoc.uncompressed_length];
+    ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length);
+    delete[] buf;
+
+    // contains main entry file
+    FileExists(buf, 1000000U, 50000000U);
+}
+
+TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
+    ZipEntry entry;
+    // contains main entry name file
+    EXPECT_EQ(FindEntry(handle, ZipString("version.txt"), &entry), 0);
+
+    char* buf = new char[entry.uncompressed_length + 1];
+    ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
+    buf[entry.uncompressed_length] = 0;
+    EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str());
+    delete[] buf;
+}
+
+TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
+    FileExists("dumpstate_board.bin", 1000000U, 80000000U);
+    FileExists("dumpstate_board.txt", 100000U, 1000000U);
+}
+
+// Spot check on some files pulled from the file system
+TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
+    // FS/proc/*/mountinfo size > 0
+    FileExists("FS/proc/1/mountinfo", 0U, 100000U);
+
+    // FS/data/misc/profiles/cur/0/*/primary.prof size > 0
+    FileExists("FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", 0U, 100000U);
+}
+
+/**
+ * Runs tests on section data generated by dumpstate and captured by DumpstateListener.
+ */
+class BugreportSectionTest : public Test {
+  public:
+    int numMatches(const std::string& substring) {
+        int matches = 0;
+        for (auto const& section : *ZippedBugreportGenerationTest::sections) {
+            if (section.name.find(substring) != std::string::npos) {
+                matches++;
+            }
+        }
+        return matches;
+    }
+    void SectionExists(const std::string& sectionName, int minsize) {
+        for (auto const& section : *ZippedBugreportGenerationTest::sections) {
+            if (sectionName == section.name) {
+                EXPECT_GE(section.size_bytes, minsize);
+                return;
+            }
+        }
+        FAIL() << sectionName << " not found.";
+    }
+};
+
+// Test all sections are generated without timeouts or errors
+TEST_F(BugreportSectionTest, GeneratedWithoutErrors) {
+    for (auto const& section : *ZippedBugreportGenerationTest::sections) {
+        EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status;
+    }
+}
+
+TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
+    int numSections = numMatches("DUMPSYS CRITICAL");
+    EXPECT_GE(numSections, 3);
+}
+
+TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
+    int numSections = numMatches("DUMPSYS HIGH");
+    EXPECT_GE(numSections, 2);
+}
+
+TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
+    int allSections = numMatches("DUMPSYS");
+    int criticalSections = numMatches("DUMPSYS CRITICAL");
+    int highSections = numMatches("DUMPSYS HIGH");
+    int normalSections = allSections - criticalSections - highSections;
+
+    EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
+                                  << "High:" << highSections << "Normal:" << normalSections << ")";
+}
+
+TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) {
+    int numSections = numMatches("proto/");
+    EXPECT_GE(numSections, 1);
+}
+
+// Test if some critical sections are being generated.
+TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
+    SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000);
+}
+
+TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
+    SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000);
+    SectionExists("DUMPSYS - activity", /* bytes= */ 10000);
+}
+
+TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
+    SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000);
+}
+
+TEST_F(BugreportSectionTest, WindowSectionGenerated) {
+    SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000);
+}
+
+TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
+    SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000);
+    SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000);
+}
+
+TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
+    SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000);
+}
+
+TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
+    SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000);
+}
+
+TEST_F(BugreportSectionTest, WifiSectionGenerated) {
+    SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
+}
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index ae0cc01..ca7d95e 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -283,7 +283,7 @@
     return services;
 }
 
-void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const {
+void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) {
     if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) ||
         (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) {
         args.add(String16("-a"));
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 1d78aa4..84f3b02 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -49,7 +49,7 @@
      * @param priorityFlags indicates priority of dump by passing additional priority args
      * to the service
      */
-    void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const;
+    static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags);
 
     /**
      * Starts a thread to connect to a service and get its dump output. The thread redirects
diff --git a/headers/media_plugin/media/arcvideobridge/IArcVideoBridge.h b/headers/media_plugin/media/arcvideobridge/IArcVideoBridge.h
new file mode 100644
index 0000000..b32c92e
--- /dev/null
+++ b/headers/media_plugin/media/arcvideobridge/IArcVideoBridge.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 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_IARC_VIDEO_BRIDGE_H
+#define ANDROID_IARC_VIDEO_BRIDGE_H
+
+#include <arc/IArcBridgeService.h>
+#include <binder/IInterface.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class IArcVideoBridge : public IInterface {
+public:
+    DECLARE_META_INTERFACE(ArcVideoBridge);
+
+    // Returns MojoBootstrapResult for creating mojo ipc channel of
+    // VideoAcceleratorFactory.
+    virtual ::arc::MojoBootstrapResult bootstrapVideoAcceleratorFactory() = 0;
+
+    // Get the version of the remote VideoHost on Chromium side.
+    virtual int32_t hostVersion() = 0;
+};
+
+class BnArcVideoBridge : public BnInterface<IArcVideoBridge> {
+public:
+    virtual status_t onTransact(
+            uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+};  // namespace android
+
+#endif // ANDROID_IARC_VIDEO_BRIDGE_H
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index f7409dc..f50379b 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -55,7 +55,8 @@
     mSurfaceDamage(),
     mAutoRefresh(false),
     mQueuedBuffer(true),
-    mIsStale(false) {
+    mIsStale(false),
+    mApi(0) {
 }
 
 BufferItem::~BufferItem() {}
@@ -84,6 +85,7 @@
     addAligned(size, mAutoRefresh);
     addAligned(size, mQueuedBuffer);
     addAligned(size, mIsStale);
+    addAligned(size, mApi);
     return size;
 }
 
@@ -177,6 +179,7 @@
     writeAligned(buffer, size, mAutoRefresh);
     writeAligned(buffer, size, mQueuedBuffer);
     writeAligned(buffer, size, mIsStale);
+    writeAligned(buffer, size, mApi);
 
     return NO_ERROR;
 }
@@ -247,6 +250,7 @@
     readAligned(buffer, size, mAutoRefresh);
     readAligned(buffer, size, mQueuedBuffer);
     readAligned(buffer, size, mIsStale);
+    readAligned(buffer, size, mApi);
 
     return NO_ERROR;
 }
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index add857c..e583b40 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -878,6 +878,7 @@
         item.mSurfaceDamage = surfaceDamage;
         item.mQueuedBuffer = true;
         item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;
+        item.mApi = mCore->mConnectedApi;
 
         mStickyTransform = stickyTransform;
 
diff --git a/libs/gui/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h
index 7740b9f..218bb42 100644
--- a/libs/gui/include/gui/BufferItem.h
+++ b/libs/gui/include/gui/BufferItem.h
@@ -127,6 +127,9 @@
     // Indicates that this BufferItem contains a stale buffer which has already
     // been released by the BufferQueue.
     bool mIsStale;
+
+    // Indicates the API (NATIVE_WINDOW_API_xxx) that queues the buffer.
+    int mApi;
 };
 
 } // namespace android
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 57eee12..e54f147 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -107,7 +107,7 @@
 // this is the strategy that applications will actually use.  Be very careful
 // when adjusting the default strategy because it can dramatically affect
 // (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "impulse";
+const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
 
 VelocityTracker::VelocityTracker(const char* strategy) :
         mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index deaf8d3..60ec38c 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -42,6 +42,9 @@
         for (const auto bit : hardware::hidl_enum_iterator<BufferUsage>()) {
             bits = bits | bit;
         }
+        // TODO(b/72323293): Remove this mask for EXTERNAL_DISP.
+        bits = bits | (1 << 13);
+
         return bits;
     }();
     return valid10UsageBits;
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 4d80e91..1c5b2d6 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -27,7 +27,6 @@
 
 staticLibraries = [
     "libsurfaceflingerincludes",
-    "libhwcomposer-command-buffer",
     "libbufferhub",
     "libbufferhubqueue",
     "libdisplay",
@@ -64,7 +63,8 @@
 ]
 
 headerLibraries = [
-    "libdvr_headers"
+    "libdvr_headers",
+    "android.hardware.graphics.composer@2.1-command-buffer",
 ]
 
 cc_library_static {
diff --git a/services/media/arcvideobridge/Android.bp b/services/media/arcvideobridge/Android.bp
new file mode 100644
index 0000000..ed0f613
--- /dev/null
+++ b/services/media/arcvideobridge/Android.bp
@@ -0,0 +1,29 @@
+cc_library_shared {
+    name: "libarcvideobridge",
+    product_variables: {
+        arc: {
+            srcs: [
+                "IArcVideoBridge.cpp",
+            ],
+            // TODO: remove the suffix "_bp" after finishing migration to Android.bp.
+            shared_libs: [
+                "libarcbridge",
+                "libarcbridgeservice",
+                "libbinder",
+                "libchrome",
+                "liblog",
+                "libmojo_bp",
+                "libutils",
+            ],
+            cflags: [
+                "-Wall",
+                "-Werror",
+                "-Wunused",
+                "-Wunreachable-code",
+            ],
+            include_dirs: [
+                "frameworks/native/include/media/arcvideobridge",
+            ]
+        }
+    }
+}
diff --git a/services/media/arcvideobridge/IArcVideoBridge.cpp b/services/media/arcvideobridge/IArcVideoBridge.cpp
new file mode 100644
index 0000000..468b76b
--- /dev/null
+++ b/services/media/arcvideobridge/IArcVideoBridge.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IArcVideoBridge"
+//#define LOG_NDEBUG 0
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "IArcVideoBridge.h"
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android {
+
+enum {
+    BOOTSTRAP_VIDEO_ACCELERATOR_FACTORY = IBinder::FIRST_CALL_TRANSACTION,
+    HOST_VERSION,
+};
+
+class BpArcVideoBridge : public BpInterface<IArcVideoBridge> {
+public:
+    BpArcVideoBridge(const sp<IBinder>& impl) : BpInterface<IArcVideoBridge>(impl) { }
+
+    virtual ::arc::MojoBootstrapResult bootstrapVideoAcceleratorFactory() {
+        Parcel data, reply;
+        ALOGV("bootstrapVideoAcceleratorFactory");
+        data.writeInterfaceToken(IArcVideoBridge::getInterfaceDescriptor());
+        status_t status = remote()->transact(
+                BOOTSTRAP_VIDEO_ACCELERATOR_FACTORY, data, &reply, 0);
+        if (status != 0) {
+            ALOGE("transact failed: %d", status);
+            return arc::MojoBootstrapResult();
+        }
+        return arc::MojoBootstrapResult::createFromParcel(reply);
+    }
+
+    virtual int32_t hostVersion() {
+        Parcel data, reply;
+        ALOGV("hostVersion");
+        data.writeInterfaceToken(IArcVideoBridge::getInterfaceDescriptor());
+        status_t status = remote()->transact(HOST_VERSION, data, &reply, 0);
+        if (status != 0) {
+            ALOGE("transact failed: %d", status);
+            return false;
+        }
+        return reply.readInt32();
+    }
+};
+
+IMPLEMENT_META_INTERFACE(ArcVideoBridge, "android.os.IArcVideoBridge");
+
+status_t BnArcVideoBridge::onTransact(
+        uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+    switch(code) {
+        case BOOTSTRAP_VIDEO_ACCELERATOR_FACTORY: {
+            ALOGV("BOOTSTRAP_VIDEO_ACCELERATOR_FACTORY");
+            CHECK_INTERFACE(IArcVideoBridge, data, reply);
+            arc::MojoBootstrapResult result = bootstrapVideoAcceleratorFactory();
+            return result.writeToParcel(reply);
+        }
+        case HOST_VERSION: {
+            ALOGV("HOST_VERSION");
+            CHECK_INTERFACE(IArcVideoBridge, data, reply);
+            reply->writeInt32(hostVersion());
+            return OK;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}  // namespace android
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 5b6c1ca..6c54ec3 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -59,13 +59,15 @@
 LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
 
 LOCAL_STATIC_LIBRARIES := \
-    libhwcomposer-command-buffer \
     libtrace_proto \
     libvkjson \
     libvr_manager \
     libvrflinger \
     libserviceutils
 
+LOCAL_HEADER_LIBRARIES := \
+    android.hardware.graphics.composer@2.1-command-buffer
+
 LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := libserviceutils
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d860f58..ab6a559 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -821,8 +821,17 @@
     engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
                               getColor());
     engine.setSourceDataSpace(mCurrentState.dataSpace);
+
+    if (mCurrentState.dataSpace == HAL_DATASPACE_BT2020_PQ &&
+        mConsumer->getCurrentApi() == NATIVE_WINDOW_API_MEDIA &&
+        getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102) {
+        engine.setSourceY410BT2020(true);
+    }
+
     engine.drawMesh(getBE().mMesh);
     engine.disableBlending();
+
+    engine.setSourceY410BT2020(false);
 }
 
 uint32_t BufferLayer::getProducerStickyTransform() const {
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 8f5c9c7..4d9b43f 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -68,6 +68,7 @@
         mCurrentFrameNumber(0),
         mCurrentTransformToDisplayInverse(false),
         mCurrentSurfaceDamage(),
+        mCurrentApi(0),
         mDefaultWidth(1),
         mDefaultHeight(1),
         mFilteringEnabled(true),
@@ -346,6 +347,7 @@
     mCurrentFrameNumber = item.mFrameNumber;
     mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse;
     mCurrentSurfaceDamage = item.mSurfaceDamage;
+    mCurrentApi = item.mApi;
 
     computeCurrentTransformMatrixLocked();
 
@@ -469,6 +471,11 @@
     return mCurrentSurfaceDamage;
 }
 
+int BufferLayerConsumer::getCurrentApi() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentApi;
+}
+
 sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const {
     Mutex::Autolock lock(mMutex);
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index f473390..a0272b3 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -138,6 +138,9 @@
     // must be called from SF main thread
     const Region& getSurfaceDamage() const;
 
+    // getCurrentApi retrieves the API which queues the current buffer.
+    int getCurrentApi() const;
+
     // See GLConsumer::setDefaultBufferSize.
     status_t setDefaultBufferSize(uint32_t width, uint32_t height);
 
@@ -337,6 +340,8 @@
     // The portion of this surface that has changed since the previous frame
     Region mCurrentSurfaceDamage;
 
+    int mCurrentApi;
+
     uint32_t mDefaultWidth, mDefaultHeight;
 
     // mFilteringEnabled indicates whether the transform matrix is computed for
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index d121a86..cf70529 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -76,7 +76,8 @@
         const wp<IBinder>& displayToken,
         const sp<DisplaySurface>& displaySurface,
         const sp<IGraphicBufferProducer>& producer,
-        bool supportWideColor)
+        bool supportWideColor,
+        bool supportHdr)
     : lastCompositionHadVisibleLayers(false),
       mFlinger(flinger),
       mType(type),
@@ -100,6 +101,8 @@
 
     mActiveColorMode = HAL_COLOR_MODE_NATIVE;
     mDisplayHasWideColor = supportWideColor;
+    mDisplayHasHdr = supportHdr;
+
     /*
      * Create our display's surface
      */
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 499bf8e..a470670 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -78,7 +78,7 @@
             const wp<IBinder>& displayToken,
             const sp<DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer,
-            bool supportWideColor);
+            bool supportWideColor, bool supportHdr);
     // clang-format on
 
     ~DisplayDevice();
@@ -128,6 +128,7 @@
     status_t beginFrame(bool mustRecompose) const;
     status_t prepareFrame(HWComposer& hwc);
     bool getWideColorSupport() const { return mDisplayHasWideColor; }
+    bool getHdrSupport() const { return mDisplayHasHdr; }
 
     void swapBuffers(HWComposer& hwc) const;
 
@@ -235,6 +236,7 @@
     // Initialized by SurfaceFlinger when the DisplayDevice is created.
     // Fed to RenderEngine during composition.
     bool mDisplayHasWideColor;
+    bool mDisplayHasHdr;
 };
 
 struct DisplayDeviceState {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 104ca60..3d9993e 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -25,8 +25,8 @@
 
 #include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
 #include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
 #include <utils/StrongPointer.h>
-#include <IComposerCommandBuffer.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
index 1b5a466..5e79e7a 100644
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ b/services/surfaceflinger/RenderEngine/Description.cpp
@@ -61,8 +61,20 @@
     return mColorMatrix;
 }
 
-void Description::setWideGamut(bool wideGamut) {
-    mIsWideGamut = wideGamut;
+void Description::setY410BT2020(bool enable) {
+    mY410BT2020 = enable;
+}
+
+void Description::setInputTransferFunction(TransferFunction transferFunction) {
+    mInputTransferFunction = transferFunction;
+}
+
+void Description::setOutputTransferFunction(TransferFunction transferFunction) {
+    mOutputTransferFunction = transferFunction;
+}
+
+void Description::enableToneMapping(bool enable) {
+    mToneMappingEnabled = enable;
 }
 
 } /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
index 1811952..75c1981 100644
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ b/services/surfaceflinger/RenderEngine/Description.h
@@ -32,6 +32,32 @@
  * Program and ProgramCache are friends and access the state directly
  */
 class Description {
+public:
+    Description() = default;
+    ~Description() = default;
+
+    void setPremultipliedAlpha(bool premultipliedAlpha);
+    void setOpaque(bool opaque);
+    void setTexture(const Texture& texture);
+    void disableTexture();
+    void setColor(const half4& color);
+    void setProjectionMatrix(const mat4& mtx);
+    void setColorMatrix(const mat4& mtx);
+    const mat4& getColorMatrix() const;
+
+    void setY410BT2020(bool enable);
+
+    enum class TransferFunction : int {
+        LINEAR,
+        SRGB,
+        ST2084,
+    };
+    void setInputTransferFunction(TransferFunction transferFunction);
+    void setOutputTransferFunction(TransferFunction transferFunction);
+
+    void enableToneMapping(bool enable);
+
+private:
     friend class Program;
     friend class ProgramCache;
 
@@ -52,21 +78,15 @@
     bool mColorMatrixEnabled = false;
     mat4 mColorMatrix;
 
-    bool mIsWideGamut = false;
+    // true if the sampled pixel values are in Y410/BT2020 rather than RGBA
+    bool mY410BT2020 = false;
 
-public:
-    Description() = default;
-    ~Description() = default;
+    // transfer functions for the input/output
+    TransferFunction mInputTransferFunction = TransferFunction::LINEAR;
+    TransferFunction mOutputTransferFunction = TransferFunction::LINEAR;
 
-    void setPremultipliedAlpha(bool premultipliedAlpha);
-    void setOpaque(bool opaque);
-    void setTexture(const Texture& texture);
-    void disableTexture();
-    void setColor(const half4& color);
-    void setProjectionMatrix(const mat4& mtx);
-    void setColorMatrix(const mat4& mtx);
-    const mat4& getColorMatrix() const;
-    void setWideGamut(bool wideGamut);
+    // tone-map the color
+    bool mToneMappingEnabled = false;
 };
 
 } /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index d1ee6f8..323cec7 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -130,14 +130,12 @@
         // Compute sRGB to DisplayP3 color transform
         // NOTE: For now, we are limiting wide-color support to
         // Display-P3 only.
-        mat3 srgbToP3 =
-                ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::DisplayP3()).getTransform();
+        mSrgbToDisplayP3 = mat4(
+                ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::DisplayP3()).getTransform());
 
-        // color transform needs to be expanded to 4x4 to be what the shader wants
-        // mat has an initializer that expands mat3 to mat4, but
-        // not an assignment operator
-        mat4 gamutTransform(srgbToP3);
-        mSrgbToDisplayP3 = gamutTransform;
+        // Compute BT2020 to DisplayP3 color transform
+        mBt2020ToDisplayP3 = mat4(
+                ColorSpaceConnector(ColorSpace::BT2020(), ColorSpace::DisplayP3()).getTransform());
     }
 }
 
@@ -235,6 +233,10 @@
     mDataSpace = source;
 }
 
+void GLES20RenderEngine::setSourceY410BT2020(bool enable) {
+    mState.setY410BT2020(enable);
+}
+
 void GLES20RenderEngine::setWideColor(bool hasWideColor) {
     ALOGV("setWideColor: %s", hasWideColor ? "true" : "false");
     mDisplayHasWideColor = hasWideColor;
@@ -324,10 +326,22 @@
 
     if (usesWideColor()) {
         Description wideColorState = mState;
-        if (mDataSpace != HAL_DATASPACE_DISPLAY_P3) {
-            wideColorState.setColorMatrix(mState.getColorMatrix() * mSrgbToDisplayP3);
-            wideColorState.setWideGamut(true);
-            ALOGV("drawMesh: gamut transform applied");
+        switch (mDataSpace) {
+            case HAL_DATASPACE_DISPLAY_P3:
+                // input matches output
+                break;
+            case HAL_DATASPACE_BT2020_PQ:
+                wideColorState.setColorMatrix(mState.getColorMatrix() * mBt2020ToDisplayP3);
+                wideColorState.setInputTransferFunction(Description::TransferFunction::ST2084);
+                wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
+                wideColorState.enableToneMapping(true);
+                break;
+            default:
+                wideColorState.setColorMatrix(mState.getColorMatrix() * mSrgbToDisplayP3);
+                wideColorState.setInputTransferFunction(Description::TransferFunction::SRGB);
+                wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
+                ALOGV("drawMesh: gamut transform applied");
+                break;
         }
         ProgramCache::getInstance().useProgram(wideColorState);
 
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
index 5ee9326..f3af547 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
@@ -71,6 +71,7 @@
     // Color management related functions and state
     void setColorMode(android_color_mode mode);
     void setSourceDataSpace(android_dataspace source);
+    void setSourceY410BT2020(bool enable);
     void setWideColor(bool hasWideColor);
     bool usesWideColor();
 
@@ -85,8 +86,9 @@
     bool mUseWideColor = false;
     uint64_t mWideColorFrameCount = 0;
 
-    // Currently only supporting sRGB and DisplayP3 color spaces
+    // Currently only supporting sRGB, BT2020 and DisplayP3 color spaces
     mat4 mSrgbToDisplayP3;
+    mat4 mBt2020ToDisplayP3;
     bool mPlatformHasWideColor = false;
 
     virtual void setupLayerTexturing(const Texture& texture);
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 3b8ac0e..7a43ea9 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -126,9 +126,42 @@
             .set(Key::OPACITY_MASK,
                  description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
             .set(Key::COLOR_MATRIX_MASK,
-                 description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON : Key::COLOR_MATRIX_OFF)
-            .set(Key::WIDE_GAMUT_MASK,
-                 description.mIsWideGamut ? Key::WIDE_GAMUT_ON : Key::WIDE_GAMUT_OFF);
+                 description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON : Key::COLOR_MATRIX_OFF);
+
+    needs.set(Key::Y410_BT2020_MASK,
+              description.mY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+
+    if (needs.hasColorMatrix()) {
+        switch (description.mInputTransferFunction) {
+            case Description::TransferFunction::LINEAR:
+            default:
+                needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
+                break;
+            case Description::TransferFunction::SRGB:
+                needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB);
+                break;
+            case Description::TransferFunction::ST2084:
+                needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084);
+                break;
+        }
+
+        switch (description.mOutputTransferFunction) {
+            case Description::TransferFunction::LINEAR:
+            default:
+                needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
+                break;
+            case Description::TransferFunction::SRGB:
+                needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB);
+                break;
+            case Description::TransferFunction::ST2084:
+                needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084);
+                break;
+        }
+
+        needs.set(Key::TONE_MAPPING_MASK,
+                  description.mToneMappingEnabled ? Key::TONE_MAPPING_ON : Key::TONE_MAPPING_OFF);
+    }
+
     return needs;
 }
 
@@ -170,56 +203,186 @@
         fs << "uniform vec4 color;";
     }
 
+    if (needs.isY410BT2020()) {
+        fs << R"__SHADER__(
+            vec3 convertY410BT2020(const vec3 color) {
+                const vec3 offset = vec3(0.0625, 0.5, 0.5);
+                const mat3 transform = mat3(
+                    vec3(1.1678,  1.1678, 1.1678),
+                    vec3(   0.0, -0.1878, 2.1481),
+                    vec3(1.6836, -0.6523,   0.0));
+                // Y is in G, U is in R, and V is in B
+                return clamp(transform * (color.grb - offset), 0.0, 1.0);
+            }
+            )__SHADER__";
+    }
+
     if (needs.hasColorMatrix()) {
         fs << "uniform mat4 colorMatrix;";
-    }
-    if (needs.hasColorMatrix()) {
-        // When in wide gamut mode, the color matrix will contain a color space
-        // conversion matrix that needs to be applied in linear space
-        // When not in wide gamut, we can simply no-op the transfer functions
-        // and let the shader compiler get rid of them
-        if (needs.isWideGamut()) {
+
+        switch (needs.getInputTF()) {
+            case Key::INPUT_TF_LINEAR:
+            default:
+                fs << R"__SHADER__(
+                    vec3 EOTF(const vec3 linear) {
+                        return linear;
+                    }
+                )__SHADER__";
+                break;
+            case Key::INPUT_TF_SRGB:
+                fs << R"__SHADER__(
+                    float EOTF_sRGB(float srgb) {
+                        return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+                    }
+
+                    vec3 EOTF_sRGB(const vec3 srgb) {
+                        return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                    }
+
+                    vec3 EOTF(const vec3 srgb) {
+                        return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                    }
+                )__SHADER__";
+                break;
+            case Key::INPUT_TF_ST2084:
+                fs << R"__SHADER__(
+                    vec3 EOTF(const highp vec3 color) {
+                        const highp float m1 = (2610.0 / 4096.0) / 4.0;
+                        const highp float m2 = (2523.0 / 4096.0) * 128.0;
+                        const highp float c1 = (3424.0 / 4096.0);
+                        const highp float c2 = (2413.0 / 4096.0) * 32.0;
+                        const highp float c3 = (2392.0 / 4096.0) * 32.0;
+
+                        highp vec3 tmp = pow(color, 1.0 / vec3(m2));
+                        tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+                        return pow(tmp, 1.0 / vec3(m1));
+                    }
+                    )__SHADER__";
+                break;
+        }
+
+        switch (needs.getOutputTF()) {
+            case Key::OUTPUT_TF_LINEAR:
+            default:
+                fs << R"__SHADER__(
+                    vec3 OETF(const vec3 linear) {
+                        return linear;
+                    }
+                )__SHADER__";
+                break;
+            case Key::OUTPUT_TF_SRGB:
+                fs << R"__SHADER__(
+                    float OETF_sRGB(const float linear) {
+                        return linear <= 0.0031308 ?
+                                linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+                    }
+
+                    vec3 OETF_sRGB(const vec3 linear) {
+                        return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                    }
+
+                    vec3 OETF(const vec3 linear) {
+                        return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                    }
+                )__SHADER__";
+                break;
+            case Key::OUTPUT_TF_ST2084:
+                fs << R"__SHADER__(
+                    vec3 OETF(const vec3 linear) {
+                        const float m1 = (2610.0 / 4096.0) / 4.0;
+                        const float m2 = (2523.0 / 4096.0) * 128.0;
+                        const float c1 = (3424.0 / 4096.0);
+                        const float c2 = (2413.0 / 4096.0) * 32.0;
+                        const float c3 = (2392.0 / 4096.0) * 32.0;
+
+                        vec3 tmp = pow(linear, vec3(m1));
+                        tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+                        return pow(tmp, vec3(m2));
+                    }
+                )__SHADER__";
+                break;
+        }
+
+        if (needs.hasToneMapping()) {
             fs << R"__SHADER__(
-                  float OETF_sRGB(const float linear) {
-                      return linear <= 0.0031308 ?
-                              linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
-                  }
+                float ToneMapChannel(const float color) {
+                    const float maxLumi = 10000.0;
+                    const float maxMasteringLumi = 1000.0;
+                    const float maxContentLumi = 1000.0;
+                    const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+                    const float maxOutLumi = 500.0;
 
-                  vec3 OETF_sRGB(const vec3 linear) {
-                      return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
-                  }
+                    // convert to nits first
+                    float nits = color * maxLumi;
 
-                  vec3 OETF_scRGB(const vec3 linear) {
-                      return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
-                  }
+                    // clamp to max input luminance
+                    nits = clamp(nits, 0.0, maxInLumi);
 
-                  float EOTF_sRGB(float srgb) {
-                      return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
-                  }
+                    // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+                    if (maxInLumi <= maxOutLumi) {
+                        nits *= maxOutLumi / maxInLumi;
+                    } else {
+                        // three control points
+                        const float x0 = 10.0;
+                        const float y0 = 17.0;
+                        const float x1 = maxOutLumi * 0.75;
+                        const float y1 = x1;
+                        const float x2 = x1 + (maxInLumi - x1) / 2.0;
+                        const float y2 = y1 + (maxOutLumi - y1) * 0.75;
 
-                  vec3 EOTF_sRGB(const vec3 srgb) {
-                      return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
-                  }
+                        // horizontal distances between the last three control points
+                        const float h12 = x2 - x1;
+                        const float h23 = maxInLumi - x2;
+                        // tangents at the last three control points
+                        const float m1 = (y2 - y1) / h12;
+                        const float m3 = (maxOutLumi - y2) / h23;
+                        const float m2 = (m1 + m3) / 2.0;
 
-                  vec3 EOTF_scRGB(const vec3 srgb) {
-                      return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
-                  }
+                        if (nits < x0) {
+                            // scale [0.0, x0] to [0.0, y0] linearly
+                            const float slope = y0 / x0;
+                            nits *= slope;
+                        } else if (nits < x1) {
+                            // scale [x0, x1] to [y0, y1] linearly
+                            const float slope = (y1 - y0) / (x1 - x0);
+                            nits = y0 + (nits - x0) * slope;
+                        } else if (nits < x2) {
+                            // scale [x1, x2] to [y1, y2] using Hermite interp
+                            float t = (nits - x1) / h12;
+                            nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
+                                   (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+                        } else {
+                            // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+                            float t = (nits - x2) / h23;
+                            nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
+                                   (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
+                        }
+                    }
+
+                    // convert back to [0.0, 1.0]
+                    return nits / maxOutLumi;
+                }
+
+                vec3 ToneMap(const vec3 color) {
+                    return vec3(ToneMapChannel(color.r), ToneMapChannel(color.g),
+                                ToneMapChannel(color.b));
+                }
             )__SHADER__";
         } else {
             fs << R"__SHADER__(
-                  vec3 OETF_scRGB(const vec3 linear) {
-                      return linear;
-                  }
-
-                  vec3 EOTF_scRGB(const vec3 srgb) {
-                      return srgb;
-                  }
+                vec3 ToneMap(const vec3 color) {
+                    return color;
+                }
             )__SHADER__";
         }
     }
+
     fs << "void main(void) {" << indent;
     if (needs.isTexturing()) {
         fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+        if (needs.isY410BT2020()) {
+            fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
+        }
     } else {
         fs << "gl_FragColor.rgb = color.rgb;";
         fs << "gl_FragColor.a = 1.0;";
@@ -243,9 +406,12 @@
             // avoid divide by 0 by adding 0.5/256 to the alpha channel
             fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
         }
-        fs << "vec4 transformed = colorMatrix * vec4(EOTF_scRGB(gl_FragColor.rgb), 1);";
+        fs << "vec4 transformed = colorMatrix * vec4(ToneMap(EOTF(gl_FragColor.rgb)), 1);";
+        // the transformation from a wider colorspace to a narrower one can
+        // result in >1.0 or <0.0 pixel values
+        fs << "transformed.rgb = clamp(transformed.rgb, 0.0, 1.0);";
         // We assume the last row is always {0,0,0,1} and we skip the division by w
-        fs << "gl_FragColor.rgb = OETF_scRGB(transformed.rgb);";
+        fs << "gl_FragColor.rgb = OETF(transformed.rgb);";
         if (!needs.isOpaque() && needs.isPremultiplied()) {
             // and re-premultiply if needed after gamma correction
             fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/services/surfaceflinger/RenderEngine/ProgramCache.h
index 54d3722..dcc8cc6 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.h
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.h
@@ -50,30 +50,53 @@
 
     public:
         enum {
-            BLEND_PREMULT = 0x00000001,
-            BLEND_NORMAL = 0x00000000,
-            BLEND_MASK = 0x00000001,
+            BLEND_SHIFT = 0,
+            BLEND_MASK = 1 << BLEND_SHIFT,
+            BLEND_PREMULT = 1 << BLEND_SHIFT,
+            BLEND_NORMAL = 0 << BLEND_SHIFT,
 
-            OPACITY_OPAQUE = 0x00000002,
-            OPACITY_TRANSLUCENT = 0x00000000,
-            OPACITY_MASK = 0x00000002,
+            OPACITY_SHIFT = 1,
+            OPACITY_MASK = 1 << OPACITY_SHIFT,
+            OPACITY_OPAQUE = 1 << OPACITY_SHIFT,
+            OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT,
 
-            ALPHA_LT_ONE = 0x00000004,
-            ALPHA_EQ_ONE = 0x00000000,
-            ALPHA_MASK = 0x00000004,
+            ALPHA_SHIFT = 2,
+            ALPHA_MASK = 1 << ALPHA_SHIFT,
+            ALPHA_LT_ONE = 1 << ALPHA_SHIFT,
+            ALPHA_EQ_ONE = 0 << ALPHA_SHIFT,
 
-            TEXTURE_OFF = 0x00000000,
-            TEXTURE_EXT = 0x00000008,
-            TEXTURE_2D = 0x00000010,
-            TEXTURE_MASK = 0x00000018,
+            TEXTURE_SHIFT = 3,
+            TEXTURE_MASK = 3 << TEXTURE_SHIFT,
+            TEXTURE_OFF = 0 << TEXTURE_SHIFT,
+            TEXTURE_EXT = 1 << TEXTURE_SHIFT,
+            TEXTURE_2D = 2 << TEXTURE_SHIFT,
 
-            COLOR_MATRIX_OFF = 0x00000000,
-            COLOR_MATRIX_ON = 0x00000020,
-            COLOR_MATRIX_MASK = 0x00000020,
+            COLOR_MATRIX_SHIFT = 5,
+            COLOR_MATRIX_MASK = 1 << COLOR_MATRIX_SHIFT,
+            COLOR_MATRIX_OFF = 0 << COLOR_MATRIX_SHIFT,
+            COLOR_MATRIX_ON = 1 << COLOR_MATRIX_SHIFT,
 
-            WIDE_GAMUT_OFF = 0x00000000,
-            WIDE_GAMUT_ON = 0x00000040,
-            WIDE_GAMUT_MASK = 0x00000040,
+            INPUT_TF_SHIFT = 6,
+            INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
+            INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
+            INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
+            INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
+
+            OUTPUT_TF_SHIFT = 8,
+            OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
+            OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
+            OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
+            OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
+
+            TONE_MAPPING_SHIFT = 10,
+            TONE_MAPPING_MASK = 1 << TONE_MAPPING_SHIFT,
+            TONE_MAPPING_OFF = 0 << TONE_MAPPING_SHIFT,
+            TONE_MAPPING_ON = 1 << TONE_MAPPING_SHIFT,
+
+            Y410_BT2020_SHIFT = 11,
+            Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
+            Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
+            Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
         };
 
         inline Key() : mKey(0) {}
@@ -90,7 +113,10 @@
         inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
         inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
         inline bool hasColorMatrix() const { return (mKey & COLOR_MATRIX_MASK) == COLOR_MATRIX_ON; }
-        inline bool isWideGamut() const { return (mKey & WIDE_GAMUT_MASK) == WIDE_GAMUT_ON; }
+        inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
+        inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
+        inline bool hasToneMapping() const { return (mKey & TONE_MAPPING_MASK) == TONE_MAPPING_ON; }
+        inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; }
 
         // this is the definition of a friend function -- not a method of class Needs
         friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 737b1dd..67c0d1c 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -138,6 +138,7 @@
                                     const half4& color) = 0;
     virtual void setColorMode(android_color_mode mode) = 0;
     virtual void setSourceDataSpace(android_dataspace source) = 0;
+    virtual void setSourceY410BT2020(bool enable) = 0;
     virtual void setWideColor(bool hasWideColor) = 0;
     virtual bool usesWideColor() = 0;
     virtual void setupLayerTexturing(const Texture& texture) = 0;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a91525d..974a261 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1041,7 +1041,19 @@
     std::unique_ptr<HdrCapabilities> capabilities =
             getBE().mHwc->getHdrCapabilities(displayDevice->getHwcDisplayId());
     if (capabilities) {
-        std::swap(*outCapabilities, *capabilities);
+        if (displayDevice->getWideColorSupport() && !displayDevice->getHdrSupport()) {
+            // insert HDR10 as we will force client composition for HDR10
+            // layers
+            std::vector<int32_t> types = capabilities->getSupportedHdrTypes();
+            types.push_back(HAL_HDR_HDR10);
+
+            *outCapabilities = HdrCapabilities(types,
+                    capabilities->getDesiredMaxLuminance(),
+                    capabilities->getDesiredMaxAverageLuminance(),
+                    capabilities->getDesiredMinLuminance());
+        } else {
+            *outCapabilities = std::move(*capabilities);
+        }
     } else {
         return BAD_VALUE;
     }
@@ -1793,7 +1805,7 @@
 }
 
 android_dataspace SurfaceFlinger::bestTargetDataSpace(
-        android_dataspace a, android_dataspace b) const {
+        android_dataspace a, android_dataspace b, bool hasHdr) const {
     // Only support sRGB and Display-P3 right now.
     if (a == HAL_DATASPACE_DISPLAY_P3 || b == HAL_DATASPACE_DISPLAY_P3) {
         return HAL_DATASPACE_DISPLAY_P3;
@@ -1804,6 +1816,9 @@
     if (a == HAL_DATASPACE_V0_SCRGB || b == HAL_DATASPACE_V0_SCRGB) {
         return HAL_DATASPACE_DISPLAY_P3;
     }
+    if (!hasHdr && (a == HAL_DATASPACE_BT2020_PQ || b == HAL_DATASPACE_BT2020_PQ)) {
+        return HAL_DATASPACE_DISPLAY_P3;
+    }
 
     return HAL_DATASPACE_V0_SRGB;
 }
@@ -1885,6 +1900,11 @@
                     "display %zd: %d", displayId, result);
         }
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+            if (layer->getDataSpace() == HAL_DATASPACE_BT2020_PQ &&
+                    !displayDevice->getHdrSupport()) {
+                layer->forceClientComposition(hwcId);
+            }
+
             if (layer->getForceClientComposition(hwcId)) {
                 ALOGV("[%s] Requesting Client composition", layer->getName().string());
                 layer->setCompositionType(hwcId, HWC2::Composition::Client);
@@ -1899,7 +1919,8 @@
             android_dataspace newDataSpace = HAL_DATASPACE_V0_SRGB;
 
             for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-                newDataSpace = bestTargetDataSpace(layer->getDataSpace(), newDataSpace);
+                newDataSpace = bestTargetDataSpace(layer->getDataSpace(), newDataSpace,
+                        displayDevice->getHdrSupport());
                 ALOGV("layer: %s, dataspace: %s (%#x), newDataSpace: %s (%#x)",
                       layer->getName().string(), dataspaceDetails(layer->getDataSpace()).c_str(),
                       layer->getDataSpace(), dataspaceDetails(newDataSpace).c_str(), newDataSpace);
@@ -2253,9 +2274,19 @@
                         useWideColorMode = hasWideColorModes && hasWideColorDisplay;
                     }
 
+                    bool hasHdrSupport = false;
+                    std::unique_ptr<HdrCapabilities> hdrCapabilities =
+                        getHwComposer().getHdrCapabilities(state.type);
+                    if (hdrCapabilities) {
+                        const std::vector<int32_t> types = hdrCapabilities->getSupportedHdrTypes();
+                        auto iter = std::find(types.cbegin(), types.cend(), HAL_HDR_HDR10);
+                        hasHdrSupport = iter != types.cend();
+                    }
+
                     sp<DisplayDevice> hw =
                             new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
-                                              dispSurface, producer, useWideColorMode);
+                                              dispSurface, producer, useWideColorMode,
+                                              hasHdrSupport);
 
                     android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
                     if (useWideColorMode) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4da0803..1349bec 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -607,7 +607,8 @@
     // Given a dataSpace, returns the appropriate color_mode to use
     // to display that dataSpace.
     android_color_mode pickColorMode(android_dataspace dataSpace) const;
-    android_dataspace bestTargetDataSpace(android_dataspace a, android_dataspace b) const;
+    android_dataspace bestTargetDataSpace(android_dataspace a, android_dataspace b,
+            bool hasHdr) const;
 
     mat4 computeSaturationMatrix() const;
 
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 47c4f4a..eeb0f54 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -31,6 +31,9 @@
         "libtrace_proto",
         "libgmock"
     ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+    ],
     cppflags: [
         "-std=c++1z",
     ],
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 76ec42d..063d83c 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -32,6 +32,10 @@
     "libutils",
   ],
 
+  header_libs: [
+    "android.hardware.graphics.composer@2.1-command-buffer",
+  ],
+
   export_static_lib_headers: [
     "libhwcomposer-client",
   ],
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
index dfc656a..63ee86f 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ b/services/vr/hardware_composer/impl/vr_composer_client.h
@@ -17,9 +17,9 @@
 #ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
 #define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
 
-#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
 #include <ComposerClient.h>
-#include <IComposerCommandBuffer.h>
+#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
 
 namespace android {
 namespace dvr {