Merge "Added gpu memory stats to gpuservice dump"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index bdd03f9..8d383f5 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -86,6 +86,7 @@
         "libdumpstateaidl",
         "libdumpstateutil",
         "libdumputils",
+        "libhardware_legacy",
         "libhidlbase",
         "libhidltransport",
         "liblog",
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 33e35f7..bbc724c 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -68,7 +68,8 @@
     }
 
     static const std::vector<std::string> group_names{
-        "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", "readproc", "bluetooth"};
+        "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats",
+            "readproc", "bluetooth", "wakelock"};
     std::vector<gid_t> groups(group_names.size(), 0);
     for (size_t i = 0; i < group_names.size(); ++i) {
         grp = getgrnam(group_names[i].c_str());
@@ -116,6 +117,11 @@
         capdata[cap_syslog_index].effective |= cap_syslog_mask;
     }
 
+    const uint32_t cap_block_suspend_mask = CAP_TO_MASK(CAP_BLOCK_SUSPEND);
+    const uint32_t cap_block_suspend_index = CAP_TO_INDEX(CAP_BLOCK_SUSPEND);
+    capdata[cap_block_suspend_index].permitted |= cap_block_suspend_mask;
+    capdata[cap_block_suspend_index].effective |= cap_block_suspend_mask;
+
     if (capset(&capheader, &capdata[0]) != 0) {
         MYLOGE("capset({%#x, %#x}) failed: %s\n", capdata[0].effective,
                capdata[1].effective, strerror(errno));
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2bcb17d..1416629 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -71,6 +71,7 @@
 #include <debuggerd/client.h>
 #include <dumpsys.h>
 #include <dumputils/dump_utils.h>
+#include <hardware_legacy/power.h>
 #include <hidl/ServiceManagement.h>
 #include <log/log.h>
 #include <openssl/sha.h>
@@ -177,6 +178,8 @@
     func_ptr(__VA_ARGS__);                                  \
     RETURN_IF_USER_DENIED_CONSENT();
 
+static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
+
 namespace android {
 namespace os {
 namespace {
@@ -2468,6 +2471,13 @@
 
     MYLOGI("begin\n");
 
+    if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
+        MYLOGE("Failed to acquire wake lock: %s\n", strerror(errno));
+    } else {
+        // Wake lock will be released automatically on process death
+        MYLOGD("Wake lock acquired.\n");
+    }
+
     register_sig_handler();
 
     // TODO(b/111441001): maybe skip if already started?
diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp
index 9513ec1..a5b1ac5 100644
--- a/cmds/service/Android.bp
+++ b/cmds/service/Android.bp
@@ -4,6 +4,7 @@
     srcs: ["service.cpp"],
 
     shared_libs: [
+        "libcutils",
         "libutils",
         "libbinder",
     ],
@@ -22,6 +23,7 @@
     srcs: ["service.cpp"],
 
     shared_libs: [
+        "libcutils",
         "libutils",
         "libbinder",
     ],
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 543357c..18b6b58 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -18,13 +18,18 @@
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
 #include <binder/TextOutput.h>
+#include <cutils/ashmem.h>
 
 #include <getopt.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/mman.h>
 #include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 using namespace android;
 
@@ -187,6 +192,57 @@
                         } else if (strcmp(argv[optind], "null") == 0) {
                             optind++;
                             data.writeStrongBinder(nullptr);
+                        } else if (strcmp(argv[optind], "fd") == 0) {
+                            optind++;
+                            if (optind >= argc) {
+                                aerr << "service: no path supplied for 'fd'" << endl;
+                                wantsUsage = true;
+                                result = 10;
+                                break;
+                            }
+                            const char *path = argv[optind++];
+                            int fd = open(path, O_RDONLY);
+                            if (fd < 0) {
+                                aerr << "service: could not open '" << path << "'" << endl;
+                                wantsUsage = true;
+                                result = 10;
+                                break;
+                            }
+                            data.writeFileDescriptor(fd, true /* take ownership */);
+                        } else if (strcmp(argv[optind], "afd") == 0) {
+                            optind++;
+                            if (optind >= argc) {
+                                aerr << "service: no path supplied for 'afd'" << endl;
+                                wantsUsage = true;
+                                result = 10;
+                                break;
+                            }
+                            const char *path = argv[optind++];
+                            int fd = open(path, O_RDONLY);
+                            struct stat statbuf;
+                            if (fd < 0 || fstat(fd, &statbuf) != 0) {
+                                aerr << "service: could not open or stat '" << path << "'" << endl;
+                                wantsUsage = true;
+                                result = 10;
+                                break;
+                            }
+                            int afd = ashmem_create_region("test", statbuf.st_size);
+                            void* ptr = mmap(NULL, statbuf.st_size,
+                                   PROT_READ | PROT_WRITE, MAP_SHARED, afd, 0);
+                            read(fd, ptr, statbuf.st_size);
+                            close(fd);
+                            data.writeFileDescriptor(afd, true /* take ownership */);
+                        } else if (strcmp(argv[optind], "nfd") == 0) {
+                            optind++;
+                            if (optind >= argc) {
+                                aerr << "service: no file descriptor supplied for 'nfd'" << endl;
+                                wantsUsage = true;
+                                result = 10;
+                                break;
+                            }
+                            data.writeFileDescriptor(
+                                    atoi(argv[optind++]), true /* take ownership */);
+
                         } else if (strcmp(argv[optind], "intent") == 0) {
 
                             char* action = nullptr;
@@ -300,13 +356,19 @@
         aout << "Usage: service [-h|-?]\n"
                 "       service list\n"
                 "       service check SERVICE\n"
-                "       service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR ] ...\n"
+                "       service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR | null"
+                " | fd f | nfd n | afd f ] ...\n"
                 "Options:\n"
                 "   i32: Write the 32-bit integer N into the send parcel.\n"
                 "   i64: Write the 64-bit integer N into the send parcel.\n"
                 "   f:   Write the 32-bit single-precision number N into the send parcel.\n"
                 "   d:   Write the 64-bit double-precision number N into the send parcel.\n"
-                "   s16: Write the UTF-16 string STR into the send parcel.\n";
+                "   s16: Write the UTF-16 string STR into the send parcel.\n"
+                "  null: Write a null binder into the send parcel.\n"
+                "    fd: Write a file descriptor for the file f to the send parcel.\n"
+                "   nfd: Write file descriptor n to the send parcel.\n"
+                "   afd: Write an ashmem file descriptor for a region containing the data from"
+                " file f to the send parcel.\n";
 //                "   intent: Write and Intent int the send parcel. ARGS can be\n"
 //                "       action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n";
         return result;
diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp
index f4005c4..d936dbe 100644
--- a/cmds/servicemanager/Access.cpp
+++ b/cmds/servicemanager/Access.cpp
@@ -69,7 +69,7 @@
         return 0;
     }
 
-    snprintf(buf, len, "service=%s pid=%d uid=%d", ad->name.c_str(), ad->debugPid, ad->uid);
+    snprintf(buf, len, "pid=%d uid=%d", ad->debugPid, ad->uid);
     return 0;
 }
 
@@ -91,7 +91,7 @@
     freecon(mThisProcessContext);
 }
 
