Merge "BufferHub: Clean up unnecessary DVR dependency"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index ce3a6aa..35cff5f 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -83,7 +83,9 @@
     ],
     srcs: [
         "DumpstateInternal.cpp",
+        "DumpstateSectionReporter.cpp",
         "DumpstateService.cpp",
+        "main.cpp",
         "utils.cpp",
         "dumpstate.cpp",
     ],
diff --git a/cmds/dumpstate/DumpstateSectionReporter.cpp b/cmds/dumpstate/DumpstateSectionReporter.cpp
new file mode 100644
index 0000000..f814bde
--- /dev/null
+++ b/cmds/dumpstate/DumpstateSectionReporter.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 LOG_TAG "dumpstate"
+
+#include "DumpstateSectionReporter.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+DumpstateSectionReporter::DumpstateSectionReporter(const std::string& title,
+                                                   sp<android::os::IDumpstateListener> listener,
+                                                   bool sendReport)
+    : title_(title), listener_(listener), sendReport_(sendReport), status_(OK), size_(-1) {
+    started_ = std::chrono::steady_clock::now();
+}
+
+DumpstateSectionReporter::~DumpstateSectionReporter() {
+    if ((listener_ != nullptr) && (sendReport_)) {
+        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
+            std::chrono::steady_clock::now() - started_);
+        listener_->onSectionComplete(title_, status_, size_, (int32_t)elapsed.count());
+    }
+}
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpstate/DumpstateSectionReporter.h b/cmds/dumpstate/DumpstateSectionReporter.h
new file mode 100644
index 0000000..e971de8
--- /dev/null
+++ b/cmds/dumpstate/DumpstateSectionReporter.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_OS_DUMPSTATESECTIONREPORTER_H_
+#define ANDROID_OS_DUMPSTATESECTIONREPORTER_H_
+
+#include <android/os/IDumpstateListener.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+
+/*
+ * Helper class used to report per section details to a listener.
+ *
+ * Typical usage:
+ *
+ *    DumpstateSectionReporter sectionReporter(title, listener, sendReport);
+ *    sectionReporter.setSize(5000);
+ *
+ */
+class DumpstateSectionReporter {
+  public:
+    DumpstateSectionReporter(const std::string& title, sp<android::os::IDumpstateListener> listener,
+                             bool sendReport);
+
+    ~DumpstateSectionReporter();
+
+    void setStatus(status_t status) {
+        status_ = status;
+    }
+
+    void setSize(int size) {
+        size_ = size;
+    }
+
+  private:
+    std::string title_;
+    android::sp<android::os::IDumpstateListener> listener_;
+    bool sendReport_;
+    status_t status_;
+    int size_;
+    std::chrono::time_point<std::chrono::steady_clock> started_;
+};
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
+
+#endif  // ANDROID_OS_DUMPSTATESECTIONREPORTER_H_
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index efe0466..49a78e7 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -52,6 +52,7 @@
 
 binder::Status DumpstateService::setListener(const std::string& name,
                                              const sp<IDumpstateListener>& listener,
+                                             bool getSectionDetails,
                                              sp<IDumpstateToken>* returned_token) {
     *returned_token = nullptr;
     if (name.empty()) {
@@ -70,6 +71,7 @@
 
     ds_.listener_name_ = name;
     ds_.listener_ = listener;
+    ds_.report_section_ = getSectionDetails;
     *returned_token = new DumpstateToken();
 
     return binder::Status::ok();
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 4352d3d..7bca24a 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -38,6 +38,7 @@
 
     status_t dump(int fd, const Vector<String16>& args) override;
     binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
+                               bool getSectionDetails,
                                sp<IDumpstateToken>* returned_token) override;
 
   private:
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 4becccf..9b11b96 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -30,6 +30,9 @@
      *
      * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already
      * set (the listener behaves like a Highlander: There Can be Only One).
+     * Set {@code getSectionDetails} to true in order to receive callbacks with per section
+     * progress details
      */
-    IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener);
+    IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener,
+                                boolean getSectionDetails);
 }
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 32717f4..030d69d 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -24,4 +24,16 @@
 interface IDumpstateListener {
     void onProgressUpdated(int progress);
     void onMaxProgressUpdated(int maxProgress);
+
+    /**
+    * Called after every section is complete.
+    * @param  name          section name
+    * @param  status        values from status_t
+    *                       {@code OK} section completed successfully
+    *                       {@code TIMEOUT} dump timed out
+    *                       {@code != OK} error
+    * @param  size          size in bytes, may be invalid if status != OK
+    * @param  durationMs    duration in ms
+    */
+    void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs);
 }
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 665695c..93f8d43 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -833,6 +833,17 @@
 
 static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000;
 
+static void DoKernelLogcat() {
+    unsigned long timeout_ms = logcat_timeout("kernel");
+    if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
+        timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
+    }
+    RunCommand(
+        "KERNEL LOG",
+        {"logcat", "-b", "kernel", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+}
+
 static void DoLogcat() {
     unsigned long timeout_ms;
     // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
@@ -848,25 +859,24 @@
     if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
         timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
     }
-    RunCommand("EVENT LOG",
-               {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid",
-                        "-d", "*:v"},
-               CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+    RunCommand(
+        "EVENT LOG",
+        {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build());
     timeout_ms = logcat_timeout("radio");
     if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
         timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
     }
-    RunCommand("RADIO LOG",
-               {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid",
-                        "-d", "*:v"},
-               CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+    RunCommand(
+        "RADIO LOG",
+        {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build());
 
     RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
 
     /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
-    RunCommand("LAST LOGCAT",
-                {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid",
-                        "-d", "*:v"});
+    RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable",
+                               "-v", "uid", "-d", "*:v"});
 }
 
 static void DumpIpTablesAsRoot() {
@@ -1187,7 +1197,12 @@
         RunCommand("LSMOD", {"lsmod"});
     }
 
-    do_dmesg();
+    if (__android_logger_property_get_bool(
+            "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) {
+        DoKernelLogcat();
+    } else {
+        do_dmesg();
+    }
 
     RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
     for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
@@ -1588,7 +1603,8 @@
     // clang-format on
 }
 
-int main(int argc, char *argv[]) {
+/** Main entry point for dumpstate. */
+int run_main(int argc, char* argv[]) {
     int do_add_date = 0;
     int do_zip_file = 0;
     int do_vibrate = 1;
@@ -1601,6 +1617,8 @@
     bool show_header_only = false;
     bool do_start_service = false;
     bool telephony_only = false;
+    int dup_stdout_fd;
+    int dup_stderr_fd;
 
     /* set as high priority, and protect from OOM killer */
     setpriority(PRIO_PROCESS, 0, -20);
@@ -1872,11 +1890,13 @@
     }
 
     if (is_redirecting) {
+        TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
         redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
         if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
                    ds.log_path_.c_str(), strerror(errno));
         }
+        TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout)));
         /* TODO: rather than generating a text file now and zipping it later,
            it would be more efficient to redirect stdout to the zip entry
            directly, but the libziparchive doesn't support that option yet. */
@@ -1950,7 +1970,7 @@
 
     /* close output if needed */
     if (is_redirecting) {
-        fclose(stdout);
+        TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
     }
 
     /* rename or zip the (now complete) .tmp file to its final location */
@@ -2081,7 +2101,7 @@
     MYLOGI("done (id %d)\n", ds.id_);
 
     if (is_redirecting) {
-        fclose(stderr);
+        TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));
     }
 
     if (use_control_socket && ds.control_socket_fd_ != -1) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 8db23a9..843c545 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -341,9 +341,10 @@
     // Pointer to the zip structure.
     std::unique_ptr<ZipWriter> zip_writer_;
 
-    // Binder object listing to progress.
+    // Binder object listening to progress.
     android::sp<android::os::IDumpstateListener> listener_;
     std::string listener_name_;
+    bool report_section_;
 
     // Notification title and description
     std::string notification_title;
@@ -433,6 +434,9 @@
 /** Gets command-line arguments. */
 void format_args(int argc, const char *argv[], std::string *args);
 
+/** Main entry point for dumpstate. */
+int run_main(int argc, char* argv[]);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
new file mode 100644
index 0000000..78aad11
--- /dev/null
+++ b/cmds/dumpstate/main.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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 "dumpstate.h"
+
+int main(int argc, char* argv[]) {
+    return run_main(argc, argv);
+}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index a2e9453..838b385 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -58,6 +58,8 @@
   public:
     MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
     MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
+    MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status,
+                                                   int32_t size, int32_t durationMs));
 
   protected:
     MOCK_METHOD0(onAsBinder, IBinder*());
@@ -601,27 +603,43 @@
 TEST_F(DumpstateServiceTest, SetListenerNoName) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
     sp<IDumpstateToken> token;
-    EXPECT_TRUE(dss.setListener("", listener, &token).isOk());
+    EXPECT_TRUE(dss.setListener("", listener, /* getSectionDetails = */ false, &token).isOk());
     ASSERT_THAT(token, IsNull());
 }
 
 TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
     sp<IDumpstateToken> token;
-    EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk());
+    EXPECT_TRUE(
+        dss.setListener("whatever", nullptr, /* getSectionDetails = */ false, &token).isOk());
     ASSERT_THAT(token, IsNull());
 }
 
 TEST_F(DumpstateServiceTest, SetListenerTwice) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
     sp<IDumpstateToken> token;
-    EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk());
+    EXPECT_TRUE(
+        dss.setListener("whatever", listener, /* getSectionDetails = */ false, &token).isOk());
     ASSERT_THAT(token, NotNull());
     EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+    EXPECT_FALSE(Dumpstate::GetInstance().report_section_);
 
     token.clear();
