Merge "TextDescription: Fix to parse ftab box" into rvc-dev
diff --git a/drm/drmserver/Android.bp b/drm/drmserver/Android.bp
index f427834..b68e6c2 100644
--- a/drm/drmserver/Android.bp
+++ b/drm/drmserver/Android.bp
@@ -24,8 +24,8 @@
     ],
 
     shared_libs: [
-        "libmedia",
         "libmediametrics",
+        "libmediautils",
         "libcutils",
         "libutils",
         "liblog",
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index 4b8b3f6..c830c6e 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -19,7 +19,7 @@
 #include <utils/Log.h>
 
 #include <private/android_filesystem_config.h>
-#include <media/MemoryLeakTrackUtil.h>
+#include <mediautils/MemoryLeakTrackUtil.h>
 
 #include <errno.h>
 #include <utils/threads.h>
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 56813c4..d686eb1 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -35,6 +35,7 @@
 constexpr char COMPONENT_NAME[] = "c2.android.avc.decoder";
 constexpr uint32_t kDefaultOutputDelay = 8;
 constexpr uint32_t kMaxOutputDelay = 16;
+constexpr uint32_t kMinInputBytes = 4;
 }  // namespace
 
 class C2SoftAvcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -817,7 +818,7 @@
           inSize, (int)work->input.ordinal.timestamp.peeku(),
           (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
     size_t inPos = 0;
-    while (inPos < inSize) {
+    while (inPos < inSize && inSize - inPos >= kMinInputBytes) {
         if (C2_OK != ensureDecoderState(pool)) {
             mSignalledError = true;
             work->workletsProcessed = 1u;
@@ -904,7 +905,6 @@
                 work->result = C2_CORRUPTED;
                 return;
             }
-            continue;
         }
         if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
             if (mHeaderDecoded == false) {
@@ -937,16 +937,7 @@
         if (s_decode_op.u4_output_present) {
             finishWork(s_decode_op.u4_ts, work);
         }
-        if (0 == s_decode_op.u4_num_bytes_consumed) {
-            ALOGD("Bytes consumed is zero. Ignoring remaining bytes");
-            break;
-        }
         inPos += s_decode_op.u4_num_bytes_consumed;
-        if (hasPicture && (inSize - inPos)) {
-            ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
-                  (int)inSize - (int)inPos);
-            break;
-        }
     }
     if (eos) {
         drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index b72a8cc..aea28c0 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -2896,7 +2896,7 @@
                 return ERROR_MALFORMED;
             }
 
-            parseID3v2MetaData(data_offset + 6);
+            parseID3v2MetaData(data_offset + 6, chunk_data_size - 6);
 
             break;
         }
@@ -4167,8 +4167,19 @@
     return OK;
 }
 
