Merge "Use std::deque for DispatchEntry'ies."
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index ad7791e..50f117d 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -40,7 +40,6 @@
     <feature name="android.software.voice_recognizers" notLowRam="true" />
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
-    <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 6da3086..734a928 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-cc_library {
+cc_library_shared {
     name: "libbinder_ndk",
 
     export_include_dirs: [
         "include_ndk",
-        "include_apex",
+        "include_platform",
     ],
 
     cflags: [
@@ -33,6 +33,7 @@
         "ibinder_jni.cpp",
         "parcel.cpp",
         "process.cpp",
+        "stability.cpp",
         "status.cpp",
         "service_manager.cpp",
     ],
@@ -54,7 +55,7 @@
     version_script: "libbinder_ndk.map.txt",
     stubs: {
         symbol_file: "libbinder_ndk.map.txt",
-        versions: ["29"],
+        versions: ["29", "30"],
     },
 }
 
@@ -79,6 +80,6 @@
     symbol_file: "libbinder_ndk.map.txt",
     export_include_dirs: [
         "include_ndk",
-        "include_apex",
+        "include_platform",
     ],
 }
diff --git a/libs/binder/ndk/include_apex/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
similarity index 100%
rename from libs/binder/ndk/include_apex/android/binder_manager.h
rename to libs/binder/ndk/include_platform/android/binder_manager.h
diff --git a/libs/binder/ndk/include_apex/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
similarity index 100%
rename from libs/binder/ndk/include_apex/android/binder_process.h
rename to libs/binder/ndk/include_platform/android/binder_process.h
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
new file mode 100644
index 0000000..e6aeb04
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/binder_ibinder.h>
+
+__BEGIN_DECLS
+
+#ifdef __ANDROID_VNDK__
+
+/**
+ * This interface has the stability of the vendor image.
+ */
+void AIBinder_markVendorStability(AIBinder* binder);
+
+static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) {
+    AIBinder_markVendorStability(binder);
+}
+
+#else  // ndef defined __ANDROID_VNDK__
+
+/**
+ * This interface has the stability of the system image.
+ */
+void AIBinder_markSystemStability(AIBinder* binder);
+
+static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) {
+    AIBinder_markSystemStability(binder);
+}
+
+#endif  // ifdef __ANDROID_VNDK__
+
+/**
+ * This interface has system<->vendor stability
+ */
+void AIBinder_markVintfStability(AIBinder* binder);
+
+__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 4f685d1..feedde6 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -98,3 +98,12 @@
   local:
     *;
 };
+
+LIBBINDER_NDK30 { # introduced=30
+  global:
+    AIBinder_markSystemStability; # apex
+    AIBinder_markVendorStability; # vndk
+    AIBinder_markVintfStability; # apex vndk
+  local:
+    *;
+};
diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp
new file mode 100644
index 0000000..a5b3ece
--- /dev/null
+++ b/libs/binder/ndk/stability.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_stability.h>
+
+#include <binder/Stability.h>
+#include "ibinder_internal.h"
+
+#include <log/log.h>
+
+using ::android::internal::Stability;
+
+#ifdef __ANDROID_VNDK__
+#error libbinder_ndk should only be built in a system context
+#endif
+
+#ifdef __ANDROID_NDK__
+#error libbinder_ndk should only be built in a system context
+#endif
+
+// explicit extern because symbol is only declared in header when __ANDROID_VNDK__
+extern "C" void AIBinder_markVendorStability(AIBinder* binder) {
+    Stability::markVndk(binder->getBinder().get());
+}
+
+void AIBinder_markSystemStability(AIBinder* binder) {
+    Stability::markCompilationUnit(binder->getBinder().get());
+}
+
+void AIBinder_markVintfStability(AIBinder* binder) {
+    Stability::markVintf(binder->getBinder().get());
+}
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index 8cd4e03..bb1fe2f 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -44,10 +44,10 @@
         "libandroid_runtime_lazy",
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libutils",
     ],
     static_libs: [
-        "libbinder_ndk",
         "test_libbinder_ndk_library",
     ],
 }
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 724cb15..bc457ce 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -138,18 +138,30 @@
     test_suites: ["device-tests"],
 }
 
+aidl_interface {
+    name: "binderStabilityTestIface",
+    srcs: [
+        "IBinderStabilityTest.aidl",
+    ],
+}
+
 cc_test {
     name: "binderStabilityTest",
     defaults: ["binder_test_defaults"],
     srcs: [
         "binderStabilityTest.cpp",
-        "IBinderStabilityTest.aidl",
-        "IBinderStabilityTestSub.aidl",
     ],
 
     shared_libs: [
+        "libbinder_ndk",
         "libbinder",
+        "liblog",
         "libutils",
     ],
+    static_libs: [
+        "binderStabilityTestIface-cpp",
+        "binderStabilityTestIface-ndk_platform",
+    ],
+
     test_suites: ["device-tests"],
 }
diff --git a/libs/binder/tests/IBinderStabilityTest.aidl b/libs/binder/tests/IBinderStabilityTest.aidl
index 9559196..36e1c2c 100644
--- a/libs/binder/tests/IBinderStabilityTest.aidl
+++ b/libs/binder/tests/IBinderStabilityTest.aidl
@@ -14,34 +14,32 @@
  * limitations under the License.
  */
 
-import IBinderStabilityTestSub;
-
 // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS!
 // THIS IS ONLY FOR TESTING!
 interface IBinderStabilityTest {
     // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS!
     // THIS IS ONLY FOR TESTING!
-    void sendBinder(IBinderStabilityTestSub binder);
+    void sendBinder(IBinder binder);
 
     // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS!
     // THIS IS ONLY FOR TESTING!
-    void sendAndCallBinder(IBinderStabilityTestSub binder);
+    void sendAndCallBinder(IBinder binder);
 
     // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS!
     // THIS IS ONLY FOR TESTING!
-    IBinderStabilityTestSub returnNoStabilityBinder();
+    IBinder returnNoStabilityBinder();
 
     // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS!
     // THIS IS ONLY FOR TESTING!
-    IBinderStabilityTestSub returnLocalStabilityBinder();
+    IBinder returnLocalStabilityBinder();
 
     // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS!
     // THIS IS ONLY FOR TESTING!
-    IBinderStabilityTestSub returnVintfStabilityBinder();
+    IBinder returnVintfStabilityBinder();
 
     // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS!
     // THIS IS ONLY FOR TESTING!
-    IBinderStabilityTestSub returnVendorStabilityBinder();
+    IBinder returnVendorStabilityBinder();
 }
 // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS!
 // THIS IS ONLY FOR TESTING!
diff --git a/libs/binder/tests/IBinderStabilityTestSub.aidl b/libs/binder/tests/IBinderStabilityTestSub.aidl
deleted file mode 100644
index a81ebf9..0000000
--- a/libs/binder/tests/IBinderStabilityTestSub.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-interface IBinderStabilityTestSub {
-    void userDefinedTransaction();
-}
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 52595e0..490850e 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <android/binder_manager.h>
+#include <android/binder_stability.h>
 #include <binder/Binder.h>
 #include <binder/IBinder.h>
 #include <binder/IPCThreadState.h>
@@ -24,143 +26,225 @@
 
 #include <sys/prctl.h>
 
-#include "BnBinderStabilityTestSub.h"
+#include "aidl/BnBinderStabilityTest.h"
 #include "BnBinderStabilityTest.h"
-#include "BpBinderStabilityTest.h"
 
 using namespace android;
+using namespace ndk;
 using android::binder::Status;
+using android::internal::Stability; // for testing only!
 
-const String16 kNoStabilityServer = String16("binder_stability_test_service_low");
-const String16 kCompilationUnitServer = String16("binder_stability_test_service_compl");
-const String16 kVintfServer = String16("binder_stability_test_service_vintf");
+const String16 kSystemStabilityServer = String16("binder_stability_test_service_system");
 
