Merge "libbinder: Do not destruct TextOutput"
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 5597bcd..a427c8d 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -29,6 +29,7 @@
 #include <utils/Log.h>
 #include <utils/Vector.h>
 
+#include <iostream>
 #include <fcntl.h>
 #include <getopt.h>
 #include <stdio.h>
@@ -231,14 +232,14 @@
     const size_t N = services.size();
     if (N > 1) {
         // first print a list of the current services
-        aout << "Currently running services:" << endl;
+        std::cout << "Currently running services:" << std::endl;
 
         for (size_t i=0; i<N; i++) {
             sp<IBinder> service = sm_->checkService(services[i]);
 
             if (service != nullptr) {
                 bool skipped = IsSkipped(skippedServices, services[i]);
-                aout << "  " << services[i] << (skipped ? " (skipped)" : "") << endl;
+                std::cout << "  " << services[i] << (skipped ? " (skipped)" : "") << std::endl;
             }
         }
     }
@@ -263,10 +264,10 @@
                           asProto, elapsedDuration, bytesWritten);
 
             if (status == TIMED_OUT) {
-                aout << endl
+                std::cout << std::endl
                      << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
-                     << "ms) EXPIRED ***" << endl
-                     << endl;
+                     << "ms) EXPIRED ***" << std::endl
+                     << std::endl;
             }
 
             if (addSeparator) {
@@ -332,14 +333,14 @@
                                   const Vector<String16>& args) {
     sp<IBinder> service = sm_->checkService(serviceName);
     if (service == nullptr) {
-        aerr << "Can't find service: " << serviceName << endl;
+        std::cerr << "Can't find service: " << serviceName << std::endl;
         return NAME_NOT_FOUND;
     }
 
     int sfd[2];
     if (pipe(sfd) != 0) {
-        aerr << "Failed to create pipe to dump service info for " << serviceName << ": "
-             << strerror(errno) << endl;
+        std::cerr << "Failed to create pipe to dump service info for " << serviceName << ": "
+             << strerror(errno) << std::endl;
         return -errno;
     }
 
@@ -359,13 +360,13 @@
             err = dumpPidToFd(service, remote_end);
             break;
         default:
-            aerr << "Unknown dump type" << static_cast<int>(type) << endl;
+            std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl;
             return;
         }
 
         if (err != OK) {
-            aerr << "Error dumping service info status_t: " << statusToString(err) << " "
-                 << serviceName << endl;
+            std::cerr << "Error dumping service info status_t: " << statusToString(err) << " "
+                 << serviceName << std::endl;
         }
     });
     return OK;
@@ -422,8 +423,8 @@
 
         int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
         if (rc < 0) {
-            aerr << "Error in poll while dumping service " << serviceName << " : "
-                 << strerror(errno) << endl;
+            std::cerr << "Error in poll while dumping service " << serviceName << " : "
+                 << strerror(errno) << std::endl;
             status = -errno;
             break;
         } else if (rc == 0) {
@@ -434,8 +435,8 @@
         char buf[4096];
         rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
         if (rc < 0) {
-            aerr << "Failed to read while dumping service " << serviceName << ": "
-                 << strerror(errno) << endl;
+            std::cerr << "Failed to read while dumping service " << serviceName << ": "
+                 << strerror(errno) << std::endl;
             status = -errno;
             break;
         } else if (rc == 0) {
@@ -444,8 +445,8 @@
         }
 
         if (!WriteFully(fd, buf, rc)) {
-            aerr << "Failed to write while dumping service " << serviceName << ": "
-                 << strerror(errno) << endl;
+            std::cerr << "Failed to write while dumping service " << serviceName << ": "
+                 << strerror(errno) << std::endl;
             status = -errno;
             break;
         }
diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp
index 8ba0eba..fa4cc97 100644
--- a/cmds/dumpsys/main.cpp
+++ b/cmds/dumpsys/main.cpp
@@ -21,10 +21,9 @@
 #include "dumpsys.h"
 
 #include <binder/IServiceManager.h>
-#include <binder/TextOutput.h>
 
+#include <iostream>
 #include <signal.h>
-#include <stdio.h>
 
 using namespace android;
 
@@ -34,7 +33,7 @@
     fflush(stdout);
     if (sm == nullptr) {
         ALOGE("Unable to get default service manager!");
-        aerr << "dumpsys: Unable to get default service manager!" << endl;
+        std::cerr << "dumpsys: Unable to get default service manager!" << std::endl;
         return 20;
     }
 
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 7c989f6..6459805 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -61,11 +61,15 @@
 
 static std::vector<apex::ApexFile> ActivateApexPackages() {
     // The logic here is (partially) copied and adapted from
-    // system/apex/apexd/apexd_main.cpp.
+    // system/apex/apexd/apexd.cpp.
     //
-    // Only scan the APEX directory under /system (within the chroot dir).
-    // Cast call to void to suppress warn_unused_result.
-    static_cast<void>(apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir));
+    // Only scan the APEX directory under /system, /system_ext and /vendor (within the chroot dir).
+    std::vector<const char*> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir,
+                                       apex::kApexPackageVendorDir};
+    for (const auto& dir : apex_dirs) {
+        // Cast call to void to suppress warn_unused_result.
+        static_cast<void>(apex::scanPackagesDirAndActivate(dir));
+    }
     return apex::getActivePackages();
 }
 
