Merge "CameraService: Enable presubmit unit tests, and fix them"
diff --git a/camera/ndk/NdkCameraManager.cpp b/camera/ndk/NdkCameraManager.cpp
index 60b4763..f14a4c9 100644
--- a/camera/ndk/NdkCameraManager.cpp
+++ b/camera/ndk/NdkCameraManager.cpp
@@ -23,6 +23,7 @@
 
 #include <camera/NdkCameraManager.h>
 #include "impl/ACameraManager.h"
+#include "impl/ACameraMetadata.h"
 
 using namespace android;
 
@@ -107,7 +108,14 @@
                 __FUNCTION__, mgr, cameraId, chars);
         return ACAMERA_ERROR_INVALID_PARAMETER;
     }
-    return mgr->getCameraCharacteristics(cameraId, chars);
+    sp<ACameraMetadata> spChars;
+    camera_status_t status = mgr->getCameraCharacteristics(cameraId, &spChars);
+    if (status != ACAMERA_OK) {
+        return status;
+    }
+    spChars->incStrong((void*) ACameraManager_getCameraCharacteristics);
+    *chars = spChars.get();
+    return ACAMERA_OK;
 }
 
 EXPORT
diff --git a/camera/ndk/NdkCameraMetadata.cpp b/camera/ndk/NdkCameraMetadata.cpp
index 65de81f..34ec2da 100644
--- a/camera/ndk/NdkCameraMetadata.cpp
+++ b/camera/ndk/NdkCameraMetadata.cpp
@@ -57,13 +57,15 @@
         ALOGE("%s: src is null!", __FUNCTION__);
         return nullptr;
     }
-    return new ACameraMetadata(*src);
+    ACameraMetadata* copy = new ACameraMetadata(*src);
+    copy->incStrong((void*) ACameraMetadata_copy);
+    return copy;
 }
 
 EXPORT
 void ACameraMetadata_free(ACameraMetadata* metadata) {
     ATRACE_CALL();
     if (metadata != nullptr) {
-        delete metadata;
+        metadata->decStrong((void*) ACameraMetadata_free);
     }
 }
diff --git a/camera/ndk/NdkCaptureRequest.cpp b/camera/ndk/NdkCaptureRequest.cpp
index ac1856b..ddb69d7 100644
--- a/camera/ndk/NdkCaptureRequest.cpp
+++ b/camera/ndk/NdkCaptureRequest.cpp
@@ -137,7 +137,7 @@
     if (request == nullptr) {
         return;
     }
-    delete request->settings;
+    request->settings.clear();
     delete request->targets;
     delete request;
     return;
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 907debc..c908323 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -50,11 +50,11 @@
 CameraDevice::CameraDevice(
         const char* id,
         ACameraDevice_StateCallbacks* cb,
-        std::unique_ptr<ACameraMetadata> chars,
+        sp<ACameraMetadata> chars,
         ACameraDevice* wrapper) :
         mCameraId(id),
         mAppCallbacks(*cb),
-        mChars(std::move(chars)),
+        mChars(chars),
         mServiceCallback(new ServiceCallback(this)),
         mWrapper(wrapper),
         mInError(false),
@@ -436,7 +436,7 @@
     if (req == nullptr) {
         return;
     }
-    delete req->settings;
+    req->settings.clear();
     delete req->targets;
     delete req;
 }
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 1369148..7d64081 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -48,7 +48,7 @@
 class CameraDevice final : public RefBase {
   public:
     CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
-                  std::unique_ptr<ACameraMetadata> chars,
+                  sp<ACameraMetadata> chars,
                   ACameraDevice* wrapper);
     ~CameraDevice();
 
@@ -156,7 +156,7 @@
     mutable Mutex mDeviceLock;
     const String8 mCameraId;                          // Camera ID
     const ACameraDevice_StateCallbacks mAppCallbacks; // Callback to app
-    const std::unique_ptr<ACameraMetadata> mChars;    // Camera characteristics
+    const sp<ACameraMetadata> mChars;                 // Camera characteristics
     const sp<ServiceCallback> mServiceCallback;
     ACameraDevice* mWrapper;
 