-Access::CallingContext Access::getCallingContext(const std::string& name) {
+Access::CallingContext Access::getCallingContext() {
     IPCThreadState* ipc = IPCThreadState::self();
 
     const char* callingSid = ipc->getCallingSid();
@@ -101,21 +101,18 @@
         .debugPid = callingPid,
         .uid = ipc->getCallingUid(),
         .sid = callingSid ? std::string(callingSid) : getPidcon(callingPid),
-        .name = name,
     };
 }
 
-bool Access::canFind(const CallingContext& ctx) {
-    return actionAllowedFromLookup(ctx, "find");
+bool Access::canFind(const CallingContext& ctx,const std::string& name) {
+    return actionAllowedFromLookup(ctx, name, "find");
 }
 
-bool Access::canAdd(const CallingContext& ctx) {
-    return actionAllowedFromLookup(ctx, "add");
+bool Access::canAdd(const CallingContext& ctx, const std::string& name) {
+    return actionAllowedFromLookup(ctx, name, "add");
 }
 
 bool Access::canList(const CallingContext& ctx) {
-    CHECK(ctx.name == "");
-
     return actionAllowed(ctx, mThisProcessContext, "list");
 }
 
@@ -125,10 +122,10 @@
     return 0 == selinux_check_access(sctx.sid.c_str(), tctx, tclass, perm, reinterpret_cast<void*>(const_cast<CallingContext*>((&sctx))));
 }
 
-bool Access::actionAllowedFromLookup(const CallingContext& sctx, const char *perm) {
+bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::string& name, const char *perm) {
     char *tctx = nullptr;
-    if (selabel_lookup(getSehandle(), &tctx, sctx.name.c_str(), 0) != 0) {
-        LOG(ERROR) << "SELinux: No match for " << sctx.name << " in service_contexts.\n";
+    if (selabel_lookup(getSehandle(), &tctx, name.c_str(), 0) != 0) {
+        LOG(ERROR) << "SELinux: No match for " << name << " in service_contexts.\n";
         return false;
     }
 
diff --git a/cmds/servicemanager/Access.h b/cmds/servicemanager/Access.h
index b2c78cc..05a60d3 100644
--- a/cmds/servicemanager/Access.h
+++ b/cmds/servicemanager/Access.h
@@ -36,22 +36,18 @@
         pid_t debugPid;
         uid_t uid;
         std::string sid;
-
-        // name of the service
-        //
-        // empty if call is unrelated to service (e.g. list)
-        std::string name;
     };
 
-    virtual CallingContext getCallingContext(const std::string& name);
+    virtual CallingContext getCallingContext();
 
-    virtual bool canFind(const CallingContext& ctx);
-    virtual bool canAdd(const CallingContext& ctx);
+    virtual bool canFind(const CallingContext& ctx, const std::string& name);
+    virtual bool canAdd(const CallingContext& ctx, const std::string& name);
     virtual bool canList(const CallingContext& ctx);
 
 private:
     bool actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm);
-    bool actionAllowedFromLookup(const CallingContext& sctx, const char *perm);
+    bool actionAllowedFromLookup(const CallingContext& sctx, const std::string& name,
+            const char *perm);
 
     char* mThisProcessContext = nullptr;
 };
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 6cfcf40..c2c71e0 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -32,7 +32,7 @@
 }
 
 Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) {
-    auto ctx = mAccess->getCallingContext(name);
+    auto ctx = mAccess->getCallingContext();
 
     auto it = mNameToService.find(name);
     if (it == mNameToService.end()) {
@@ -53,7 +53,7 @@
     }
 
     // TODO(b/136023468): move this check to be first
-    if (!mAccess->canFind(ctx)) {
+    if (!mAccess->canFind(ctx, name)) {
         // returns ok and null for legacy reasons
         *outBinder = nullptr;
         return Status::ok();
@@ -79,14 +79,14 @@
 }
 
 Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
-    auto ctx = mAccess->getCallingContext(name);
+    auto ctx = mAccess->getCallingContext();
 
     // apps cannot add services
     if (multiuser_get_app_id(ctx.uid) >= AID_APP) {
         return Status::fromExceptionCode(Status::EX_SECURITY);
     }
 
-    if (!mAccess->canAdd(ctx)) {
+    if (!mAccess->canAdd(ctx, name)) {
         return Status::fromExceptionCode(Status::EX_SECURITY);
     }
 
@@ -121,7 +121,7 @@
 }
 
 Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::string>* outList) {
-    if (!mAccess->canList(mAccess->getCallingContext(""))) {
+    if (!mAccess->canList(mAccess->getCallingContext())) {
         return Status::fromExceptionCode(Status::EX_SECURITY);
     }
 
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 25d2aa8..91485e4 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -40,18 +40,18 @@
 
 class MockAccess : public Access {
 public:
-    MOCK_METHOD1(getCallingContext, CallingContext(const std::string& name));
-    MOCK_METHOD1(canAdd, bool(const CallingContext&));
-    MOCK_METHOD1(canFind, bool(const CallingContext&));
+    MOCK_METHOD0(getCallingContext, CallingContext());
+    MOCK_METHOD2(canAdd, bool(const CallingContext&, const std::string& name));
+    MOCK_METHOD2(canFind, bool(const CallingContext&, const std::string& name));
     MOCK_METHOD1(canList, bool(const CallingContext&));
 };
 
 static sp<ServiceManager> getPermissiveServiceManager() {
     std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
 
-    ON_CALL(*access, getCallingContext(_)).WillByDefault(Return(Access::CallingContext{}));
-    ON_CALL(*access, canAdd(_)).WillByDefault(Return(true));
-    ON_CALL(*access, canFind(_)).WillByDefault(Return(true));
+    ON_CALL(*access, getCallingContext()).WillByDefault(Return(Access::CallingContext{}));
+    ON_CALL(*access, canAdd(_, _)).WillByDefault(Return(true));
+    ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true));
     ON_CALL(*access, canList(_)).WillByDefault(Return(true));
 
     sp<ServiceManager> sm = new ServiceManager(std::move(access));
@@ -97,11 +97,11 @@
 TEST(AddService, AddDisallowedFromApp) {
     for (uid_t uid : { AID_APP_START, AID_APP_START + 1, AID_APP_END }) {
         std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
-        EXPECT_CALL(*access, getCallingContext(_)).WillOnce(Return(Access::CallingContext{
+        EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{
             .debugPid = 1337,
             .uid = uid,
         }));
-        EXPECT_CALL(*access, canAdd(_)).Times(0);
+        EXPECT_CALL(*access, canAdd(_, _)).Times(0);
         sp<ServiceManager> sm = new ServiceManager(std::move(access));
 
         EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
@@ -121,8 +121,8 @@
 TEST(AddService, NoPermissions) {
     std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
 
-    EXPECT_CALL(*access, getCallingContext(_)).WillOnce(Return(Access::CallingContext{}));
-    EXPECT_CALL(*access, canAdd(_)).WillOnce(Return(false));
+    EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
+    EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false));
 
     sp<ServiceManager> sm = new ServiceManager(std::move(access));
 
@@ -151,9 +151,9 @@
 TEST(GetService, NoPermissionsForGettingService) {
     std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
 
-    EXPECT_CALL(*access, getCallingContext(_)).WillRepeatedly(Return(Access::CallingContext{}));
-    EXPECT_CALL(*access, canAdd(_)).WillOnce(Return(true));
-    EXPECT_CALL(*access, canFind(_)).WillOnce(Return(false));
+    EXPECT_CALL(*access, getCallingContext()).WillRepeatedly(Return(Access::CallingContext{}));
+    EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
+    EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
 
     sp<ServiceManager> sm = new ServiceManager(std::move(access));
 
@@ -169,15 +169,15 @@
 TEST(GetService, AllowedFromIsolated) {
     std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
 
-    EXPECT_CALL(*access, getCallingContext(_))
+    EXPECT_CALL(*access, getCallingContext())
         // something adds it
         .WillOnce(Return(Access::CallingContext{}))
         // next call is from isolated app
         .WillOnce(Return(Access::CallingContext{
             .uid = AID_ISOLATED_START,
         }));
-    EXPECT_CALL(*access, canAdd(_)).WillOnce(Return(true));
-    EXPECT_CALL(*access, canFind(_)).WillOnce(Return(true));
+    EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
+    EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
 
     sp<ServiceManager> sm = new ServiceManager(std::move(access));
 
@@ -192,17 +192,17 @@
 TEST(GetService, NotAllowedFromIsolated) {
     std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
 
-    EXPECT_CALL(*access, getCallingContext(_))
+    EXPECT_CALL(*access, getCallingContext())
         // something adds it
         .WillOnce(Return(Access::CallingContext{}))
         // next call is from isolated app
         .WillOnce(Return(Access::CallingContext{
             .uid = AID_ISOLATED_START,
         }));
-    EXPECT_CALL(*access, canAdd(_)).WillOnce(Return(true));
+    EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
 
     // TODO(b/136023468): when security check is first, this should be called first
-    // EXPECT_CALL(*access, canFind(_)).WillOnce(Return(true));
+    // EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
 
     sp<ServiceManager> sm = new ServiceManager(std::move(access));
 
@@ -218,7 +218,7 @@
 TEST(ListServices, NoPermissions) {
     std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
 
-    EXPECT_CALL(*access, getCallingContext(_)).WillOnce(Return(Access::CallingContext{}));
+    EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canList(_)).WillOnce(Return(false));
 
     sp<ServiceManager> sm = new ServiceManager(std::move(access));
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 21bef2e..6da3086 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -16,7 +16,6 @@
 
 cc_library {
     name: "libbinder_ndk",
-    vendor_available: true,
 
     export_include_dirs: [
         "include_ndk",
@@ -74,3 +73,12 @@
     symbol_file: "libbinder_ndk.map.txt",
     first_version: "29",
 }
+
+llndk_library {
+    name: "libbinder_ndk",
+    symbol_file: "libbinder_ndk.map.txt",
+    export_include_dirs: [
+        "include_ndk",
+        "include_apex",
+    ],
+}
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 7e65817..4f685d1 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -89,12 +89,12 @@
     AStatus_getStatus;
     AStatus_isOk;
     AStatus_newOk;
-    ABinderProcess_joinThreadPool; # apex
-    ABinderProcess_setThreadPoolMaxThreadCount; # apex
-    ABinderProcess_startThreadPool; # apex
-    AServiceManager_addService; # apex
-    AServiceManager_checkService; # apex
-    AServiceManager_getService; # apex
+    ABinderProcess_joinThreadPool; # apex vndk
+    ABinderProcess_setThreadPoolMaxThreadCount; # apex vndk
+    ABinderProcess_startThreadPool; # apex vndk
+    AServiceManager_addService; # apex vndk
+    AServiceManager_checkService; # apex vndk
+    AServiceManager_getService; # apex vndk
   local:
     *;
 };
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index b2a7557..beb13ad 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -38,6 +38,7 @@
         "BufferItemConsumer.cpp",
         "ConsumerBase.cpp",
         "CpuConsumer.cpp",
+        "DebugEGLImageTracker.cpp",
         "DisplayEventReceiver.cpp",
         "GLConsumer.cpp",
         "GuiConfig.cpp",
diff --git a/libs/gui/DebugEGLImageTracker.cpp b/libs/gui/DebugEGLImageTracker.cpp
new file mode 100644
index 0000000..ab6f364
--- /dev/null
+++ b/libs/gui/DebugEGLImageTracker.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gui/DebugEGLImageTracker.h>
+
+#include <cinttypes>
+#include <unordered_map>
+
+using android::base::StringAppendF;
+
+std::mutex DebugEGLImageTracker::mInstanceLock;
+std::atomic<DebugEGLImageTracker *> DebugEGLImageTracker::mInstance;
+
+class DebugEGLImageTrackerNoOp : public DebugEGLImageTracker {
+public:
+    DebugEGLImageTrackerNoOp() = default;
+    ~DebugEGLImageTrackerNoOp() override = default;
+    void create(const char * /*from*/) override {}
+    void destroy(const char * /*from*/) override {}
+
+    void dump(std::string & /*result*/) override {}
+};
+
+class DebugEGLImageTrackerImpl : public DebugEGLImageTracker {
+public:
+    DebugEGLImageTrackerImpl() = default;
+    ~DebugEGLImageTrackerImpl() override = default;
+    void create(const char * /*from*/) override;
+    void destroy(const char * /*from*/) override;
+
+    void dump(std::string & /*result*/) override;
+
+private:
+    std::mutex mLock;
+    std::unordered_map<std::string, int64_t> mCreateTracker;
+    std::unordered_map<std::string, int64_t> mDestroyTracker;
+
+    int64_t mTotalCreated = 0;
+    int64_t mTotalDestroyed = 0;
+};
+
+DebugEGLImageTracker *DebugEGLImageTracker::getInstance() {
+    std::lock_guard lock(mInstanceLock);
+    if (mInstance == nullptr) {
+        char value[PROPERTY_VALUE_MAX];
+        property_get("debug.sf.enable_egl_image_tracker", value, "0");
+        const bool enabled = static_cast<bool>(atoi(value));
+
+        if (enabled) {
+            mInstance = new DebugEGLImageTrackerImpl();
+        } else {
+            mInstance = new DebugEGLImageTrackerNoOp();
+        }
+    }
+
+    return mInstance;
+}
+
+void DebugEGLImageTrackerImpl::create(const char *from) {
+    std::lock_guard lock(mLock);
+    mCreateTracker[from]++;
+    mTotalCreated++;
+}
+
+void DebugEGLImageTrackerImpl::destroy(const char *from) {
+    std::lock_guard lock(mLock);
+    mDestroyTracker[from]++;
+    mTotalDestroyed++;
+}
+
+void DebugEGLImageTrackerImpl::dump(std::string &result) {
+    std::lock_guard lock(mLock);
+    StringAppendF(&result, "Live EGL Image objects: %" PRIi64 "\n",
+                  mTotalCreated - mTotalDestroyed);
+    StringAppendF(&result, "Total EGL Image created: %" PRIi64 "\n", mTotalCreated);
+    for (const auto &[from, count] : mCreateTracker) {
+        StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count);
+    }
+    StringAppendF(&result, "Total EGL Image destroyed: %" PRIi64 "\n", mTotalDestroyed);
+    for (const auto &[from, count] : mDestroyTracker) {
+        StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count);
+    }
+}
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 8d66154..8199c98 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -34,6 +34,7 @@
 #include <math/mat4.h>
 
 #include <gui/BufferItem.h>
+#include <gui/DebugEGLImageTracker.h>
 #include <gui/GLConsumer.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
@@ -944,6 +945,7 @@
         if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
            ALOGE("~EglImage: eglDestroyImageKHR failed");
         }
+        DEBUG_EGL_IMAGE_TRACKER_DESTROY();
         eglTerminate(mEglDisplay);
     }
 }
@@ -957,6 +959,7 @@
         if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
            ALOGE("createIfNeeded: eglDestroyImageKHR failed");
         }
+        DEBUG_EGL_IMAGE_TRACKER_DESTROY();
         eglTerminate(mEglDisplay);
         mEglImage = EGL_NO_IMAGE_KHR;
         mEglDisplay = EGL_NO_DISPLAY;
@@ -1006,7 +1009,10 @@
         EGLint error = eglGetError();
         ALOGE("error creating EGLImage: %#x", error);
         eglTerminate(dpy);
+    } else {
+        DEBUG_EGL_IMAGE_TRACKER_CREATE();
     }