diff --git a/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml
new file mode 100644
index 0000000..9c67d4a
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan deQP
+     tests associated with date 2019-03-01 (0x07E30301). -->
+<permissions>
+    <feature name="android.software.vulkan.deqp.level" version="132317953" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml
new file mode 100644
index 0000000..19b269b
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan deQP
+     tests associated with date 2020-03-01 (0x07E40301). -->
+<permissions>
+    <feature name="android.software.vulkan.deqp.level" version="132383489" />
+</permissions>
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 4dcd07a..9e89c57 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1230,6 +1230,11 @@
                 if (error < NO_ERROR) reply.setError(error);
                 sendReply(reply, 0);
             } else {
+                if (error != OK || reply.dataSize() != 0) {
+                    alog << "oneway function results will be dropped but finished with status "
+                         << statusToString(error)
+                         << " and parcel size " << reply.dataSize() << endl;
+                }
                 LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
             }
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
index 7331ba2..33e4586 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -86,9 +86,15 @@
         return t->template ref<T>();
     }
 
+    static void operator delete(void* p) { std::free(p); }
+
    private:
     std::once_flag mFlagThis;
     std::weak_ptr<SharedRefBase> mThis;
+
+    // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
+    // ownership. Making this operator private to avoid double-ownership.
+    static void* operator new(size_t s) { return std::malloc(s); }
 };
 
 /**
@@ -232,7 +238,9 @@
     // ourselves. The defaults are harmless.
     AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
 #ifdef HAS_BINDER_SHELL_COMMAND
-    AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
+    if (AIBinder_Class_setHandleShellCommand != nullptr) {
+        AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
+    }
 #endif
     return clazz;
 }
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
new file mode 100644
index 0000000..ac46cb8
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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_parcel.h>
+
+__BEGIN_DECLS
+
+/**
+ * Gets whether or not FDs are allowed by this AParcel
+ *
+ * \return true if FDs are allowed, false if they are not. That is
+ * if this returns false then AParcel_writeParcelFileDescriptor will
+ * return STATUS_FDS_NOT_ALLOWED.
+ */
+bool AParcel_getAllowFds(const AParcel*);
+
+__END_DECLS
\ No newline at end of file
diff --git a/libs/binder/ndk/include_platform/android/binder_shell.h b/libs/binder/ndk/include_platform/android/binder_shell.h
index 17b38b0..07d89e6 100644
--- a/libs/binder/ndk/include_platform/android/binder_shell.h
+++ b/libs/binder/ndk/include_platform/android/binder_shell.h
@@ -48,8 +48,7 @@
  * \param handleShellCommand function to call when a shell transaction is
  * received
  */