-void MPEG4Extractor::parseID3v2MetaData(off64_t offset) {
-    ID3 id3(mDataSource, true /* ignorev1 */, offset);
+void MPEG4Extractor::parseID3v2MetaData(off64_t offset, uint64_t size) {
+    uint8_t *buffer = new (std::nothrow) uint8_t[size];
+    if (buffer == NULL) {
+        return;
+    }
+    if (mDataSource->readAt(offset, buffer, size) != (ssize_t)size) {
+        delete[] buffer;
+        buffer = NULL;
+        return;
+    }
+
+    ID3 id3(buffer, size, true /* ignorev1 */);
+    delete[] buffer;
 
     if (id3.isValid()) {
         struct Map {
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 3af432d..1e49d50 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -161,7 +161,7 @@
     status_t parseITunesMetaData(off64_t offset, size_t size);
     status_t parseColorInfo(off64_t offset, size_t size);
     status_t parse3GPPMetaData(off64_t offset, size_t size, int depth);
-    void parseID3v2MetaData(off64_t offset);
+    void parseID3v2MetaData(off64_t offset, uint64_t size);
     status_t parseQTMetaKey(off64_t data_offset, size_t data_size);
     status_t parseQTMetaVal(int32_t keyId, off64_t data_offset, size_t data_size);
 
diff --git a/media/libmediametrics/include/media/MediaMetricsItem.h b/media/libmediametrics/include/media/MediaMetricsItem.h
index 08720f1..591e714 100644
--- a/media/libmediametrics/include/media/MediaMetricsItem.h
+++ b/media/libmediametrics/include/media/MediaMetricsItem.h
@@ -208,11 +208,11 @@
 
 template<size_t N>
 static inline bool startsWith(const std::string &s, const char (&comp)[N]) {
-    return !strncmp(s.c_str(), comp, N - 1);
+    return !strncmp(s.c_str(), comp, N - 1); // last char is null termination
 }
 
 static inline bool startsWith(const std::string& s, const std::string& comp) {
-    return !strncmp(s.c_str(), comp.c_str(), comp.size() - 1);
+    return !strncmp(s.c_str(), comp.c_str(), comp.size());
 }
 
 /**
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 81ffcbc..42a0622 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -57,7 +57,6 @@
 #include <media/MediaMetadataRetrieverInterface.h>
 #include <media/Metadata.h>
 #include <media/AudioTrack.h>
-#include <media/MemoryLeakTrackUtil.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/MediaCodecList.h>
@@ -68,7 +67,7 @@
 #include <media/stagefright/foundation/ALooperRoster.h>
 #include <media/stagefright/SurfaceUtils.h>
 #include <mediautils/BatteryNotifier.h>
-
+#include <mediautils/MemoryLeakTrackUtil.h>
 #include <memunreachable/memunreachable.h>
 #include <system/audio.h>
 
diff --git a/media/utils/MemoryLeakTrackUtil.cpp b/media/utils/MemoryLeakTrackUtil.cpp
index 6166859..fdb8c4f 100644
--- a/media/utils/MemoryLeakTrackUtil.cpp
+++ b/media/utils/MemoryLeakTrackUtil.cpp
@@ -19,7 +19,7 @@
 #define LOG_TAG "MemoryLeackTrackUtil"
 #include <utils/Log.h>
 
-#include "media/MemoryLeakTrackUtil.h"
+#include <mediautils/MemoryLeakTrackUtil.h>
 #include <sstream>
 
 #include <bionic/malloc.h>
diff --git a/media/libmedia/include/media/MemoryLeakTrackUtil.h b/media/utils/include/mediautils/MemoryLeakTrackUtil.h
similarity index 100%
rename from media/libmedia/include/media/MemoryLeakTrackUtil.h
rename to media/utils/include/mediautils/MemoryLeakTrackUtil.h
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 1be2fcb..5c891e2 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -67,10 +67,10 @@
 #include <powermanager/PowerManager.h>
 
 #include <media/IMediaLogService.h>
-#include <media/MemoryLeakTrackUtil.h>
 #include <media/nbaio/Pipe.h>
 #include <media/nbaio/PipeReader.h>
 #include <mediautils/BatteryNotifier.h>
+#include <mediautils/MemoryLeakTrackUtil.h>
 #include <mediautils/ServiceUtilities.h>
 #include <mediautils/TimeCheck.h>
 #include <private/android_filesystem_config.h>
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
index 6e29632..1596ff7 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
@@ -41,11 +41,24 @@
         IoDescriptor& desc, Filter filter, bool& active, const DeviceVector& devices)
 {
     auto activeClients = desc->clientsList(true /*activeOnly*/);
-    auto activeClientsWithRoute =
-        desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/);
     active = activeClients.size() > 0;
-    if (active && activeClients.size() == activeClientsWithRoute.size()) {
-        return devices.getDeviceFromId(activeClientsWithRoute[0]->preferredDeviceId());
+
+    if (active) {
+        // On MMAP IOs, the preferred device is selected by the first client (virtual client
+        // created when the mmap stream is opened). This client is never active.
+        // On non MMAP IOs, the preferred device is honored only if all active clients have
+        // a preferred device in which case the first client drives the selection.
+        if (desc->getPolicyAudioPort()->isMmap()) {
+            // The client list is never empty on a MMAP IO
+            return devices.getDeviceFromId(
+                    desc->clientsList(false /*activeOnly*/)[0]->preferredDeviceId());
+        } else {
+            auto activeClientsWithRoute =
+                desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/);
+            if (activeClients.size() == activeClientsWithRoute.size()) {
+                return devices.getDeviceFromId(activeClientsWithRoute[0]->preferredDeviceId());
+            }
+        }
     }
     return nullptr;
 }
diff --git a/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h b/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
index 99df3c0..d2f6297 100644
--- a/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
@@ -107,6 +107,15 @@
                 (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD));
     }
 