+
     return image;
 }
 
diff --git a/libs/gui/include/gui/DebugEGLImageTracker.h b/libs/gui/include/gui/DebugEGLImageTracker.h
new file mode 100644
index 0000000..5d369c9
--- /dev/null
+++ b/libs/gui/include/gui/DebugEGLImageTracker.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <mutex>
+#include <string>
+
+class DebugEGLImageTracker {
+public:
+    static DebugEGLImageTracker *getInstance();
+
+    virtual void create(const char *from) = 0;
+    virtual void destroy(const char *from) = 0;
+
+    virtual void dump(std::string &result) = 0;
+
+protected:
+    DebugEGLImageTracker() = default;
+    virtual ~DebugEGLImageTracker() = default;
+    DebugEGLImageTracker(const DebugEGLImageTracker &) = delete;
+
+    static std::mutex mInstanceLock;
+    static std::atomic<DebugEGLImageTracker *> mInstance;
+};
+
+#define DEBUG_EGL_IMAGE_TRACKER_CREATE() \
+    (DebugEGLImageTracker::getInstance()->create(__PRETTY_FUNCTION__))
+#define DEBUG_EGL_IMAGE_TRACKER_DESTROY() \
+    (DebugEGLImageTracker::getInstance()->destroy(__PRETTY_FUNCTION__))
\ No newline at end of file
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index a2f12d0..8bfd3dd 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -31,6 +31,7 @@
 #include <android-base/stringprintf.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