-void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
-                                          AIBinder_handleShellCommand handleShellCommand)
-        __INTRODUCED_IN(30);
+__attribute__((weak)) void AIBinder_Class_setHandleShellCommand(
+        AIBinder_Class* clazz, AIBinder_handleShellCommand handleShellCommand) __INTRODUCED_IN(30);
 
 __END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 7e72f22..a9eba47 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -114,3 +114,8 @@
   local:
     *;
 };
+
+LIBBINDER_NDK_PLATFORM {
+  global:
+    AParcel_getAllowFds;
+};
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index f18e118..703ceae 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android/binder_parcel.h>
+#include <android/binder_parcel_platform.h>
 #include "parcel_internal.h"
 
 #include "ibinder_internal.h"
@@ -242,23 +243,18 @@
 }
 
 binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) {
-    std::unique_ptr<ParcelFileDescriptor> parcelFd;
-
     if (fd < 0) {
         if (fd != -1) {
             return STATUS_UNKNOWN_ERROR;
         }
-        // parcelFd = nullptr
-    } else {  // fd >= 0
-        parcelFd = std::make_unique<ParcelFileDescriptor>(unique_fd(fd));
+        return parcel->get()->writeInt32(0);  // null
     }
 
-    status_t status = parcel->get()->writeNullableParcelable(parcelFd);
+    ParcelFileDescriptor parcelFd = ParcelFileDescriptor(unique_fd(fd));
+    status_t status = parcel->get()->writeParcelable(parcelFd);
 
     // ownership is retained by caller
-    if (parcelFd != nullptr) {
-        (void)parcelFd->release().release();
-    }
+    (void)parcelFd.release().release();
 
     return PruneStatusT(status);
 }
@@ -650,4 +646,8 @@
     return ReadArray<int8_t>(parcel, arrayData, allocator);
 }
 
+bool AParcel_getAllowFds(const AParcel* parcel) {
+    return parcel->get()->allowFds();
+}
+
 // @END
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 5a7f9a9..3ee8187 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -40,7 +40,7 @@
     },
 
     srcs: ["binderDriverInterfaceTest.cpp"],
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "vts-core"],
 }
 
 cc_test {
@@ -69,7 +69,7 @@
         "libbinder",
         "libutils",
     ],
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "vts-core"],
     require_root: true,
 }
 
@@ -131,7 +131,7 @@
         "liblog",
         "libutils",
     ],
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "vts-core"],
     require_root: true,
 }
 
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 4655e1d..c186110 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -63,15 +63,3 @@
     ],
     require_root: true,
 }
-
-// TODO(b/148692216): remove empty lib
-cc_library {
-    name: "libbinderthreadstate",
-    recovery_available: true,
-    vendor_available: false,
-    vndk: {
-        enabled: true,
-        support_system_process: true,
-    },
-    host_supported: true,
-}
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 1465296..a03775b 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -58,6 +58,7 @@
 static std::set<uint32_t> gAllFreqs;
 static unique_fd gTisMapFd;
 static unique_fd gConcurrentMapFd;
+static unique_fd gUidLastUpdateMapFd;
 
 static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) {
     std::string data;
@@ -144,6 +145,10 @@
             unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
     if (gConcurrentMapFd < 0) return false;
 
+    gUidLastUpdateMapFd =
+            unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")};
+    if (gUidLastUpdateMapFd < 0) return false;
+
     gInitialized = true;
     return true;
 }
@@ -151,7 +156,7 @@
 static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) {
     std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s",
                                     eventType.c_str(), eventName.c_str());
-    int prog_fd = bpf_obj_get(path.c_str());
+    int prog_fd = bpfFdGet(path.c_str(), BPF_F_RDONLY);
     if (prog_fd < 0) return false;
     return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0;
 }
@@ -263,6 +268,18 @@
     return out;
 }
 