-    EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk());
+    EXPECT_TRUE(
+        dss.setListener("whatsoever", listener, /* getSectionDetails = */ false, &token).isOk());
     ASSERT_THAT(token, IsNull());
     EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+    EXPECT_FALSE(Dumpstate::GetInstance().report_section_);
+}
+
+TEST_F(DumpstateServiceTest, SetListenerWithSectionDetails) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    sp<IDumpstateToken> token;
+    Dumpstate::GetInstance().listener_ = nullptr;
+    EXPECT_TRUE(
+        dss.setListener("whatever", listener, /* getSectionDetails = */ true, &token).isOk());
+    ASSERT_THAT(token, NotNull());
+    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+    EXPECT_TRUE(Dumpstate::GetInstance().report_section_);
 }
 
 class ProgressTest : public DumpstateBaseTest {
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 0862a40..ae0cc01 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -43,9 +43,11 @@
 #include "dumpsys.h"
 
 using namespace android;
-using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::base::WriteFully;
+using ::android::base::StringAppendF;
+using ::android::base::StringPrintf;
+using ::android::base::unique_fd;
+using ::android::base::WriteFully;
+using ::android::base::WriteStringToFd;
 
 static int sort_func(const String16* lhs, const String16* rhs)
 {
@@ -96,6 +98,19 @@
     return false;
 }
 
+String16 ConvertBitmaskToPriorityType(int bitmask) {
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) {
+        return String16(PriorityDumper::PRIORITY_ARG_CRITICAL);
+    }
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) {
+        return String16(PriorityDumper::PRIORITY_ARG_HIGH);
+    }
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+        return String16(PriorityDumper::PRIORITY_ARG_NORMAL);
+    }
+    return String16("");
+}
+
 int Dumpsys::main(int argc, char* const argv[]) {
     Vector<String16> services;
     Vector<String16> args;
@@ -104,9 +119,9 @@
     Vector<String16> protoServices;
     bool showListOnly = false;
     bool skipServices = false;
-    bool filterByProto = false;
+    bool asProto = false;
     int timeoutArgMs = 10000;
-    int dumpPriorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
+    int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
     static struct option longOptions[] = {{"priority", required_argument, 0, 0},
                                           {"proto", no_argument, 0, 0},
                                           {"skip", no_argument, 0, 0},
@@ -131,13 +146,13 @@
             if (!strcmp(longOptions[optionIndex].name, "skip")) {
                 skipServices = true;
             } else if (!strcmp(longOptions[optionIndex].name, "proto")) {
-                filterByProto = true;
+                asProto = true;
             } else if (!strcmp(longOptions[optionIndex].name, "help")) {
                 usage();
                 return 0;
             } else if (!strcmp(longOptions[optionIndex].name, "priority")) {
                 priorityType = String16(String8(optarg));
-                if (!ConvertPriorityTypeToBitmask(priorityType, dumpPriorityFlags)) {
+                if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) {
                     fprintf(stderr, "\n");
                     usage();
                     return -1;
@@ -198,28 +213,11 @@
     }
 
     if (services.empty() || showListOnly) {
-        // gets all services
-        services = sm_->listServices(dumpPriorityFlags);
-        services.sort(sort_func);
-        if (filterByProto) {
-            protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
-            protoServices.sort(sort_func);
-            Vector<String16> intersection;
-            std::set_intersection(services.begin(), services.end(), protoServices.begin(),
-                                  protoServices.end(), std::back_inserter(intersection));
-            services = std::move(intersection);
-            args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
-        }
-        if (dumpPriorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
-            args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
-            args.insertAt(priorityType, 1);
-        } else {
-            args.add(String16("-a"));
-        }
+        services = listServices(priorityFlags, asProto);
+        setServiceArgs(args, asProto, priorityFlags);
     }
 
     const size_t N = services.size();
-
     if (N > 1) {
         // first print a list of the current services
         aout << "Currently running services:" << endl;
@@ -239,129 +237,204 @@
     }
 
     for (size_t i = 0; i < N; i++) {
-        const String16& service_name = std::move(services[i]);
-        if (IsSkipped(skippedServices, service_name)) continue;
+        const String16& serviceName = services[i];
+        if (IsSkipped(skippedServices, serviceName)) continue;
 
-        sp<IBinder> service = sm_->checkService(service_name);
-        if (service != nullptr) {
-            int sfd[2];
-
-            if (pipe(sfd) != 0) {
-                aerr << "Failed to create pipe to dump service info for " << service_name
-                     << ": " << strerror(errno) << endl;
-                continue;
+        if (startDumpThread(serviceName, args) == OK) {
+            bool addSeparator = (N > 1);
+            if (addSeparator) {
+                writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
             }
+            std::chrono::duration<double> elapsedDuration;
+            size_t bytesWritten = 0;
+            status_t status =
+                writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
+                          asProto, elapsedDuration, bytesWritten);
 
-            unique_fd local_end(sfd[0]);
-            unique_fd remote_end(sfd[1]);
-            sfd[0] = sfd[1] = -1;
-
-            if (N > 1) {
-                aout << "------------------------------------------------------------"
-                        "-------------------" << endl;
-                if (dumpPriorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
-                    aout << "DUMP OF SERVICE " << service_name << ":" << endl;
-                } else {
-                    aout << "DUMP OF SERVICE " << priorityType << " " << service_name << ":" << endl;
-                }
-            }
-
-            // dump blocks until completion, so spawn a thread..
-            std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
-                int err = service->dump(remote_end.get(), args);
-
-                // It'd be nice to be able to close the remote end of the socketpair before the dump
-                // call returns, to terminate our reads if the other end closes their copy of the
-                // file descriptor, but then hangs for some reason. There doesn't seem to be a good
-                // way to do this, though.
-                remote_end.reset();
-
-                if (err != 0) {
-                    aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
-                         << endl;
-                }
-            });
-
-            auto timeout = std::chrono::milliseconds(timeoutArgMs);
-            auto start = std::chrono::steady_clock::now();
-            auto end = start + timeout;
-
-            struct pollfd pfd = {
-                .fd = local_end.get(),
-                .events = POLLIN
-            };
-
-            bool timed_out = false;
-            bool error = false;
-            while (true) {
-                // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates 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) {
-                    aerr << "Error in poll while dumping service " << service_name << " : "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                } else if (rc == 0) {
-                    timed_out = true;
-                    break;
-                }
-
-                char buf[4096];
-                rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
-                if (rc < 0) {
-                    aerr << "Failed to read while dumping service " << service_name << ": "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                } else if (rc == 0) {
-                    // EOF.
-                    break;
-                }
-
-                if (!WriteFully(STDOUT_FILENO, buf, rc)) {
-                    aerr << "Failed to write while dumping service " << service_name << ": "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                }
-            }
-
-            if (timed_out) {
+            if (status == TIMED_OUT) {
                 aout << endl
-                     << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArgMs
+                     << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
                      << "ms) EXPIRED ***" << endl
                      << endl;
             }
 
-            if (timed_out || error) {
-                dump_thread.detach();
-            } else {
-                dump_thread.join();
+            if (addSeparator) {
+                writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
             }
-
-            if (N > 1) {
-              std::chrono::duration<double> elapsed_seconds =
-                  std::chrono::steady_clock::now() - start;
-              aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
-                   << "was the duration of dumpsys " << service_name;
-
-              using std::chrono::system_clock;
-              const auto finish = system_clock::to_time_t(system_clock::now());
-              std::tm finish_tm;
-              localtime_r(&finish, &finish_tm);
-              aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S")
-                   << endl;
-            }
-        } else {
-            aerr << "Can't find service: " << service_name << endl;
+            bool dumpComplete = (status == OK);
+            stopDumpThread(dumpComplete);
         }
     }
 
     return 0;
 }
+
+Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const {
+    Vector<String16> services = sm_->listServices(priorityFilterFlags);
+    services.sort(sort_func);
+    if (filterByProto) {
+        Vector<String16> protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
+        protoServices.sort(sort_func);
+        Vector<String16> intersection;
+        std::set_intersection(services.begin(), services.end(), protoServices.begin(),
+                              protoServices.end(), std::back_inserter(intersection));
+        services = std::move(intersection);
+    }
+    return services;
+}
+
+void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const {
+    if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) ||
+        (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) {
+        args.add(String16("-a"));
+    }
+    if (asProto) {
+        args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
+    }
+    if (priorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
+        String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+        args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
+        args.insertAt(priorityType, 1);
+    }
+}
+
+status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<String16>& args) {
+    sp<IBinder> service = sm_->checkService(serviceName);
+    if (service == nullptr) {
+        aerr << "Can't find service: " << serviceName << endl;
+        return NAME_NOT_FOUND;
+    }
+
+    int sfd[2];
+    if (pipe(sfd) != 0) {
+        aerr << "Failed to create pipe to dump service info for " << serviceName << ": "
+             << strerror(errno) << endl;
+        return -errno;
+    }
+
+    redirectFd_ = unique_fd(sfd[0]);
+    unique_fd remote_end(sfd[1]);
+    sfd[0] = sfd[1] = -1;
+
+    // dump blocks until completion, so spawn a thread..
+    activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
+        int err = service->dump(remote_end.get(), args);
+
+        // It'd be nice to be able to close the remote end of the socketpair before the dump
+        // call returns, to terminate our reads if the other end closes their copy of the
+        // file descriptor, but then hangs for some reason. There doesn't seem to be a good
+        // way to do this, though.
+        remote_end.reset();
+
+        if (err != 0) {
+            aerr << "Error dumping service info: (" << strerror(err) << ") "
+                 << serviceName << endl;
+        }
+    });
+    return OK;
+}
+
+void Dumpsys::stopDumpThread(bool dumpComplete) {
+    if (dumpComplete) {
+        activeThread_.join();
+    } else {
+        activeThread_.detach();
+    }
+    /* close read end of the dump output redirection pipe */
+    redirectFd_.reset();
+}
+
+void Dumpsys::writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const {
+    std::string msg(
+        "----------------------------------------"
+        "---------------------------------------\n");
+    if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL ||
+        priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+        StringAppendF(&msg, "DUMP OF SERVICE %s:\n", String8(serviceName).c_str());
+    } else {
+        String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+        StringAppendF(&msg, "DUMP OF SERVICE %s %s:\n", String8(priorityType).c_str(),
+                      String8(serviceName).c_str());
+    }
+    WriteStringToFd(msg, fd);
+}
+
+status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
+                            bool asProto, std::chrono::duration<double>& elapsedDuration,
+                            size_t& bytesWritten) const {
+    status_t status = OK;
+    size_t totalBytes = 0;
+    auto start = std::chrono::steady_clock::now();
+    auto end = start + timeout;
+
+    int serviceDumpFd = redirectFd_.get();
+    if (serviceDumpFd == -1) {
+        return INVALID_OPERATION;
+    }
+
+    struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN};
+
+    while (true) {
+        // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates 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) {
+            aerr << "Error in poll while dumping service " << serviceName << " : "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        } else if (rc == 0) {
+            status = TIMED_OUT;
+            break;
+        }
+
+        char buf[4096];
+        rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
+        if (rc < 0) {
+            aerr << "Failed to read while dumping service " << serviceName << ": "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        } else if (rc == 0) {
+            // EOF.
+            break;
+        }
+
+        if (!WriteFully(fd, buf, rc)) {
+            aerr << "Failed to write while dumping service " << serviceName << ": "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        }
+        totalBytes += rc;
+    }
+
+    if ((status == TIMED_OUT) && (!asProto)) {
+        std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n",
+                                       String8(serviceName).string(), timeout.count());
+        WriteStringToFd(msg, fd);
+    }
+
+    elapsedDuration = std::chrono::steady_clock::now() - start;
+    bytesWritten = totalBytes;
+    return status;
+}
+
+void Dumpsys::writeDumpFooter(int fd, const String16& serviceName,
+                              const std::chrono::duration<double>& elapsedDuration) const {
+    using std::chrono::system_clock;
+    const auto finish = system_clock::to_time_t(system_clock::now());
+    std::tm finish_tm;
+    localtime_r(&finish, &finish_tm);
+    std::stringstream oss;
+    oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S");
+    std::string msg =
+        StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n",
+                     elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str());
+    WriteStringToFd(msg, fd);
+}
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 2534dde..1d78aa4 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -17,6 +17,9 @@
 #ifndef FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
 #define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
 