-class BadStabilityTestSub : public BnBinderStabilityTestSub {
-    Status userDefinedTransaction() {
-        return Status::ok();
+// This is handwritten so that we can test different stability levels w/o having the AIDL
+// compiler assign them. Hand-writing binder interfaces is considered a bad practice
+// sanity reasons. YOU SHOULD DEFINE AN AIDL INTERFACE INSTEAD!
+class BadStableBinder : public BBinder {
+public:
+    static constexpr uint32_t USER_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION;
+    static String16 kDescriptor;
+
+    bool gotUserTransaction = false;
+
+    static status_t doUserTransaction(const sp<IBinder>& binder) {
+        Parcel data, reply;
+        data.writeInterfaceToken(kDescriptor);
+        return binder->transact(USER_TRANSACTION, data, &reply, 0/*flags*/);
+    }
+
+    status_t onTransact(uint32_t code,
+            const Parcel& data, Parcel* reply, uint32_t flags) override {
+        if (code == USER_TRANSACTION) {
+            // not interested in this kind of stability. Make sure
+            // we have a test failure
+            LOG_ALWAYS_FATAL_IF(!data.enforceInterface(kDescriptor));
+
+            gotUserTransaction = true;
+
+            ALOGE("binder stability: Got user transaction");
+            return OK;
+        }
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+
+    static sp<BadStableBinder> undef() {
+        sp<BadStableBinder> iface = new BadStableBinder();
+        return iface;
+    }
+
+    static sp<BadStableBinder> system() {
+        sp<BadStableBinder> iface = new BadStableBinder();
+        Stability::markCompilationUnit(iface.get()); // <- for test only
+        return iface;
+    }
+
+    static sp<BadStableBinder> vintf() {
+        sp<BadStableBinder> iface = new BadStableBinder();
+        Stability::markVintf(iface.get()); // <- for test only
+        return iface;
+    }
+
+    static sp<BadStableBinder> vendor() {
+        sp<BadStableBinder> iface = new BadStableBinder();
+        Stability::markVndk(iface.get()); // <- for test only
+        return iface;
     }
 };
-
-sp<IBinderStabilityTestSub> getCompilationUnitStability() {
-    sp<BnBinderStabilityTestSub> iface = new BadStabilityTestSub();
-    // NO! NO! NO! NO! DO NOT EVERY DO SOMETHING LIKE THIS?
-    // WHAT ARE YOU CRAZY? IT'S VERY DANGEROUS
-    internal::Stability::markCompilationUnit(iface.get()); // <- BAD, NO! DO NOT COPY
-    return iface;
-}
-
-sp<IBinderStabilityTestSub> getVintfStability() {
-    sp<BnBinderStabilityTestSub> iface = new BadStabilityTestSub();
-    // NO! NO! NO! NO! DO NOT EVERY DO SOMETHING LIKE THIS?
-    // WHAT ARE YOU CRAZY? IT'S VERY DANGEROUS
-    internal::Stability::markVintf(iface.get()); // <- BAD, NO! DO NOT COPY
-    return iface;
-}
-
-sp<IBinderStabilityTestSub> getVendorStability() {
-    sp<BnBinderStabilityTestSub> iface = new BadStabilityTestSub();
-    // NO! NO! NO! NO! DO NOT EVERY DO SOMETHING LIKE THIS?
-    // WHAT ARE YOU CRAZY? IT'S VERY DANGEROUS
-    internal::Stability::markVndk(iface.get()); // <- BAD, NO! DO NOT COPY
-    return iface;
-}
+String16 BadStableBinder::kDescriptor = String16("BadStableBinder.test");
 
 // NO! NO! NO! Do not even think of doing something like this!
 // This is for testing! If a class like this was actually used in production,
 // it would ruin everything!
-class BadStabilityTester : public BnBinderStabilityTest {
+class MyBinderStabilityTest : public BnBinderStabilityTest {
 public:
-    Status sendBinder(const sp<IBinderStabilityTestSub>& /*binder*/) override {
+    Status sendBinder(const sp<IBinder>& /*binder*/) override {
         return Status::ok();
     }
-    Status sendAndCallBinder(const sp<IBinderStabilityTestSub>& binder) override {
-        return binder->userDefinedTransaction();
+    Status sendAndCallBinder(const sp<IBinder>& binder) override {
+        Stability::debugLogStability("sendAndCallBinder got binder", binder);
+        return Status::fromExceptionCode(BadStableBinder::doUserTransaction(binder));
     }
-    Status returnNoStabilityBinder(sp<IBinderStabilityTestSub>* _aidl_return) override {
-        *_aidl_return = new BadStabilityTestSub();
+    Status returnNoStabilityBinder(sp<IBinder>* _aidl_return) override {
+        *_aidl_return = BadStableBinder::undef();
         return Status::ok();
     }
-    Status returnLocalStabilityBinder(sp<IBinderStabilityTestSub>* _aidl_return) override {
-        *_aidl_return = getCompilationUnitStability();
+    Status returnLocalStabilityBinder(sp<IBinder>* _aidl_return) override {
+        *_aidl_return = BadStableBinder::system();
         return Status::ok();
     }
-    Status returnVintfStabilityBinder(sp<IBinderStabilityTestSub>* _aidl_return) override {
-        *_aidl_return = getVintfStability();
+    Status returnVintfStabilityBinder(sp<IBinder>* _aidl_return) override {
+        *_aidl_return = BadStableBinder::vintf();
         return Status::ok();
     }
-    Status returnVendorStabilityBinder(sp<IBinderStabilityTestSub>* _aidl_return) override {
-        *_aidl_return = getVendorStability();
+    Status returnVendorStabilityBinder(sp<IBinder>* _aidl_return) override {
+        *_aidl_return = BadStableBinder::vendor();
         return Status::ok();
     }
 };
 
-void checkSystemStabilityBinder(const sp<IBinderStabilityTest>& complServer) {
-    EXPECT_TRUE(complServer->sendBinder(new BadStabilityTestSub()).isOk());
-    EXPECT_TRUE(complServer->sendBinder(getCompilationUnitStability()).isOk());
-    EXPECT_TRUE(complServer->sendBinder(getVintfStability()).isOk());
-    EXPECT_TRUE(complServer->sendBinder(getVendorStability()).isOk());
+TEST(BinderStability, CantCallVendorBinderInSystemContext) {
+    sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer);
+    auto server = interface_cast<IBinderStabilityTest>(serverBinder);
 
-    EXPECT_TRUE(complServer->sendAndCallBinder(new BadStabilityTestSub()).isOk());
-    EXPECT_TRUE(complServer->sendAndCallBinder(getCompilationUnitStability()).isOk());
-    EXPECT_TRUE(complServer->sendAndCallBinder(getVintfStability()).isOk());
+    ASSERT_NE(nullptr, server.get());
+    ASSERT_NE(nullptr, IInterface::asBinder(server)->remoteBinder());
 
-    // !!! user-defined transaction may not be stable for remote server !!!
-    EXPECT_FALSE(complServer->sendAndCallBinder(getVendorStability()).isOk());
+    EXPECT_TRUE(server->sendBinder(BadStableBinder::undef()).isOk());
+    EXPECT_TRUE(server->sendBinder(BadStableBinder::system()).isOk());
+    EXPECT_TRUE(server->sendBinder(BadStableBinder::vintf()).isOk());
+    EXPECT_TRUE(server->sendBinder(BadStableBinder::vendor()).isOk());
 
-    sp<IBinderStabilityTestSub> out;
-    EXPECT_TRUE(complServer->returnNoStabilityBinder(&out).isOk());
+    {
+        sp<BadStableBinder> binder = BadStableBinder::undef();
+        EXPECT_TRUE(server->sendAndCallBinder(binder).isOk());
+        EXPECT_TRUE(binder->gotUserTransaction);
+    }
+    {
+        sp<BadStableBinder> binder = BadStableBinder::system();
+        EXPECT_TRUE(server->sendAndCallBinder(binder).isOk());
+        EXPECT_TRUE(binder->gotUserTransaction);
+    }
+    {
+        sp<BadStableBinder> binder = BadStableBinder::vintf();
+        EXPECT_TRUE(server->sendAndCallBinder(binder).isOk());
+        EXPECT_TRUE(binder->gotUserTransaction);
+    }
+    {
+        // !!! user-defined transaction may not be stable for remote server !!!
+        // !!! so, it does not work !!!
+        sp<BadStableBinder> binder = BadStableBinder::vendor();
+        EXPECT_EQ(BAD_TYPE, server->sendAndCallBinder(binder).exceptionCode());
+        EXPECT_FALSE(binder->gotUserTransaction);
+    }
+
+    sp<IBinder> out;
+    EXPECT_TRUE(server->returnNoStabilityBinder(&out).isOk());
     ASSERT_NE(nullptr, out.get());
-    EXPECT_EQ(OK, IInterface::asBinder(out)->pingBinder());
-    EXPECT_TRUE(out->userDefinedTransaction().isOk());
+    EXPECT_EQ(OK, out->pingBinder());
+    EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out));
 
-    EXPECT_TRUE(complServer->returnLocalStabilityBinder(&out).isOk());
+    EXPECT_TRUE(server->returnLocalStabilityBinder(&out).isOk());
     ASSERT_NE(nullptr, out.get());
-    EXPECT_EQ(OK, IInterface::asBinder(out)->pingBinder());
-    EXPECT_TRUE(out->userDefinedTransaction().isOk());
+    EXPECT_EQ(OK, out->pingBinder());
+    EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out));
 
-    EXPECT_TRUE(complServer->returnVintfStabilityBinder(&out).isOk());
+    EXPECT_TRUE(server->returnVintfStabilityBinder(&out).isOk());
     ASSERT_NE(nullptr, out.get());
-    EXPECT_EQ(OK, IInterface::asBinder(out)->pingBinder());
-    EXPECT_TRUE(out->userDefinedTransaction().isOk());
+    EXPECT_EQ(OK, out->pingBinder());
+    EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out));
 
-    EXPECT_TRUE(complServer->returnVendorStabilityBinder(&out).isOk());
+    EXPECT_TRUE(server->returnVendorStabilityBinder(&out).isOk());
     ASSERT_NE(nullptr, out.get());
 
     // !!! libbinder-defined transaction works !!!
-    EXPECT_EQ(OK, IInterface::asBinder(out)->pingBinder());
+    EXPECT_EQ(OK, out->pingBinder());
 
     // !!! user-defined transaction may not be stable !!!
-    EXPECT_FALSE(out->userDefinedTransaction().isOk());
+    // !!! so, it does not work !!!
+    EXPECT_EQ(BAD_TYPE, BadStableBinder::doUserTransaction(out));
 }
 
-TEST(BinderStability, RemoteNoStabilityServer) {
-    sp<IBinder> remoteBinder = android::defaultServiceManager()->getService(kNoStabilityServer);
-    auto remoteServer = interface_cast<IBinderStabilityTest>(remoteBinder);
+// This is handwritten so that we can test different stability levels w/o having the AIDL
+// compiler assign them. Hand-writing binder interfaces is considered a bad practice
+// sanity reasons. YOU SHOULD DEFINE AN AIDL INTERFACE INSTEAD!
 
-    ASSERT_NE(nullptr, remoteServer.get());
-    ASSERT_NE(nullptr, IInterface::asBinder(remoteServer)->remoteBinder());
+struct NdkBinderStable_DataClass {
+    bool gotUserTransaction = false;
+};
+void* NdkBadStableBinder_Class_onCreate(void* args) {
+    LOG_ALWAYS_FATAL_IF(args != nullptr, "Takes no args");
+    return static_cast<void*>(new NdkBinderStable_DataClass);
+}
+void NdkBadStableBinder_Class_onDestroy(void* userData) {
+    delete static_cast<NdkBinderStable_DataClass*>(userData);
+}
+NdkBinderStable_DataClass* NdkBadStableBinder_getUserData(AIBinder* binder) {
+    LOG_ALWAYS_FATAL_IF(binder == nullptr);
+    void* userData = AIBinder_getUserData(binder);
+    LOG_ALWAYS_FATAL_IF(userData == nullptr, "null data - binder is remote?");
 
-    checkSystemStabilityBinder(remoteServer);
+    return static_cast<NdkBinderStable_DataClass*>(userData);
+}
+binder_status_t NdkBadStableBinder_Class_onTransact(
+    AIBinder* binder, transaction_code_t code, const AParcel* /*in*/, AParcel* /*out*/) {
+
+    if (code == BadStableBinder::USER_TRANSACTION) {
+        ALOGE("ndk binder stability: Got user transaction");
+        NdkBadStableBinder_getUserData(binder)->gotUserTransaction = true;
+        return STATUS_OK;
+    }
+
+    return STATUS_UNKNOWN_TRANSACTION;
 }
 
-TEST(BinderStability, RemoteLowStabilityServer) {
-    sp<IBinder> remoteBinder = android::defaultServiceManager()->getService(kCompilationUnitServer);
-    auto remoteServer = interface_cast<IBinderStabilityTest>(remoteBinder);
+static AIBinder_Class* kNdkBadStableBinder =
+    AIBinder_Class_define(String8(BadStableBinder::kDescriptor).c_str(),
+                          NdkBadStableBinder_Class_onCreate,
+                          NdkBadStableBinder_Class_onDestroy,
+                          NdkBadStableBinder_Class_onTransact);
+
+// for testing only to get around __ANDROID_VNDK__ guard.
+extern "C" void AIBinder_markVendorStability(AIBinder* binder); // <- BAD DO NOT COPY
+
+TEST(BinderStability, NdkCantCallVendorBinderInSystemContext) {
+    SpAIBinder binder = SpAIBinder(AServiceManager_getService(
+        String8(kSystemStabilityServer).c_str()));
+
+    std::shared_ptr<aidl::IBinderStabilityTest> remoteServer =
+        aidl::IBinderStabilityTest::fromBinder(binder);
 
     ASSERT_NE(nullptr, remoteServer.get());
-    ASSERT_NE(nullptr, IInterface::asBinder(remoteServer)->remoteBinder());
 
-    checkSystemStabilityBinder(remoteServer);
-}
+    SpAIBinder comp = SpAIBinder(AIBinder_new(kNdkBadStableBinder, nullptr /*args*/));
+    EXPECT_TRUE(remoteServer->sendBinder(comp).isOk());
+    EXPECT_TRUE(remoteServer->sendAndCallBinder(comp).isOk());
+    EXPECT_TRUE(NdkBadStableBinder_getUserData(comp.get())->gotUserTransaction);
 
-TEST(BinderStability, RemoteVintfServer) {
-    sp<IBinder> remoteBinder = android::defaultServiceManager()->getService(kVintfServer);
-    auto remoteServer = interface_cast<IBinderStabilityTest>(remoteBinder);
-
-    ASSERT_NE(nullptr, remoteServer.get());
-    ASSERT_NE(nullptr, IInterface::asBinder(remoteServer)->remoteBinder());
-
-    checkSystemStabilityBinder(remoteServer);
+    SpAIBinder vendor = SpAIBinder(AIBinder_new(kNdkBadStableBinder, nullptr /*args*/));
+    AIBinder_markVendorStability(vendor.get());
+    EXPECT_TRUE(remoteServer->sendBinder(vendor).isOk());
+    EXPECT_FALSE(remoteServer->sendAndCallBinder(vendor).isOk());
+    EXPECT_FALSE(NdkBadStableBinder_getUserData(vendor.get())->gotUserTransaction);
 }
 
 class MarksStabilityInConstructor : public BBinder {
@@ -168,7 +252,7 @@
     static bool gDestructed;
 
     MarksStabilityInConstructor() {
-        internal::Stability::markCompilationUnit(this);
+        Stability::markCompilationUnit(this);
     }
     ~MarksStabilityInConstructor() {
         gDestructed = true;
@@ -198,16 +282,8 @@
         // child process
         prctl(PR_SET_PDEATHSIG, SIGHUP);
 
-        sp<IBinder> noStability = new BadStabilityTester;
-        android::defaultServiceManager()->addService(kNoStabilityServer, noStability);
-
-        sp<IBinder> compil = new BadStabilityTester;
-        internal::Stability::markCompilationUnit(compil.get());
-        android::defaultServiceManager()->addService(kCompilationUnitServer, compil);
-
-        sp<IBinder> vintf = new BadStabilityTester;
-        internal::Stability::markVintf(vintf.get());
-        android::defaultServiceManager()->addService(kVintfServer, vintf);
+        sp<IBinder> server = new MyBinderStabilityTest;
+        android::defaultServiceManager()->addService(kSystemStabilityServer, server);
 
         IPCThreadState::self()->joinThreadPool(true);
         exit(1);  // should not reach
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index ff1ba0a..386f731 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -410,6 +410,19 @@
     bgSurface->expectTap(1, 1);
 }
 
+TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    // In case we pass the very big inset without any checking.
+    fgSurface->mInputInfo.surfaceInset = INT32_MAX;
+    fgSurface->showAt(100, 100);
+
+    fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
+
+    // expect no crash for overflow, and inset size to be clamped to surface size
+    injectTap(202, 202);
+    fgSurface->expectTap(1, 1);
+}
+
 // Ensure we ignore transparent region when getting screen bounds when positioning input frame.
 TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index c11b88e..14ed73d 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -299,14 +299,10 @@
 }
 
 void SensorService::setSensorAccess(uid_t uid, bool hasAccess) {
-    SortedVector< sp<SensorEventConnection> > activeConnections;
-    populateActiveConnections(&activeConnections);
-    {
-        Mutex::Autolock _l(mLock);
-        for (size_t i = 0 ; i < activeConnections.size(); i++) {
-            if (activeConnections[i] != nullptr && activeConnections[i]->getUid() == uid) {
-                activeConnections[i]->setSensorAccess(hasAccess);
-            }
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+    for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) {
+        if (conn->getUid() == uid) {
+            conn->setSensorAccess(hasAccess);
         }
     }
 }
@@ -360,7 +356,7 @@
         if (args.size() > 2) {
            return INVALID_OPERATION;
         }
-        Mutex::Autolock _l(mLock);
+        ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
         SensorDevice& dev(SensorDevice::getInstance());
         if (args.size() == 2 && args[0] == String16("restrict")) {
             // If already in restricted mode. Ignore.
@@ -374,7 +370,7 @@
 
             mCurrentOperatingMode = RESTRICTED;
             // temporarily stop all sensor direct report and disable sensors
-            disableAllSensorsLocked();
+            disableAllSensorsLocked(&connLock);
             mWhiteListedPackage.setTo(String8(args[1]));
             return status_t(NO_ERROR);
         } else if (args.size() == 1 && args[0] == String16("enable")) {
@@ -382,7 +378,7 @@
             if (mCurrentOperatingMode == RESTRICTED) {
                 mCurrentOperatingMode = NORMAL;
                 // enable sensors and recover all sensor direct report
-                enableAllSensorsLocked();
+                enableAllSensorsLocked(&connLock);
             }
             if (mCurrentOperatingMode == DATA_INJECTION) {
                resetToNormalModeLocked();
@@ -473,22 +469,18 @@
             result.appendFormat("Sensor Privacy: %s\n",
                     mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
 
-            result.appendFormat("%zd active connections\n", mActiveConnections.size());
-            for (size_t i=0 ; i < mActiveConnections.size() ; i++) {
-                sp<SensorEventConnection> connection(mActiveConnections[i].promote());
-                if (connection != nullptr) {
-                    result.appendFormat("Connection Number: %zu \n", i);
-                    connection->dump(result);
-                }
+            const auto& activeConnections = connLock.getActiveConnections();
+            result.appendFormat("%zd active connections\n", activeConnections.size());
+            for (size_t i=0 ; i < activeConnections.size() ; i++) {
+                result.appendFormat("Connection Number: %zu \n", i);
+                activeConnections[i]->dump(result);
             }
 
-            result.appendFormat("%zd direct connections\n", mDirectConnections.size());
-            for (size_t i = 0 ; i < mDirectConnections.size() ; i++) {
-                sp<SensorDirectConnection> connection(mDirectConnections[i].promote());
-                if (connection != nullptr) {
-                    result.appendFormat("Direct connection %zu:\n", i);
-                    connection->dump(result);
-                }
+            const auto& directConnections = connLock.getDirectConnections();
+            result.appendFormat("%zd direct connections\n", directConnections.size());
+            for (size_t i = 0 ; i < directConnections.size() ; i++) {
+                result.appendFormat("Direct connection %zu:\n", i);
+                directConnections[i]->dump(result);
             }
 
             result.appendFormat("Previous Registrations:\n");
@@ -515,17 +507,14 @@
 }
 
 void SensorService::disableAllSensors() {
-    Mutex::Autolock _l(mLock);
-    disableAllSensorsLocked();
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+    disableAllSensorsLocked(&connLock);
 }
 
-void SensorService::disableAllSensorsLocked() {
+void SensorService::disableAllSensorsLocked(ConnectionSafeAutolock* connLock) {
     SensorDevice& dev(SensorDevice::getInstance());
-    for (auto &i : mDirectConnections) {
-        sp<SensorDirectConnection> connection(i.promote());
-        if (connection != nullptr) {
-            connection->stopAll(true /* backupRecord */);
-        }
+    for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) {
+        connection->stopAll(true /* backupRecord */);
     }
     dev.disableAllSensors();
     // Clear all pending flush connections for all active sensors. If one of the active
@@ -537,11 +526,11 @@
 }
 
 void SensorService::enableAllSensors() {
-    Mutex::Autolock _l(mLock);
-    enableAllSensorsLocked();
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+    enableAllSensorsLocked(&connLock);
 }
 
-void SensorService::enableAllSensorsLocked() {
+void SensorService::enableAllSensorsLocked(ConnectionSafeAutolock* connLock) {
     // sensors should only be enabled if the operating state is not restricted and sensor
     // privacy is not enabled.
     if (mCurrentOperatingMode == RESTRICTED || mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
@@ -552,14 +541,12 @@
     }
     SensorDevice& dev(SensorDevice::getInstance());
     dev.enableAllSensors();
-    for (auto &i : mDirectConnections) {
-        sp<SensorDirectConnection> connection(i.promote());
-        if (connection != nullptr) {
-            connection->recoverAll();
-        }
+    for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) {
+        connection->recoverAll();
     }
 }
 
+
 // NOTE: This is a remote API - make sure all args are validated
 status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) {
     if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
@@ -734,17 +721,8 @@
         for (int i = 0; i < count; i++) {
              mSensorEventBuffer[i].flags = 0;
         }
+        ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
 
-        // Make a copy of the connection vector as some connections may be removed during the course
-        // of this loop (especially when one-shot sensor events are present in the sensor_event
-        // buffer). Promote all connections to StrongPointers before the lock is acquired. If the
-        // destructor of the sp gets called when the lock is acquired, it may result in a deadlock
-        // as ~SensorEventConnection() needs to acquire mLock again for cleanup. So copy all the
-        // strongPointers to a vector before the lock is acquired.
-        SortedVector< sp<SensorEventConnection> > activeConnections;
-        populateActiveConnections(&activeConnections);
-
-        Mutex::Autolock _l(mLock);
         // Poll has returned. Hold a wakelock if one of the events is from a wake up sensor. The
         // rest of this loop is under a critical section protected by mLock. Acquiring a wakeLock,
         // sending events to clients (incrementing SensorEventConnection::mWakeLockRefCount) should
@@ -818,6 +796,10 @@
             }
         }
 
+        // Cache the list of active connections, since we use it in multiple places below but won't
+        // modify it here
+        const std::vector<sp<SensorEventConnection>> activeConnections = connLock.getActiveConnections();
+
         for (int i = 0; i < count; ++i) {
             // Map flush_complete_events in the buffer to SensorEventConnections which called flush
             // on the hardware sensor. mapFlushEventsToConnections[i] will be the
@@ -869,11 +851,8 @@
                         ALOGE("Dynamic sensor release error.");
                     }
 
-                    size_t numConnections = activeConnections.size();
-                    for (size_t i=0 ; i < numConnections; ++i) {
-                        if (activeConnections[i] != nullptr) {
-                            activeConnections[i]->removeSensor(handle);
-                        }
+                    for (const sp<SensorEventConnection>& connection : activeConnections) {
+                        connection->removeSensor(handle);
                     }
                 }
             }
@@ -882,18 +861,14 @@
         // Send our events to clients. Check the state of wake lock for each client and release the
         // lock if none of the clients need it.
         bool needsWakeLock = false;
-        size_t numConnections = activeConnections.size();
-        for (size_t i=0 ; i < numConnections; ++i) {
-            if (activeConnections[i] != nullptr) {
-                activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
-                        mMapFlushEventsToConnections);
-                needsWakeLock |= activeConnections[i]->needsWakeLock();
-                // If the connection has one-shot sensors, it may be cleaned up after first trigger.
-                // Early check for one-shot sensors.
-                if (activeConnections[i]->hasOneShotSensors()) {
-                    cleanupAutoDisabledSensorLocked(activeConnections[i], mSensorEventBuffer,
-                            count);
-                }
+        for (const sp<SensorEventConnection>& connection : activeConnections) {
+            connection->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
+                    mMapFlushEventsToConnections);
+            needsWakeLock |= connection->needsWakeLock();
+            // If the connection has one-shot sensors, it may be cleaned up after first trigger.
+            // Early check for one-shot sensors.
+            if (connection->hasOneShotSensors()) {
+                cleanupAutoDisabledSensorLocked(connection, mSensorEventBuffer, count);
             }
         }
 
@@ -912,17 +887,11 @@
 }
 
 void SensorService::resetAllWakeLockRefCounts() {
-    SortedVector< sp<SensorEventConnection> > activeConnections;
-    populateActiveConnections(&activeConnections);
-    {
-        Mutex::Autolock _l(mLock);
-        for (size_t i=0 ; i < activeConnections.size(); ++i) {
-            if (activeConnections[i] != nullptr) {
-                activeConnections[i]->resetWakeLockRefCount();
-            }
-        }
-        setWakeLockAcquiredLocked(false);
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+    for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
+        connection->resetWakeLockRefCount();
     }
+    setWakeLockAcquiredLocked(false);
 }
 
 void SensorService::setWakeLockAcquiredLocked(bool acquire) {
@@ -1144,9 +1113,7 @@
     sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName,
             requestedMode == DATA_INJECTION, connOpPackageName, hasSensorAccess));
     if (requestedMode == DATA_INJECTION) {
-        if (mActiveConnections.indexOf(result) < 0) {
-            mActiveConnections.add(result);
-        }
+        mConnectionHolder.addEventConnectionIfNotPresent(result);
         // Add the associated file descriptor to the Looper for polling whenever there is data to
         // be injected.
         result->updateLooperRegistration(mLooper);
@@ -1162,7 +1129,7 @@
 sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
         const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
         const native_handle *resource) {
-    Mutex::Autolock _l(mLock);
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
 
     // No new direct connections are allowed when sensor privacy is enabled
     if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
@@ -1190,9 +1157,8 @@
     }
 
     // check for duplication
-    for (auto &i : mDirectConnections) {
-        sp<SensorDirectConnection> connection(i.promote());
-        if (connection != nullptr && connection->isEquivalent(&mem)) {
+    for (const sp<SensorDirectConnection>& connection : connLock.getDirectConnections()) {
+        if (connection->isEquivalent(&mem)) {
             ALOGE("Duplicate create channel request for the same share memory");
             return nullptr;
         }
@@ -1229,7 +1195,7 @@
         return nullptr;
     }
 
-    SensorDirectConnection* conn = nullptr;
+    sp<SensorDirectConnection> conn;
     SensorDevice& dev(SensorDevice::getInstance());
     int channelHandle = dev.registerDirectChannel(&mem);
 
@@ -1246,7 +1212,7 @@
     } else {
         // add to list of direct connections
         // sensor service should never hold pointer or sp of SensorDirectConnection object.
-        mDirectConnections.add(wp<SensorDirectConnection>(conn));
+        mConnectionHolder.addDirectConnection(conn);
     }
     return conn;
 }
@@ -1358,7 +1324,7 @@
 }
 
 void SensorService::cleanupConnection(SensorEventConnection* c) {
-    Mutex::Autolock _l(mLock);
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     const wp<SensorEventConnection> connection(c);
     size_t size = mActiveSensors.size();
     ALOGD_IF(DEBUG_CONNECTIONS, "%zu active sensors", size);
@@ -1391,10 +1357,10 @@
         }
     }
     c->updateLooperRegistration(mLooper);
-    mActiveConnections.remove(connection);
+    mConnectionHolder.removeEventConnection(connection);
     BatteryService::cleanup(c->getUid());
     if (c->needsWakeLock()) {
-        checkWakeLockStateLocked();
+        checkWakeLockStateLocked(&connLock);
     }
 
     {
@@ -1414,7 +1380,7 @@
 
     SensorDevice& dev(SensorDevice::getInstance());
     dev.unregisterDirectChannel(c->getHalChannelHandle());
-    mDirectConnections.remove(c);
+    mConnectionHolder.removeDirectConnection(c);
 }
 
 sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
@@ -1433,7 +1399,7 @@
         return BAD_VALUE;
     }
 
-    Mutex::Autolock _l(mLock);
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     if (mCurrentOperatingMode != NORMAL
            && !isWhiteListedPackage(connection->getPackageName())) {
         return INVALID_OPERATION;
@@ -1484,7 +1450,7 @@
                             }
                             connection->sendEvents(&event, 1, nullptr);
                             if (!connection->needsWakeLock() && mWakeLockAcquired) {
-                                checkWakeLockStateLocked();
+                                checkWakeLockStateLocked(&connLock);
                             }
                         }
                     }
@@ -1497,9 +1463,7 @@
         BatteryService::enableSensor(connection->getUid(), handle);
         // the sensor was added (which means it wasn't already there)
         // so, see if this connection becomes active
-        if (mActiveConnections.indexOf(connection) < 0) {
-            mActiveConnections.add(connection);
-        }
+        mConnectionHolder.addEventConnectionIfNotPresent(connection);
     } else {
         ALOGW("sensor %08x already enabled in connection %p (ignoring)",
             handle, connection.get());
@@ -1603,7 +1567,7 @@
         }
         if (connection->hasAnySensor() == false) {
             connection->updateLooperRegistration(mLooper);
-            mActiveConnections.remove(connection);
+            mConnectionHolder.removeEventConnection(connection);
         }
         // see if this sensor becomes inactive
         if (rec->removeConnection(connection)) {
@@ -1762,22 +1726,19 @@
 }
 
 void SensorService::checkWakeLockState() {
-    Mutex::Autolock _l(mLock);
-    checkWakeLockStateLocked();
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+    checkWakeLockStateLocked(&connLock);
 }
 
-void SensorService::checkWakeLockStateLocked() {
+void SensorService::checkWakeLockStateLocked(ConnectionSafeAutolock* connLock) {
     if (!mWakeLockAcquired) {
         return;
     }
     bool releaseLock = true;
-    for (size_t i=0 ; i<mActiveConnections.size() ; i++) {
-        sp<SensorEventConnection> connection(mActiveConnections[i].promote());
-        if (connection != nullptr) {
-            if (connection->needsWakeLock()) {
-                releaseLock = false;
-                break;
-            }
+    for (const sp<SensorEventConnection>& connection : connLock->getActiveConnections()) {
+        if (connection->needsWakeLock()) {
+            releaseLock = false;
+            break;
         }
     }
     if (releaseLock) {
@@ -1793,17 +1754,6 @@
     }
 }
 
-void SensorService::populateActiveConnections(
-        SortedVector< sp<SensorEventConnection> >* activeConnections) {
-    Mutex::Autolock _l(mLock);
-    for (size_t i=0 ; i < mActiveConnections.size(); ++i) {
-        sp<SensorEventConnection> connection(mActiveConnections[i].promote());
-        if (connection != nullptr) {
-            activeConnections->add(connection);
-        }
-    }
-}
-
 bool SensorService::isWhiteListedPackage(const String8& packageName) {
     return (packageName.contains(mWhiteListedPackage.string()));
 }
@@ -1938,4 +1888,62 @@
     }
     return binder::Status::ok();
 }
-}; // namespace android
+
+SensorService::ConnectionSafeAutolock::ConnectionSafeAutolock(
+        SensorService::SensorConnectionHolder& holder, Mutex& mutex)
+        : mConnectionHolder(holder), mAutolock(mutex) {}
+
+template<typename ConnectionType>
+const std::vector<sp<ConnectionType>>& SensorService::ConnectionSafeAutolock::getConnectionsHelper(
+        const SortedVector<wp<ConnectionType>>& connectionList,
+        std::vector<std::vector<sp<ConnectionType>>>* referenceHolder) {
+    referenceHolder->emplace_back();
+    std::vector<sp<ConnectionType>>& connections = referenceHolder->back();
+    for (const wp<ConnectionType>& weakConnection : connectionList){
+        sp<ConnectionType> connection = weakConnection.promote();
+        if (connection != nullptr) {
+            connections.push_back(std::move(connection));
+        }
+    }
+    return connections;
+}
+
+const std::vector<sp<SensorService::SensorEventConnection>>&
+        SensorService::ConnectionSafeAutolock::getActiveConnections() {
+    return getConnectionsHelper(mConnectionHolder.mActiveConnections,
+                                &mReferencedActiveConnections);
+}
+
+const std::vector<sp<SensorService::SensorDirectConnection>>&
+        SensorService::ConnectionSafeAutolock::getDirectConnections() {
+    return getConnectionsHelper(mConnectionHolder.mDirectConnections,
+                                &mReferencedDirectConnections);
+}
+
+void SensorService::SensorConnectionHolder::addEventConnectionIfNotPresent(
+        const sp<SensorService::SensorEventConnection>& connection) {
+    if (mActiveConnections.indexOf(connection) < 0) {
+        mActiveConnections.add(connection);
+    }
+}
+
+void SensorService::SensorConnectionHolder::removeEventConnection(
+        const wp<SensorService::SensorEventConnection>& connection) {
+    mActiveConnections.remove(connection);
+}
+
+void SensorService::SensorConnectionHolder::addDirectConnection(
+        const sp<SensorService::SensorDirectConnection>& connection) {
+    mDirectConnections.add(connection);
+}
+
+void SensorService::SensorConnectionHolder::removeDirectConnection(
+        const wp<SensorService::SensorDirectConnection>& connection) {
+    mDirectConnections.remove(connection);
+}
+
+SensorService::ConnectionSafeAutolock SensorService::SensorConnectionHolder::lock(Mutex& mutex) {
+    return ConnectionSafeAutolock(*this, mutex);
+}
+
+} // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index e6ec96d..060b5eb 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -20,6 +20,7 @@
 #include "SensorList.h"
 #include "RecentEventLogger.h"
 