+    inline bool isMmap() const
+    {
+        return (asAudioPort()->getType() == AUDIO_PORT_TYPE_MIX)
+                && (((asAudioPort()->getRole() == AUDIO_PORT_ROLE_SOURCE) &&
+                        ((mFlags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) != 0))
+                    || ((asAudioPort()->getRole() == AUDIO_PORT_ROLE_SINK) &&
+                        ((mFlags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0)));
+    }
+
     void addRoute(const sp<AudioRoute> &route) { mRoutes.add(route); }
     const AudioRouteVector &getRoutes() const { return mRoutes; }
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index bdd2427..5aa6271 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -2405,9 +2405,7 @@
     for (size_t i = 0; i < mInputs.size(); i++) {
         const sp<AudioInputDescriptor> input = mInputs.valueAt(i);
         if (input->clientsList().size() == 0
-                || !mAvailableInputDevices.containsAtLeastOne(input->supportedDevices())
-                || (input->getPolicyAudioPort()->getFlags()
-                        & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0) {
+                || !mAvailableInputDevices.containsAtLeastOne(input->supportedDevices())) {
             inputsToClose.push_back(mInputs.keyAt(i));
         } else {
             bool close = false;
diff --git a/services/audiopolicy/service/CaptureStateNotifier.cpp b/services/audiopolicy/service/CaptureStateNotifier.cpp
index 8a18efa..135e0a2 100644
--- a/services/audiopolicy/service/CaptureStateNotifier.cpp
+++ b/services/audiopolicy/service/CaptureStateNotifier.cpp
@@ -1,7 +1,7 @@
-#include "CaptureStateNotifier.h"
-
 #define LOG_TAG "CaptureStateNotifier"
 
+#include "CaptureStateNotifier.h"
+
 #include <android/media/ICaptureStateListener.h>
 #include <binder/IBinder.h>
 #include <utils/Log.h>
@@ -22,9 +22,9 @@
     CaptureStateNotifier* const mNotifier;
 };
 
-CaptureStateNotifier::CaptureStateNotifier(bool initialActive) {
-    mActive = initialActive;
-}
+CaptureStateNotifier::CaptureStateNotifier(bool initialActive)
+    : mDeathRecipient(new DeathRecipient(this)), mActive(
+    initialActive) {}
 
 CaptureStateNotifier::~CaptureStateNotifier() {
     LOG_ALWAYS_FATAL_IF(mListener != nullptr);
@@ -33,11 +33,20 @@
 bool CaptureStateNotifier::RegisterListener(const sp<ICaptureStateListener>& listener) {
     std::lock_guard<std::mutex> _l(mMutex);
     LOG_ALWAYS_FATAL_IF(mListener != nullptr);
+    LOG_ALWAYS_FATAL_IF(listener == nullptr);
 
     ALOGI("Registering a listener");
-    mListener = listener;
-    sp<IBinder> binder = IInterface::asBinder(mListener);
-    binder->linkToDeath(new DeathRecipient(this));
+    sp<IBinder> binder = IInterface::asBinder(listener);
+    if (binder != nullptr) {
+        status_t status = binder->linkToDeath(mDeathRecipient);
+        if (status == NO_ERROR) {
+            mListener = listener;
+        } else {
+            ALOGE("Failed to register death listener: %u", status);
+        }
+    } else {
+        ALOGE("Listener failed to cast to a binder.");
+    }
     return mActive;
 }
 
diff --git a/services/audiopolicy/service/CaptureStateNotifier.h b/services/audiopolicy/service/CaptureStateNotifier.h
index 4ca6601..166de2a 100644
--- a/services/audiopolicy/service/CaptureStateNotifier.h
+++ b/services/audiopolicy/service/CaptureStateNotifier.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <mutex>
+#include <binder/IBinder.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
@@ -63,6 +64,7 @@
 private:
     std::mutex mMutex;
     sp<media::ICaptureStateListener> mListener;
+    sp<IBinder::DeathRecipient> mDeathRecipient;
     bool mActive;
 
     class DeathRecipient;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 238356e..4c8366f 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -246,6 +246,8 @@
                 frameNumber);
         return;
     }
+    nsecs_t sensorTimestamp = timestamp.data.i64[0];
+
     for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
         camera_metadata_entry timestamp =
                 physicalMetadata.mPhysicalCameraMetadata.find(ANDROID_SENSOR_TIMESTAMP);
@@ -337,7 +339,7 @@
                 CameraMetadata(m.mPhysicalCameraMetadata));
     }
     states.tagMonitor.monitorMetadata(TagMonitor::RESULT,
-            frameNumber, timestamp.data.i64[0], captureResult.mMetadata,
+            frameNumber, sensorTimestamp, captureResult.mMetadata,
             monitoredPhysicalMetadata);
 
     insertResultLocked(states, &captureResult, frameNumber);