+static std::optional<bool> uidUpdatedSince(uint32_t uid, uint64_t lastUpdate,
+                                           uint64_t *newLastUpdate) {
+    uint64_t uidLastUpdate;
+    if (findMapEntry(gUidLastUpdateMapFd, &uid, &uidLastUpdate)) return {};
+    // Updates that occurred during the previous read may have been missed. To mitigate
+    // this, don't ignore entries updated up to 1s before *lastUpdate
+    constexpr uint64_t NSEC_PER_SEC = 1000000000;
+    if (uidLastUpdate + NSEC_PER_SEC < lastUpdate) return false;
+    if (uidLastUpdate > *newLastUpdate) *newLastUpdate = uidLastUpdate;
+    return true;
+}
+
 // Retrieve the times in ns that each uid spent running at each CPU freq.
 // Return contains no value on error, otherwise it contains a map from uids to vectors of vectors
 // using the format:
@@ -271,6 +288,14 @@
 // where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq.
 std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
 getUidsCpuFreqTimes() {
+    return getUidsUpdatedCpuFreqTimes(nullptr);
+}
+
+// Retrieve the times in ns that each uid spent running at each CPU freq, excluding UIDs that have
+// not run since before lastUpdate.
+// Return format is the same as getUidsCpuFreqTimes()
+std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
+getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate) {
     if (!gInitialized && !initGlobals()) return {};
     time_key_t key, prevKey;
     std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map;
@@ -282,8 +307,14 @@
     std::vector<std::vector<uint64_t>> mapFormat;
     for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0);
 
+    uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0;
     std::vector<tis_val_t> vals(gNCpus);
     do {
+        if (lastUpdate) {
+            auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate);
+            if (!uidUpdated.has_value()) return {};
+            if (!*uidUpdated) continue;
+        }
         if (findMapEntry(gTisMapFd, &key, vals.data())) return {};
         if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat);
 
@@ -299,8 +330,9 @@
             }
         }
         prevKey = key;
-    } while (!getNextMapKey(gTisMapFd, &prevKey, &key));
+    } while (prevKey = key, !getNextMapKey(gTisMapFd, &prevKey, &key));
     if (errno != ENOENT) return {};
+    if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate;
     return map;
 }
 
@@ -365,6 +397,15 @@
 // where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent
 // running on the ith cluster, concurrently with tasks on j other cpus in the same cluster.
 std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes() {
+    return getUidsUpdatedConcurrentTimes(nullptr);
+}
+
+// Retrieve the times in ns that each uid spent running concurrently with each possible number of
+// other tasks on each cluster (policy times) and overall (active times), excluding UIDs that have
+// not run since before lastUpdate.
+// Return format is the same as getUidsConcurrentTimes()
+std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsUpdatedConcurrentTimes(
+        uint64_t *lastUpdate) {
     if (!gInitialized && !initGlobals()) return {};
     time_key_t key, prevKey;
     std::unordered_map<uint32_t, concurrent_time_t> ret;
@@ -379,7 +420,13 @@
     std::vector<concurrent_val_t> vals(gNCpus);
     std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd;
 
+    uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0;
     do {
+        if (lastUpdate) {
+            auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate);
+            if (!uidUpdated.has_value()) return {};
+            if (!*uidUpdated) continue;
+        }
         if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {};
         if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat);
 
@@ -405,8 +452,7 @@
                                std::plus<uint64_t>());
             }
         }
-        prevKey = key;
-    } while (!getNextMapKey(gConcurrentMapFd, &prevKey, &key));
+    } while (prevKey = key, !getNextMapKey(gConcurrentMapFd, &prevKey, &key));
     if (errno != ENOENT) return {};
     for (const auto &[key, value] : ret) {
         if (!verifyConcurrentTimes(value)) {
@@ -414,6 +460,7 @@
             if (val.has_value()) ret[key] = val.value();
         }
     }
+    if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate;
     return ret;
 }
 
@@ -446,6 +493,8 @@
             return false;
         if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false;
     }
+
+    if (deleteMapEntry(gUidLastUpdateMapFd, &uid) && errno != ENOENT) return false;
     return true;
 }
 
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index 49469d8..b7600f5 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -26,6 +26,8 @@
 std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid);
 std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
     getUidsCpuFreqTimes();
+std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
+    getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate);
 std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs();
 
 struct concurrent_time_t {
@@ -35,6 +37,8 @@
 
 std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true);
 std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes();