+#include <android-base/macros.h>
 #include <binder/AppOpsManager.h>
 #include <binder/BinderService.h>
 #include <binder/IUidObserver.h>
@@ -42,6 +43,7 @@
 #include <sys/types.h>
 #include <unordered_map>
 #include <unordered_set>
+#include <vector>
 
 #if __clang__
 // Clang warns about SensorEventConnection::dump hiding BBinder::dump. The cause isn't fixable
@@ -95,10 +97,67 @@
     friend class BinderService<SensorService>;
 
     // nested class/struct for internal use
-    class SensorRecord;
+    class ConnectionSafeAutolock;
+    class SensorConnectionHolder;
     class SensorEventAckReceiver;
+    class SensorRecord;
     class SensorRegistrationInfo;
 
+    // Promoting a SensorEventConnection or SensorDirectConnection from wp to sp must be done with
+    // mLock held, but destroying that sp must be done unlocked to avoid a race condition that
+    // causes a deadlock (remote dies while we hold a local sp, then our decStrong() call invokes
+    // the dtor -> cleanupConnection() tries to re-lock the mutex). This class ensures safe usage
+    // by wrapping a Mutex::Autolock on SensorService's mLock, plus vectors that hold promoted sp<>
+    // references until the lock is released, when they are safely destroyed.
+    // All read accesses to the connection lists in mConnectionHolder must be done via this class.
+    class ConnectionSafeAutolock final {
+    public:
+        // Returns a list of non-null promoted connection references
+        const std::vector<sp<SensorEventConnection>>& getActiveConnections();
+        const std::vector<sp<SensorDirectConnection>>& getDirectConnections();
+
+    private:
+        // Constructed via SensorConnectionHolder::lock()
+        friend class SensorConnectionHolder;
+        explicit ConnectionSafeAutolock(SensorConnectionHolder& holder, Mutex& mutex);
+        DISALLOW_IMPLICIT_CONSTRUCTORS(ConnectionSafeAutolock);
+
+        // NOTE: Order of these members is important, as the destructor for non-static members
+        // get invoked in the reverse order of their declaration. Here we are relying on the
+        // Autolock to be destroyed *before* the vectors, so the sp<> objects are destroyed without
+        // the lock held, which avoids the deadlock.
+        SensorConnectionHolder& mConnectionHolder;
+        std::vector<std::vector<sp<SensorEventConnection>>> mReferencedActiveConnections;
+        std::vector<std::vector<sp<SensorDirectConnection>>> mReferencedDirectConnections;
+        Mutex::Autolock mAutolock;
+
+        template<typename ConnectionType>
+        const std::vector<sp<ConnectionType>>& getConnectionsHelper(
+                const SortedVector<wp<ConnectionType>>& connectionList,
+                std::vector<std::vector<sp<ConnectionType>>>* referenceHolder);
+    };
+
+    // Encapsulates the collection of active SensorEventConection and SensorDirectConnection
+    // references. Write access is done through this class with mLock held, but all read access
+    // must be routed through ConnectionSafeAutolock.
+    class SensorConnectionHolder {
+    public:
+        void addEventConnectionIfNotPresent(const sp<SensorEventConnection>& connection);
+        void removeEventConnection(const wp<SensorEventConnection>& connection);
+
+        void addDirectConnection(const sp<SensorDirectConnection>& connection);
+        void removeDirectConnection(const wp<SensorDirectConnection>& connection);
+
+        // Pass in the mutex that protects this connection holder; acquires the lock and returns an
+        // object that can be used to safely read the lists of connections
+        ConnectionSafeAutolock lock(Mutex& mutex);
+
+    private:
+        friend class ConnectionSafeAutolock;
+        SortedVector< wp<SensorEventConnection> > mActiveConnections;
+        SortedVector< wp<SensorDirectConnection> > mDirectConnections;
+    };
+
     // If accessing a sensor we need to make sure the UID has access to it. If
     // the app UID is idle then it cannot access sensors and gets no trigger
     // events, no on-change events, flush event behavior does not change, and