+#include <gui/DebugEGLImageTracker.h>
 #include <renderengine/Mesh.h>
 #include <renderengine/Texture.h>
 #include <renderengine/private/Description.h>
@@ -433,6 +434,7 @@
         EGLImageKHR expired = mFramebufferImageCache.front().second;
         mFramebufferImageCache.pop_front();
         eglDestroyImageKHR(mEGLDisplay, expired);
+        DEBUG_EGL_IMAGE_TRACKER_DESTROY();
     }
     mImageCache.clear();
     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -841,6 +843,7 @@
                                                              bool useFramebufferCache) {
     sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
     if (useFramebufferCache) {
+        std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
         for (const auto& image : mFramebufferImageCache) {
             if (image.first == graphicBuffer->getId()) {
                 return image.second;
@@ -856,14 +859,20 @@
                                           nativeBuffer, attributes);
     if (useFramebufferCache) {
         if (image != EGL_NO_IMAGE_KHR) {
+            std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
             if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
                 EGLImageKHR expired = mFramebufferImageCache.front().second;
                 mFramebufferImageCache.pop_front();
                 eglDestroyImageKHR(mEGLDisplay, expired);
+                DEBUG_EGL_IMAGE_TRACKER_DESTROY();
             }
             mFramebufferImageCache.push_back({graphicBuffer->getId(), image});
         }
     }
+
+    if (image != EGL_NO_IMAGE_KHR) {
+        DEBUG_EGL_IMAGE_TRACKER_CREATE();
+    }
     return image;
 }
 
@@ -1266,6 +1275,23 @@
     StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n",
                   dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
                   dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
+    {
+        std::lock_guard<std::mutex> lock(mRenderingMutex);
+        StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size());
+        StringAppendF(&result, "Dumping buffer ids...\n");
+        for (const auto& [id, unused] : mImageCache) {
+            StringAppendF(&result, "0x%" PRIx64 "\n", id);
+        }
+    }
+    {
+        std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+        StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n",
+                      mFramebufferImageCache.size());
+        StringAppendF(&result, "Dumping buffer ids...\n");
+        for (const auto& [id, unused] : mFramebufferImageCache) {
+            StringAppendF(&result, "0x%" PRIx64 "\n", id);
+        }
+    }
 }
 
 GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) {
@@ -1392,7 +1418,7 @@
 }
 
 bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