@@ -294,8 +294,8 @@
  */
 struct ACameraDevice {
     ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
-                  std::unique_ptr<ACameraMetadata> chars) :
-            mDevice(new CameraDevice(id, cb, std::move(chars), this)) {}
+                  sp<ACameraMetadata> chars) :
+            mDevice(new CameraDevice(id, cb, chars, this)) {}
 
     ~ACameraDevice() {};
 
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index c59d0e7..ee67677 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -402,7 +402,7 @@
 }
 
 camera_status_t ACameraManager::getCameraCharacteristics(
-        const char *cameraIdStr, ACameraMetadata **characteristics) {
+        const char* cameraIdStr, sp<ACameraMetadata>* characteristics) {
     Mutex::Autolock _l(mLock);
 
     sp<hardware::ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
@@ -437,18 +437,16 @@
         const char* cameraId,
         ACameraDevice_StateCallbacks* callback,
         /*out*/ACameraDevice** outDevice) {
-    ACameraMetadata* rawChars;
-    camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars);
+    sp<ACameraMetadata> chars;
+    camera_status_t ret = getCameraCharacteristics(cameraId, &chars);
     Mutex::Autolock _l(mLock);
     if (ret != ACAMERA_OK) {
         ALOGE("%s: cannot get camera characteristics for camera %s. err %d",
                 __FUNCTION__, cameraId, ret);
         return ACAMERA_ERROR_INVALID_PARAMETER;
     }
-    std::unique_ptr<ACameraMetadata> chars(rawChars);
-    rawChars = nullptr;
 
-    ACameraDevice* device = new ACameraDevice(cameraId, callback, std::move(chars));
+    ACameraDevice* device = new ACameraDevice(cameraId, callback, chars);
 
     sp<hardware::ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
     if (cs == nullptr) {
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index cc42f77..ce65769 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -186,7 +186,7 @@
     static void     deleteCameraIdList(ACameraIdList* cameraIdList);
 
     camera_status_t getCameraCharacteristics(
-            const char *cameraId, ACameraMetadata **characteristics);
+            const char* cameraId, android::sp<ACameraMetadata>* characteristics);
     camera_status_t openCamera(const char* cameraId,
                                ACameraDevice_StateCallbacks* callback,
                                /*out*/ACameraDevice** device);
diff --git a/camera/ndk/impl/ACaptureRequest.h b/camera/ndk/impl/ACaptureRequest.h
index 06b2cc3..b11dafb 100644
--- a/camera/ndk/impl/ACaptureRequest.h
+++ b/camera/ndk/impl/ACaptureRequest.h
@@ -55,7 +55,7 @@
         return ACAMERA_OK;
     }
 
-    ACameraMetadata*      settings;
+    sp<ACameraMetadata>   settings;
     ACameraOutputTargets* targets;
     void*                 context;
 };
diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp
index f5d2d6d..d659445 100644
--- a/media/libnblog/NBLog.cpp
+++ b/media/libnblog/NBLog.cpp
@@ -20,11 +20,7 @@
 
 #include <algorithm>
 #include <climits>
-#include <deque>
-#include <fstream>
-#include <iostream>
 #include <math.h>
-#include <numeric>
 #include <unordered_set>
 #include <vector>
 #include <stdarg.h>
@@ -727,7 +723,7 @@
 };
 
 NBLog::Reader::Reader(const void *shared, size_t size, const std::string &name)