@@ -250,7 +309,7 @@
     // method checks whether all the events from these wake up sensors have been delivered to the
     // corresponding applications, if yes the wakelock is released.
     void checkWakeLockState();
-    void checkWakeLockStateLocked();
+    void checkWakeLockStateLocked(ConnectionSafeAutolock* connLock);
     bool isWakeLockAcquired();
     bool isWakeUpSensorEvent(const sensors_event_t& event) const;
 
@@ -268,10 +327,6 @@
     // Send events from the event cache for this particular connection.
     void sendEventsFromCache(const sp<SensorEventConnection>& connection);
 
-    // Promote all weak referecences in mActiveConnections vector to strong references and add them
-    // to the output vector.
-    void populateActiveConnections( SortedVector< sp<SensorEventConnection> >* activeConnections);
-
     // If SensorService is operating in RESTRICTED mode, only select whitelisted packages are
     // allowed to register for or call flush on sensors. Typically only cts test packages are
     // allowed.
@@ -306,10 +361,10 @@
 
     // temporarily stops all active direct connections and disables all sensors
     void disableAllSensors();
-    void disableAllSensorsLocked();
+    void disableAllSensorsLocked(ConnectionSafeAutolock* connLock);
     // restarts the previously stopped direct connections and enables all sensors
     void enableAllSensors();