-    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
     return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
                        [=](std::pair<uint64_t, EGLImageKHR> image) {
                            return image.first == bufferId;
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 54fc987..c8b45d2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -78,17 +78,20 @@
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
     EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected,
-                                               bool useFramebufferCache);
+                                               bool useFramebufferCache)
+            EXCLUDES(mFramebufferImageCacheMutex);
 
     // Test-only methods
     // Returns true iff mImageCache contains an image keyed by bufferId
     bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
     // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
-    bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+    bool isFramebufferImageCachedForTesting(uint64_t bufferId)
+            EXCLUDES(mFramebufferImageCacheMutex);
 
 protected:
     Framebuffer* getFramebufferForDrawing() override;
-    void dump(std::string& result) override;
+    void dump(std::string& result) override EXCLUDES(mRenderingMutex)
+            EXCLUDES(mFramebufferImageCacheMutex);
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
 
@@ -190,7 +193,11 @@
     uint32_t mFramebufferImageCacheSize = 0;
 
     // Cache of output images, keyed by corresponding GraphicBuffer ID.
-    std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache;
+    std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache
+            GUARDED_BY(mFramebufferImageCacheMutex);
+    // The only reason why we have this mutex is so that we don't segfault when
+    // dumping info.
+    std::mutex mFramebufferImageCacheMutex;
 
     // Current dataspace of layer being rendered
     ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index dacf8d3..5fbb5ba 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -22,6 +22,7 @@
 #include <GLES/glext.h>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