+#include <thread>
+
+#include <android-base/unique_fd.h>
 #include <binder/IServiceManager.h>
 
 namespace android {
@@ -25,10 +28,97 @@
   public:
     Dumpsys(android::IServiceManager* sm) : sm_(sm) {
     }
+    /**
+     * Main entry point into dumpsys.
+     */
     int main(int argc, char* const argv[]);
 
+    /**
+     * Returns a list of services.
+     * @param priorityFlags filter services by specified priorities
+     * @param supportsProto filter services that support proto dumps
+     * @return list of services
+     */
+    Vector<String16> listServices(int priorityFlags, bool supportsProto) const;
+
+    /**
+     * Modifies @{code args} to add additional arguments  to indicate if the service
+     * must dump as proto or dump to a certian priority bucket.
+     * @param args initial list of arguments to pass to service dump method.
+     * @param asProto dump service as proto by passing an additional --proto arg
+     * @param priorityFlags indicates priority of dump by passing additional priority args
+     * to the service
+     */
+    void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const;
+
+    /**
+     * Starts a thread to connect to a service and get its dump output. The thread redirects
+     * the output to a pipe. Thread must be stopped by a subsequent callto {@code
+     * stopDumpThread}.
+     * @param serviceName
+     * @param args list of arguments to pass to service dump method.
+     * @return {@code OK} thread is started successfully.
+     *         {@code NAME_NOT_FOUND} service could not be found.
+     *         {@code != OK} error
+     */
+    status_t startDumpThread(const String16& serviceName, const Vector<String16>& args);
+
+    /**
+     * Writes a section header to a file descriptor.
+     * @param fd file descriptor to write data
+     * @param serviceName
+     * @param priorityFlags dump priority specified
+     */
+    void writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const;
+
+    /**
+     * Redirects service dump to a file descriptor. This requires
+     * {@code startDumpThread} to be called successfully otherwise the function will
+     * return {@code INVALID_OPERATION}.
+     * @param fd file descriptor to write data
+     * @param serviceName
+     * @param timeout timeout to terminate the dump if not completed
+     * @param asProto used to supresses additional output to the fd such as timeout
+     * error messages
+     * @param elapsedDuration returns elapsed time in seconds
+     * @param bytesWritten returns number of bytes written
+     * @return {@code OK} if successful
+     *         {@code TIMED_OUT} dump timed out
+     *         {@code INVALID_OPERATION} invalid state
+     *         {@code != OK} error
+     */
+    status_t writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
+                       bool asProto, std::chrono::duration<double>& elapsedDuration,
+                       size_t& bytesWritten) const;
+
+    /**
+     * Writes a section footer to a file descriptor with duration info.
+     * @param fd file descriptor to write data
+     * @param serviceName
+     * @param elapsedDuration duration of dump
+     */
+    void writeDumpFooter(int fd, const String16& serviceName,
+                         const std::chrono::duration<double>& elapsedDuration) const;
+
+    /**
+     * Terminates dump thread.
+     * @param dumpComplete If {@code true}, indicates the dump was successfully completed and
+     * tries to join the thread. Otherwise thread is detached.
+     */
+    void stopDumpThread(bool dumpComplete);
+
+    /**
+     * Returns file descriptor of the pipe used to dump service data. This assumes
+     * {@code startDumpThread} was called successfully.
+     */
+    int getDumpFd() const {
+        return redirectFd_.get();
+    }
+
   private:
     android::IServiceManager* sm_;
+    std::thread activeThread_;
+    mutable android::base::unique_fd redirectFd_;
 };
 }
 
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index bdb0a9a..b13f59d 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -188,6 +188,22 @@
         EXPECT_THAT(status, Eq(0));
     }
 
+    void CallSingleService(const String16& serviceName, Vector<String16>& args, int priorityFlags,
+                           bool supportsProto, std::chrono::duration<double>& elapsedDuration,
+                           size_t& bytesWritten) {
+        CaptureStdout();
+        CaptureStderr();
+        dump_.setServiceArgs(args, supportsProto, priorityFlags);
+        status_t status = dump_.startDumpThread(serviceName, args);
+        EXPECT_THAT(status, Eq(0));
+        status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false,
+                                 elapsedDuration, bytesWritten);
+        EXPECT_THAT(status, Eq(0));
+        dump_.stopDumpThread(/* dumpCompleted = */ true);
+        stdout_ = GetCapturedStdout();
+        stderr_ = GetCapturedStderr();
+    }
+
     void AssertRunningServices(const std::vector<std::string>& services) {
         std::string expected;
         if (services.size() > 1) {
@@ -209,6 +225,7 @@
 
     void AssertDumped(const std::string& service, const std::string& dump) {
         EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
+        EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: "));
     }
 
     void AssertDumpedWithPriority(const std::string& service, const std::string& dump,
@@ -216,6 +233,7 @@
         std::string priority = String8(priorityType).c_str();
         EXPECT_THAT(stdout_,
                     HasSubstr("DUMP OF SERVICE " + priority + " " + service + ":\n" + dump));
+        EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: "));
     }
 
     void AssertNotDumped(const std::string& dump) {
@@ -425,8 +443,8 @@
     CallMain({"--priority", "NORMAL"});
 
     AssertRunningServices({"runningnormal1", "runningnormal2"});
-    AssertDumpedWithPriority("runningnormal1", "dump1", PriorityDumper::PRIORITY_ARG_NORMAL);
-    AssertDumpedWithPriority("runningnormal2", "dump2", PriorityDumper::PRIORITY_ARG_NORMAL);
+    AssertDumped("runningnormal1", "dump1");
+    AssertDumped("runningnormal2", "dump2");
 }
 
 // Tests 'dumpsys --proto'
@@ -461,3 +479,29 @@
     AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH);
     AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH);
 }
+
+TEST_F(DumpsysTest, GetBytesWritten) {
+    const char* serviceName = "service2";
+    const char* dumpContents = "dump1";
+    ExpectDump(serviceName, dumpContents);
+
+    String16 service(serviceName);
+    Vector<String16> args;
+    std::chrono::duration<double> elapsedDuration;
+    size_t bytesWritten;
+
+    CallSingleService(service, args, IServiceManager::DUMP_FLAG_PRIORITY_ALL,
+                      /* as_proto = */ false, elapsedDuration, bytesWritten);
+
+    AssertOutput(dumpContents);
+    EXPECT_THAT(bytesWritten, Eq(strlen(dumpContents)));
+}
+
+TEST_F(DumpsysTest, WriteDumpWithoutThreadStart) {
+    std::chrono::duration<double> elapsedDuration;
+    size_t bytesWritten;
+    status_t status =
+        dump_.writeDump(STDOUT_FILENO, String16("service"), std::chrono::milliseconds(500),
+                        /* as_proto = */ false, elapsedDuration, bytesWritten);
+    EXPECT_THAT(status, Eq(INVALID_OPERATION));
+}
\ No newline at end of file
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 1053522..6f8c841 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -84,6 +84,8 @@
 static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
 static constexpr const char* IDMAP_SUFFIX = "@idmap";
 
+static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
+
 // NOTE: keep in sync with Installer
 static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
 static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
@@ -2356,6 +2358,17 @@
     return res ? ok() : error();
 }
 