+std::optional<std::unordered_map<uint32_t, concurrent_time_t>>
+    getUidsUpdatedConcurrentTimes(uint64_t *lastUpdate);
 bool clearUidTimes(unsigned int uid);
 
 } // namespace bpf
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 23d87fd..ea2a200 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -115,71 +115,169 @@
 }
 
 TEST(TimeInStateTest, AllUidTimeInState) {
-    vector<size_t> sizes;
-    auto map = getUidsCpuFreqTimes();
-    ASSERT_TRUE(map.has_value());
+    uint64_t zero = 0;
+    auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
 
-    ASSERT_FALSE(map->empty());
+        ASSERT_FALSE(map->empty());
 
-    auto firstEntry = map->begin()->second;
-    for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
+        vector<size_t> sizes;
+        auto firstEntry = map->begin()->second;
+        for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
 
-    for (const auto &vec : *map) {
-        ASSERT_EQ(vec.second.size(), sizes.size());
-        for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
+        for (const auto &vec : *map) {
+            ASSERT_EQ(vec.second.size(), sizes.size());
+            for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
+        }
+    }
+}
+
+void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before,
+                     const std::vector<std::vector<uint64_t>> &after) {
+    ASSERT_EQ(before.size(), after.size());
+    uint64_t sumBefore = 0, sumAfter = 0;
+    for (size_t i = 0; i < before.size(); ++i) {
+        ASSERT_EQ(before[i].size(), after[i].size());
+        for (size_t j = 0; j < before[i].size(); ++j) {
+            // Times should never decrease
+            ASSERT_LE(before[i][j], after[i][j]);
+        }
+        sumBefore += std::accumulate(before[i].begin(), before[i].end(), (uint64_t)0);
+        sumAfter += std::accumulate(after[i].begin(), after[i].end(), (uint64_t)0);
+    }
+    ASSERT_LE(sumBefore, sumAfter);
+    ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC);
+}
+
+TEST(TimeInStateTest, AllUidUpdatedTimeInState) {
+    uint64_t lastUpdate = 0;
+    auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
+    ASSERT_TRUE(map1.has_value());
+    ASSERT_FALSE(map1->empty());
+    ASSERT_NE(lastUpdate, (uint64_t)0);
+    uint64_t oldLastUpdate = lastUpdate;
+
+    // Sleep briefly to trigger a context switch, ensuring we see at least one update.
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 1000000;
+    nanosleep (&ts, NULL);
+
+    auto map2 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
+    ASSERT_TRUE(map2.has_value());
+    ASSERT_FALSE(map2->empty());
+    ASSERT_NE(lastUpdate, oldLastUpdate);
+
+    bool someUidsExcluded = false;
+    for (const auto &[uid, v] : *map1) {
+        if (map2->find(uid) == map2->end()) {
+            someUidsExcluded = true;
+            break;
+        }
+    }
+    ASSERT_TRUE(someUidsExcluded);
+
+    for (const auto &[uid, newTimes] : *map2) {
+        ASSERT_NE(map1->find(uid), map1->end());
+        ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid], newTimes));
     }
 }
 
 TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
-    auto map = getUidsCpuFreqTimes();
-    ASSERT_TRUE(map.has_value());
-    ASSERT_FALSE(map->empty());
+    uint64_t zero = 0;
+    auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
+        ASSERT_FALSE(map->empty());
 