-    : mFd(-1), mIndent(0), mLost(0), mName(name),
+    : mName(name),
       mShared((/*const*/ Shared *) shared), /*mIMemory*/
       mFifo(mShared != NULL ?
         new audio_utils_fifo(size, sizeof(uint8_t),
@@ -769,10 +765,10 @@
 // Copies content of a Reader FIFO into its Snapshot
 // The Snapshot has the same raw data, but represented as a sequence of entries
 // and an EntryIterator making it possible to process the data.
-std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot()
+std::unique_ptr<NBLog::Snapshot> NBLog::Reader::getSnapshot()
 {
     if (mFifoReader == NULL) {
-        return std::unique_ptr<NBLog::Reader::Snapshot>(new Snapshot());
+        return std::make_unique<Snapshot>();
     }
 
     // This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the
@@ -802,7 +798,7 @@
 
     if (availToRead <= 0) {
         ALOGW_IF(availToRead < 0, "NBLog Reader %s failed to catch up with Writer", mName.c_str());
-        return std::make_unique<NBLog::Reader::Snapshot>();
+        return std::make_unique<Snapshot>();
     }
 
     std::unique_ptr<Snapshot> snapshot(new Snapshot(availToRead));
@@ -853,17 +849,12 @@
 
 // Takes raw content of the local merger FIFO, processes log entries, and
 // writes the data to a map of class PerformanceAnalysis, based on their thread ID.
-void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Reader::Snapshot &snapshot)
+void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int author)
 {
-    String8 timestamp, body;
-
-    for (auto entry = snapshot.begin(); entry != snapshot.end();) {
-        switch (entry->type) {
-        case EVENT_START_FMT:
-            entry = handleFormat(FormatEntry(entry), &timestamp, &body);
-            break;
+    for (const entry &etr : snapshot) {
+        switch (etr.type) {
         case EVENT_HISTOGRAM_ENTRY_TS: {
-            HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data);
+            HistTsEntry *data = (HistTsEntry *) (etr.data);
             // TODO This memcpies are here to avoid unaligned memory access crash.
             // There's probably a more efficient way to do it
             log_hash_t hash;
@@ -872,45 +863,41 @@
             memcpy(&ts, &data->ts, sizeof(ts));
             // TODO: hash for histogram ts and audio state need to match
             // and correspond to audio production source file location
-            mThreadPerformanceAnalysis[data->author][0 /*hash*/].logTsEntry(ts);
-            ++entry;
-            break;
-        }
+            mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(ts);
+        } break;
         case EVENT_AUDIO_STATE: {
-            HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data);
+            HistTsEntry *data = (HistTsEntry *) (etr.data);
             // TODO This memcpies are here to avoid unaligned memory access crash.
             // There's probably a more efficient way to do it
             log_hash_t hash;
             memcpy(&hash, &(data->hash), sizeof(hash));
-            // TODO: remove ts if unused
-            int64_t ts;
-            memcpy(&ts, &data->ts, sizeof(ts));
-            mThreadPerformanceAnalysis[data->author][0 /*hash*/].handleStateChange();
-            ++entry;
-            break;
-        }
+            mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange();
+        } break;
         case EVENT_END_FMT:
-            body.appendFormat("warning: got to end format event");
-            ++entry;
-            break;
         case EVENT_RESERVED:
+        case EVENT_UPPER_BOUND:
+            ALOGW("warning: unexpected event %d", etr.type);
         default:
-            body.appendFormat("warning: unexpected event %d", entry->type);
-            ++entry;
             break;
         }
     }
-    // FIXME: decide whether to print the warnings here or elsewhere
-    if (!body.isEmpty()) {
-        dumpLine(&timestamp, &body);
-    }
 }
 
 void NBLog::MergeReader::getAndProcessSnapshot()
 {
-    // get a snapshot, process it
-    std::unique_ptr<Snapshot> snap = getSnapshot();
-    getAndProcessSnapshot(*snap);
+    // get a snapshot of each reader and process them
+    // TODO insert lock here
+    const size_t nLogs = mReaders.size();
+    std::vector<std::unique_ptr<Snapshot>> snapshots(nLogs);
+    for (size_t i = 0; i < nLogs; i++) {
+        snapshots[i] = mReaders[i]->getSnapshot();
+    }
+    // TODO unlock lock here
+    for (size_t i = 0; i < nLogs; i++) {
+        if (snapshots[i] != nullptr) {
+            getAndProcessSnapshot(*(snapshots[i]), i);
+        }
+    }
 }
 
 void NBLog::MergeReader::dump(int fd, int indent)
@@ -919,66 +906,41 @@
     ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis);
 }
 