+binder::Status InstalldNativeService::installApkVerity(const std::string& /*filePath*/,
+        const ::android::base::unique_fd& /*verityInput*/) {
+    ENFORCE_UID(AID_SYSTEM);
+    if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
+        return ok();
+    }
+    // TODO: Append verity to filePath then issue ioctl to enable
+    // it and hide the tree.  See b/30972906.
+    return error("not implemented yet");
+}
+
 binder::Status InstalldNativeService::reconcileSecondaryDexFile(
         const std::string& dexPath, const std::string& packageName, int32_t uid,
         const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 57dd834..b494c7a 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -117,6 +117,8 @@
             const std::string& outputPath);
     binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
             const std::unique_ptr<std::string>& outputPath);
+    binder::Status installApkVerity(const std::string& filePath,
+            const ::android::base::unique_fd& verityInput);
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
         const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
         const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 394c028..89585a0 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -81,6 +81,7 @@
             @utf8InCpp String outputPath);
     void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
             @nullable @utf8InCpp String outputPath);
+    void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput);
 
     boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
         int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
diff --git a/data/etc/android.hardware.telephony.mbms.xml b/data/etc/android.hardware.telephony.mbms.xml
new file mode 100644
index 0000000..271ea58
--- /dev/null
+++ b/data/etc/android.hardware.telephony.mbms.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<!-- Feature for devices that support MBMS. -->
+<permissions>
+    <feature name="android.hardware.telephony.mbms" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index b6abc1b..d790bed 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -46,7 +46,7 @@
     <feature name="android.software.home_screen" />
     <feature name="android.software.input_methods" />
     <feature name="android.software.picture_in_picture" notLowRam="true" />
-    <feature name="android.software.activities_on_secondary_displays" />
+    <feature name="android.software.activities_on_secondary_displays" notLowRam="true" />
     <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index e54f147..57eee12 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 = "lsq2";
+const char* VelocityTracker::DEFAULT_STRATEGY = "impulse";
 
 VelocityTracker::VelocityTracker(const char* strategy) :
         mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
index 80b1769..2cc9664 100644
--- a/libs/vr/libpdx/private/pdx/rpc/variant.h
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -450,12 +450,19 @@
   Variant(Variant&& other)
       : index_{other.index_}, value_{std::move(other.value_), other.index_} {}
 
+// Recent Clang versions has a regression that produces bogus
+// unused-lambda-capture warning. Suppress the warning as a temporary
+// workaround. http://b/71356631
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-lambda-capture"
   // Copy and move construction from Variant types. Each element of OtherTypes
   // must be convertible to an element of Types.
   template <typename... OtherTypes>
   explicit Variant(const Variant<OtherTypes...>& other) {
     other.Visit([this](const auto& value) { Construct(value); });
   }
+#pragma clang diagnostic pop
+
   template <typename... OtherTypes>
   explicit Variant(Variant<OtherTypes...>&& other) {
     other.Visit([this](auto&& value) { Construct(std::move(value)); });
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 9822849..91a3455 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -407,9 +407,8 @@
 
             DIR* d = opendir(search);
             if (d != NULL) {
-                struct dirent cur;
                 struct dirent* e;
-                while (readdir_r(d, &cur, &e) == 0 && e) {
+                while ((e = readdir(d)) != NULL) {
                     if (e->d_type == DT_DIR) {
                         continue;
                     }
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9772f9a..a663487 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -127,20 +127,6 @@
     mPowerMode = (mType >= DisplayDevice::DISPLAY_VIRTUAL) ?
                   HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
 
-    // Name the display.  The name will be replaced shortly if the display
-    // was created with createDisplay().
-    switch (mType) {
-        case DISPLAY_PRIMARY:
-            mDisplayName = "Built-in Screen";
-            break;
-        case DISPLAY_EXTERNAL:
-            mDisplayName = "HDMI Screen";
-            break;
-        default:
-            mDisplayName = "Virtual Screen";    // e.g. Overlay #n
-            break;
-    }
-
     // initialize the display orientation transform.
     setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 8b5d4c9..4faba3b 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -76,6 +76,10 @@
             SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
 }
 
+void FramebufferSurface::resizeBuffers(const uint32_t width, const uint32_t height) {
+    mConsumer->setDefaultBufferSize(width, height);
+}
+
 status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index eaa5455..ed756c4 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -46,9 +46,7 @@
     virtual void onFrameCommitted();
     virtual void dumpAsString(String8& result) const;
 
-    // Cannot resize a buffers in a FramebufferSurface. Only works with virtual
-    // displays.
-    virtual void resizeBuffers(const uint32_t /*w*/, const uint32_t /*h*/) { };
+    virtual void resizeBuffers(const uint32_t width, const uint32_t height);
 
     virtual const sp<Fence>& getClientTargetAcquireFence() const override;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index affe505..070b691 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -52,24 +52,13 @@
 class ComposerCallbackBridge : public Hwc2::IComposerCallback {
 public:
     ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
-            : mCallback(callback), mSequenceId(sequenceId),
-              mHasPrimaryDisplay(false) {}
+            : mCallback(callback), mSequenceId(sequenceId) {}
 
     Return<void> onHotplug(Hwc2::Display display,
                            IComposerCallback::Connection conn) override
     {
         HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
-        if (!mHasPrimaryDisplay) {
-            LOG_ALWAYS_FATAL_IF(connection != HWC2::Connection::Connected,
-                    "Initial onHotplug callback should be "
-                    "primary display connected");
-            mHasPrimaryDisplay = true;
-            mCallback->onHotplugReceived(mSequenceId, display,
-                                         connection, true);
-        } else {
-            mCallback->onHotplugReceived(mSequenceId, display,
-                                         connection, false);
-        }
+        mCallback->onHotplugReceived(mSequenceId, display, connection);
         return Void();
     }
 
@@ -85,12 +74,9 @@
         return Void();
     }
 
-    bool HasPrimaryDisplay() { return mHasPrimaryDisplay; }
-
 private:
     ComposerCallback* mCallback;
     int32_t mSequenceId;
-    bool mHasPrimaryDisplay;
 };
 
 } // namespace anonymous
@@ -117,8 +103,6 @@
     sp<ComposerCallbackBridge> callbackBridge(
             new ComposerCallbackBridge(callback, sequenceId));
     mComposer->registerCallback(callbackBridge);
-    LOG_ALWAYS_FATAL_IF(!callbackBridge->HasPrimaryDisplay(),
-            "Registered composer callback but didn't get primary display");
 }
 
 // Required by HWC2 device
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index a15c6d9..7b98b3e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -65,8 +65,7 @@
 class ComposerCallback {
  public:
     virtual void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
-                                   Connection connection,
-                                   bool primaryDisplay) = 0;
+                                   Connection connection) = 0;
     virtual void onRefreshReceived(int32_t sequenceId,
                                    hwc2_display_t display) = 0;
     virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index c0f8f96..1677b07 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -119,23 +119,22 @@
     }
 }
 
-void HWComposer::onHotplug(hwc2_display_t displayId,
+void HWComposer::onHotplug(hwc2_display_t displayId, int32_t displayType,
                            HWC2::Connection connection) {
-    ALOGV("hotplug: %" PRIu64 ", %s", displayId,
+    if (displayType >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
+        ALOGE("Invalid display type of %d", displayType);
+        return;
+    }
+
+    ALOGV("hotplug: %" PRIu64 ", %s %s", displayId,
+            displayType == DisplayDevice::DISPLAY_PRIMARY ? "primary" : "external",
             to_string(connection).c_str());
     mHwcDevice->onHotplug(displayId, connection);
-    if (!mDisplayData[0].hwcDisplay) {
-        ALOGE_IF(connection != HWC2::Connection::Connected, "Assumed primary"
-                " display would be connected");
-        mDisplayData[0].hwcDisplay = mHwcDevice->getDisplayById(displayId);
-        mHwcDisplaySlots[displayId] = 0;
-    } else {
-        // Disconnect is handled through HWComposer::disconnectDisplay via
-        // SurfaceFlinger's onHotplugReceived callback handling
-        if (connection == HWC2::Connection::Connected) {
-            mDisplayData[1].hwcDisplay = mHwcDevice->getDisplayById(displayId);
-            mHwcDisplaySlots[displayId] = 1;
-        }
+    // Disconnect is handled through HWComposer::disconnectDisplay via
+    // SurfaceFlinger's onHotplugReceived callback handling
+    if (connection == HWC2::Connection::Connected) {
+        mDisplayData[displayType].hwcDisplay = mHwcDevice->getDisplayById(displayId);
+        mHwcDisplaySlots[displayId] = displayType;
     }
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 74b3a38..ee0a725 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -136,7 +136,7 @@
     // DisplayDevice::DisplayType of the display is returned as an output param.
     bool onVsync(hwc2_display_t displayId, int64_t timestamp,
                  int32_t* outDisplay);
-    void onHotplug(hwc2_display_t displayId, HWC2::Connection connection);
+    void onHotplug(hwc2_display_t displayId, int32_t displayType, HWC2::Connection connection);
 
     void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e967265..f21a691 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -136,6 +136,7 @@
 bool SurfaceFlinger::hasSyncFramework;
 bool SurfaceFlinger::useVrFlinger;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+// TODO(courtneygo): Rename hasWideColorDisplay to clarify its actual meaning.
 bool SurfaceFlinger::hasWideColorDisplay;
 
 
@@ -368,17 +369,6 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) {
-    ALOGV("createBuiltinDisplayLocked(%d)", type);
-    ALOGW_IF(mBuiltinDisplays[type],
-            "Overwriting display token for display type %d", type);
-    mBuiltinDisplays[type] = new BBinder();
-    // All non-virtual displays are currently considered secure.
-    DisplayDeviceState info(type, true);
-    mCurrentState.displays.add(mBuiltinDisplays[type], info);
-    mInterceptor.saveDisplayCreation(info);
-}
-
 sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
     if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
         ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id);
@@ -618,6 +608,14 @@
             "Starting with vr flinger active is not currently supported.");
     getBE().mHwc.reset(new HWComposer(getBE().mHwcServiceName));
     getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId);