-    void enableAllSensorsLocked();
+    void enableAllSensorsLocked(ConnectionSafeAutolock* connLock);
 
     static uint8_t sHmacGlobalKey[128];
     static bool sHmacGlobalKeyIsValid;
@@ -327,12 +382,13 @@
     mutable Mutex mLock;
     DefaultKeyedVector<int, SensorRecord*> mActiveSensors;
     std::unordered_set<int> mActiveVirtualSensors;
-    SortedVector< wp<SensorEventConnection> > mActiveConnections;
+    SensorConnectionHolder mConnectionHolder;
     bool mWakeLockAcquired;
     sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
+    // WARNING: these SensorEventConnection instances must not be promoted to sp, except via
+    // modification to add support for them in ConnectionSafeAutolock
     wp<const SensorEventConnection> * mMapFlushEventsToConnections;
     std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
-    SortedVector< wp<SensorDirectConnection> > mDirectConnections;
     Mode mCurrentOperatingMode;
 
     // This packagaName is set when SensorService is in RESTRICTED or DATA_INJECTION mode. Only
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index b411e0a..6878e99 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -300,10 +300,10 @@
         return;
     }
 
-    mRenderSurface->onPresentDisplayCompleted();
-
     auto frame = presentAndGetFrameFences();
 
+    mRenderSurface->onPresentDisplayCompleted();
+
     for (auto& layer : getOutputLayersOrderedByZ()) {
         // The layer buffer from the previous frame (if any) is released
         // by HWC only when the release fence from this frame (if any) is
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a2eeea5..e7fbfe9 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1965,13 +1965,6 @@
     return mRemovedFromCurrentState;
 }
 
-// Debug helper for b/137560795
-#define INT32_MIGHT_OVERFLOW(n) (((n) >= INT32_MAX / 2) || ((n) <= INT32_MIN / 2))
-
-#define RECT_BOUNDS_INVALID(rect)                                               \
-    (INT32_MIGHT_OVERFLOW((rect).left) || INT32_MIGHT_OVERFLOW((rect).right) || \
-     INT32_MIGHT_OVERFLOW((rect).bottom) || INT32_MIGHT_OVERFLOW((rect).top))
-
 InputWindowInfo Layer::fillInputInfo() {
     InputWindowInfo info = mDrawingState.inputInfo;
 
@@ -1982,14 +1975,14 @@
     ui::Transform t = getTransform();
     const float xScale = t.sx();
     const float yScale = t.sy();
-    float xSurfaceInset = info.surfaceInset;
-    float ySurfaceInset = info.surfaceInset;
+    int32_t xSurfaceInset = info.surfaceInset;
+    int32_t ySurfaceInset = info.surfaceInset;
     if (xScale != 1.0f || yScale != 1.0f) {
-        info.windowXScale *= 1.0f / xScale;
-        info.windowYScale *= 1.0f / yScale;
+        info.windowXScale *= (xScale != 0.0f) ? 1.0f / xScale : 0.0f;
+        info.windowYScale *= (yScale != 0.0f) ? 1.0f / yScale : 0.0f;
         info.touchableRegion.scaleSelf(xScale, yScale);
-        xSurfaceInset *= xScale;
-        ySurfaceInset *= yScale;
+        xSurfaceInset = std::round(xSurfaceInset * xScale);
+        ySurfaceInset = std::round(ySurfaceInset * yScale);
     }
 
     // Transform layer size to screen space and inset it by surface insets.
@@ -2002,25 +1995,10 @@
     }
     layerBounds = t.transform(layerBounds);
 
-    // debug check for b/137560795
-    {
-        if (RECT_BOUNDS_INVALID(layerBounds)) {
-            ALOGE("layer %s bounds are invalid (%" PRIi32 ", %" PRIi32 ", %" PRIi32 ", %" PRIi32
-                  ")",
-                  mName.c_str(), layerBounds.left, layerBounds.top, layerBounds.right,
-                  layerBounds.bottom);
-            std::string out;
-            getTransform().dump(out, "Transform");
-            ALOGE("%s", out.c_str());
-            layerBounds.left = layerBounds.top = layerBounds.right = layerBounds.bottom = 0;
-        }
+    // clamp inset to layer bounds
+    xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0;
+    ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0;
 
-        if (INT32_MIGHT_OVERFLOW(xSurfaceInset) || INT32_MIGHT_OVERFLOW(ySurfaceInset)) {
-            ALOGE("layer %s surface inset are invalid (%" PRIi32 ", %" PRIi32 ")", mName.c_str(),
-                  int32_t(xSurfaceInset), int32_t(ySurfaceInset));
-            xSurfaceInset = ySurfaceInset = 0;
-        }
-    }
     layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset);
 
     // Input coordinate should match the layer bounds.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 488b9ef..213160d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4485,7 +4485,8 @@
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
-        if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+        const auto it = dumpers.find(flag);
+        if (it != dumpers.end()) {
             (it->second)(args, asProto, result);
         } else if (!asProto) {
             dumpAllLocked(args, result);
@@ -4495,13 +4496,15 @@
             mStateLock.unlock();
         }
 
-        LayersProto layersProto = dumpProtoFromMainThread();
-        if (asProto) {
-            result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
-        } else {
-            auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
-            result.append(LayerProtoParser::layerTreeToString(layerTree));
-            result.append("\n");
+        if (it == dumpers.end()) {
+            const LayersProto layersProto = dumpProtoFromMainThread();
+            if (asProto) {
+                result.append(layersProto.SerializeAsString());
+            } else {
+                const auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+                result.append(LayerProtoParser::layerTreeToString(layerTree));
+                result.append("\n");
+            }
         }
     }
     write(fd, result.c_str(), result.size());
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index c97a19b..93fe7d0 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -173,8 +173,8 @@
         ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
               timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
 
-        const std::string& layerName = layerRecord.layerName;
         if (prevTimeRecord.ready) {
+            const std::string& layerName = layerRecord.layerName;
             if (!mTimeStats.stats.count(layerName)) {
                 mTimeStats.stats[layerName].layerName = layerName;
                 mTimeStats.stats[layerName].packageName = getPackageName(layerName);
@@ -220,18 +220,6 @@
                   timeRecords[0].frameTime.frameNumber, presentToPresentMs);
             timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
         }
-
-        // Output additional trace points to track frame time.
-        ATRACE_INT64(("TimeStats-Post - " + layerName).c_str(), timeRecords[0].frameTime.postTime);
-        ATRACE_INT64(("TimeStats-Acquire - " + layerName).c_str(),
-                     timeRecords[0].frameTime.acquireTime);
-        ATRACE_INT64(("TimeStats-Latch - " + layerName).c_str(),
-                     timeRecords[0].frameTime.latchTime);
-        ATRACE_INT64(("TimeStats-Desired - " + layerName).c_str(),
-                     timeRecords[0].frameTime.desiredTime);
-        ATRACE_INT64(("TimeStats-Present - " + layerName).c_str(),
-                     timeRecords[0].frameTime.presentTime);
-
         prevTimeRecord = timeRecords[0];
         timeRecords.pop_front();
         layerRecord.waitData--;