-void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
+// TODO for future compatibility, would prefer to have a dump() go to string, and then go
+// to fd only when invoked through binder.
+void NBLog::DumpReader::dump(int fd, size_t indent)
 {
-    mFd = fd;
-    mIndent = indent;
-    String8 timestamp, body;
-
-    // Range-based for loop isn't used here because handleFormat() returns an EntryIterator
-    // that points to the next entry (it handles all of the necessary operator++() calls).
-    for (auto entry = snapshot.begin(); entry != snapshot.end();) {
-        switch (entry->type) {
-        case EVENT_START_FMT:
-            entry = handleFormat(FormatEntry(entry), &timestamp, &body);
-            break;
-        case EVENT_HISTOGRAM_ENTRY_TS:
-            ++entry;
-            break;
-        case EVENT_AUDIO_STATE:
-            ++entry;
-            break;
-        case EVENT_END_FMT:
-            body.appendFormat("warning: got to end format event");
-            ++entry;
-            break;
-        case EVENT_MONOTONIC_CYCLE_TIME: {
-            uint32_t monotonicNs = *(uint32_t *) (entry->data);
-            body.appendFormat("Thread cycle took %u ns", monotonicNs);
-            ++entry;
-        } break;
-        case EVENT_RESERVED:
-        default:
-            body.appendFormat("warning: unexpected event %d", entry->type);
-            ++entry;
-            break;
-        }
-        // FIXME: decide whether to print the warnings here or elsewhere
-        if (!body.isEmpty()) {
-            dumpLine(&timestamp, &body);
-        }
-    }
-}
-
-void NBLog::Reader::dump(int fd, size_t indent)
-{
-    // get a snapshot, dump it
-    std::unique_ptr<Snapshot> snap = getSnapshot();
-    dump(fd, indent, *snap);
-}
-
-// Writes a string to the console
-void NBLog::Reader::dumpLine(const String8 *timestamp, String8 *body)
-{
-    if (timestamp == nullptr || body == nullptr) {
+    if (fd < 0) return;
+    std::unique_ptr<Snapshot> snapshot = getSnapshot();
+    if (snapshot == nullptr) {
         return;
     }
-    if (mFd >= 0) {
-        dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp->string(), body->string());
-    } else {
-        ALOGI("%.*s%s %s", mIndent, "", timestamp->string(), body->string());
+    String8 timestamp, body;
+
+    // TODO all logged types should have a printable format.
+    for (auto it = snapshot->begin(); it != snapshot->end(); ++it) {
+        switch (it->type) {
+        case EVENT_START_FMT:
+            it = handleFormat(FormatEntry(it), &timestamp, &body);
+            break;
+        case EVENT_MONOTONIC_CYCLE_TIME: {
+            uint32_t monotonicNs;
+            memcpy(&monotonicNs, it->data, sizeof(monotonicNs));
+            body.appendFormat("Thread cycle took %u ns", monotonicNs);
+        } break;
+        case EVENT_END_FMT:
+        case EVENT_RESERVED:
+        case EVENT_UPPER_BOUND:
+            body.appendFormat("warning: unexpected event %d", it->type);
+        default:
+            break;
+        }
+        if (!body.isEmpty()) {
+            dprintf(fd, "%.*s%s %s\n", (int)indent, "", timestamp.string(), body.string());
+            body.clear();
+        }
+        timestamp.clear();
     }
-    body->clear();
 }
 
 bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
@@ -986,9 +948,7 @@
     return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
 }
 
-// ---------------------------------------------------------------------------
-
-void NBLog::appendTimestamp(String8 *body, const void *data)
+void NBLog::DumpReader::appendTimestamp(String8 *body, const void *data)
 {
     if (body == nullptr || data == nullptr) {
         return;
@@ -999,7 +959,7 @@
                     (int) ((ts / (1000 * 1000)) % 1000));
 }
 