+    // Process any initial hotplug and resulting display changes.
+    processDisplayHotplugEventsLocked();
+    LOG_ALWAYS_FATAL_IF(!getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY),
+            "Registered composer callback but didn't create the default primary display");
+
+    // make the default display GLContext current so that we can create textures
+    // when creating Layers (which may happens before we render something)
+    getDefaultDisplayDeviceLocked()->makeCurrent();
 
     if (useVrFlinger) {
         auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) {
@@ -801,7 +799,7 @@
 
             // TODO: this needs to go away (currently needed only by webkit)
             sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
-            info.orientation = hw->getOrientation();
+            info.orientation = hw ? hw->getOrientation() : 0;
         } else {
             // TODO: where should this value come from?
             static const int TV_DENSITY = 213;
@@ -1266,94 +1264,25 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
-void SurfaceFlinger::createDefaultDisplayDevice() {
-    const DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_PRIMARY;
-    wp<IBinder> token = mBuiltinDisplays[type];
+void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
+                                       HWC2::Connection connection) {
+    ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s)", sequenceId, display,
+          connection == HWC2::Connection::Connected ? "connected" : "disconnected");
 
-    // All non-virtual displays are currently considered secure.
-    const bool isSecure = true;
-
-    sp<IGraphicBufferProducer> producer;
-    sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer);
-
-    sp<FramebufferSurface> fbs = new FramebufferSurface(*getBE().mHwc, type, consumer);
-
-    bool hasWideColorModes = false;
-    std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(type);
-    for (android_color_mode_t colorMode : modes) {
-        switch (colorMode) {
-            case HAL_COLOR_MODE_DISPLAY_P3:
-            case HAL_COLOR_MODE_ADOBE_RGB:
-            case HAL_COLOR_MODE_DCI_P3:
-                hasWideColorModes = true;
-                break;
-            default:
-                break;
-        }
+    // Ignore events that do not have the right sequenceId.
+    if (sequenceId != getBE().mComposerSequenceId) {
+        return;
     }
-    bool useWideColorMode = hasWideColorModes && hasWideColorDisplay && !mForceNativeColorMode;
-    sp<DisplayDevice> hw = new DisplayDevice(this, DisplayDevice::DISPLAY_PRIMARY, type, isSecure,
-                                             token, fbs, producer, useWideColorMode);
-    mDisplays.add(token, hw);
-    android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
-    if (useWideColorMode) {
-        defaultColorMode = HAL_COLOR_MODE_SRGB;
-    }
-    setActiveColorModeInternal(hw, defaultColorMode);
-    hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN);
-
-    // Add the primary display token to mDrawingState so we don't try to
-    // recreate the DisplayDevice for the primary display.
-    mDrawingState.displays.add(token, DisplayDeviceState(type, true));
-
-    // make the GLContext current so that we can create textures when creating
-    // Layers (which may happens before we render something)
-    hw->makeCurrent();
-}
-
-void SurfaceFlinger::onHotplugReceived(int32_t sequenceId,
-        hwc2_display_t display, HWC2::Connection connection,
-        bool primaryDisplay) {
-    ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s, %s)",
-          sequenceId, display,
-          connection == HWC2::Connection::Connected ?
-                  "connected" : "disconnected",
-          primaryDisplay ? "primary" : "external");
 
     // Only lock if we're not on the main thread. This function is normally
     // called on a hwbinder thread, but for the primary display it's called on
     // the main thread with the state lock already held, so don't attempt to
     // acquire it here.
-    ConditionalLock lock(mStateLock,
-            std::this_thread::get_id() != mMainThreadId);
+    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
 
-    if (primaryDisplay) {
-        getBE().mHwc->onHotplug(display, connection);
-        if (!mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY].get()) {
-            createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY);
-        }
-        createDefaultDisplayDevice();
-    } else {
-        if (sequenceId != getBE().mComposerSequenceId) {
-            return;
-        }
-        if (getBE().mHwc->isUsingVrComposer()) {
-            ALOGE("External displays are not supported by the vr hardware composer.");
-            return;
-        }
-        getBE().mHwc->onHotplug(display, connection);
-        auto type = DisplayDevice::DISPLAY_EXTERNAL;
-        if (connection == HWC2::Connection::Connected) {
-            createBuiltinDisplayLocked(type);
-        } else {
-            mCurrentState.displays.removeItem(mBuiltinDisplays[type]);
-            mBuiltinDisplays[type].clear();
-        }
-        setTransactionFlags(eDisplayTransactionNeeded);
+    mPendingHotplugEvents.emplace_back(HotplugEvent{display, connection});
 
-        // Defer EventThread notification until SF has updated mDisplays.
-    }
+    setTransactionFlags(eDisplayTransactionNeeded);
 }
 
 void SurfaceFlinger::onRefreshReceived(int sequenceId,
@@ -1668,7 +1597,7 @@
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    if (getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
+    if (hw && getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
         glCompositionDoneFenceTime =
                 std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
         getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
@@ -1713,7 +1642,7 @@
     }
 
     if (!hasSyncFramework) {
-        if (hw->isDisplayOn()) {
+        if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) && hw->isDisplayOn()) {
             enableHardwareVsync();
         }
     }
@@ -1724,7 +1653,7 @@
         if (presentFenceTime->isValid()) {
             mAnimFrameTracker.setActualPresentFence(
                     std::move(presentFenceTime));
-        } else {
+        } else if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
             nsecs_t presentTime =
@@ -1734,7 +1663,8 @@
         mAnimFrameTracker.advanceFrame();
     }
 
-    if (hw->getPowerMode() == HWC_POWER_MODE_OFF) {
+    if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) &&
+            hw->getPowerMode() == HWC_POWER_MODE_OFF) {
         return;
     }
 
@@ -2077,9 +2007,11 @@
     mDebugInSwapBuffers = 0;
 
     // |mStateLock| not needed as we are on the main thread
-    uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
-    if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
-        logFrameStats();
+    if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
+        uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
+        if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
+            logFrameStats();
+        }
     }
 }
 
@@ -2112,6 +2044,245 @@
     // here the transaction has been committed
 }
 
+DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t display,
+        HWC2::Connection connection) const {
+    // Figure out whether the event is for the primary display or an
+    // external display by matching the Hwc display id against one for a
+    // connected display. If we did not find a match, we then check what
+    // displays are not already connected to determine the type. If we don't
+    // have a connected primary display, we assume the new display is meant to
+    // be the primary display, and then if we don't have an external display,
+    // we assume it is that.
+    const auto primaryDisplayId =
+            getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_PRIMARY);
+    const auto externalDisplayId =
+            getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_EXTERNAL);
+    if (primaryDisplayId && primaryDisplayId == display) {
+        return DisplayDevice::DISPLAY_PRIMARY;
+    } else if (externalDisplayId && externalDisplayId == display) {
+        return  DisplayDevice::DISPLAY_EXTERNAL;
+    } else if (connection == HWC2::Connection::Connected && !primaryDisplayId) {
+        return DisplayDevice::DISPLAY_PRIMARY;
+    } else if (connection == HWC2::Connection::Connected && !externalDisplayId) {
+        return DisplayDevice::DISPLAY_EXTERNAL;
+    }
+
+    return DisplayDevice::DISPLAY_ID_INVALID;
+}
+
+void SurfaceFlinger::processDisplayHotplugEventsLocked() {
+    for (const auto& event : mPendingHotplugEvents) {
+        auto displayType = determineDisplayType(event.display, event.connection);
+        if (displayType == DisplayDevice::DISPLAY_ID_INVALID) {
+            ALOGW("Unable to determine the display type for display %" PRIu64, event.display);
+            continue;
+        }
+
+        if (getBE().mHwc->isUsingVrComposer() && displayType == DisplayDevice::DISPLAY_EXTERNAL) {
+            ALOGE("External displays are not supported by the vr hardware composer.");
+            continue;
+        }
+
+        getBE().mHwc->onHotplug(event.display, displayType, event.connection);
+
+        if (event.connection == HWC2::Connection::Connected) {
+            ALOGV("Creating built in display %d", displayType);
+            ALOGW_IF(mBuiltinDisplays[displayType],
+                    "Overwriting display token for display type %d", displayType);
+            mBuiltinDisplays[displayType] = new BBinder();
+            // All non-virtual displays are currently considered secure.
+            DisplayDeviceState info(displayType, true);
+            info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ?
+                    "Built-in Screen" : "External Screen";
+            mCurrentState.displays.add(mBuiltinDisplays[displayType], info);
+            mInterceptor.saveDisplayCreation(info);
+        } else {
+            ALOGV("Removing built in display %d", displayType);
+
+            ssize_t idx = mCurrentState.displays.indexOfKey(mBuiltinDisplays[displayType]);
+            if (idx >= 0) {
+                const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
+                mInterceptor.saveDisplayDeletion(info.displayId);
+                mCurrentState.displays.removeItemsAt(idx);
+            }
+            mBuiltinDisplays[displayType].clear();
+        }
+
+        processDisplayChangesLocked();
+    }
+
+    mPendingHotplugEvents.clear();
+}
+
+void SurfaceFlinger::processDisplayChangesLocked() {
+    // here we take advantage of Vector's copy-on-write semantics to
+    // improve performance by skipping the transaction entirely when
+    // know that the lists are identical
+    const KeyedVector<wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
+    const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
+    if (!curr.isIdenticalTo(draw)) {
+        mVisibleRegionsDirty = true;
+        const size_t cc = curr.size();
+        size_t dc = draw.size();
+
+        // find the displays that were removed
+        // (ie: in drawing state but not in current state)
+        // also handle displays that changed
+        // (ie: displays that are in both lists)
+        for (size_t i = 0; i < dc;) {
+            const ssize_t j = curr.indexOfKey(draw.keyAt(i));
+            if (j < 0) {
+                // in drawing state but not in current state
+                // Call makeCurrent() on the primary display so we can
+                // be sure that nothing associated with this display
+                // is current.
+                const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
+                if (defaultDisplay != nullptr) defaultDisplay->makeCurrent();
+                sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
+                if (hw != nullptr) hw->disconnect(getHwComposer());
+                if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
+                    mEventThread->onHotplugReceived(draw[i].type, false);
+                mDisplays.removeItem(draw.keyAt(i));
+            } else {
+                // this display is in both lists. see if something changed.
+                const DisplayDeviceState& state(curr[j]);
+                const wp<IBinder>& display(curr.keyAt(j));
+                const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
+                const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
+                if (state_binder != draw_binder) {
+                    // changing the surface is like destroying and
+                    // recreating the DisplayDevice, so we just remove it
+                    // from the drawing state, so that it get re-added
+                    // below.
+                    sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
+                    if (hw != nullptr) hw->disconnect(getHwComposer());
+                    mDisplays.removeItem(display);
+                    mDrawingState.displays.removeItemsAt(i);
+                    dc--;
+                    // at this point we must loop to the next item
+                    continue;
+                }
+
+                const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
+                if (disp != nullptr) {
+                    if (state.layerStack != draw[i].layerStack) {
+                        disp->setLayerStack(state.layerStack);
+                    }
+                    if ((state.orientation != draw[i].orientation) ||
+                        (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
+                        disp->setProjection(state.orientation, state.viewport, state.frame);
+                    }
+                    if (state.width != draw[i].width || state.height != draw[i].height) {
+                        disp->setDisplaySize(state.width, state.height);
+                    }
+                }
+            }
+            ++i;
+        }
+
+        // find displays that were added
+        // (ie: in current state but not in drawing state)
+        for (size_t i = 0; i < cc; i++) {
+            if (draw.indexOfKey(curr.keyAt(i)) < 0) {
+                const DisplayDeviceState& state(curr[i]);
+
+                sp<DisplaySurface> dispSurface;
+                sp<IGraphicBufferProducer> producer;
+                sp<IGraphicBufferProducer> bqProducer;
+                sp<IGraphicBufferConsumer> bqConsumer;
+                BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
+
+                int32_t hwcId = -1;
+                if (state.isVirtualDisplay()) {
+                    // Virtual displays without a surface are dormant:
+                    // they have external state (layer stack, projection,
+                    // etc.) but no internal state (i.e. a DisplayDevice).
+                    if (state.surface != nullptr) {
+                        // Allow VR composer to use virtual displays.
+                        if (mUseHwcVirtualDisplays || getBE().mHwc->isUsingVrComposer()) {
+                            int width = 0;
+                            int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
+                            ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
+                            int height = 0;
+                            status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
+                            ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
+                            int intFormat = 0;
+                            status = state.surface->query(NATIVE_WINDOW_FORMAT, &intFormat);
+                            ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
+                            auto format = static_cast<android_pixel_format_t>(intFormat);
+
+                            getBE().mHwc->allocateVirtualDisplay(width, height, &format, &hwcId);
+                        }
+
+                        // TODO: Plumb requested format back up to consumer
+
+                        sp<VirtualDisplaySurface> vds =
+                                new VirtualDisplaySurface(*getBE().mHwc, hwcId, state.surface,
+                                                          bqProducer, bqConsumer,
+                                                          state.displayName);
+
+                        dispSurface = vds;
+                        producer = vds;
+                    }
+                } else {
+                    ALOGE_IF(state.surface != nullptr,
+                             "adding a supported display, but rendering "
+                             "surface is provided (%p), ignoring it",
+                             state.surface.get());
+
+                    hwcId = state.type;
+                    dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer);
+                    producer = bqProducer;
+                }
+
+                const wp<IBinder>& display(curr.keyAt(i));
+
+                if (dispSurface != nullptr) {
+                    bool useWideColorMode = hasWideColorDisplay;
+                    if (!mForceNativeColorMode) {
+                        bool hasWideColorModes = false;
+                        std::vector<android_color_mode_t> modes =
+                                getHwComposer().getColorModes(state.type);
+                        for (android_color_mode_t colorMode : modes) {
+                            switch (colorMode) {
+                                case HAL_COLOR_MODE_DISPLAY_P3:
+                                case HAL_COLOR_MODE_ADOBE_RGB:
+                                case HAL_COLOR_MODE_DCI_P3:
+                                    hasWideColorModes = true;
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                        useWideColorMode = hasWideColorModes && hasWideColorDisplay;
+                    }
+
+                    sp<DisplayDevice> hw =
+                            new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
+                                              dispSurface, producer, useWideColorMode);
+
+                    android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
+                    if (useWideColorMode) {
+                        defaultColorMode = HAL_COLOR_MODE_SRGB;
+                    }
+                    setActiveColorModeInternal(hw, defaultColorMode);
+                    hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN);
+                    hw->setLayerStack(state.layerStack);
+                    hw->setProjection(state.orientation, state.viewport, state.frame);
+                    hw->setDisplayName(state.displayName);
+
+                    mDisplays.add(display, hw);
+                    if (!state.isVirtualDisplay()) {
+                        mEventThread->onHotplugReceived(state.type, true);
+                    }
+                }
+            }
+        }
+    }
+
+    mDrawingState.displays = mCurrentState.displays;
+}
+
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
 {
     // Notify all layers of available frames
@@ -2140,161 +2311,8 @@
      */
 
     if (transactionFlags & eDisplayTransactionNeeded) {
-        // here we take advantage of Vector's copy-on-write semantics to
-        // improve performance by skipping the transaction entirely when
-        // know that the lists are identical
-        const KeyedVector<  wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
-        const KeyedVector<  wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
-        if (!curr.isIdenticalTo(draw)) {
-            mVisibleRegionsDirty = true;
-            const size_t cc = curr.size();
-                  size_t dc = draw.size();
-
-            // find the displays that were removed
-            // (ie: in drawing state but not in current state)
-            // also handle displays that changed
-            // (ie: displays that are in both lists)
-            for (size_t i=0 ; i<dc ;) {
-                const ssize_t j = curr.indexOfKey(draw.keyAt(i));
-                if (j < 0) {
-                    // in drawing state but not in current state
-                    if (!draw[i].isMainDisplay()) {
-                        // Call makeCurrent() on the primary display so we can
-                        // be sure that nothing associated with this display
-                        // is current.
-                        const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
-                        defaultDisplay->makeCurrent();
-                        sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
-                        if (hw != nullptr)
-                            hw->disconnect(getHwComposer());
-                        if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
-                            mEventThread->onHotplugReceived(draw[i].type, false);
-                        mDisplays.removeItem(draw.keyAt(i));
-                    } else {
-                        ALOGW("trying to remove the main display");
-                    }
-                } else {
-                    // this display is in both lists. see if something changed.
-                    const DisplayDeviceState& state(curr[j]);
-                    const wp<IBinder>& display(curr.keyAt(j));
-                    const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
-                    const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
-                    if (state_binder != draw_binder) {
-                        // changing the surface is like destroying and
-                        // recreating the DisplayDevice, so we just remove it
-                        // from the drawing state, so that it get re-added
-                        // below.
-                        sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
-                        if (hw != nullptr)
-                            hw->disconnect(getHwComposer());
-                        mDisplays.removeItem(display);
-                        mDrawingState.displays.removeItemsAt(i);
-                        dc--;
-                        // at this point we must loop to the next item
-                        continue;
-                    }
-
-                    const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
-                    if (disp != nullptr) {
-                        if (state.layerStack != draw[i].layerStack) {
-                            disp->setLayerStack(state.layerStack);
-                        }
-                        if ((state.orientation != draw[i].orientation)
-                                || (state.viewport != draw[i].viewport)
-                                || (state.frame != draw[i].frame))
-                        {
-                            disp->setProjection(state.orientation,
-                                    state.viewport, state.frame);
-                        }
-                        if (state.width != draw[i].width || state.height != draw[i].height) {
-                            disp->setDisplaySize(state.width, state.height);
-                        }
-                    }
-                }
-                ++i;
-            }
-
-            // find displays that were added
-            // (ie: in current state but not in drawing state)
-            for (size_t i=0 ; i<cc ; i++) {
-                if (draw.indexOfKey(curr.keyAt(i)) < 0) {
-                    const DisplayDeviceState& state(curr[i]);
-
-                    sp<DisplaySurface> dispSurface;
-                    sp<IGraphicBufferProducer> producer;
-                    sp<IGraphicBufferProducer> bqProducer;
-                    sp<IGraphicBufferConsumer> bqConsumer;
-                    BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
-
-                    int32_t hwcId = -1;
-                    if (state.isVirtualDisplay()) {
-                        // Virtual displays without a surface are dormant:
-                        // they have external state (layer stack, projection,
-                        // etc.) but no internal state (i.e. a DisplayDevice).
-                        if (state.surface != nullptr) {
-
-                            // Allow VR composer to use virtual displays.
-                            if (mUseHwcVirtualDisplays || getBE().mHwc->isUsingVrComposer()) {
-                                int width = 0;
-                                int status = state.surface->query(
-                                        NATIVE_WINDOW_WIDTH, &width);
-                                ALOGE_IF(status != NO_ERROR,
-                                        "Unable to query width (%d)", status);
-                                int height = 0;
-                                status = state.surface->query(
-                                        NATIVE_WINDOW_HEIGHT, &height);
-                                ALOGE_IF(status != NO_ERROR,
-                                        "Unable to query height (%d)", status);
-                                int intFormat = 0;
-                                status = state.surface->query(
-                                        NATIVE_WINDOW_FORMAT, &intFormat);
-                                ALOGE_IF(status != NO_ERROR,
-                                        "Unable to query format (%d)", status);
-                                auto format = static_cast<android_pixel_format_t>(
-                                        intFormat);
-
-                                getBE().mHwc->allocateVirtualDisplay(width, height, &format,
-                                        &hwcId);
-                            }
-
-                            // TODO: Plumb requested format back up to consumer
-
-                            sp<VirtualDisplaySurface> vds =
-                                    new VirtualDisplaySurface(*getBE().mHwc,
-                                            hwcId, state.surface, bqProducer,
-                                            bqConsumer, state.displayName);
-
-                            dispSurface = vds;
-                            producer = vds;
-                        }
-                    } else {
-                        ALOGE_IF(state.surface != nullptr,
-                                "adding a supported display, but rendering "
-                                "surface is provided (%p), ignoring it",
-                                state.surface.get());
-
-                        hwcId = state.type;
-                        dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer);
-                        producer = bqProducer;
-                    }
-
-                    const wp<IBinder>& display(curr.keyAt(i));
-                    if (dispSurface != nullptr) {
-                        sp<DisplayDevice> hw =
-                                new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
-                                                  dispSurface, producer, hasWideColorDisplay);
-                        hw->setLayerStack(state.layerStack);
-                        hw->setProjection(state.orientation,
-                                state.viewport, state.frame);
-                        hw->setDisplayName(state.displayName);
-                        mDisplays.add(display, hw);
-                        if (!state.isVirtualDisplay()) {
-                            mEventThread->onHotplugReceived(state.type, true);
-                        }
-                    }
-                }
-            }
-        }
+        processDisplayChangesLocked();
+        processDisplayHotplugEventsLocked();
     }
 
     if (transactionFlags & (eTraversalNeeded|eDisplayTransactionNeeded)) {
@@ -2356,6 +2374,9 @@
                 }
                 layer->updateTransformHint(disp);
             }