diff --git a/services/mediametrics/AnalyticsState.h b/services/mediametrics/AnalyticsState.h
index 290ed21..e8fedcf 100644
--- a/services/mediametrics/AnalyticsState.h
+++ b/services/mediametrics/AnalyticsState.h
@@ -84,8 +84,11 @@
      * delivered.
      *
      * \param lines the maximum number of lines in the string returned.
+     * \param sinceNs the nanoseconds since Unix epoch to start dump (0 shows all)
+     * \param prefix the desired key prefix to match (nullptr shows all)
      */
-    std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const {
+    std::pair<std::string, int32_t> dump(
+            int32_t lines = INT32_MAX, int64_t sinceNs = 0, const char *prefix = nullptr) const {
         std::stringstream ss;
         int32_t ll = lines;
 
@@ -94,8 +97,8 @@
             --ll;
         }
         if (ll > 0) {
-            auto [s, l] = mTransactionLog.dump(ll);
-            ss << s;
+            auto [s, l] = mTransactionLog.dump(ll, sinceNs, prefix);
+            ss << std::move(s);
             ll -= l;
         }
         if (ll > 0) {
@@ -103,8 +106,8 @@
             --ll;
         }
         if (ll > 0) {
-            auto [s, l] = mTimeMachine.dump(ll);
-            ss << s;
+            auto [s, l] = mTimeMachine.dump(ll, sinceNs, prefix);
+            ss << std::move(s);
             ll -= l;
         }
         return { ss.str(), lines - ll };
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index ec59ec1..58f3ea8 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -57,6 +57,7 @@
         "liblog",
         "libmediametrics",
         "libmediautils",
+        "libmemunreachable",
         "libprotobuf-cpp-lite",
         "libstatslog",
         "libutils",
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 126e501..f80516e 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -107,14 +107,15 @@
     return NO_ERROR;
 }
 