+#include <gui/DebugEGLImageTracker.h>
 #include <nativebase/nativebase.h>
 #include <utils/Trace.h>
 #include "GLESRenderEngine.h"
@@ -47,6 +48,7 @@
     if (mEGLImage != EGL_NO_IMAGE_KHR) {
         if (!usingFramebufferCache) {
             eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+            DEBUG_EGL_IMAGE_TRACKER_DESTROY();
         }
         mEGLImage = EGL_NO_IMAGE_KHR;
         mBufferWidth = 0;
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
index 77e648e..8497721 100644
--- a/libs/renderengine/gl/GLImage.cpp
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -20,6 +20,7 @@
 
 #include <vector>
 
+#include <gui/DebugEGLImageTracker.h>
 #include <log/log.h>
 #include <utils/Trace.h>
 #include "GLESRenderEngine.h"
@@ -58,6 +59,7 @@
         if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
             ALOGE("failed to destroy image: %#x", eglGetError());
         }
+        DEBUG_EGL_IMAGE_TRACKER_DESTROY();
         mEGLImage = EGL_NO_IMAGE_KHR;
     }
 
@@ -69,6 +71,7 @@
             ALOGE("failed to create EGLImage: %#x", eglGetError());
             return false;
         }
+        DEBUG_EGL_IMAGE_TRACKER_CREATE();
         mProtected = isProtected;
     }
 
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 0861a1f..9c7d1fd 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -20,6 +20,7 @@
 
 #include <ui/GraphicBufferAllocator.h>
 