@@ -613,7 +601,7 @@
     if (asProto) {
         ALOGD("Dumping TimeStats as proto");
         SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers);
-        result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
+        result.append(timeStatsProto.SerializeAsString());
     } else {
         ALOGD("Dumping TimeStats as text");
         result.append(mTimeStats.toString(maxLayers));
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index 7747734..4934970 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -31,6 +31,5 @@
 subdirs = [
     "nulldrv",
     "libvulkan",
-    "tools",
     "vkjson",
 ]
diff --git a/vulkan/tools/Android.bp b/vulkan/tools/Android.bp
deleted file mode 100644
index 2514094..0000000
--- a/vulkan/tools/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-cc_binary {
-    name: "vkinfo",
-
-    clang: true,
-    cflags: [
-        "-fvisibility=hidden",
-        "-fstrict-aliasing",
-
-        "-DLOG_TAG=\"vkinfo\"",
-
-        "-Weverything",
-        "-Werror",
-        "-Wno-padded",
-        "-Wno-undef",
-        "-Wno-switch-enum",
-    ],
-    cppflags: [
-        "-Wno-c++98-compat-pedantic",
-        "-Wno-c99-extensions",
-        "-Wno-old-style-cast",
-    ],
-
-    srcs: ["vkinfo.cpp"],
-
-    shared_libs: [
-        "libvulkan",
-        "liblog",
-    ],
-}
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
deleted file mode 100644
index 89bc926..0000000
--- a/vulkan/tools/vkinfo.cpp
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <array>
-#include <sstream>
-#include <vector>
-
-#include <vulkan/vulkan.h>
-
-namespace {
-
-struct Options {
-    bool layer_description;
-    bool layer_extensions;
-    bool unsupported_features;
-    bool validate;
-};
-
-struct GpuInfo {
-    VkPhysicalDeviceProperties properties;
-    VkPhysicalDeviceMemoryProperties memory;
-    VkPhysicalDeviceFeatures features;
-    std::vector<VkQueueFamilyProperties> queue_families;
-    std::vector<VkExtensionProperties> extensions;
-    std::vector<VkLayerProperties> layers;
-    std::vector<std::vector<VkExtensionProperties>> layer_extensions;
-};
-struct VulkanInfo {
-    std::vector<VkExtensionProperties> extensions;
-    std::vector<VkLayerProperties> layers;
-    std::vector<std::vector<VkExtensionProperties>> layer_extensions;
-    std::vector<GpuInfo> gpus;
-};
-
-// ----------------------------------------------------------------------------
-
-[[noreturn]] void die(const char* proc, VkResult result) {
-    const char* result_str;
-    switch (result) {
-        // clang-format off
-        case VK_SUCCESS: result_str = "VK_SUCCESS"; break;
-        case VK_NOT_READY: result_str = "VK_NOT_READY"; break;
-        case VK_TIMEOUT: result_str = "VK_TIMEOUT"; break;
-        case VK_EVENT_SET: result_str = "VK_EVENT_SET"; break;
-        case VK_EVENT_RESET: result_str = "VK_EVENT_RESET"; break;
-        case VK_INCOMPLETE: result_str = "VK_INCOMPLETE"; break;
-        case VK_ERROR_OUT_OF_HOST_MEMORY: result_str = "VK_ERROR_OUT_OF_HOST_MEMORY"; break;
-        case VK_ERROR_OUT_OF_DEVICE_MEMORY: result_str = "VK_ERROR_OUT_OF_DEVICE_MEMORY"; break;
-        case VK_ERROR_INITIALIZATION_FAILED: result_str = "VK_ERROR_INITIALIZATION_FAILED"; break;
-        case VK_ERROR_DEVICE_LOST: result_str = "VK_ERROR_DEVICE_LOST"; break;
-        case VK_ERROR_MEMORY_MAP_FAILED: result_str = "VK_ERROR_MEMORY_MAP_FAILED"; break;
-        case VK_ERROR_LAYER_NOT_PRESENT: result_str = "VK_ERROR_LAYER_NOT_PRESENT"; break;
-        case VK_ERROR_EXTENSION_NOT_PRESENT: result_str = "VK_ERROR_EXTENSION_NOT_PRESENT"; break;
-        case VK_ERROR_INCOMPATIBLE_DRIVER: result_str = "VK_ERROR_INCOMPATIBLE_DRIVER"; break;
-        default: result_str = "<unknown VkResult>"; break;
-            // clang-format on
-    }
-    fprintf(stderr, "%s failed: %s (%d)\n", proc, result_str, result);
-    exit(1);
-}
-
-bool HasExtension(const std::vector<VkExtensionProperties>& extensions,
-                  const char* name) {
-    return std::find_if(extensions.cbegin(), extensions.cend(),
-                        [=](const VkExtensionProperties& prop) {
-                            return strcmp(prop.extensionName, name) == 0;
-                        }) != extensions.end();
-}
-
-void EnumerateInstanceExtensions(
-    const char* layer_name,
-    std::vector<VkExtensionProperties>* extensions) {
-    VkResult result;
-    uint32_t count;
-    result =
-        vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateInstanceExtensionProperties (count)", result);
-    do {
-        extensions->resize(count);
-        result = vkEnumerateInstanceExtensionProperties(layer_name, &count,
-                                                        extensions->data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateInstanceExtensionProperties (data)", result);
-}
-
-void EnumerateDeviceExtensions(VkPhysicalDevice gpu,
-                               const char* layer_name,
-                               std::vector<VkExtensionProperties>* extensions) {
-    VkResult result;
-    uint32_t count;
-    result =
-        vkEnumerateDeviceExtensionProperties(gpu, layer_name, &count, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateDeviceExtensionProperties (count)", result);
-    do {
-        extensions->resize(count);
-        result = vkEnumerateDeviceExtensionProperties(gpu, layer_name, &count,
-                                                      extensions->data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateDeviceExtensionProperties (data)", result);
-}
-
-void GatherGpuInfo(VkPhysicalDevice gpu,
-                   const Options &options,
-                   GpuInfo& info) {
-    VkResult result;
-    uint32_t count;
-
-    vkGetPhysicalDeviceProperties(gpu, &info.properties);
-    vkGetPhysicalDeviceMemoryProperties(gpu, &info.memory);
-    vkGetPhysicalDeviceFeatures(gpu, &info.features);
-
-    vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, nullptr);
-    info.queue_families.resize(count);
-    vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count,
-                                             info.queue_families.data());
-
-    result = vkEnumerateDeviceLayerProperties(gpu, &count, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateDeviceLayerProperties (count)", result);
-    do {
-        info.layers.resize(count);
-        result =
-            vkEnumerateDeviceLayerProperties(gpu, &count, info.layers.data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateDeviceLayerProperties (data)", result);
-    info.layer_extensions.resize(info.layers.size());
-
-    EnumerateDeviceExtensions(gpu, nullptr, &info.extensions);
-    for (size_t i = 0; i < info.layers.size(); i++) {
-        EnumerateDeviceExtensions(gpu, info.layers[i].layerName,
-                                  &info.layer_extensions[i]);
-    }
-
-    const std::array<const char*, 1> kDesiredExtensions = {
-        {VK_KHR_SWAPCHAIN_EXTENSION_NAME},
-    };
-    const char* extensions[kDesiredExtensions.size()];
-    uint32_t num_extensions = 0;
-    for (const auto& desired_ext : kDesiredExtensions) {
-        bool available = HasExtension(info.extensions, desired_ext);
-        if (options.validate) {
-            for (size_t i = 0; !available && i < info.layer_extensions.size();
-                 i++)
-                available = HasExtension(info.layer_extensions[i], desired_ext);
-        }
-        if (available)
-            extensions[num_extensions++] = desired_ext;
-    }
-
-    VkDevice device;
-    float queue_priorities[] = {0.0};
-    const VkDeviceQueueCreateInfo queue_create_info = {
-        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
-        .queueFamilyIndex = 0,
-        .queueCount = 1,
-        .pQueuePriorities = queue_priorities
-    };
-    // clang-format off
-    const char *kValidationLayers[] = {
-        "VK_LAYER_GOOGLE_threading",
-        "VK_LAYER_LUNARG_parameter_validation",
-        "VK_LAYER_LUNARG_device_limits",
-        "VK_LAYER_LUNARG_object_tracker",
-        "VK_LAYER_LUNARG_image",
-        "VK_LAYER_LUNARG_core_validation",
-        "VK_LAYER_LUNARG_swapchain",
-        "VK_LAYER_GOOGLE_unique_objects"
-    };
-    // clang-format on
-    uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*);
-    const VkDeviceCreateInfo create_info = {
-        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
-        .queueCreateInfoCount = 1,
-        .pQueueCreateInfos = &queue_create_info,
-        .enabledExtensionCount = num_extensions,
-        .ppEnabledExtensionNames = extensions,
-        .enabledLayerCount = (options.validate) ? num_layers : 0,
-        .ppEnabledLayerNames = kValidationLayers,
-        .pEnabledFeatures = &info.features,
-    };
-    result = vkCreateDevice(gpu, &create_info, nullptr, &device);
-    if (result != VK_SUCCESS)
-        die("vkCreateDevice", result);
-    vkDestroyDevice(device, nullptr);
-}
-
-void GatherInfo(VulkanInfo* info, const Options& options) {
-    VkResult result;
-    uint32_t count;
-
-    result = vkEnumerateInstanceLayerProperties(&count, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateInstanceLayerProperties (count)", result);
-    do {
-        info->layers.resize(count);
-        result =
-            vkEnumerateInstanceLayerProperties(&count, info->layers.data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateInstanceLayerProperties (data)", result);
-    info->layer_extensions.resize(info->layers.size());
-
-    EnumerateInstanceExtensions(nullptr, &info->extensions);
-    for (size_t i = 0; i < info->layers.size(); i++) {
-        EnumerateInstanceExtensions(info->layers[i].layerName,
-                                    &info->layer_extensions[i]);
-    }
-
-    const char* kDesiredExtensions[] = {
-        VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
-    };
-    const char*
-        extensions[sizeof(kDesiredExtensions) / sizeof(kDesiredExtensions[0])];
-    uint32_t num_extensions = 0;
-    for (const auto& desired_ext : kDesiredExtensions) {
-        bool available = HasExtension(info->extensions, desired_ext);
-        if (options.validate) {
-            for (size_t i = 0; !available && i < info->layer_extensions.size();
-                 i++)
-                available =
-                    HasExtension(info->layer_extensions[i], desired_ext);
-        }
-        if (available)
-            extensions[num_extensions++] = desired_ext;
-    }
-
-    // clang-format off
-    const char *kValidationLayers[] = {
-        "VK_LAYER_GOOGLE_threading",
-        "VK_LAYER_LUNARG_parameter_validation",
-        "VK_LAYER_LUNARG_device_limits",
-        "VK_LAYER_LUNARG_object_tracker",
-        "VK_LAYER_LUNARG_image",
-        "VK_LAYER_LUNARG_core_validation",
-        "VK_LAYER_LUNARG_swapchain",
-        "VK_LAYER_GOOGLE_unique_objects"
-    };
-    // clang-format on
-    uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*);
-
-    const VkApplicationInfo application_info = {
-        .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
-        .pApplicationName = "vkinfo",
-        .applicationVersion = 0,
-        .pEngineName = "vkinfo",
-        .engineVersion = 0,
-        .apiVersion = VK_API_VERSION_1_0,
-    };
-    const VkInstanceCreateInfo create_info = {
-        .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
-        .pApplicationInfo = &application_info,
-        .enabledExtensionCount = num_extensions,
-        .ppEnabledExtensionNames = extensions,
-        .enabledLayerCount = (options.validate) ? num_layers : 0,
-        .ppEnabledLayerNames = kValidationLayers,
-    };
-    VkInstance instance;
-    result = vkCreateInstance(&create_info, nullptr, &instance);
-    if (result != VK_SUCCESS)
-        die("vkCreateInstance", result);
-
-    uint32_t num_gpus;
-    result = vkEnumeratePhysicalDevices(instance, &num_gpus, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumeratePhysicalDevices (count)", result);
-    std::vector<VkPhysicalDevice> gpus(num_gpus, VK_NULL_HANDLE);
-    do {
-        gpus.resize(num_gpus, VK_NULL_HANDLE);
-        result = vkEnumeratePhysicalDevices(instance, &num_gpus, gpus.data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumeratePhysicalDevices (data)", result);
-
-    info->gpus.resize(num_gpus);
-    for (size_t i = 0; i < gpus.size(); i++)
-        GatherGpuInfo(gpus[i], options, info->gpus.at(i));
-
-    vkDestroyInstance(instance, nullptr);
-}
-
-// ----------------------------------------------------------------------------
-
-const size_t kMaxIndent = 8;
-const size_t kIndentSize = 3;
-std::array<char, kMaxIndent * kIndentSize + 1> kIndent;
-const char* Indent(size_t n) {
-    static bool initialized = false;
-    if (!initialized) {
-        kIndent.fill(' ');
-        kIndent.back() = '\0';
-        initialized = true;
-    }
-    return kIndent.data() +
-           (kIndent.size() - (kIndentSize * std::min(n, kMaxIndent) + 1));
-}
-
-const char* VkPhysicalDeviceTypeStr(VkPhysicalDeviceType type) {
-    switch (type) {
-        case VK_PHYSICAL_DEVICE_TYPE_OTHER:
-            return "OTHER";
-        case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
-            return "INTEGRATED_GPU";
-        case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
-            return "DISCRETE_GPU";
-        case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
-            return "VIRTUAL_GPU";
-        case VK_PHYSICAL_DEVICE_TYPE_CPU:
-            return "CPU";
-        default:
-            return "<UNKNOWN>";
-    }
-}
-
-void PrintExtensions(const std::vector<VkExtensionProperties>& extensions,
-                     const Options& /*options*/,
-                     size_t indent) {
-    for (const auto& e : extensions)
-        printf("%s%s (v%u)\n", Indent(indent), e.extensionName, e.specVersion);
-}
-
-void PrintLayers(
-    const std::vector<VkLayerProperties>& layers,
-    const std::vector<std::vector<VkExtensionProperties>> extensions,
-    const Options& options,
-    size_t indent) {
-    for (size_t i = 0; i < layers.size(); i++) {
-        printf("%s%s %u.%u.%u/%u\n", Indent(indent), layers[i].layerName,
-               VK_VERSION_MAJOR(layers[i].specVersion),
-               VK_VERSION_MINOR(layers[i].specVersion),
-               VK_VERSION_PATCH(layers[i].specVersion),
-               layers[i].implementationVersion);
-        if (options.layer_description)
-            printf("%s%s\n", Indent(indent + 1), layers[i].description);
-        if (options.layer_extensions && !extensions[i].empty()) {
-            if (!extensions[i].empty()) {
-                printf("%sExtensions [%zu]:\n", Indent(indent + 1),
-                       extensions[i].size());
-                PrintExtensions(extensions[i], options, indent + 2);
-            }
-        }
-    }
-}
-
-void PrintAllFeatures(const char* indent,
-                      const VkPhysicalDeviceFeatures& features) {
-    // clang-format off
-    printf("%srobustBufferAccess: %s\n", indent, features.robustBufferAccess ? "YES" : "NO");
-    printf("%sfullDrawIndexUint32: %s\n", indent, features.fullDrawIndexUint32 ? "YES" : "NO");
-    printf("%simageCubeArray: %s\n", indent, features.imageCubeArray ? "YES" : "NO");
-    printf("%sindependentBlend: %s\n", indent, features.independentBlend ? "YES" : "NO");
-    printf("%sgeometryShader: %s\n", indent, features.geometryShader ? "YES" : "NO");
-    printf("%stessellationShader: %s\n", indent, features.tessellationShader ? "YES" : "NO");
-    printf("%ssampleRateShading: %s\n", indent, features.sampleRateShading ? "YES" : "NO");
-    printf("%sdualSrcBlend: %s\n", indent, features.dualSrcBlend ? "YES" : "NO");
-    printf("%slogicOp: %s\n", indent, features.logicOp ? "YES" : "NO");
-    printf("%smultiDrawIndirect: %s\n", indent, features.multiDrawIndirect ? "YES" : "NO");
-    printf("%sdrawIndirectFirstInstance: %s\n", indent, features.drawIndirectFirstInstance ? "YES" : "NO");
-    printf("%sdepthClamp: %s\n", indent, features.depthClamp ? "YES" : "NO");
-    printf("%sdepthBiasClamp: %s\n", indent, features.depthBiasClamp ? "YES" : "NO");
-    printf("%sfillModeNonSolid: %s\n", indent, features.fillModeNonSolid ? "YES" : "NO");
-    printf("%sdepthBounds: %s\n", indent, features.depthBounds ? "YES" : "NO");
-    printf("%swideLines: %s\n", indent, features.wideLines ? "YES" : "NO");
-    printf("%slargePoints: %s\n", indent, features.largePoints ? "YES" : "NO");
-    printf("%salphaToOne: %s\n", indent, features.alphaToOne ? "YES" : "NO");
-    printf("%smultiViewport: %s\n", indent, features.multiViewport ? "YES" : "NO");
-    printf("%ssamplerAnisotropy: %s\n", indent, features.samplerAnisotropy ? "YES" : "NO");
-    printf("%stextureCompressionETC2: %s\n", indent, features.textureCompressionETC2 ? "YES" : "NO");
-    printf("%stextureCompressionASTC_LDR: %s\n", indent, features.textureCompressionASTC_LDR ? "YES" : "NO");
-    printf("%stextureCompressionBC: %s\n", indent, features.textureCompressionBC ? "YES" : "NO");
-    printf("%socclusionQueryPrecise: %s\n", indent, features.occlusionQueryPrecise ? "YES" : "NO");
-    printf("%spipelineStatisticsQuery: %s\n", indent, features.pipelineStatisticsQuery ? "YES" : "NO");
-    printf("%svertexPipelineStoresAndAtomics: %s\n", indent, features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
-    printf("%sfragmentStoresAndAtomics: %s\n", indent, features.fragmentStoresAndAtomics ? "YES" : "NO");
-    printf("%sshaderTessellationAndGeometryPointSize: %s\n", indent, features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
-    printf("%sshaderImageGatherExtended: %s\n", indent, features.shaderImageGatherExtended ? "YES" : "NO");
-    printf("%sshaderStorageImageExtendedFormats: %s\n", indent, features.shaderStorageImageExtendedFormats ? "YES" : "NO");
-    printf("%sshaderStorageImageMultisample: %s\n", indent, features.shaderStorageImageMultisample ? "YES" : "NO");
-    printf("%sshaderStorageImageReadWithoutFormat: %s\n", indent, features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
-    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", indent, features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
-    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", indent, features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", indent, features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", indent, features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", indent, features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderClipDistance: %s\n", indent, features.shaderClipDistance ? "YES" : "NO");
-    printf("%sshaderCullDistance: %s\n", indent, features.shaderCullDistance ? "YES" : "NO");
-    printf("%sshaderFloat64: %s\n", indent, features.shaderFloat64 ? "YES" : "NO");
-    printf("%sshaderInt64: %s\n", indent, features.shaderInt64 ? "YES" : "NO");
-    printf("%sshaderInt16: %s\n", indent, features.shaderInt16 ? "YES" : "NO");
-    printf("%sshaderResourceResidency: %s\n", indent, features.shaderResourceResidency ? "YES" : "NO");
-    printf("%sshaderResourceMinLod: %s\n", indent, features.shaderResourceMinLod ? "YES" : "NO");
-    printf("%ssparseBinding: %s\n", indent, features.sparseBinding ? "YES" : "NO");
-    printf("%ssparseResidencyBuffer: %s\n", indent, features.sparseResidencyBuffer ? "YES" : "NO");
-    printf("%ssparseResidencyImage2D: %s\n", indent, features.sparseResidencyImage2D ? "YES" : "NO");
-    printf("%ssparseResidencyImage3D: %s\n", indent, features.sparseResidencyImage3D ? "YES" : "NO");
-    printf("%ssparseResidency2Samples: %s\n", indent, features.sparseResidency2Samples ? "YES" : "NO");
-    printf("%ssparseResidency4Samples: %s\n", indent, features.sparseResidency4Samples ? "YES" : "NO");
-    printf("%ssparseResidency8Samples: %s\n", indent, features.sparseResidency8Samples ? "YES" : "NO");
-    printf("%ssparseResidency16Samples: %s\n", indent, features.sparseResidency16Samples ? "YES" : "NO");
-    printf("%ssparseResidencyAliased: %s\n", indent, features.sparseResidencyAliased ? "YES" : "NO");
-    printf("%svariableMultisampleRate: %s\n", indent, features.variableMultisampleRate ? "YES" : "NO");
-    printf("%sinheritedQueries: %s\n", indent, features.inheritedQueries ? "YES" : "NO");
-    // clang-format on
-}
-
-void PrintSupportedFeatures(const char* indent,
-                            const VkPhysicalDeviceFeatures& features) {
-    // clang-format off
-    if (features.robustBufferAccess) printf("%srobustBufferAccess\n", indent);
-    if (features.fullDrawIndexUint32) printf("%sfullDrawIndexUint32\n", indent);
-    if (features.imageCubeArray) printf("%simageCubeArray\n", indent);
-    if (features.independentBlend) printf("%sindependentBlend\n", indent);
-    if (features.geometryShader) printf("%sgeometryShader\n", indent);
-    if (features.tessellationShader) printf("%stessellationShader\n", indent);
-    if (features.sampleRateShading) printf("%ssampleRateShading\n", indent);
-    if (features.dualSrcBlend) printf("%sdualSrcBlend\n", indent);
-    if (features.logicOp) printf("%slogicOp\n", indent);
-    if (features.multiDrawIndirect) printf("%smultiDrawIndirect\n", indent);
-    if (features.drawIndirectFirstInstance) printf("%sdrawIndirectFirstInstance\n", indent);
-    if (features.depthClamp) printf("%sdepthClamp\n", indent);
-    if (features.depthBiasClamp) printf("%sdepthBiasClamp\n", indent);
-    if (features.fillModeNonSolid) printf("%sfillModeNonSolid\n", indent);
-    if (features.depthBounds) printf("%sdepthBounds\n", indent);
-    if (features.wideLines) printf("%swideLines\n", indent);
-    if (features.largePoints) printf("%slargePoints\n", indent);
-    if (features.alphaToOne) printf("%salphaToOne\n", indent);
-    if (features.multiViewport) printf("%smultiViewport\n", indent);
-    if (features.samplerAnisotropy) printf("%ssamplerAnisotropy\n", indent);
-    if (features.textureCompressionETC2) printf("%stextureCompressionETC2\n", indent);
-    if (features.textureCompressionASTC_LDR) printf("%stextureCompressionASTC_LDR\n", indent);
-    if (features.textureCompressionBC) printf("%stextureCompressionBC\n", indent);
-    if (features.occlusionQueryPrecise) printf("%socclusionQueryPrecise\n", indent);
-    if (features.pipelineStatisticsQuery) printf("%spipelineStatisticsQuery\n", indent);
-    if (features.vertexPipelineStoresAndAtomics) printf("%svertexPipelineStoresAndAtomics\n", indent);
-    if (features.fragmentStoresAndAtomics) printf("%sfragmentStoresAndAtomics\n", indent);
-    if (features.shaderTessellationAndGeometryPointSize) printf("%sshaderTessellationAndGeometryPointSize\n", indent);
-    if (features.shaderImageGatherExtended) printf("%sshaderImageGatherExtended\n", indent);
-    if (features.shaderStorageImageExtendedFormats) printf("%sshaderStorageImageExtendedFormats\n", indent);
-    if (features.shaderStorageImageMultisample) printf("%sshaderStorageImageMultisample\n", indent);
-    if (features.shaderStorageImageReadWithoutFormat) printf("%sshaderStorageImageReadWithoutFormat\n", indent);
-    if (features.shaderStorageImageWriteWithoutFormat) printf("%sshaderStorageImageWriteWithoutFormat\n", indent);
-    if (features.shaderUniformBufferArrayDynamicIndexing) printf("%sshaderUniformBufferArrayDynamicIndexing\n", indent);
-    if (features.shaderSampledImageArrayDynamicIndexing) printf("%sshaderSampledImageArrayDynamicIndexing\n", indent);
-    if (features.shaderStorageBufferArrayDynamicIndexing) printf("%sshaderStorageBufferArrayDynamicIndexing\n", indent);
-    if (features.shaderStorageImageArrayDynamicIndexing) printf("%sshaderStorageImageArrayDynamicIndexing\n", indent);
-    if (features.shaderClipDistance) printf("%sshaderClipDistance\n", indent);
-    if (features.shaderCullDistance) printf("%sshaderCullDistance\n", indent);
-    if (features.shaderFloat64) printf("%sshaderFloat64\n", indent);
-    if (features.shaderInt64) printf("%sshaderInt64\n", indent);
-    if (features.shaderInt16) printf("%sshaderInt16\n", indent);
-    if (features.shaderResourceResidency) printf("%sshaderResourceResidency\n", indent);
-    if (features.shaderResourceMinLod) printf("%sshaderResourceMinLod\n", indent);
-    if (features.sparseBinding) printf("%ssparseBinding\n", indent);
-    if (features.sparseResidencyBuffer) printf("%ssparseResidencyBuffer\n", indent);
-    if (features.sparseResidencyImage2D) printf("%ssparseResidencyImage2D\n", indent);
-    if (features.sparseResidencyImage3D) printf("%ssparseResidencyImage3D\n", indent);
-    if (features.sparseResidency2Samples) printf("%ssparseResidency2Samples\n", indent);
-    if (features.sparseResidency4Samples) printf("%ssparseResidency4Samples\n", indent);
-    if (features.sparseResidency8Samples) printf("%ssparseResidency8Samples\n", indent);
-    if (features.sparseResidency16Samples) printf("%ssparseResidency16Samples\n", indent);
-    if (features.sparseResidencyAliased) printf("%ssparseResidencyAliased\n", indent);
-    if (features.variableMultisampleRate) printf("%svariableMultisampleRate\n", indent);
-    if (features.inheritedQueries) printf("%sinheritedQueries\n", indent);
-    // clang-format on
-}
-
-void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) {
-    VkResult result;
-    std::ostringstream strbuf;
-
-    printf("%s\"%s\" (%s) %u.%u.%u/%#x [%04x:%04x]\n", Indent(indent),
-           info.properties.deviceName,
-           VkPhysicalDeviceTypeStr(info.properties.deviceType),
-           VK_VERSION_MAJOR(info.properties.apiVersion),
-           VK_VERSION_MINOR(info.properties.apiVersion),
-           VK_VERSION_PATCH(info.properties.apiVersion),
-           info.properties.driverVersion, info.properties.vendorID,
-           info.properties.deviceID);
-
-    for (uint32_t heap = 0; heap < info.memory.memoryHeapCount; heap++) {
-        if ((info.memory.memoryHeaps[heap].flags &
-             VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
-            strbuf << "DEVICE_LOCAL";
-        printf("%sHeap %u: %" PRIu64 " MiB (0x%" PRIx64 " B) %s\n",
-               Indent(indent + 1), heap,
-               info.memory.memoryHeaps[heap].size / 0x100000,
-               info.memory.memoryHeaps[heap].size, strbuf.str().c_str());
-        strbuf.str(std::string());
-
-        for (uint32_t type = 0; type < info.memory.memoryTypeCount; type++) {
-            if (info.memory.memoryTypes[type].heapIndex != heap)
-                continue;
-            VkMemoryPropertyFlags flags =
-                info.memory.memoryTypes[type].propertyFlags;
-            if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
-                strbuf << " DEVICE_LOCAL";
-            if ((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
-                strbuf << " HOST_VISIBLE";
-            if ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
-                strbuf << " COHERENT";
-            if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
-                strbuf << " CACHED";
-            if ((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
-                strbuf << " LAZILY_ALLOCATED";
-            printf("%sType %u:%s\n", Indent(indent + 2), type,
-                   strbuf.str().c_str());
-            strbuf.str(std::string());
-        }
-    }
-
-    for (uint32_t family = 0; family < info.queue_families.size(); family++) {
-        const VkQueueFamilyProperties& qprops = info.queue_families[family];
-        VkQueueFlags flags = qprops.queueFlags;
-        char flags_str[5];
-        flags_str[0] = (flags & VK_QUEUE_GRAPHICS_BIT) ? 'G' : '_';
-        flags_str[1] = (flags & VK_QUEUE_COMPUTE_BIT) ? 'C' : '_';
-        flags_str[2] = (flags & VK_QUEUE_TRANSFER_BIT) ? 'T' : '_';
-        flags_str[3] = (flags & VK_QUEUE_SPARSE_BINDING_BIT) ? 'S' : '_';
-        flags_str[4] = '\0';
-        printf(
-            "%sQueue Family %u: %ux %s\n"
-            "%stimestampValidBits: %ub\n"
-            "%sminImageTransferGranularity: (%u,%u,%u)\n",
-            Indent(indent + 1), family, qprops.queueCount, flags_str,
-            Indent(indent + 2), qprops.timestampValidBits, Indent(indent + 2),
-            qprops.minImageTransferGranularity.width,
-            qprops.minImageTransferGranularity.height,
-            qprops.minImageTransferGranularity.depth);
-    }
-
-    printf("%sFeatures:\n", Indent(indent + 1));
-    if (options.unsupported_features) {
-        PrintAllFeatures(Indent(indent + 2), info.features);
-    } else {
-        PrintSupportedFeatures(Indent(indent + 2), info.features);
-    }
-
-    printf("%sExtensions [%zu]:\n", Indent(indent + 1), info.extensions.size());
-    if (!info.extensions.empty())
-        PrintExtensions(info.extensions, options, indent + 2);
-    printf("%sLayers [%zu]:\n", Indent(indent + 1), info.layers.size());
-    if (!info.layers.empty())
-        PrintLayers(info.layers, info.layer_extensions, options, indent + 2);
-}
-
-void PrintInfo(const VulkanInfo& info, const Options& options) {
-    std::ostringstream strbuf;
-    size_t indent = 0;
-
-    printf("%sInstance Extensions [%zu]:\n", Indent(indent),
-           info.extensions.size());
-    PrintExtensions(info.extensions, options, indent + 1);
-    printf("%sInstance Layers [%zu]:\n", Indent(indent), info.layers.size());
-    if (!info.layers.empty())
-        PrintLayers(info.layers, info.layer_extensions, options, indent + 1);
-
-    printf("%sPhysicalDevices [%zu]:\n", Indent(indent), info.gpus.size());
-    for (const auto& gpu : info.gpus)
-        PrintGpuInfo(gpu, options, indent + 1);
-}
-
-const char kUsageString[] =
-    "usage: vkinfo [options]\n"
-    "  -v                       enable all the following verbose options\n"
-    "    -layer_description     print layer description strings\n"
-    "    -layer_extensions      print extensions supported by each layer\n"
-    "    -unsupported_features  print all physical device features\n"
-    "  -validate                enable validation layers if present\n"
-    "  -debug_pause             pause at start until resumed via debugger\n";
-
-}  // namespace
-
-// ----------------------------------------------------------------------------
-
-int main(int argc, char const* argv[]) {
-    static volatile bool startup_pause = false;
-    Options options = {
-        .layer_description = false, .layer_extensions = false,
-        .unsupported_features = false,
-        .validate = false,
-    };
-    for (int argi = 1; argi < argc; argi++) {
-        if (strcmp(argv[argi], "-h") == 0) {
-            fputs(kUsageString, stdout);
-            return 0;
-        }
-        if (strcmp(argv[argi], "-v") == 0) {
-            options.layer_description = true;
-            options.layer_extensions = true;
-            options.unsupported_features = true;
-        } else if (strcmp(argv[argi], "-layer_description") == 0) {
-            options.layer_description = true;
-        } else if (strcmp(argv[argi], "-layer_extensions") == 0) {
-            options.layer_extensions = true;
-        } else if (strcmp(argv[argi], "-unsupported_features") == 0) {
-            options.unsupported_features = true;
-        } else if (strcmp(argv[argi], "-validate") == 0) {
-            options.validate = true;
-        } else if (strcmp(argv[argi], "-debug_pause") == 0) {
-            startup_pause = true;
-        }
-    }
-
-    while (startup_pause) {
-        sleep(0);
-    }
-
-    VulkanInfo info;
-    GatherInfo(&info, options);
-    PrintInfo(info, options);
-    return 0;
-}