-std::pair<std::string, int32_t> AudioAnalytics::dump(int32_t lines) const
+std::pair<std::string, int32_t> AudioAnalytics::dump(
+        int32_t lines, int64_t sinceNs, const char *prefix) const
 {
     std::stringstream ss;
     int32_t ll = lines;
 
     if (ll > 0) {
-        auto [s, l] = mAnalyticsState->dump(ll);
-        ss << s;
+        auto [s, l] = mAnalyticsState->dump(ll, sinceNs, prefix);
+        ss << std::move(s);
         ll -= l;
     }
     if (ll > 0) {
@@ -122,8 +123,8 @@
         --ll;
     }
     if (ll > 0) {
-        auto [s, l] = mPreviousAnalyticsState->dump(ll);
-        ss << s;
+        auto [s, l] = mPreviousAnalyticsState->dump(ll, sinceNs, prefix);
+        ss << std::move(s);
         ll -= l;
     }
     return { ss.str(), lines - ll };
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index 4a42e22..ba4c3f2 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -60,8 +60,17 @@
      * delivered.
      *
      * \param lines the maximum number of lines in the string returned.
+     * \param sinceNs the nanoseconds since Unix epoch to start dump (0 shows all)
+     * \param prefix the desired key prefix to match (nullptr shows all)
      */
-    std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const;
+    std::pair<std::string, int32_t> dump(
+            int32_t lines = INT32_MAX, int64_t sinceNs = 0, const char *prefix = nullptr) const;
+
+    void clear() {
+        // underlying state is locked.
+        mPreviousAnalyticsState->clear();
+        mAnalyticsState->clear();
+    }
 
 private:
 
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index a6fefd2..b9703ba 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -26,6 +26,8 @@
 #include <audio_utils/clock.h>                 // clock conversions
 #include <binder/IPCThreadState.h>             // get calling uid
 #include <cutils/properties.h>                 // for property_get
+#include <mediautils/MemoryLeakTrackUtil.h>
+#include <memunreachable/memunreachable.h>
 #include <private/android_filesystem_config.h> // UID
 
 namespace android {
@@ -205,91 +207,112 @@
         return NO_ERROR;
     }
 
-    // crack any parameters
-    const String16 protoOption("--proto");
-    const String16 clearOption("--clear");
+    static const String16 allOption("--all");
+    static const String16 clearOption("--clear");
+    static const String16 heapOption("--heap");
+    static const String16 helpOption("--help");
+    static const String16 prefixOption("--prefix");
+    static const String16 sinceOption("--since");
+    static const String16 unreachableOption("--unreachable");
+
+    bool all = false;
     bool clear = false;
-    const String16 sinceOption("--since");
-    nsecs_t ts_since = 0;
-    const String16 helpOption("--help");
-    const String16 onlyOption("--only");
-    std::string only;
-    const int n = args.size();
-    for (int i = 0; i < n; i++) {
-        if (args[i] == clearOption) {
+    bool heap = false;
+    bool unreachable = false;
+    int64_t sinceNs = 0;
+    std::string prefix;
+
+    const size_t n = args.size();
+    for (size_t i = 0; i < n; i++) {
+        if (args[i] == allOption) {
+            all = true;
+        } else if (args[i] == clearOption) {
             clear = true;
-        } else if (args[i] == protoOption) {
-            i++;
-            if (i < n) {
-                // ignore
-            } else {
-                result.append("missing value for -proto\n\n");
-            }
-        } else if (args[i] == sinceOption) {
-            i++;
-            if (i < n) {
-                String8 value(args[i]);
-                char *endp;
-                const char *p = value.string();
-                ts_since = strtoll(p, &endp, 10);
-                if (endp == p || *endp != '\0') {
-                    ts_since = 0;
-                }
-            } else {
-                ts_since = 0;
-            }
-            // command line is milliseconds; internal units are nano-seconds
-            ts_since *= NANOS_PER_MILLISECOND;
-        } else if (args[i] == onlyOption) {
-            i++;
-            if (i < n) {
-                String8 value(args[i]);
-                only = value.string();
-            }
+        } else if (args[i] == heapOption) {
+            heap = true;
         } else if (args[i] == helpOption) {
             // TODO: consider function area dumping.
             // dumpsys media.metrics audiotrack,codec
             // or dumpsys media.metrics audiotrack codec
 
             result.append("Recognized parameters:\n");
-            result.append("--help        this help message\n");
-            result.append("--proto #     dump using protocol #");
-            result.append("--clear       clears out saved records\n");
-            result.append("--only X      process records for component X\n");
-            result.append("--since X     include records since X\n");
-            result.append("             (X is milliseconds since the UNIX epoch)\n");
+            result.append("--all         show all records\n");
+            result.append("--clear       clear out saved records\n");
+            result.append("--heap        show heap usage (top 100)\n");
+            result.append("--help        display help\n");
+            result.append("--prefix X    process records for component X\n");
+            result.append("--since X     X < 0: records from -X seconds in the past\n");
+            result.append("              X = 0: ignore\n");
+            result.append("              X > 0: records from X seconds since Unix epoch\n");
+            result.append("--unreachable show unreachable memory (leaks)\n");
             write(fd, result.string(), result.size());
             return NO_ERROR;
+        } else if (args[i] == prefixOption) {
+            ++i;
+            if (i < n) {
+                prefix = String8(args[i]).string();
+            }
+        } else if (args[i] == sinceOption) {
+            ++i;
+            if (i < n) {
+                String8 value(args[i]);
+                char *endp;
+                const char *p = value.string();
+                long long sec = strtoll(p, &endp, 10);
+                if (endp == p || *endp != '\0' || sec == 0) {
+                    sinceNs = 0;
+                } else if (sec < 0) {
+                    sinceNs = systemTime(SYSTEM_TIME_REALTIME) + sec * NANOS_PER_SECOND;
+                } else {
+                    sinceNs = sec * NANOS_PER_SECOND;
+                }
+            }
+        } else if (args[i] == unreachableOption) {
+            unreachable = true;
         }
     }
 
     {
         std::lock_guard _l(mLock);
 
-        result.appendFormat("Dump of the %s process:\n", kServiceName);
-        dumpHeaders_l(result, ts_since);
-        dumpRecent_l(result, ts_since, only.c_str());
-
         if (clear) {
             mItemsDiscarded += mItems.size();
             mItems.clear();
-            // shall we clear the summary data too?
-        }
-        // TODO: maybe consider a better way of dumping audio analytics info.
-        constexpr int32_t linesToDump = 1000;
-        auto [ dumpString, lines ] = mAudioAnalytics.dump(linesToDump);
-        result.append(dumpString.c_str());
-        if (lines == linesToDump) {
-            result.append("-- some lines may be truncated --\n");
+            mAudioAnalytics.clear();
+        } else {
+            result.appendFormat("Dump of the %s process:\n", kServiceName);
+            const char *prefixptr = prefix.size() > 0 ? prefix.c_str() : nullptr;
+            dumpHeaders_l(result, sinceNs, prefixptr);
+            dumpQueue_l(result, sinceNs, prefixptr);
+
+            // TODO: maybe consider a better way of dumping audio analytics info.
+            const int32_t linesToDump = all ? INT32_MAX : 1000;
+            auto [ dumpString, lines ] = mAudioAnalytics.dump(linesToDump, sinceNs, prefixptr);
+            result.append(dumpString.c_str());
+            if (lines == linesToDump) {
+                result.append("-- some lines may be truncated --\n");
+            }
         }
     }
-
     write(fd, result.string(), result.size());
+
+    // Check heap and unreachable memory outside of lock.
+    if (heap) {
+        dprintf(fd, "\nDumping heap:\n");
+        std::string s = dumpMemoryAddresses(100 /* limit */);
+        write(fd, s.c_str(), s.size());
+    }
+    if (unreachable) {
+        dprintf(fd, "\nDumping unreachable memory:\n");
+        // TODO - should limit be an argument parameter?
+        std::string s = GetUnreachableMemoryString(true /* contents */, 100 /* limit */);
+        write(fd, s.c_str(), s.size());
+    }
     return NO_ERROR;
 }
 
 // dump headers
-void MediaMetricsService::dumpHeaders_l(String8 &result, nsecs_t ts_since)
+void MediaMetricsService::dumpHeaders_l(String8 &result, int64_t sinceNs, const char* prefix)
 {
     if (mediametrics::Item::isEnabled()) {
         result.append("Metrics gathering: enabled\n");
@@ -303,54 +326,36 @@
             "Records Discarded: %lld (by Count: %lld by Expiration: %lld)\n",
             (long long)mItemsDiscarded, (long long)mItemsDiscardedCount,
             (long long)mItemsDiscardedExpire);
-    if (ts_since != 0) {
+    if (prefix != nullptr) {
+        result.appendFormat("Restricting to prefix %s", prefix);
+    }
+    if (sinceNs != 0) {
         result.appendFormat(
             "Emitting Queue entries more recent than: %lld\n",
-            (long long)ts_since);
+            (long long)sinceNs);
     }
 }
 
-void MediaMetricsService::dumpRecent_l(
-        String8 &result, nsecs_t ts_since, const char * only)
+// TODO: should prefix be a set<string>?
+void MediaMetricsService::dumpQueue_l(String8 &result, int64_t sinceNs, const char* prefix)
 {
-    if (only != nullptr && *only == '\0') {
-        only = nullptr;
-    }
-    result.append("\nFinalized Metrics (oldest first):\n");
-    dumpQueue_l(result, ts_since, only);
-
-    // show who is connected and injecting records?
-    // talk about # records fed to the 'readers'
-    // talk about # records we discarded, perhaps "discarded w/o reading" too
-}
-
-void MediaMetricsService::dumpQueue_l(String8 &result) {
-    dumpQueue_l(result, (nsecs_t) 0, nullptr /* only */);
-}
-
-void MediaMetricsService::dumpQueue_l(
-        String8 &result, nsecs_t ts_since, const char * only) {
-    int slot = 0;
-
     if (mItems.empty()) {
         result.append("empty\n");
-    } else {
-        for (const auto &item : mItems) {
-            nsecs_t when = item->getTimestamp();
-            if (when < ts_since) {
-                continue;
-            }
-            // TODO: Only should be a set<string>
-            if (only != nullptr &&
-                    item->getKey() /* std::string */ != only) {
-                ALOGV("%s: omit '%s', it's not '%s'",
-                        __func__, item->getKey().c_str(), only);
-                continue;
-            }
-            result.appendFormat("%5d: %s\n",
-                   slot, item->toString().c_str());
-            slot++;
+        return;
+    }
+
+    int slot = 0;
+    for (const auto &item : mItems) {         // TODO: consider std::lower_bound() on mItems
+        if (item->getTimestamp() < sinceNs) { // sinceNs == 0 means all items shown
+            continue;
         }
+        if (prefix != nullptr && !startsWith(item->getKey(), prefix)) {
+            ALOGV("%s: omit '%s', it's not '%s'",
+                    __func__, item->getKey().c_str(), prefix);
+            continue;
+        }
+        result.appendFormat("%5d: %s\n", slot, item->toString().c_str());
+        slot++;
     }
 }
 
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index 935bee2..a93f7fb 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -85,11 +85,8 @@
     bool expirations_l(const std::shared_ptr<const mediametrics::Item>& item);
 
     // support for generating output
-    void dumpQueue_l(String8 &result);
-    void dumpQueue_l(String8 &result, nsecs_t, const char *only);
-    void dumpHeaders_l(String8 &result, nsecs_t ts_since);
-    void dumpSummaries_l(String8 &result, nsecs_t ts_since, const char * only);
-    void dumpRecent_l(String8 &result, nsecs_t ts_since, const char * only);
+    void dumpQueue_l(String8 &result, int64_t sinceNs, const char* prefix);
+    void dumpHeaders_l(String8 &result, int64_t sinceNs, const char* prefix);
 
     // The following variables accessed without mLock
 
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
index a4c3693..75819ba 100644
--- a/services/mediametrics/TimeMachine.h
+++ b/services/mediametrics/TimeMachine.h
@@ -149,8 +149,11 @@
             int32_t ll = lines;
             for (auto& tsPair : mPropertyMap) {
                 if (ll <= 0) break;
-                ss << dump(mKey, tsPair, time);
-                --ll;
+                std::string s = dump(mKey, tsPair, time);
+                if (s.size() > 0) {
+                    --ll;
+                    ss << std::move(s);
+                }
             }
             return { ss.str(), lines - ll };
         }
@@ -165,7 +168,7 @@
             const auto timeSequence = tsPair.second;
             auto eptr = timeSequence.lower_bound(time);
             if (eptr == timeSequence.end()) {
-                return tsPair.first + "={};\n";
+                return {}; // don't dump anything. tsPair.first + "={};\n";
             }
             std::stringstream ss;
             ss << key << "." << tsPair.first << "={";
@@ -407,25 +410,23 @@
      *
      * \param lines the maximum number of lines in the string returned.
      * \param key selects only that key.
-     * \param time to start the dump from.
+     * \param sinceNs the nanoseconds since Unix epoch to start dump (0 shows all)
+     * \param prefix the desired key prefix to match (nullptr shows all)
      */
     std::pair<std::string, int32_t> dump(
-            int32_t lines = INT32_MAX, const std::string &key = {}, int64_t time = 0) const {
+            int32_t lines = INT32_MAX, int64_t sinceNs = 0, const char *prefix = nullptr) const {
         std::lock_guard lock(mLock);
-        if (!key.empty()) {  // use std::regex
-            const auto it = mHistory.find(key);
-            if (it == mHistory.end()) return {};
-            std::lock_guard lock(getLockForKey(it->first));
-            return it->second->dump(lines, time);
-        }
-
         std::stringstream ss;
         int32_t ll = lines;
-        for (const auto &[lkey, lhist] : mHistory) {
-            std::lock_guard lock(getLockForKey(lkey));
-            if (lines <= 0) break;
-            auto [s, l] = lhist->dump(ll, time);
-            ss << s;
+
+        for (auto it = prefix != nullptr ? mHistory.lower_bound(prefix) : mHistory.begin();
+                it != mHistory.end();
+                ++it) {
+            if (ll <= 0) break;
+            if (prefix != nullptr && !startsWith(it->first, prefix)) break;
+            std::lock_guard lock(getLockForKey(it->first));
+            auto [s, l] = it->second->dump(ll, sinceNs);
+            ss << std::move(s);
             ll -= l;
         }
         return { ss.str(), lines - ll };
diff --git a/services/mediametrics/TransactionLog.h b/services/mediametrics/TransactionLog.h
index 190a99e..2359eec 100644
--- a/services/mediametrics/TransactionLog.h
+++ b/services/mediametrics/TransactionLog.h
@@ -127,8 +127,11 @@
      * for subsequent line limiting.
      *
      * \param lines the maximum number of lines in the string returned.
+     * \param sinceNs the nanoseconds since Unix epoch to start dump (0 shows all)
+     * \param prefix the desired key prefix to match (nullptr shows all)
      */
-    std::pair<std::string, int32_t> dump(int32_t lines) const {
+    std::pair<std::string, int32_t> dump(
+            int32_t lines, int64_t sinceNs, const char *prefix = nullptr) const {
         std::stringstream ss;
         int32_t ll = lines;
         std::lock_guard lock(mLock);
@@ -138,26 +141,25 @@
             ss << "Consolidated:\n";
             --ll;
         }
-        for (const auto &log : mLog) {
-            if (ll <= 0) break;
-            ss << "  " << log.second->toString() << "\n";
-            --ll;
-        }
+        auto [s, l] = dumpMapTimeItem(mLog, ll, sinceNs, prefix);
+        ss << std::move(s);
+        ll -= l;
 
         // Grouped by item key (category)
         if (ll > 0) {
             ss << "Categorized:\n";
             --ll;
         }
-        for (const auto &itemMap : mItemMap) {
+
+        for (auto it = prefix != nullptr ? mItemMap.lower_bound(prefix) : mItemMap.begin();
+                it != mItemMap.end();
+                ++it) {
             if (ll <= 0) break;
-            ss << " " << itemMap.first << "\n";
-            --ll;
-            for (const auto &item : itemMap.second) {
-                if (ll <= 0) break;
-                ss << "  " << item.second->toString() << "\n";
-                --ll;
-            }
+            if (prefix != nullptr && !startsWith(it->first, prefix)) break;
+            auto [s, l] = dumpMapTimeItem(it->second, ll - 1, sinceNs, prefix);
+            if (l == 0) continue; // don't show empty groups (due to sinceNs).
+            ss << " " << it->first << "\n" << std::move(s);
+            ll -= l + 1;
         }
         return { ss.str(), lines - ll };
     }
@@ -184,6 +186,24 @@
     using MapTimeItem =
             std::multimap<int64_t /* time */, std::shared_ptr<const mediametrics::Item>>;
 
+    static std::pair<std::string, int32_t> dumpMapTimeItem(
+            const MapTimeItem& mapTimeItem,
+            int32_t lines, int64_t sinceNs = 0, const char *prefix = nullptr) {
+        std::stringstream ss;
+        int32_t ll = lines;
+        // Note: for our data, mapTimeItem.lower_bound(0) == mapTimeItem.begin().
+        for (auto it = mapTimeItem.lower_bound(sinceNs);
+                it != mapTimeItem.end(); ++it) {
+            if (ll <= 0) break;
+            if (prefix != nullptr && !startsWith(it->second->getKey(), prefix)) {
+                continue;
+            }
+            ss << "  " << it->second->toString() << "\n";
+            --ll;
+        }
+        return { ss.str(), lines - ll };
+    }
+
     // GUARDED_BY mLock
     /**
      * Garbage collects if the TimeMachine size exceeds the high water mark.