+            if (disp != nullptr) {
+                layer->updateTransformHint(disp);
+            }
 
             first = false;
         });
@@ -3891,9 +3912,11 @@
 
     getBE().mRenderEngine->dump(result);
 
-    hw->undefinedRegion.dump(result, "undefinedRegion");
-    result.appendFormat("  orientation=%d, isDisplayOn=%d\n",
-            hw->getOrientation(), hw->isDisplayOn());
+    if (hw) {
+        hw->undefinedRegion.dump(result, "undefinedRegion");
+        result.appendFormat("  orientation=%d, isDisplayOn=%d\n",
+                hw->getOrientation(), hw->isDisplayOn());
+    }
     result.appendFormat(
             "  last eglSwapBuffers() time: %f us\n"
             "  last transaction time     : %f us\n"
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8030fbd..a18be9b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -142,14 +142,14 @@
     std::unique_ptr<RenderEngine> mRenderEngine;
     EGLContext mEGLContext;
     EGLDisplay mEGLDisplay;
-    
+
     FenceTimeline mGlCompositionDoneTimeline;
     FenceTimeline mDisplayTimeline;
 
     // protected by mCompositorTimingLock;
     mutable std::mutex mCompositorTimingLock;
     CompositorTiming mCompositorTiming;
-    
+
     // Only accessed from the main thread.
     struct CompositePresentTime {
         nsecs_t composite { -1 };
@@ -417,8 +417,7 @@
     void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
                          int64_t timestamp) override;
     void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
-                           HWC2::Connection connection,
-                           bool primaryDisplay) override;
+                           HWC2::Connection connection) override;
     void onRefreshReceived(int32_t sequenceId, hwc2_display_t display) override;
 
     /* ------------------------------------------------------------------------
@@ -547,10 +546,6 @@
     // called when starting, or restarting after system_server death
     void initializeDisplays();
 
-    // Create an IBinder for a builtin display and add it to current state
-    void createBuiltinDisplayLocked(DisplayDevice::DisplayType type);
-
-
     sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) const {
       Mutex::Autolock _l(mStateLock);
       return getDisplayDeviceLocked(dpy);
@@ -575,8 +570,6 @@
         return getDisplayDeviceLocked(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]);
     }
 
-    void createDefaultDisplayDevice();
-
     int32_t getDisplayType(const sp<IBinder>& display) {
         if (!display.get()) return NAME_NOT_FOUND;
         for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) {
@@ -637,6 +630,10 @@
     /* ------------------------------------------------------------------------
      * Display management
      */