-    for (const auto &kv : *map) {
-        uint32_t uid = kv.first;
-        auto times1 = kv.second;
-        auto times2 = getUidCpuFreqTimes(uid);
-        ASSERT_TRUE(times2.has_value());
+        for (const auto &kv : *map) {
+            uint32_t uid = kv.first;
+            auto times1 = kv.second;
+            auto times2 = getUidCpuFreqTimes(uid);
+            ASSERT_TRUE(times2.has_value());
 
-        ASSERT_EQ(times1.size(), times2->size());
-        for (uint32_t i = 0; i < times1.size(); ++i) {
-            ASSERT_EQ(times1[i].size(), (*times2)[i].size());
-            for (uint32_t j = 0; j < times1[i].size(); ++j) {
-                ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
+            ASSERT_EQ(times1.size(), times2->size());
+            for (uint32_t i = 0; i < times1.size(); ++i) {
+                ASSERT_EQ(times1[i].size(), (*times2)[i].size());
+                for (uint32_t j = 0; j < times1[i].size(); ++j) {
+                    ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
+                }
             }
         }
     }
 }
 
 TEST(TimeInStateTest, AllUidConcurrentTimes) {
-    auto map = getUidsConcurrentTimes();
-    ASSERT_TRUE(map.has_value());
-    ASSERT_FALSE(map->empty());
+    uint64_t zero = 0;
+    auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
+        ASSERT_FALSE(map->empty());
 
-    auto firstEntry = map->begin()->second;
-    for (const auto &kv : *map) {
-        ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
-        ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
-        for (size_t i = 0; i < kv.second.policy.size(); ++i) {
-            ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
+        auto firstEntry = map->begin()->second;
+        for (const auto &kv : *map) {
+            ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
+            ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
+            for (size_t i = 0; i < kv.second.policy.size(); ++i) {
+                ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
+            }
         }
     }
 }
 
-TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
-    auto map = getUidsConcurrentTimes();
-    ASSERT_TRUE(map.has_value());
-    for (const auto &kv : *map) {
-        uint32_t uid = kv.first;
-        auto times1 = kv.second;
-        auto times2 = getUidConcurrentTimes(uid);
-        ASSERT_TRUE(times2.has_value());
-        for (uint32_t i = 0; i < times1.active.size(); ++i) {
-            ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
+TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) {
+    uint64_t lastUpdate = 0;
+    auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate);
+    ASSERT_TRUE(map1.has_value());
+    ASSERT_FALSE(map1->empty());
+    ASSERT_NE(lastUpdate, (uint64_t)0);
+
+    // Sleep briefly to trigger a context switch, ensuring we see at least one update.
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 1000000;
+    nanosleep (&ts, NULL);
+
+    uint64_t oldLastUpdate = lastUpdate;
+    auto map2 = getUidsUpdatedConcurrentTimes(&lastUpdate);
+    ASSERT_TRUE(map2.has_value());
+    ASSERT_FALSE(map2->empty());
+    ASSERT_NE(lastUpdate, oldLastUpdate);
+
+    bool someUidsExcluded = false;
+    for (const auto &[uid, v] : *map1) {
+        if (map2->find(uid) == map2->end()) {
+            someUidsExcluded = true;
+            break;
         }
-        for (uint32_t i = 0; i < times1.policy.size(); ++i) {
-            for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
-                ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
+    }
+    ASSERT_TRUE(someUidsExcluded);
+
+    for (const auto &[uid, newTimes] : *map2) {
+        ASSERT_NE(map1->find(uid), map1->end());
+        ASSERT_NO_FATAL_FAILURE(TestCheckUpdate({(*map1)[uid].active},{newTimes.active}));
+        ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid].policy, newTimes.policy));
+    }
+}
+
+TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
+    uint64_t zero = 0;
+    auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
+        for (const auto &kv : *map) {
+            uint32_t uid = kv.first;
+            auto times1 = kv.second;
+            auto times2 = getUidConcurrentTimes(uid);
+            ASSERT_TRUE(times2.has_value());
+            for (uint32_t i = 0; i < times1.active.size(); ++i) {
+                ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
+            }
+            for (uint32_t i = 0; i < times1.policy.size(); ++i) {
+                for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
+                    ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
+                }
             }
         }
     }