+#include <limits.h>
 #include <stdio.h>
 
 #include <grallocusage/GrallocUsageConversion.h>
@@ -114,6 +115,14 @@
     if (!width || !height)
         width = height = 1;
 
+    const uint32_t bpp = bytesPerPixel(format);
+    if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) {
+        ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+              "usage %" PRIx64 ": Requesting too large a buffer size",
+              width, height, layerCount, format, usage);
+        return BAD_VALUE;
+    }
+
     // Ensure that layerCount is valid.
     if (layerCount < 1)
         layerCount = 1;
@@ -126,7 +135,6 @@
     if (error == NO_ERROR) {
         Mutex::Autolock _l(sLock);
         KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
-        uint32_t bpp = bytesPerPixel(format);
         alloc_rec_t rec;
         rec.width = width;
         rec.height = height;
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
index a7c248c..127f7ee 100644
--- a/libs/ui/tests/GraphicBuffer_test.cpp
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -35,6 +35,22 @@
 
 class GraphicBufferTest : public testing::Test {};
 
+TEST_F(GraphicBufferTest, AllocateNoError) {
+    PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+    sp<GraphicBuffer> gb(new GraphicBuffer(kTestWidth, kTestHeight, format, kTestLayerCount,
+                                           kTestUsage, std::string("test")));
+    ASSERT_EQ(NO_ERROR, gb->initCheck());
+}
+
+TEST_F(GraphicBufferTest, AllocateBadDimensions) {
+    PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+    uint32_t width, height;
+    width = height = std::numeric_limits<uint32_t>::max();
+    sp<GraphicBuffer> gb(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage,
+                                           std::string("test")));
+    ASSERT_EQ(BAD_VALUE, gb->initCheck());
+}
+
 TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
     std::unique_ptr<BufferHubBuffer> b1 =
             BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 83fd42b..0c94052 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -668,7 +668,13 @@
         nsecs_t durationSum = 0;
         nsecs_t minDuration = INT64_MAX;
         nsecs_t maxDuration = 0;
-        for (size_t i = 1; i < mNumResyncSamples; i++) {
+        // We skip the first 2 samples because the first vsync duration on some
+        // devices may be much more inaccurate than on other devices, e.g. due
+        // to delays in ramping up from a power collapse. By doing so this
+        // actually increases the accuracy of the DispSync model even though
+        // we're effectively relying on fewer sample points.
+        static constexpr size_t numSamplesSkipped = 2;
+        for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
             size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
             size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
             nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
@@ -679,15 +685,14 @@
 
         // Exclude the min and max from the average
         durationSum -= minDuration + maxDuration;
-        mPeriod = durationSum / (mNumResyncSamples - 3);
+        mPeriod = durationSum / (mNumResyncSamples - numSamplesSkipped - 2);
 
         ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
 
         double sampleAvgX = 0;
         double sampleAvgY = 0;
         double scale = 2.0 * M_PI / double(mPeriod);
-        // Intentionally skip the first sample
-        for (size_t i = 1; i < mNumResyncSamples; i++) {
+        for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
             size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
             nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
             double samplePhase = double(sample % mPeriod) * scale;
@@ -695,8 +700,8 @@
             sampleAvgY += sin(samplePhase);
         }
 
-        sampleAvgX /= double(mNumResyncSamples - 1);
-        sampleAvgY /= double(mNumResyncSamples - 1);
+        sampleAvgX /= double(mNumResyncSamples - numSamplesSkipped);
+        sampleAvgY /= double(mNumResyncSamples - numSamplesSkipped);
 
         mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 66df9dc..a733781 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -129,8 +129,8 @@
 
     private:
         std::deque<nsecs_t> mElements;
-        static constexpr size_t HISTORY_SIZE = 10;
-        static constexpr std::chrono::nanoseconds HISTORY_TIME = 500ms;
+        static constexpr size_t HISTORY_SIZE = 90;
+        static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
     };
 
 public:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5b82556..3f32f35 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -48,6 +48,8 @@
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <dvr/vr_flinger.h>
 #include <gui/BufferQueue.h>
+#include <gui/DebugEGLImageTracker.h>
+
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
@@ -5019,6 +5021,8 @@
 
     getRenderEngine().dump(result);
 