+    DisplayDevice::DisplayType determineDisplayType(hwc2_display_t display,
+            HWC2::Connection connection) const;
+    void processDisplayChangesLocked();
+    void processDisplayHotplugEventsLocked();
 
     /* ------------------------------------------------------------------------
      * VSync
@@ -738,6 +735,13 @@
     sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
     bool mHadClientComposition = false;
 
+    struct HotplugEvent {
+        hwc2_display_t display;
+        HWC2::Connection connection = HWC2::Connection::Invalid;
+    };
+    // protected by mStateLock
+    std::vector<HotplugEvent> mPendingHotplugEvents;
+
     // this may only be written from the main thread with mStateLock held
     // it may be read from other threads with mStateLock held
     DefaultKeyedVector< wp<IBinder>, sp<DisplayDevice> > mDisplays;
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index cb22932..e16e7ec 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -181,6 +181,12 @@
     }
 }
 
+void FakeComposerClient::refreshDisplay(Display display) {
+    if (mCallbacksOn) {
+        mClient->onRefresh(display);
+    }
+}
+
 uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
     ALOGV("getMaxVirtualDisplayCount");
     return 1;
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index de8cffd..cef7f5b 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -142,6 +142,7 @@
     Layer getLayer(size_t index) const;
 
     void hotplugDisplay(Display display, IComposerCallback::Connection state);
+    void refreshDisplay(Display display);
 
 private:
     LayerImpl& getLayerImpl(Layer handle);
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 8a97ea4..1873832 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -22,25 +22,22 @@
 #include "FakeComposerService.h"
 #include "FakeComposerUtils.h"
 
+#include <gui/DisplayEventReceiver.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 
-#include <private/gui/ComposerService.h>
-
-#include <ui/DisplayInfo.h>
-
-#include <android/native_window.h>
-
 #include <android/hidl/manager/1.0/IServiceManager.h>
-
-#include <hwbinder/ProcessState.h>
-
+#include <android/looper.h>
+#include <android/native_window.h>
 #include <binder/ProcessState.h>
-
+#include <hwbinder/ProcessState.h>
 #include <log/log.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayInfo.h>
+#include <utils/Looper.h>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -142,13 +139,22 @@
     };
 
 protected:
+    static int processDisplayEvents(int fd, int events, void* data);
+
     void SetUp() override;
     void TearDown() override;
 
+    void waitForDisplayTransaction();
+    bool waitForHotplugEvent(uint32_t id, bool connected);
+
     sp<IComposer> mFakeService;
     sp<SurfaceComposerClient> mComposerClient;
 
     MockComposerClient* mMockComposer;
+
+    std::unique_ptr<DisplayEventReceiver> mReceiver;
+    sp<Looper> mLooper;;
+    std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
 };
 
 void DisplayTest::SetUp() {
@@ -188,9 +194,16 @@
 
     mComposerClient = new SurfaceComposerClient;
     ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+    mReceiver.reset(new DisplayEventReceiver());
+    mLooper = new Looper(false);
+    mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
 }
 
 void DisplayTest::TearDown() {
+    mLooper = nullptr;
+    mReceiver = nullptr;
+
     mComposerClient->dispose();
     mComposerClient = nullptr;
 
@@ -204,6 +217,55 @@
     mMockComposer = nullptr;
 }
 
+
+int DisplayTest::processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
+    auto self = static_cast<DisplayTest*>(data);
+
+    ssize_t n;
+    DisplayEventReceiver::Event buffer[1];
+
+    while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
+        for (int i=0 ; i<n ; i++) {
+            self->mReceivedDisplayEvents.push_back(buffer[i]);
+        }
+    }
+    ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
+    return 1;
+}
+
+void DisplayTest::waitForDisplayTransaction() {
+    // Both a refresh and a vsync event are needed to apply pending display
+    // transactions.
+    mMockComposer->refreshDisplay(EXTERNAL_DISPLAY);
+    mMockComposer->runVSyncAndWait();
+
+    // Extra vsync and wait to avoid a 10% flake due to a race.
+    mMockComposer->runVSyncAndWait();
+}
+
+bool DisplayTest::waitForHotplugEvent(uint32_t id, bool connected) {
+    int waitCount = 20;
+    while (waitCount--) {
+        while (!mReceivedDisplayEvents.empty()) {
+            auto event = mReceivedDisplayEvents.front();
+            mReceivedDisplayEvents.pop_front();
+
+            ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
+                    "event hotplug: id %d, connected %d\t", event.header.id,
+                    event.hotplug.connected);
+
+            if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
+                event.header.id == id && event.hotplug.connected == connected) {
+                return true;
+            }
+        }
+
+        mLooper->pollOnce(1);
+    }
+
+    return false;
+}
+
 TEST_F(DisplayTest, Hotplug) {
     ALOGD("DisplayTest::Hotplug");
 
@@ -215,7 +277,7 @@
     EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
             .Times(2 * 3)
             .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-    // ... and then special handling for dimensions. Specifying this
+    // ... and then special handling for dimensions. Specifying these
     // rules later means that gmock will try them first, i.e.,
     // ordering of width/height vs. the default implementation for
     // other queries is significant.
@@ -229,11 +291,12 @@
             .Times(2)
             .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
 
-    // TODO: Width and height queries are not actually called. Display
-    // info returns dimensions 0x0 in display info. Why?
-
     mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
 
+    waitForDisplayTransaction();
+
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+
     {
         sp<android::IBinder> display(
                 SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
@@ -264,6 +327,11 @@
 
     mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
 
+    waitForDisplayTransaction();
+
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, false));
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+
     {
         sp<android::IBinder> display(
                 SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
@@ -290,6 +358,64 @@
     mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
 }
 
+TEST_F(DisplayTest, HotplugPrimaryDisplay) {
+    ALOGD("DisplayTest::HotplugPrimaryDisplay");
+
+    mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+
+    waitForDisplayTransaction();
+
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, false));
+
+    {
+        sp<android::IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+        DisplayInfo info;
+        auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+        EXPECT_NE(NO_ERROR, result);
+    }
+
+    mMockComposer->clearFrames();
+
+    EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+                                  Return(Error::NONE)));
+    // The attribute queries will get done twice. This is for defaults
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
+            .Times(2 * 3)
+            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+    // ... and then special handling for dimensions. Specifying these
+    // rules later means that gmock will try them first, i.e.,
+    // ordering of width/height vs. the default implementation for
+    // other queries is significant.
+    EXPECT_CALL(*mMockComposer,
+                getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+
+    EXPECT_CALL(*mMockComposer,
+                getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+
+    mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+
+    waitForDisplayTransaction();
+
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, true));
+
+    {
+        sp<android::IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+        DisplayInfo info;
+        auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+        EXPECT_EQ(NO_ERROR, result);
+        ASSERT_EQ(400u, info.w);
+        ASSERT_EQ(200u, info.h);
+    }
+}
+
 ////////////////////////////////////////////////
 
 class TransactionTest : public ::testing::Test {
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index ade0bde..26f60fb 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -19,9 +19,9 @@
 #include <string.h>
 #include <sys/prctl.h>
 
+#include <dlfcn.h>
 #include <algorithm>
 #include <array>
-#include <dlfcn.h>
 #include <new>
 
 #include <log/log.h>
@@ -1047,17 +1047,54 @@
     VkInstance instance,
     uint32_t* pPhysicalDeviceGroupCount,
     VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
+    VkResult result = VK_SUCCESS;
     const auto& data = GetData(instance);
 
-    VkResult result = data.driver.EnumeratePhysicalDeviceGroups(
-        instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
-    if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
-        *pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
-        for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
-            for (uint32_t j = 0;
-                 j < pPhysicalDeviceGroupProperties->physicalDeviceCount; j++) {
-                SetData(pPhysicalDeviceGroupProperties->physicalDevices[j],
+    if (!data.driver.EnumeratePhysicalDeviceGroups) {
+        uint32_t device_count = 0;
+        result = EnumeratePhysicalDevices(instance, &device_count, nullptr);
+        if (result < 0)
+            return result;
+        if (!pPhysicalDeviceGroupProperties) {
+            *pPhysicalDeviceGroupCount = device_count;
+            return result;
+        }
+
+        device_count = std::min(device_count, *pPhysicalDeviceGroupCount);
+        if (!device_count) {
+            *pPhysicalDeviceGroupCount = 0;
+            return result;
+        }
+
+        android::Vector<VkPhysicalDevice> devices;
+        devices.resize(device_count);
+
+        result = EnumeratePhysicalDevices(instance, &device_count,
+                                          devices.editArray());
+        if (result < 0)
+            return result;
+
+        devices.resize(device_count);
+        *pPhysicalDeviceGroupCount = device_count;
+        for (uint32_t i = 0; i < device_count; ++i) {
+            pPhysicalDeviceGroupProperties[i].physicalDeviceCount = 1;
+            pPhysicalDeviceGroupProperties[i].physicalDevices[0] = devices[i];
+            pPhysicalDeviceGroupProperties[i].subsetAllocation = 0;
+        }
+    } else {
+        result = data.driver.EnumeratePhysicalDeviceGroups(
+            instance, pPhysicalDeviceGroupCount,
+            pPhysicalDeviceGroupProperties);
+        if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
+            *pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
+            for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
+                for (uint32_t j = 0;
+                     j < pPhysicalDeviceGroupProperties[i].physicalDeviceCount;
+                     j++) {
+                    SetData(
+                        pPhysicalDeviceGroupProperties[i].physicalDevices[j],
                         data);
+                }
             }
         }
     }
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index 1745925..d1f6241 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -1,12 +1,15 @@
 LIBVULKAN {
   global:
+    vkAcquireNextImage2KHR; # introduced=28
     vkAcquireNextImageKHR;
     vkAllocateCommandBuffers;
     vkAllocateDescriptorSets;
     vkAllocateMemory;
     vkBeginCommandBuffer;
     vkBindBufferMemory;
+    vkBindBufferMemory2; # introduced=28
     vkBindImageMemory;
+    vkBindImageMemory2; # introduced=28
     vkCmdBeginQuery;
     vkCmdBeginRenderPass;
     vkCmdBindDescriptorSets;
@@ -23,6 +26,7 @@
     vkCmdCopyImageToBuffer;
     vkCmdCopyQueryPoolResults;
     vkCmdDispatch;
+    vkCmdDispatchBase; # introduced=28
     vkCmdDispatchIndirect;
     vkCmdDraw;
     vkCmdDrawIndexed;
@@ -41,6 +45,7 @@
     vkCmdSetBlendConstants;
     vkCmdSetDepthBias;
     vkCmdSetDepthBounds;
+    vkCmdSetDeviceMask; # introduced=28
     vkCmdSetEvent;
     vkCmdSetLineWidth;
     vkCmdSetScissor;
@@ -58,6 +63,7 @@
     vkCreateComputePipelines;
     vkCreateDescriptorPool;
     vkCreateDescriptorSetLayout;
+    vkCreateDescriptorUpdateTemplate; # introduced=28
     vkCreateDevice;
     vkCreateEvent;
     vkCreateFence;
@@ -71,6 +77,7 @@
     vkCreateQueryPool;
     vkCreateRenderPass;
     vkCreateSampler;
+    vkCreateSamplerYcbcrConversion; # introduced=28
     vkCreateSemaphore;
     vkCreateShaderModule;
     vkCreateSwapchainKHR;
@@ -79,6 +86,7 @@
     vkDestroyCommandPool;
     vkDestroyDescriptorPool;
     vkDestroyDescriptorSetLayout;
+    vkDestroyDescriptorUpdateTemplate; # introduced=28
     vkDestroyDevice;
     vkDestroyEvent;
     vkDestroyFence;
@@ -92,6 +100,7 @@
     vkDestroyQueryPool;
     vkDestroyRenderPass;
     vkDestroySampler;
+    vkDestroySamplerYcbcrConversion; # introduced=28
     vkDestroySemaphore;
     vkDestroyShaderModule;
     vkDestroySurfaceKHR;
@@ -102,28 +111,49 @@
     vkEnumerateDeviceLayerProperties;
     vkEnumerateInstanceExtensionProperties;
     vkEnumerateInstanceLayerProperties;
+    vkEnumerateInstanceVersion; # introduced=28
+    vkEnumeratePhysicalDeviceGroups; # introduced=28
     vkEnumeratePhysicalDevices;
     vkFlushMappedMemoryRanges;
     vkFreeCommandBuffers;
     vkFreeDescriptorSets;
     vkFreeMemory;
     vkGetBufferMemoryRequirements;
+    vkGetBufferMemoryRequirements2; # introduced=28
+    vkGetDescriptorSetLayoutSupport; # introduced=28
+    vkGetDeviceGroupPeerMemoryFeatures; # introduced=28
+    vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28
+    vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
     vkGetDeviceMemoryCommitment;
     vkGetDeviceProcAddr;
     vkGetDeviceQueue;
+    vkGetDeviceQueue2; # introduced=28
     vkGetEventStatus;
     vkGetFenceStatus;
     vkGetImageMemoryRequirements;
+    vkGetImageMemoryRequirements2; # introduced=28
     vkGetImageSparseMemoryRequirements;
+    vkGetImageSparseMemoryRequirements2; # introduced=28
     vkGetImageSubresourceLayout;
     vkGetInstanceProcAddr;
+    vkGetPhysicalDeviceExternalBufferProperties; # introduced=28
+    vkGetPhysicalDeviceExternalFenceProperties; # introduced=28
+    vkGetPhysicalDeviceExternalSemaphoreProperties; # introduced=28
     vkGetPhysicalDeviceFeatures;
+    vkGetPhysicalDeviceFeatures2; # introduced=28
     vkGetPhysicalDeviceFormatProperties;
+    vkGetPhysicalDeviceFormatProperties2; # introduced=28
     vkGetPhysicalDeviceImageFormatProperties;
+    vkGetPhysicalDeviceImageFormatProperties2; # introduced=28
     vkGetPhysicalDeviceMemoryProperties;
+    vkGetPhysicalDeviceMemoryProperties2; # introduced=28
+    vkGetPhysicalDevicePresentRectanglesKHR; # introduced=28
     vkGetPhysicalDeviceProperties;
+    vkGetPhysicalDeviceProperties2; # introduced=28
     vkGetPhysicalDeviceQueueFamilyProperties;
+    vkGetPhysicalDeviceQueueFamilyProperties2; # introduced=28
     vkGetPhysicalDeviceSparseImageFormatProperties;
+    vkGetPhysicalDeviceSparseImageFormatProperties2; # introduced=28
     vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
     vkGetPhysicalDeviceSurfaceFormatsKHR;
     vkGetPhysicalDeviceSurfacePresentModesKHR;
@@ -145,8 +175,10 @@
     vkResetEvent;
     vkResetFences;
     vkSetEvent;
+    vkTrimCommandPool; # introduced=28
     vkUnmapMemory;
     vkUpdateDescriptorSets;
+    vkUpdateDescriptorSetWithTemplate; # introduced=28
     vkWaitForFences;
   local:
     *;