@@ -242,45 +340,51 @@
 }
 
 TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) {
-    auto map = getUidsCpuFreqTimes();
-    ASSERT_TRUE(map.has_value());
+    uint64_t zero = 0;
+    auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
 
-    bool foundLargeValue = false;
-    for (const auto &kv : *map) {
-        for (const auto &timeVec : kv.second) {
-            for (const auto &time : timeVec) {
-                ASSERT_LE(time, NSEC_PER_YEAR);
-                if (time > UINT32_MAX) foundLargeValue = true;
+        bool foundLargeValue = false;
+        for (const auto &kv : *map) {
+            for (const auto &timeVec : kv.second) {
+                for (const auto &time : timeVec) {
+                    ASSERT_LE(time, NSEC_PER_YEAR);
+                    if (time > UINT32_MAX) foundLargeValue = true;
+                }
             }
         }
+        // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
+        // uint64_t as expected, we should have some times higher than that.
+        ASSERT_TRUE(foundLargeValue);
     }
-    // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
-    // uint64_t as expected, we should have some times higher than that.
-    ASSERT_TRUE(foundLargeValue);
 }
 
 TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
-    auto concurrentMap = getUidsConcurrentTimes();
-    ASSERT_TRUE(concurrentMap);
+    uint64_t zero = 0;
+    auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
+    for (const auto &concurrentMap : maps) {
+        ASSERT_TRUE(concurrentMap);
 
-    bool activeFoundLargeValue = false;
-    bool policyFoundLargeValue = false;
-    for (const auto &kv : *concurrentMap) {
-        for (const auto &time : kv.second.active) {
-            ASSERT_LE(time, NSEC_PER_YEAR);
-            if (time > UINT32_MAX) activeFoundLargeValue = true;
-        }
-        for (const auto &policyTimeVec : kv.second.policy) {
-            for (const auto &time : policyTimeVec) {
+        bool activeFoundLargeValue = false;
+        bool policyFoundLargeValue = false;
+        for (const auto &kv : *concurrentMap) {
+            for (const auto &time : kv.second.active) {
                 ASSERT_LE(time, NSEC_PER_YEAR);
-                if (time > UINT32_MAX) policyFoundLargeValue = true;
+                if (time > UINT32_MAX) activeFoundLargeValue = true;
+            }
+            for (const auto &policyTimeVec : kv.second.policy) {
+                for (const auto &time : policyTimeVec) {
+                    ASSERT_LE(time, NSEC_PER_YEAR);
+                    if (time > UINT32_MAX) policyFoundLargeValue = true;
+                }
             }
         }
+        // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
+        // uint64_t as expected, we should have some times higher than that.
+        ASSERT_TRUE(activeFoundLargeValue);
+        ASSERT_TRUE(policyFoundLargeValue);
     }
-    // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
-    // uint64_t as expected, we should have some times higher than that.
-    ASSERT_TRUE(activeFoundLargeValue);
-    ASSERT_TRUE(policyFoundLargeValue);
 }
 
 TEST(TimeInStateTest, AllUidTimesConsistent) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index ba3195a..f3d5aab 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -166,6 +166,10 @@
         "bufferqueue/2.0/types.cpp",
     ],
 
+    whole_static_libs: [
+        "LibGuiProperties",
+    ],
+
     shared_libs: [
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hardware.graphics.bufferqueue@2.0",
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index c04d907..3215eca 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -18,6 +18,7 @@
 
 #define LOG_TAG "FrameEvents"
 
+#include <LibGuiProperties.sysprop.h>
 #include <android-base/stringprintf.h>
 #include <cutils/compiler.h>  // For CC_[UN]LIKELY
 #include <inttypes.h>
@@ -167,6 +168,11 @@
 
 }  // namespace
 
+const size_t FrameEventHistory::MAX_FRAME_HISTORY =
+        sysprop::LibGuiProperties::frame_event_history_size().value_or(8);
+
+FrameEventHistory::FrameEventHistory() : mFrames(std::vector<FrameEvents>(MAX_FRAME_HISTORY)) {}
+
 FrameEventHistory::~FrameEventHistory() = default;
 
 FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
@@ -348,6 +354,9 @@
 // ConsumerFrameEventHistory
 // ============================================================================
 
+ConsumerFrameEventHistory::ConsumerFrameEventHistory()
+      : mFramesDirty(std::vector<FrameEventDirtyFields>(MAX_FRAME_HISTORY)) {}
+
 ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
 
 void ConsumerFrameEventHistory::onDisconnect() {
@@ -443,9 +452,8 @@
     mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
 }
 
-void ConsumerFrameEventHistory::getFrameDelta(
-        FrameEventHistoryDelta* delta,
-        const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) {
+void ConsumerFrameEventHistory::getFrameDelta(FrameEventHistoryDelta* delta,
+                                              const std::vector<FrameEvents>::iterator& frame) {
     mProducerWantsEvents = true;
     size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame));
     if (mFramesDirty[i].anyDirty()) {
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index df02494..4670edd 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -106,6 +106,7 @@
 // producer via deltas.
 class FrameEventHistory {
 public:
+    FrameEventHistory();
     virtual ~FrameEventHistory();
 
     FrameEvents* getFrame(uint64_t frameNumber);
@@ -113,10 +114,10 @@
     void checkFencesForCompletion();
     void dump(std::string& outString) const;
 
-    static constexpr size_t MAX_FRAME_HISTORY = 8;
+    static const size_t MAX_FRAME_HISTORY;
 
 protected:
-    std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames;
+    std::vector<FrameEvents> mFrames;
 
     CompositorTiming mCompositorTiming;
 };
@@ -204,6 +205,7 @@
 // The consumer's interface to FrameEventHistory
 class ConsumerFrameEventHistory : public FrameEventHistory {
 public:
+    ConsumerFrameEventHistory();
     ~ConsumerFrameEventHistory() override;
 
     void onDisconnect();
@@ -224,9 +226,9 @@
 
 private:
     void getFrameDelta(FrameEventHistoryDelta* delta,
-            const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame);
+                       const std::vector<FrameEvents>::iterator& frame);
 
-    std::array<FrameEventDirtyFields, MAX_FRAME_HISTORY> mFramesDirty;
+    std::vector<FrameEventDirtyFields> mFramesDirty;
 
     size_t mQueueOffset{0};
     size_t mCompositionOffset{0};
diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp
new file mode 100644
index 0000000..e7f7c1f
--- /dev/null
+++ b/libs/gui/sysprop/Android.bp
@@ -0,0 +1,7 @@
+sysprop_library {
+    name: "LibGuiProperties",
+    srcs: ["*.sysprop"],
+    api_packages: ["android.sysprop"],
+    property_owner: "Platform",
+    vendor_available: true,
+}
diff --git a/libs/gui/sysprop/LibGuiProperties.sysprop b/libs/gui/sysprop/LibGuiProperties.sysprop
new file mode 100644
index 0000000..0d54711
--- /dev/null
+++ b/libs/gui/sysprop/LibGuiProperties.sysprop
@@ -0,0 +1,25 @@
+# Copyright (C) 2020 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.
+
+module: "android.sysprop.LibGuiProperties"
+owner: Platform
+
+# Indicates how many elements should be present in the frame event histories.
+prop {
+    api_name: "frame_event_history_size"
+    type: Integer
+    scope: Public
+    access: Readonly
+    prop_name: "ro.lib_gui.frame_event_history_size"
+}
diff --git a/libs/gui/sysprop/api/LibGuiProperties-current.txt b/libs/gui/sysprop/api/LibGuiProperties-current.txt
new file mode 100644
index 0000000..5b7f74e
--- /dev/null
+++ b/libs/gui/sysprop/api/LibGuiProperties-current.txt
@@ -0,0 +1,8 @@
+props {
+  module: "android.sysprop.LibGuiProperties"
+  prop {
+    api_name: "frame_event_history_size"
+    type: Integer
+    prop_name: "ro.lib_gui.frame_event_history_size"
+  }
+}
diff --git a/libs/gui/sysprop/api/LibGuiProperties-latest.txt b/libs/gui/sysprop/api/LibGuiProperties-latest.txt
new file mode 100644
index 0000000..5b7f74e
--- /dev/null
+++ b/libs/gui/sysprop/api/LibGuiProperties-latest.txt
@@ -0,0 +1,8 @@
+props {
+  module: "android.sysprop.LibGuiProperties"
+  prop {
+    api_name: "frame_event_history_size"
+    type: Integer
+    prop_name: "ro.lib_gui.frame_event_history_size"
+  }
+}