-void NBLog::appendInt(String8 *body, const void *data)
+void NBLog::DumpReader::appendInt(String8 *body, const void *data)
 {
     if (body == nullptr || data == nullptr) {
         return;
@@ -1008,7 +968,7 @@
     body->appendFormat("<%d>", x);
 }
 
-void NBLog::appendFloat(String8 *body, const void *data)
+void NBLog::DumpReader::appendFloat(String8 *body, const void *data)
 {
     if (body == nullptr || data == nullptr) {
         return;
@@ -1018,7 +978,7 @@
     body->appendFormat("<%f>", f);
 }
 
-void NBLog::appendPID(String8 *body, const void* data, size_t length)
+void NBLog::DumpReader::appendPID(String8 *body, const void* data, size_t length)
 {
     if (body == nullptr || data == nullptr) {
         return;
@@ -1028,7 +988,7 @@
     body->appendFormat("<PID: %d, name: %.*s>", id, (int) (length - sizeof(pid_t)), name);
 }
 
-String8 NBLog::bufferDump(const uint8_t *buffer, size_t size)
+String8 NBLog::DumpReader::bufferDump(const uint8_t *buffer, size_t size)
 {
     String8 str;
     if (buffer == nullptr) {
@@ -1042,14 +1002,14 @@
     return str;
 }
 
-String8 NBLog::bufferDump(const EntryIterator &it)
+String8 NBLog::DumpReader::bufferDump(const EntryIterator &it)
 {
     return bufferDump(it, it->length + Entry::kOverhead);
 }
 
-NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
-                                                         String8 *timestamp,
-                                                         String8 *body)
+NBLog::EntryIterator NBLog::DumpReader::handleFormat(const FormatEntry &fmtEntry,
+                                                           String8 *timestamp,
+                                                           String8 *body)
 {
     // log timestamp
     int64_t ts = fmtEntry.timestamp();
@@ -1135,7 +1095,6 @@
         ++arg;
     }
     ALOGW_IF(arg->type != EVENT_END_FMT, "Expected end of format, got %d", arg->type);
-    ++arg;
     return arg;
 }
 
@@ -1172,10 +1131,10 @@
 // Merge registered readers, sorted by timestamp, and write data to a single FIFO in local memory
 void NBLog::Merger::merge()
 {
-    // FIXME This is called by merge thread
-    //       but the access to shared variable mNamedReaders is not yet protected by a lock.
+    if (true) return; // Merging is not necessary at the moment, so this is to disable it
+                      // and bypass compiler warnings about member variables not being used.
     const int nLogs = mReaders.size();
-    std::vector<std::unique_ptr<NBLog::Reader::Snapshot>> snapshots(nLogs);
+    std::vector<std::unique_ptr<Snapshot>> snapshots(nLogs);
     std::vector<EntryIterator> offsets;
     offsets.reserve(nLogs);
     for (int i = 0; i < nLogs; ++i) {
@@ -1258,13 +1217,12 @@
     {
         AutoMutex _l(mMutex);
         // If mTimeoutUs is negative, wait on the condition variable until it's positive.
-        // If it's positive, wait kThreadSleepPeriodUs and then merge
-        nsecs_t waitTime = mTimeoutUs > 0 ? kThreadSleepPeriodUs * 1000 : LLONG_MAX;
-        mCond.waitRelative(mMutex, waitTime);
+        // If it's positive, merge. The minimum period between waking the condition variable
+        // is handled in AudioFlinger::MediaLogNotifier::threadLoop().
+        mCond.wait(mMutex);
         doMerge = mTimeoutUs > 0;
         mTimeoutUs -= kThreadSleepPeriodUs;
     }
-    doMerge = false;    // Disable merging for now.
     if (doMerge) {
         // Merge data from all the readers
         mMerger.merge();
diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h
index 71941a4..c9bfaae 100644
--- a/media/libnblog/include/media/nblog/NBLog.h
+++ b/media/libnblog/include/media/nblog/NBLog.h
@@ -19,7 +19,6 @@
 #ifndef ANDROID_MEDIA_NBLOG_H
 #define ANDROID_MEDIA_NBLOG_H
 
-#include <deque>
 #include <map>
 #include <unordered_set>
 #include <vector>
@@ -76,17 +75,6 @@
 private:
 
     // ---------------------------------------------------------------------------
-    // API for handling format entry operations
-
-    // a formatted entry has the following structure:
-    //    * START_FMT entry, containing the format string
-    //    * TIMESTAMP entry
-    //    * HASH entry
-    //    * author entry of the thread that generated it (optional, present in merged log)
-    //    * format arg1
-    //    * format arg2
-    //    * ...
-    //    * END_FMT entry
 
     // entry representation in memory
     struct entry {
@@ -144,6 +132,9 @@
         const uint8_t  *mPtr;   // Should not be nullptr except for dummy initialization
     };
 
+    // ---------------------------------------------------------------------------
+    // The following classes are used for merging into the Merger's buffer.
+
     class AbstractEntry {
     public:
         virtual ~AbstractEntry() {}
@@ -175,6 +166,17 @@
         const uint8_t  *mEntry;
     };
 
+    // API for handling format entry operations
+
+    // a formatted entry has the following structure:
+    //    * START_FMT entry, containing the format string
+    //    * TIMESTAMP entry
+    //    * HASH entry
+    //    * author entry of the thread that generated it (optional, present in merged log)
+    //    * format arg1
+    //    * format arg2
+    //    * ...
+    //    * END_FMT entry
     class FormatEntry : public AbstractEntry {
     public:
         // explicit FormatEntry(const EntryIterator &it);
@@ -226,7 +228,16 @@
 
     // ---------------------------------------------------------------------------
 
-    // representation of a single log entry in private memory
+    // representation of a single log entry in shared memory
+    //  byte[0]             mEvent
+    //  byte[1]             mLength
+    //  byte[2]             mData[0]
+    //  ...
+    //  byte[2+i]           mData[i]
+    //  ...
+    //  byte[2+mLength-1]   mData[mLength-1]
+    //  byte[2+mLength]     duplicate copy of mLength to permit reverse scan
+    //  byte[3+mLength]     start of next log entry
     class Entry {
     public:
         Entry(Event event, const void *data, size_t length)
@@ -267,24 +278,6 @@
         int value;
     }; //TODO __attribute__((packed));
 
-    // representation of a single log entry in shared memory
-    //  byte[0]             mEvent
-    //  byte[1]             mLength
-    //  byte[2]             mData[0]
-    //  ...
-    //  byte[2+i]           mData[i]
-    //  ...
-    //  byte[2+mLength-1]   mData[mLength-1]
-    //  byte[2+mLength]     duplicate copy of mLength to permit reverse scan
-    //  byte[3+mLength]     start of next log entry
-
-    static void    appendInt(String8 *body, const void *data);
-    static void    appendFloat(String8 *body, const void *data);
-    static void    appendPID(String8 *body, const void *data, size_t length);
-    static void    appendTimestamp(String8 *body, const void *data);
-    static size_t  fmtEntryLength(const uint8_t *data);
-    static String8 bufferDump(const uint8_t *buffer, size_t size);
-    static String8 bufferDump(const EntryIterator &it);
 public:
 
     // Located in shared memory, must be POD.
@@ -420,89 +413,86 @@
 
     // ---------------------------------------------------------------------------
 
+    // A snapshot of a readers buffer
+    // This is raw data. No analysis has been done on it
+    class Snapshot {
+    public:
+        Snapshot() = default;
+
+        explicit Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
+
+        ~Snapshot() { delete[] mData; }
+
+        // amount of data lost (given by audio_utils_fifo_reader)
+        size_t   lost() const { return mLost; }
+
+        // iterator to beginning of readable segment of snapshot
+        // data between begin and end has valid entries
+        EntryIterator begin() const { return mBegin; }
+
+        // iterator to end of readable segment of snapshot
+        EntryIterator end() const { return mEnd; }
+
+    private:
+        friend class Reader;
+        uint8_t * const       mData{};
+        size_t                mLost{0};
+        EntryIterator mBegin;
+        EntryIterator mEnd;
+    };
+
     class Reader : public RefBase {
     public:
-        // A snapshot of a readers buffer
-        // This is raw data. No analysis has been done on it
-        class Snapshot {
-        public:
-            Snapshot() : mData(NULL), mLost(0) {}
-
-            Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
-
-            ~Snapshot() { delete[] mData; }
-
-            // copy of the buffer
-            uint8_t *data() const { return mData; }
-
-            // amount of data lost (given by audio_utils_fifo_reader)
-            size_t   lost() const { return mLost; }
-
-            // iterator to beginning of readable segment of snapshot
-            // data between begin and end has valid entries
-            EntryIterator begin() { return mBegin; }
-
-            // iterator to end of readable segment of snapshot
-            EntryIterator end() { return mEnd; }
-
-        private:
-            friend class Reader;
-            uint8_t              *mData;
-            size_t                mLost;
-            EntryIterator mBegin;
-            EntryIterator mEnd;
-        };
-
         // Input parameter 'size' is the desired size of the timeline in byte units.
         // The size of the shared memory must be at least Timeline::sharedSize(size).
         Reader(const void *shared, size_t size, const std::string &name);
         Reader(const sp<IMemory>& iMemory, size_t size, const std::string &name);
-
         virtual ~Reader();
 
         // get snapshot of readers fifo buffer, effectively consuming the buffer
         std::unique_ptr<Snapshot> getSnapshot();
-        // dump a particular snapshot of the reader
-        void dump(int fd, size_t indent, Snapshot &snap);
-        // dump the current content of the reader's buffer (call getSnapshot() and previous dump())
-        void dump(int fd, size_t indent = 0);
-
         bool     isIMemory(const sp<IMemory>& iMemory) const;
-
         const std::string &name() const { return mName; }
 
-    protected:
-        // print a summary of the performance to the console
-        void    dumpLine(const String8 *timestamp, String8 *body);
-        EntryIterator   handleFormat(const FormatEntry &fmtEntry,
-                                     String8 *timestamp,
-                                     String8 *body);
-        int mFd;                // file descriptor
-        int mIndent;            // indentation level
-        int mLost;              // bytes of data lost before buffer was read
-        const std::string mName;      // name of reader (actually name of writer)
-        static constexpr int kMaxObtainTries = 3;
-
     private:
+        static constexpr int kMaxObtainTries = 3;
+        // startingTypes and endingTypes are used to check for log corruption.
         static const std::unordered_set<Event> startingTypes;
         static const std::unordered_set<Event> endingTypes;
-
         // declared as const because audio_utils_fifo() constructor
         sp<IMemory> mIMemory;       // ref-counted version, assigned only in constructor
 
+        const std::string mName;            // name of reader (actually name of writer)
         /*const*/ Shared* const mShared;    // raw pointer to shared memory, actually const but not
         audio_utils_fifo * const mFifo;                 // FIFO itself,
-        // non-NULL unless constructor fails
+                                                        // non-NULL unless constructor fails
         audio_utils_fifo_reader * const mFifoReader;    // used to read from FIFO,
-        // non-NULL unless constructor fails
+                                                        // non-NULL unless constructor fails
 
         // Searches for the last entry of type <type> in the range [front, back)
         // back has to be entry-aligned. Returns nullptr if none enconuntered.
         static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
                                                    const std::unordered_set<Event> &types);
+    };
 
-        // dummy method for handling absent author entry
-        virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {}
+    class DumpReader : public Reader {
+    public:
+        DumpReader(const void *shared, size_t size, const std::string &name)
+            : Reader(shared, size, name) {}
+        DumpReader(const sp<IMemory>& iMemory, size_t size, const std::string &name)
+            : Reader(iMemory, size, name) {}
+        void dump(int fd, size_t indent = 0);
+    private:
+        void handleAuthor(const AbstractEntry& fmtEntry __unused, String8* body __unused) {}
+        EntryIterator handleFormat(const FormatEntry &fmtEntry, String8 *timestamp, String8 *body);
+
+        static void    appendInt(String8 *body, const void *data);
+        static void    appendFloat(String8 *body, const void *data);
+        static void    appendPID(String8 *body, const void *data, size_t length);
+        static void    appendTimestamp(String8 *body, const void *data);
+        //static size_t  fmtEntryLength(const uint8_t *data);   // TODO Eric remove if not used
+        static String8 bufferDump(const uint8_t *buffer, size_t size);
+        static String8 bufferDump(const EntryIterator &it);
     };
 
     // ---------------------------------------------------------------------------
@@ -542,7 +532,7 @@
 
         void dump(int fd, int indent = 0);
         // process a particular snapshot of the reader
-        void getAndProcessSnapshot(Snapshot & snap);
+        void getAndProcessSnapshot(Snapshot &snap, int author);
         // call getSnapshot of the content of the reader's buffer and process the data
         void getAndProcessSnapshot();
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 43566b7..ed2b3c0 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -66,7 +66,6 @@
 #include <media/MemoryLeakTrackUtil.h>
 #include <media/nbaio/Pipe.h>
 #include <media/nbaio/PipeReader.h>
-#include <media/AudioParameter.h>
 #include <mediautils/BatteryNotifier.h>
 #include <mediautils/ServiceUtilities.h>
 #include <private/android_filesystem_config.h>
@@ -517,6 +516,15 @@
 
         mPatchPanel.dump(fd);
 
+        // dump external setParameters
+        auto dumpLogger = [fd](SimpleLog& logger, const char* name) {
+            dprintf(fd, "\n%s setParameters:\n", name);
+            logger.dump(fd, "    " /* prefix */);
+        };
+        dumpLogger(mRejectedSetParameterLog, "Rejected");
+        dumpLogger(mAppSetParameterLog, "App");
+        dumpLogger(mSystemSetParameterLog, "System");
+
         BUFLOG_RESET;
 
         if (locked) {
@@ -1229,16 +1237,33 @@
 
     AudioParameter param = AudioParameter(keyValuePairs);
     String8 value;
+    AudioParameter rejectedParam;
     for (auto& key : kReservedParameters) {
         if (param.get(key, value) == NO_ERROR) {
-            ALOGW("%s: filtering key %s value %s from uid %d",
-                  __func__, key.string(), value.string(), callingUid);
+            rejectedParam.add(key, value);
             param.remove(key);
         }
     }
+    logFilteredParameters(param.size() + rejectedParam.size(), keyValuePairs,
+                          rejectedParam.size(), rejectedParam.toString(), callingUid);
     keyValuePairs = param.toString();
 }
 
+void AudioFlinger::logFilteredParameters(size_t originalKVPSize, const String8& originalKVPs,
+                                         size_t rejectedKVPSize, const String8& rejectedKVPs,
+                                         uid_t callingUid) {
+    auto prefix = String8::format("UID %5d", callingUid);
+    auto suffix = String8::format("%zu KVP received: %s", originalKVPSize, originalKVPs.c_str());
+    if (rejectedKVPSize != 0) {
+        auto error = String8::format("%zu KVP rejected: %s", rejectedKVPSize, rejectedKVPs.c_str());
+        ALOGW("%s: %s, %s, %s", __func__, prefix.c_str(), error.c_str(), suffix.c_str());
+        mRejectedSetParameterLog.log("%s, %s, %s", prefix.c_str(), error.c_str(), suffix.c_str());
+    } else {
+        auto& logger = (isServiceUid(callingUid) ? mSystemSetParameterLog : mAppSetParameterLog);
+        logger.log("%s, %s", prefix.c_str(), suffix.c_str());
+    }
+}
+
 status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
 {
     ALOGV("setParameters(): io %d, keyvalue %s, calling pid %d calling uid %d",
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 2d2da3e..9c36479 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -812,6 +812,9 @@
     status_t    checkStreamType(audio_stream_type_t stream) const;
 
     void        filterReservedParameters(String8& keyValuePairs, uid_t callingUid);
+    void        logFilteredParameters(size_t originalKVPSize, const String8& originalKVPs,
+                                      size_t rejectedKVPSize, const String8& rejectedKVPs,
+                                      uid_t callingUid);
 
 public:
     // These methods read variables atomically without mLock,
@@ -832,7 +835,11 @@
     PatchPanel mPatchPanel;
     sp<EffectsFactoryHalInterface> mEffectsFactoryHal;
 
-    bool        mSystemReady;
+    bool       mSystemReady;
+
+    SimpleLog  mRejectedSetParameterLog;
+    SimpleLog  mAppSetParameterLog;
+    SimpleLog  mSystemSetParameterLog;
 };
 
 #undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index 095dd5c..b23832e 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -58,9 +58,10 @@
             shared->size() < NBLog::Timeline::sharedSize(size)) {
         return;
     }
-    sp<NBLog::Reader> reader(new NBLog::Reader(shared, size, name));
+    sp<NBLog::Reader> reader(new NBLog::Reader(shared, size, name)); // Reader handled by merger
+    sp<NBLog::DumpReader> dumpReader(new NBLog::DumpReader(shared, size, name)); // for dumpsys
     Mutex::Autolock _l(mLock);
-    mReaders.add(reader);
+    mDumpReaders.add(dumpReader);
     mMerger.addReader(reader);
 }
 
@@ -70,9 +71,10 @@
         return;
     }
     Mutex::Autolock _l(mLock);
-    for (size_t i = 0; i < mReaders.size(); ) {
-        if (mReaders[i]->isIMemory(shared)) {
-            mReaders.removeAt(i);
+    for (size_t i = 0; i < mDumpReaders.size(); ) {
+        if (mDumpReaders[i]->isIMemory(shared)) {
+            mDumpReaders.removeAt(i);
+            // TODO mMerger.removeReaders(shared)
         } else {
             i++;
         }
@@ -120,18 +122,18 @@
                 return NO_ERROR;
             }
 
-            for (auto reader : mReaders) {
+            for (const auto &dumpReader : mDumpReaders) {
                 if (fd >= 0) {
-                    dprintf(fd, "\n%s:\n", reader->name().c_str());
-                    reader->dump(fd, 0 /*indent*/);
+                    dprintf(fd, "\n%s:\n", dumpReader->name().c_str());
+                    dumpReader->dump(fd, 0 /*indent*/);
                 } else {
-                    ALOGI("%s:", reader->name().c_str());
+                    ALOGI("%s:", dumpReader->name().c_str());
                 }
             }
             mLock.unlock();
         }
     }
-    //mMergeReader.dump(fd);
+    mMergeReader.dump(fd);
     return NO_ERROR;
 }
 
diff --git a/services/medialog/MediaLogService.h b/services/medialog/MediaLogService.h
index 3763484..a1572f9 100644
--- a/services/medialog/MediaLogService.h
+++ b/services/medialog/MediaLogService.h
@@ -55,7 +55,7 @@
 
     Mutex               mLock;
 
-    Vector<sp<NBLog::Reader>> mReaders;   // protected by mLock
+    Vector<sp<NBLog::DumpReader>> mDumpReaders;   // protected by mLock
 
     // FIXME Need comments on all of these, especially about locking
     NBLog::Shared *mMergerShared;