+    DebugEGLImageTracker::getInstance()->dump(result);
+
     if (const auto display = getDefaultDisplayDeviceLocked()) {
         display->getCompositionDisplay()->getState().undefinedRegion.dump(result,
                                                                           "undefinedRegion");
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 7ec9066..8e7440c 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -27,6 +27,15 @@
 
     static constexpr float MIN_REFRESH_RATE = 30.f;
     static constexpr float MAX_REFRESH_RATE = 90.f;
+    static constexpr auto RELEVANT_FRAME_THRESHOLD = 90u;
+    static constexpr uint64_t THIRTY_FPS_INTERVAL = 33'333'333;
+
+    void forceRelevancy(const std::unique_ptr<LayerHistory::LayerHandle>& testLayer) {
+        mLayerHistory->setVisibility(testLayer, true);
+        for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
+            mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+        }
+    };
 };
 
 LayerHistoryTest::LayerHistoryTest() {
@@ -39,24 +48,18 @@
     std::unique_ptr<LayerHistory::LayerHandle> testLayer =
             mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
     mLayerHistory->setVisibility(testLayer, true);
+    for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
+        EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+        mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+    }
 
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    // This is still 0, because the layer is not considered recently active if it
-    // has been present in less than 10 frames.
-    EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-    // This should be MAX_REFRESH_RATE as we have more than 10 samples
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+    // Add a few more. This time we should get MAX refresh rate as the layer
+    // becomes relevant
+    static constexpr auto A_FEW = 10;
+    for (auto i = 0u; i < A_FEW; i++) {
+        EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+        mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+    }
 }
 
 TEST_F(LayerHistoryTest, oneHDRLayer) {
@@ -79,8 +82,9 @@
     mLayerHistory->setVisibility(test30FpsLayer, true);
 
     nsecs_t startTime = systemTime();
-    for (int i = 0; i < 31; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
+        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+                              false /*isHDR*/);
     }
 
     EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
@@ -98,19 +102,21 @@
     mLayerHistory->setVisibility(testLayer2, true);
 
     nsecs_t startTime = systemTime();
-    for (int i = 0; i < 10; i++) {
+    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
         mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
     }
     EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
     startTime = systemTime();
-    for (int i = 0; i < 10; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
+        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+                              false /*isHDR*/);
     }
     EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
-    for (int i = 10; i < 30; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+    for (int i = 10; i < RELEVANT_FRAME_THRESHOLD; i++) {
+        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+                              false /*isHDR*/);
     }
     EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
@@ -122,8 +128,10 @@
     EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
     // After 1200 ms frames become obsolete.
     std::this_thread::sleep_for(std::chrono::milliseconds(1500));
-    // Insert the 31st frame.
-    mLayerHistory->insert(test30FpsLayer, startTime + (30 * 33333333), false /*isHDR*/);
+
+    mLayerHistory->insert(test30FpsLayer,
+                          startTime + (RELEVANT_FRAME_THRESHOLD * THIRTY_FPS_INTERVAL),
+                          false /*isHDR*/);
     EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 }
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index e5ac2de..d60eaa7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -370,9 +370,6 @@
             nullptr,  //&first_composition_start_time,
             nullptr,  //&last_composition_start_time,
             nullptr,  //&composition_finish_time,
-            // TODO(ianelliott): Maybe ask if this one is
-            // supported, at startup time (since it may not be
-            // supported):
             &actual_present_time,
             nullptr,  //&dequeue_ready_time,
             nullptr /*&reads_done_time*/);
@@ -399,7 +396,6 @@
     return num_ready;
 }
 
-// TODO(ianelliott): DEAL WITH RETURN VALUE (e.g. VK_INCOMPLETE)!!!
 void copy_ready_timings(Swapchain& swapchain,
                         uint32_t* count,
                         VkPastPresentationTimingGOOGLE* timings) {
@@ -1773,6 +1769,10 @@
     ATRACE_CALL();
 
     Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+    if (swapchain.surface.swapchain_handle != swapchain_handle) {
+        return VK_ERROR_OUT_OF_DATE_KHR;
+    }
+
     ANativeWindow* window = swapchain.surface.window.get();
     VkResult result = VK_SUCCESS;
 
@@ -1783,8 +1783,15 @@
     }
 
     if (timings) {
-        // TODO(ianelliott): plumb return value (e.g. VK_INCOMPLETE)
+        // Get the latest ready timing count before copying, since the copied
+        // timing info will be erased in copy_ready_timings function.
+        uint32_t n = get_num_ready_timings(swapchain);
         copy_ready_timings(swapchain, count, timings);
+        // Check the *count here against the recorded ready timing count, since
+        // *count can be overwritten per spec describes.
+        if (*count < n) {
+            result = VK_INCOMPLETE;
+        }
     } else {
         *count = get_num_ready_timings(swapchain);
     }