Merge changes I3f135b98,Ifadb1d24,Ieef3e05d,Id342c224

* changes:
  audio policy: fix getDevicesForStream() with preferred route
  audiopolicy: refactor playback activity ref counting
  audio policy: remove AudioSession class
  audio policy: refactor preferred route implementation
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 300adec..0672ca9 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -60,7 +60,7 @@
         "libprotobuf-cpp-lite",
         "libstagefright_nuplayer2",
         "libstagefright_rtsp",
-        "libstagefright_timedtext",
+        "libstagefright_timedtext2",
     ],
 
     export_include_dirs: [
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index 715bbe0..55d812b 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -60,7 +60,8 @@
 class MediaPlayer2InterfaceListener: public RefBase
 {
 public:
-    virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+    virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+           const PlayerMessage *obj) = 0;
 };
 
 class MediaPlayer2Interface : public AHandler {
@@ -240,7 +241,7 @@
         mListener = listener;
     }
 
-    void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const Parcel *obj=NULL) {
+    void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const PlayerMessage *obj=NULL) {
         sp<MediaPlayer2InterfaceListener> listener;
         {
             Mutex::Autolock autoLock(mListenerLock);
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 628e732..e03b499 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -42,7 +42,8 @@
 class MediaPlayer2Listener: virtual public RefBase
 {
 public:
-    virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+    virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+            const PlayerMessage *obj = NULL) = 0;
 };
 
 class MediaPlayer2 : public MediaPlayer2InterfaceListener
@@ -89,7 +90,7 @@
             bool            isLooping();
             status_t        setVolume(float leftVolume, float rightVolume);
             void            notify(int64_t srcId, int msg, int ext1, int ext2,
-                                   const Parcel *obj = NULL);
+                                   const PlayerMessage *obj = NULL);
             status_t        invoke(const PlayerMessage &request, PlayerMessage *reply);
             status_t        setMetadataFilter(const Parcel& filter);
             status_t        getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index 18cf040..8d0df8a 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -223,7 +223,8 @@
 
     ~proxyListener() { };
 
-    virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) override {
+    virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+            const PlayerMessage *obj) override {
         sp<MediaPlayer2> player = mPlayer.promote();
         if (player != NULL) {
             player->notify(srcId, msg, ext1, ext2, obj);
@@ -1271,7 +1272,7 @@
     }
 }
 
-void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) {
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *obj) {
     ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
           (long long)srcId, msg, ext1, ext2);
 
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index e317e23..4ce1a88 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -1590,15 +1590,20 @@
         return OK; // source without DRM info
     }
 
-    sp<ABuffer> drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(psshInfo);
-    ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)drmInfoBuffer->size());
+    PlayerMessage playerMsg;
+    status_t ret = NuPlayer2Drm::retrieveDrmInfo(psshInfo, &playerMsg);
+    ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)playerMsg.ByteSize());
 
-    if (drmInfoBuffer->size() == 0) {
-        ALOGE("checkDrmInfo: Unexpected parcel size: 0");
+    if (ret != OK) {
+        ALOGE("checkDrmInfo: failed to retrive DrmInfo %d", ret);
         return UNKNOWN_ERROR;
     }
 
-    notifyDrmInfo(drmInfoBuffer);
+    int size = playerMsg.ByteSize();
+    sp<ABuffer> drmInfoBuf = new ABuffer(size);
+    playerMsg.SerializeToArray(drmInfoBuf->data(), size);
+    drmInfoBuf->setRange(0, size);
+    notifyDrmInfo(drmInfoBuf);
 
     return OK;
 }
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 2a0fbdd..2f90825 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -33,7 +33,7 @@
 #include "NuPlayer2Source.h"
 #include "RTSPSource2.h"
 #include "GenericSource2.h"
-#include "TextDescriptions.h"
+#include "TextDescriptions2.h"
 
 #include "ATSParser.h"
 
@@ -2145,7 +2145,8 @@
             displayHeight);
 }
 
-void NuPlayer2::notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+void NuPlayer2::notifyListener(
+        int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
     if (mDriver == NULL) {
         return;
     }
@@ -2616,15 +2617,15 @@
         // Modular DRM
         case Source::kWhatDrmInfo:
         {
-            Parcel parcel;
+            PlayerMessage playerMsg;
             sp<ABuffer> drmInfo;
             CHECK(msg->findBuffer("drmInfo", &drmInfo));
-            parcel.setData(drmInfo->data(), drmInfo->size());
+            playerMsg.ParseFromArray(drmInfo->data(), drmInfo->size());
 
-            ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p  parcel size: %zu",
-                    drmInfo.get(), parcel.dataSize());
+            ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p  playerMsg size: %d",
+                    drmInfo.get(), playerMsg.ByteSize());
 
-            notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
+            notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &playerMsg);
 
             break;
         }
@@ -2845,35 +2846,31 @@
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
     CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
 
-    Parcel in;
-    in.writeInt32(trackIndex + baseIndex);
-    in.writeInt64(timeUs);
-    in.writeInt64(durationUs);
-    in.writeInt32(buffer->size());
-    in.writeInt32(buffer->size());
-    in.write(buffer->data(), buffer->size());
+    PlayerMessage playerMsg;
+    playerMsg.add_values()->set_int32_value(trackIndex + baseIndex);
+    playerMsg.add_values()->set_int64_value(timeUs);
+    playerMsg.add_values()->set_int64_value(durationUs);
+    playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size());
 
-    notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &in);
+    notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &playerMsg);
 }
 
 void NuPlayer2::sendTimedMetaData(const sp<ABuffer> &buffer) {
     int64_t timeUs;
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
-    Parcel in;
-    in.writeInt64(timeUs);
-    in.writeInt32(buffer->size());
-    in.writeInt32(buffer->size());
-    in.write(buffer->data(), buffer->size());
+    PlayerMessage playerMsg;
+    playerMsg.add_values()->set_int64_value(timeUs);
+    playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size());
 
-    notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &in);
+    notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &playerMsg);
 }
 
 void NuPlayer2::sendTimedTextData(const sp<ABuffer> &buffer) {
     const void *data;
     size_t size = 0;
     int64_t timeUs;
-    int32_t flag = TextDescriptions::IN_BAND_TEXT_3GPP;
+    int32_t flag = TextDescriptions2::IN_BAND_TEXT_3GPP;
 
     AString mime;
     CHECK(buffer->meta()->findString("mime", &mime));
@@ -2882,21 +2879,21 @@
     data = buffer->data();
     size = buffer->size();
 
-    Parcel parcel;
+    PlayerMessage playerMsg;
     if (size > 0) {
         CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
         int32_t global = 0;
         if (buffer->meta()->findInt32("global", &global) && global) {
-            flag |= TextDescriptions::GLOBAL_DESCRIPTIONS;
+            flag |= TextDescriptions2::GLOBAL_DESCRIPTIONS;
         } else {
-            flag |= TextDescriptions::LOCAL_DESCRIPTIONS;
+            flag |= TextDescriptions2::LOCAL_DESCRIPTIONS;
         }
-        TextDescriptions::getParcelOfDescriptions(
-                (const uint8_t *)data, size, flag, timeUs / 1000, &parcel);
+        TextDescriptions2::getPlayerMessageOfDescriptions(
+                (const uint8_t *)data, size, flag, timeUs / 1000, &playerMsg);
     }
 
-    if ((parcel.dataSize() > 0)) {
-        notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel);
+    if (playerMsg.values_size() > 0) {
+        notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &playerMsg);
     } else {  // send an empty timed text
         notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0);
     }
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
index c92d408..77845ac 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -296,7 +296,7 @@
             const sp<AMessage> &inputFormat,
             const sp<AMessage> &outputFormat = NULL);
 
-    void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in = NULL);
+    void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in = NULL);
 
     void handleFlushComplete(bool audio, bool isDecoder);
     void finishFlushIfPossible();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index fd231a3..4d799b8 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -44,40 +44,39 @@
 
 namespace android {
 
-struct ParcelWrapper : public RefBase {
-    static sp<ParcelWrapper> Create(const Parcel *p) {
+struct PlayerMessageWrapper : public RefBase {
+    static sp<PlayerMessageWrapper> Create(const PlayerMessage *p) {
         if (p != NULL) {
-            sp<ParcelWrapper> pw = new ParcelWrapper();
-            if (pw->appendFrom(p) == OK) {
-                return pw;
-            }
+            sp<PlayerMessageWrapper> pw = new PlayerMessageWrapper();
+            pw->copyFrom(p);
+            return pw;
         }
         return NULL;
     }
 
-    const Parcel *getParcel() {
-        return mParcel;
+    const PlayerMessage *getPlayerMessage() {
+        return mPlayerMessage;
     }
 
 protected:
-    virtual ~ParcelWrapper() {
-        if (mParcel != NULL) {
-            delete mParcel;
+    virtual ~PlayerMessageWrapper() {
+        if (mPlayerMessage != NULL) {
+            delete mPlayerMessage;
         }
     }
 
 private:
-    ParcelWrapper()
-        : mParcel(NULL) { }
+    PlayerMessageWrapper()
+        : mPlayerMessage(NULL) { }
 
-    status_t appendFrom(const Parcel *p) {
-        if (mParcel == NULL) {
-            mParcel = new Parcel;
+    void copyFrom(const PlayerMessage *p) {
+        if (mPlayerMessage == NULL) {
+            mPlayerMessage = new PlayerMessage;
         }
-        return mParcel->appendFrom(p, 0 /* start */, p->dataSize());
+        mPlayerMessage->CopyFrom(*p);
     }
 
-    Parcel *mParcel;
+    PlayerMessage *mPlayerMessage;
 };
 
 // key for media statistics
@@ -828,12 +827,12 @@
             CHECK(msg->findInt32("messageId", &msgId));
             msg->findInt32("ext1", &ext1);
             msg->findInt32("ext2", &ext2);
-            sp<ParcelWrapper> in;
+            sp<PlayerMessageWrapper> in;
             sp<RefBase> obj;
-            if (msg->findObject("parcel", &obj) && obj != NULL) {
-                in = static_cast<ParcelWrapper *>(obj.get());
+            if (msg->findObject("obj", &obj) && obj != NULL) {
+                in = static_cast<PlayerMessageWrapper *>(obj.get());
             }
-            sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel()));
+            sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getPlayerMessage()));
             break;
         }
         default:
@@ -842,16 +841,16 @@
 }
 
 void NuPlayer2Driver::notifyListener(
-        int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+        int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
     Mutex::Autolock autoLock(mLock);
     notifyListener_l(srcId, msg, ext1, ext2, in);
 }
 
 void NuPlayer2Driver::notifyListener_l(
-        int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+        int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
     ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
             this, (long long)srcId, msg, ext1, ext2,
-            (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
+            (in == NULL ? -1 : (int)in->ByteSize()), mAutoLoop, mLooping);
     if (srcId == mSrcId) {
         switch (msg) {
             case MEDIA2_PLAYBACK_COMPLETE:
@@ -918,7 +917,7 @@
     notify->setInt32("messageId", msg);
     notify->setInt32("ext1", ext1);
     notify->setInt32("ext2", ext2);
-    notify->setObject("parcel", ParcelWrapper::Create(in));
+    notify->setObject("obj", PlayerMessageWrapper::Create((PlayerMessage*)in));
     notify->post();
 }
 
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 8dfcaa1..50ee173 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -78,7 +78,7 @@
     void notifyRebufferingWhenExit(int64_t srcId, bool status);
     void notifySeekComplete(int64_t srcId);
     void notifyListener(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
-                        const Parcel *in = NULL);
+                        const PlayerMessage *in = NULL);
     void notifyFlagsChanged(int64_t srcId, uint32_t flags);
 
     // Modular DRM
@@ -145,7 +145,7 @@
 
     status_t start_l();
     void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
-                          const Parcel *in = NULL);
+                          const PlayerMessage *in = NULL);
 
     DISALLOW_EVIL_CONSTRUCTORS(NuPlayer2Driver);
 };
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
index 4853ae1..57d1147 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
@@ -133,9 +133,8 @@
     return drmInfoBuffer;
 }
 
-sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo)
+status_t NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo, PlayerMessage *playerMsg)
 {
-
     std::ostringstream pssh, drmInfo;
 
     // 0) Generate PSSH bytes
@@ -153,22 +152,20 @@
     ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  PSSH: size: %u %s", psshSize, psshHex);
 
     // 1) Write PSSH bytes
-    drmInfo.write(reinterpret_cast<const char *>(&psshSize), sizeof(psshSize));
-    drmInfo.write(reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
+    playerMsg->add_values()->set_bytes_value(
+            reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
 
     // 2) Write supportedDRMs
     uint32_t numentries = psshInfo->numentries;
-    drmInfo.write(reinterpret_cast<const char *>(&numentries), sizeof(numentries));
+    playerMsg->add_values()->set_int32_value(numentries);
     for (size_t i = 0; i < numentries; i++) {
         PsshEntry *entry = &psshInfo->entries[i];
-        drmInfo.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
+        playerMsg->add_values()->set_bytes_value(
+                reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
         ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  supportedScheme[%zu] %s", i,
                 DrmUUID::arrayToHex((const uint8_t*)&entry->uuid, sizeof(AMediaUUID)).string());
     }
-
-    sp<ABuffer> drmInfoBuf = ABuffer::CreateAsCopy(drmInfo.str().c_str(), drmInfo.tellp());
-    drmInfoBuf->setRange(0, drmInfo.tellp());
-    return drmInfoBuf;
+    return OK;
 }
 
 }   // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
index 99d2415..968d1be 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
@@ -23,6 +23,10 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
 namespace android {
 
     struct DrmUUID {
@@ -80,7 +84,7 @@
 
     public:
         static sp<ABuffer> retrieveDrmInfo(const void *pssh, uint32_t psshsize);
-        static sp<ABuffer> retrieveDrmInfo(PsshInfo *);
+        static status_t retrieveDrmInfo(PsshInfo *, PlayerMessage *);
 
     };  // NuPlayer2Drm
 
diff --git a/media/libnblog/Android.bp b/media/libnblog/Android.bp
index 74aaf77..4a4aac1 100644
--- a/media/libnblog/Android.bp
+++ b/media/libnblog/Android.bp
@@ -13,9 +13,14 @@
         "libbinder",
         "libcutils",
         "liblog",
+        "libmediametrics",
         "libutils",
     ],
 
+    static_libs: [
+        "libjsoncpp",
+    ],
+
     cflags: [
         "-Werror",
         "-Wall",
diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp
index f73e2ae..aee0ea3 100644
--- a/media/libnblog/NBLog.cpp
+++ b/media/libnblog/NBLog.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <climits>
 #include <math.h>
+#include <memory>
 #include <unordered_set>
 #include <vector>
 #include <stdarg.h>
@@ -31,12 +32,14 @@
 #include <time.h>
 #include <new>
 #include <audio_utils/roundup.h>
+#include <json/json.h>
 #include <media/nblog/NBLog.h>
 #include <media/nblog/PerformanceAnalysis.h>
 #include <media/nblog/ReportPerformance.h>
 #include <utils/CallStack.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
+#include <utils/Timers.h>
 
 #include <queue>
 #include <utility>
@@ -417,7 +420,7 @@
     if (!mEnabled) {
         return;
     }
-    int64_t ts = get_monotonic_ns();
+    const nsecs_t ts = systemTime();
     if (ts > 0) {
         log(EVENT_FMT_TIMESTAMP, &ts, sizeof(ts));
     } else {
@@ -432,7 +435,7 @@
     }
     HistTsEntry data;
     data.hash = hash;
-    data.ts = get_monotonic_ns();
+    data.ts = systemTime();
     if (data.ts > 0) {
         log(event, &data, sizeof(data));
     } else {
@@ -780,15 +783,17 @@
     return snapshot;
 }
 
+// TODO separate this method from the rest of NBLog
 // 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::Snapshot &snapshot, int author)
+void NBLog::MergeReader::processSnapshot(NBLog::Snapshot &snapshot, int author)
 {
+    ReportPerformance::PerformanceData& data = mThreadPerformanceData[author];
     // We don't do "auto it" because it reduces readability in this case.
     for (EntryIterator it = snapshot.begin(); it != snapshot.end(); ++it) {
         switch (it->type) {
         case EVENT_HISTOGRAM_ENTRY_TS: {
-            HistTsEntry payload = it.payload<HistTsEntry>();
+            const HistTsEntry payload = it.payload<HistTsEntry>();
             // TODO: hash for histogram ts and audio state need to match
             // and correspond to audio production source file location
             mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(payload.ts);
@@ -796,15 +801,37 @@
         case EVENT_AUDIO_STATE: {
             mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange();
         } break;
+        case EVENT_THREAD_INFO: {
+            const thread_info_t info = it.payload<thread_info_t>();
+            // TODO make PerformanceData hold a type of thread_info_t.
+            // Currently, thread_info_t is defined in NBLog.h, which includes
+            // PerformanceAnalysis.h. PerformanceData is defined in PerformanceAnalysis.h,
+            // where including NBLog.h would result in circular includes. The organization
+            // of files will need to change to avoid this problem.
+            data.type = info.type;
+            data.frameCount = info.frameCount;
+            data.sampleRate = info.sampleRate;
+        } break;
         case EVENT_LATENCY: {
-            double latencyMs = it.payload<double>();
-            mPerformanceData.addLatencyEntry(author, latencyMs);
+            const double latencyMs = it.payload<double>();
+            data.latencyHist.add(latencyMs);
         } break;
         case EVENT_WORK_TIME: {
-            uint64_t monotonicNs = it.payload<uint64_t>();
+            const int64_t monotonicNs = it.payload<int64_t>();
             const double monotonicMs = monotonicNs * 1e-6;
-            mPerformanceData.addCycleTimeEntry(author, monotonicMs);
+            data.workHist.add(monotonicMs);
+            data.active += monotonicNs;
         } break;
+        case EVENT_WARMUP_TIME: {
+            const double timeMs = it.payload<double>();
+            data.warmupHist.add(timeMs);
+        } break;
+        case EVENT_UNDERRUN:
+            data.underruns++;
+            break;
+        case EVENT_OVERRUN:
+            data.overruns++;
+            break;
         case EVENT_RESERVED:
         case EVENT_UPPER_BOUND:
             ALOGW("warning: unexpected event %d", it->type);
@@ -826,7 +853,20 @@
     // TODO unlock lock here
     for (size_t i = 0; i < nLogs; i++) {
         if (snapshots[i] != nullptr) {
-            getAndProcessSnapshot(*(snapshots[i]), i);
+            processSnapshot(*(snapshots[i]), i);
+        }
+    }
+    checkPushToMediaMetrics();
+}
+
+void NBLog::MergeReader::checkPushToMediaMetrics()
+{
+    const nsecs_t now = systemTime();
+    for (auto& item : mThreadPerformanceData) {
+        ReportPerformance::PerformanceData& data = item.second;
+        if (now - data.start >= kPeriodicMediaMetricsPush) {
+            (void)ReportPerformance::sendToMediaMetrics(data);
+            data.reset();   // data is persistent per thread
         }
     }
 }
@@ -835,7 +875,19 @@
 {
     // TODO: add a mutex around media.log dump
     ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis);
-    mPerformanceData.dump(fd);
+    Json::Value root(Json::arrayValue);
+    for (const auto& item : mThreadPerformanceData) {
+        const ReportPerformance::PerformanceData& data = item.second;
+        std::unique_ptr<Json::Value> threadData = ReportPerformance::dumpToJson(data);
+        if (threadData == nullptr) {
+            continue;
+        }
+        (*threadData)["threadNum"] = item.first;
+        root.append(*threadData);
+    }
+    Json::StyledWriter writer;
+    std::string rootStr = writer.write(root);
+    dprintf(fd, "%s", rootStr.c_str());
 }
 
 // TODO for future compatibility, would prefer to have a dump() go to string, and then go
@@ -856,13 +908,23 @@
             it = handleFormat(FormatEntry(it), &timestamp, &body);
             break;
         case EVENT_WORK_TIME: {
-            uint64_t monotonicNs = it.payload<uint64_t>();
-            body.appendFormat("Thread cycle: %lu ns", (unsigned long)monotonicNs);
+            const int64_t monotonicNs = it.payload<int64_t>();
+            body.appendFormat("Thread cycle: %ld ns", (long)monotonicNs);
         } break;
         case EVENT_LATENCY: {
-            double latencyMs = it.payload<double>();
+            const double latencyMs = it.payload<double>();
             body.appendFormat("latency: %.3f ms", latencyMs);
         } break;
+        case EVENT_WARMUP_TIME: {
+            const double timeMs = it.payload<double>();
+            body.appendFormat("warmup time: %.3f ms", timeMs);
+        } break;
+        case EVENT_UNDERRUN:
+            body.appendFormat("underrun");
+            break;
+        case EVENT_OVERRUN:
+            body.appendFormat("overrun");
+            break;
         case EVENT_FMT_END:
         case EVENT_RESERVED:
         case EVENT_UPPER_BOUND:
@@ -947,7 +1009,7 @@
                                                            String8 *body)
 {
     // log timestamp
-    int64_t ts = fmtEntry.timestamp();
+    const int64_t ts = fmtEntry.timestamp();
     timestamp->clear();
     timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
                     (int) ((ts / (1000 * 1000)) % 1000));
diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp
index ce9e22a..ca9bc2c 100644
--- a/media/libnblog/PerformanceAnalysis.cpp
+++ b/media/libnblog/PerformanceAnalysis.cpp
@@ -41,12 +41,15 @@
 #include <media/nblog/ReportPerformance.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
+#include <utils/Timers.h>
 
 #include <queue>
 #include <utility>
 
 namespace android {
 
+namespace ReportPerformance {
+
 void Histogram::add(double value)
 {
     // TODO Handle domain and range error exceptions?
@@ -74,7 +77,7 @@
     return mTotalCount;
 }
 
-std::string Histogram::serializeToString() const {
+std::string Histogram::toString() const {
     std::stringstream ss;
     static constexpr char kDivider = '|';
     ss << mBinSize << "," << mNumBins << "," << mLow << ",{";
@@ -104,42 +107,8 @@
     return ss.str();
 }
 
-// TODO make a hash map from Event type to std::pair<HistConfig, unordered_map<int, Histogram>>
-// so that we don't have to create a "add histogram entry" method for every different metric.
-void PerformanceData::addCycleTimeEntry(int author, double cycleTimeMs)
-{
-    if (mCycleTimeMsHists.count(author) == 0) {
-        mCycleTimeMsHists.emplace(author, Histogram(kCycleTimeConfig));
-    }
-    mCycleTimeMsHists.at(author).add(cycleTimeMs);
-}
-
-void PerformanceData::addLatencyEntry(int author, double latencyMs)
-{
-    if (mLatencyMsHists.count(author) == 0) {
-        mLatencyMsHists.emplace(author, Histogram(kLatencyConfig));
-    }
-    mLatencyMsHists.at(author).add(latencyMs);
-}
-
-void PerformanceData::dump(int fd, int indent __unused)
-{
-    // TODO add thread metadata for better context.
-    // Also output in a more machine-readable friendly format.
-    dprintf(fd, "Thread cycle time histograms:\n");
-    for (const auto &item : mCycleTimeMsHists) {
-        dprintf(fd, "  Thread %d: %s\n", item.first, item.second.serializeToString().c_str());
-    }
-    dprintf(fd, "Latency histograms:\n");
-    for (const auto &item : mLatencyMsHists) {
-        dprintf(fd, "  Thread %d: %s\n", item.first, item.second.serializeToString().c_str());
-    }
-}
-
 //------------------------------------------------------------------------------
 
-namespace ReportPerformance {
-
 // Given an audio processing wakeup timestamp, buckets the time interval
 // since the previous timestamp into a histogram, searches for
 // outliers, analyzes the outlier series for unexpectedly
diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp
index 827e731..aab67c6 100644
--- a/media/libnblog/ReportPerformance.cpp
+++ b/media/libnblog/ReportPerformance.cpp
@@ -18,6 +18,7 @@
 
 #include <fstream>
 #include <iostream>
+#include <memory>
 #include <queue>
 #include <stdarg.h>
 #include <stdint.h>
@@ -27,6 +28,8 @@
 #include <sys/prctl.h>
 #include <sys/time.h>
 #include <utility>
+#include <json/json.h>
+#include <media/MediaAnalyticsItem.h>
 #include <media/nblog/NBLog.h>
 #include <media/nblog/PerformanceAnalysis.h>
 #include <media/nblog/ReportPerformance.h>
@@ -37,13 +40,93 @@
 
 namespace ReportPerformance {
 
+std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data)
+{
+    std::unique_ptr<Json::Value> rootPtr = std::make_unique<Json::Value>(Json::objectValue);
+    Json::Value& root = *rootPtr;
+    root["type"] = data.type;
+    root["frameCount"] = (Json::Value::Int)data.frameCount;
+    root["sampleRate"] = (Json::Value::Int)data.sampleRate;
+    root["workMsHist"] = data.workHist.toString();
+    root["latencyMsHist"] = data.latencyHist.toString();
+    root["warmupMsHist"] = data.warmupHist.toString();
+    root["underruns"] = (Json::Value::Int64)data.underruns;
+    root["overruns"] = (Json::Value::Int64)data.overruns;
+    root["activeMs"] = (Json::Value::Int64)ns2ms(data.active);
+    root["durationMs"] = (Json::Value::Int64)ns2ms(systemTime() - data.start);
+    return rootPtr;
+}
+
+bool sendToMediaMetrics(const PerformanceData& data)
+{
+    // See documentation for these metrics here:
+    // docs.google.com/document/d/11--6dyOXVOpacYQLZiaOY5QVtQjUyqNx2zT9cCzLKYE/edit?usp=sharing
+    static constexpr char kThreadType[] = "android.media.audiothread.type";
+    static constexpr char kThreadFrameCount[] = "android.media.audiothread.framecount";
+    static constexpr char kThreadSampleRate[] = "android.media.audiothread.samplerate";
+    static constexpr char kThreadWorkHist[] = "android.media.audiothread.workMs.hist";
+    static constexpr char kThreadLatencyHist[] = "android.media.audiothread.latencyMs.hist";
+    static constexpr char kThreadWarmupHist[] = "android.media.audiothread.warmupMs.hist";
+    static constexpr char kThreadUnderruns[] = "android.media.audiothread.underruns";
+    static constexpr char kThreadOverruns[] = "android.media.audiothread.overruns";
+    static constexpr char kThreadActive[] = "android.media.audiothread.activeMs";
+    static constexpr char kThreadDuration[] = "android.media.audiothread.durationMs";
+
+    std::unique_ptr<MediaAnalyticsItem> item(new MediaAnalyticsItem("audiothread"));
+
+    const Histogram &workHist = data.workHist;
+    if (workHist.totalCount() > 0) {
+        item->setCString(kThreadWorkHist, workHist.toString().c_str());
+    }
+
+    const Histogram &latencyHist = data.latencyHist;
+    if (latencyHist.totalCount() > 0) {
+        item->setCString(kThreadLatencyHist, latencyHist.toString().c_str());
+    }
+
+    const Histogram &warmupHist = data.warmupHist;
+    if (warmupHist.totalCount() > 0) {
+        item->setCString(kThreadWarmupHist, warmupHist.toString().c_str());
+    }
+
+    if (data.underruns > 0) {
+        item->setInt64(kThreadUnderruns, data.underruns);
+    }
+
+    if (data.overruns > 0) {
+        item->setInt64(kThreadOverruns, data.overruns);
+    }
+
+    // Send to Media Metrics if the record is not empty.
+    // The thread and time info are added inside the if statement because
+    // we want to send them only if there are performance metrics to send.
+    if (item->count() > 0) {
+        // Add thread info fields.
+        const int type = data.type;
+        // TODO have a int-to-string mapping defined somewhere else for other thread types.
+        if (type == 2) {
+            item->setCString(kThreadType, "FASTMIXER");
+        } else {
+            item->setCString(kThreadType, "UNKNOWN");
+        }
+        item->setInt32(kThreadFrameCount, data.frameCount);
+        item->setInt32(kThreadSampleRate, data.sampleRate);
+        // Add time info fields.
+        item->setInt64(kThreadActive, data.active / 1000000);
+        item->setInt64(kThreadDuration, (systemTime() - data.start) / 1000000);
+        return item->selfrecord();
+    }
+    return false;
+}
+
+//------------------------------------------------------------------------------
 
 // TODO: use a function like this to extract logic from writeToFile
 // https://stackoverflow.com/a/9279620
 
 // Writes outlier intervals, timestamps, and histograms spanning long time intervals to file.
 // TODO: write data in binary format
-void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
                  const std::deque<std::pair<msInterval, timestamp>> &outlierData,
                  const std::deque<timestamp> &peakTimestamps,
                  const char * directory, bool append, int author, log_hash_t hash) {
diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h
index 561e8c7..e43b026 100644
--- a/media/libnblog/include/media/nblog/NBLog.h
+++ b/media/libnblog/include/media/nblog/NBLog.h
@@ -20,6 +20,7 @@
 #define ANDROID_MEDIA_NBLOG_H
 
 #include <map>
+#include <memory>
 #include <type_traits>
 #include <unordered_set>
 #include <vector>
@@ -30,6 +31,7 @@
 #include <media/nblog/ReportPerformance.h>
 #include <utils/Mutex.h>
 #include <utils/threads.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -39,7 +41,7 @@
 
 public:
 
-    using log_hash_t = ReportPerformance::log_hash_t;
+    using log_hash_t = uint64_t;
 
     // FIXME Everything needed for client (writer API and registration) should be isolated
     //       from the rest of the implementation.
@@ -78,13 +80,29 @@
         EVENT_AUDIO_STATE,          // audio on/off event: logged on FastMixer::onStateChange call
 
         // Types representing audio performance metrics
+        EVENT_THREAD_INFO,          // thread type, frameCount and sampleRate, which give context
+                                    // for the metrics below.
         EVENT_LATENCY,              // difference between frames presented by HAL and frames
                                     // written to HAL output sink, divided by sample rate.
         EVENT_WORK_TIME,            // the time a thread takes to do work, e.g. read, write, etc.
+        EVENT_WARMUP_TIME,          // thread warmup time
+        EVENT_UNDERRUN,             // predicted thread underrun event timestamp
+        EVENT_OVERRUN,              // predicted thread overrun event timestamp
 
         EVENT_UPPER_BOUND,          // to check for invalid events
     };
 
+    // NBLog custom-defined structs. Some NBLog Event types map to these structs.
+
+    // mapped from EVENT_THREAD_INFO
+    struct thread_info_t {
+        // TODO make type an enum
+        int type;               // Thread type: 0 for MIXER, 1 for CAPTURE,
+                                // 2 for FASTMIXER, 3 for FASTCAPTURE
+        size_t frameCount;      // number of frames per read or write buffer
+        unsigned sampleRate;    // in frames per second
+    };
+
     template <Event E> struct get_mapped;
 #define MAP_EVENT_TO_TYPE(E, T) \
     template<> struct get_mapped<E> { \
@@ -95,8 +113,12 @@
     }
 
     // Maps an NBLog Event type to a C++ POD type.
+    MAP_EVENT_TO_TYPE(EVENT_THREAD_INFO, thread_info_t);
     MAP_EVENT_TO_TYPE(EVENT_LATENCY, double);
-    MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, uint64_t);
+    MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, int64_t);
+    MAP_EVENT_TO_TYPE(EVENT_WARMUP_TIME, double);
+    MAP_EVENT_TO_TYPE(EVENT_UNDERRUN, int64_t);
+    MAP_EVENT_TO_TYPE(EVENT_OVERRUN, int64_t);
 
 private:
 
@@ -154,7 +176,7 @@
         }
 #else
         template<typename T>
-        inline T payload() {
+        inline T payload() const {
             static_assert(std::is_trivially_copyable<T>::value
                     && !std::is_pointer<T>::value,
                     "NBLog::EntryIterator payload must be trivially copyable, non-pointer type.");
@@ -418,6 +440,7 @@
         void    log(Event event, const void *data, size_t length);
 
         void    logvf(const char *fmt, va_list ap);
+
         // helper functions for logging parts of a formatted entry
         void    logStart(const char *fmt);
         void    logTimestampFormat();
@@ -477,10 +500,12 @@
     private:
         // Amount of tries for reader to catch up with writer in getSnapshot().
         static constexpr int kMaxObtainTries = 3;
+
         // invalidBeginTypes and invalidEndTypes are used to align the Snapshot::begin() and
         // Snapshot::end() EntryIterators to valid entries.
         static const std::unordered_set<Event> invalidBeginTypes;
         static const std::unordered_set<Event> invalidEndTypes;
+
         // declared as const because audio_utils_fifo() constructor
         sp<IMemory> mIMemory;       // ref-counted version, assigned only in constructor
 
@@ -539,12 +564,14 @@
         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);
+
         // The bufferDump functions are used for debugging only.
         static String8 bufferDump(const uint8_t *buffer, size_t size);
         static String8 bufferDump(const EntryIterator &it);
     };
 
     // ---------------------------------------------------------------------------
+    // TODO move Merger, MergeReader, and MergeThread to a separate file.
 
     // This class is used to read data from each thread's individual FIFO in shared memory
     // and write it to a single FIFO in local memory.
@@ -580,11 +607,17 @@
         MergeReader(const void *shared, size_t size, Merger &merger);
 
         void dump(int fd, int indent = 0);
+
         // process a particular snapshot of the reader
-        void getAndProcessSnapshot(Snapshot &snap, int author);
+        void processSnapshot(Snapshot &snap, int author);
+
         // call getSnapshot of the content of the reader's buffer and process the data
         void getAndProcessSnapshot();
 
+        // check for periodic push of performance data to media metrics, and perform
+        // the send if it is time to do so.
+        void checkPushToMediaMetrics();
+
     private:
         // FIXME Needs to be protected by a lock,
         //       because even though our use of it is read-only there may be asynchronous updates
@@ -596,7 +629,12 @@
         // location within each author
         ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis;
 
-        PerformanceData mPerformanceData;
+        // compresses and stores audio performance data from each thread's buffers.
+        // first parameter is author, i.e. thread index.
+        std::map<int, ReportPerformance::PerformanceData> mThreadPerformanceData;
+
+        // how often to push data to Media Metrics
+        static constexpr nsecs_t kPeriodicMediaMetricsPush = s2ns((nsecs_t)2 * 60 * 60); // 2 hours
 
         // handle author entry by looking up the author's name and appending it to the body
         // returns number of bytes read from fmtEntry
@@ -645,15 +683,6 @@
 
 };  // class NBLog
 
-// TODO put somewhere else
-static inline int64_t get_monotonic_ns() {
-    timespec ts;
-    if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
-        return (uint64_t) ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
-    }
-    return 0; // should not happen.
-}
-
 }   // namespace android
 
 #endif  // ANDROID_MEDIA_NBLOG_H
diff --git a/media/libnblog/include/media/nblog/PerformanceAnalysis.h b/media/libnblog/include/media/nblog/PerformanceAnalysis.h
index f2c3a48..80eddb1 100644
--- a/media/libnblog/include/media/nblog/PerformanceAnalysis.h
+++ b/media/libnblog/include/media/nblog/PerformanceAnalysis.h
@@ -19,15 +19,18 @@
 
 #include <deque>
 #include <map>
-#include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include <media/nblog/ReportPerformance.h>
+#include <utils/Timers.h>
 
 namespace android {
 
 class String8;
 
+namespace ReportPerformance {
+
 // TODO make this a templated class and put it in a separate file.
 // The templated parameters would be bin size and low limit.
 /*
@@ -106,7 +109,7 @@
      *
      * \return the histogram serialized as a string.
      */
-    std::string serializeToString() const;
+    std::string toString() const;
 
 private:
     const double mBinSize;      // Size of each bucket
@@ -119,38 +122,54 @@
     uint64_t mTotalCount = 0;   // Total number of values recorded
 };
 
-// TODO For now this is a holder of audio performance metrics. The idea is essentially the same
-// as PerformanceAnalysis, but the design structure is different. There is a PerformanceAnalysis
-// instance for each thread writer (see PerformanceAnalysisMap later in this file), while a
-// PerformanceData instance already takes into account each thread writer in its calculations.
-// Eventually, this class should be merged with PerformanceAnalysis into some single entity.
-/*
- * PerformanceData stores audio performance data from audioflinger threads as histograms,
- * time series, or counts, and outputs them in a machine-readable format.
- */
-class PerformanceData {
-public:
-    void addCycleTimeEntry(int author, double cycleTimeMs);
-    void addLatencyEntry(int author, double latencyMs);
-    void addWarmupTimeEntry(int author, double warmupTimeMs);
-    void addWarmupCyclesEntry(int author, double warmupCycles);
-    void dump(int fd, int indent = 0);
-private:
-    // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192 and
-    // mSampleRate = 48000, which correspond to 2 and 7 seconds.
-    static constexpr Histogram::Config kCycleTimeConfig = { 0.25, 20, 2.};
-    std::unordered_map<int /*author, i.e. thread number*/, Histogram> mCycleTimeMsHists;
+// This is essentially the same as class PerformanceAnalysis, but PerformanceAnalysis
+// also does some additional analyzing of data, while the purpose of this struct is
+// to hold data.
+struct PerformanceData {
+    // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192
+    // and mSampleRate = 48000, which correspond to 2 and 7 seconds.
+    static constexpr Histogram::Config kWorkConfig = { 0.25, 20, 2.};
 
     // Values based on trial and error logging. Need a better way to determine
     // bin size and lower/upper limits.
     static constexpr Histogram::Config kLatencyConfig = { 2., 10, 10.};
-    std::unordered_map<int, Histogram> mLatencyMsHists;
+
+    // Values based on trial and error logging. Need a better way to determine
+    // bin size and lower/upper limits.
+    static constexpr Histogram::Config kWarmupConfig = { 5., 10, 10.};
+
+    // Thread Info
+    // TODO make type an enum
+    int type = -1;              // Thread type: 0 for MIXER, 1 for CAPTURE,
+                                // 2 for FASTMIXER, 3 for FASTCAPTURE
+    size_t frameCount = 0;
+    unsigned sampleRate = 0;
+
+    // Performance Data
+    Histogram workHist{kWorkConfig};
+    Histogram latencyHist{kLatencyConfig};
+    Histogram warmupHist{kWarmupConfig};
+    int64_t underruns = 0;
+    int64_t overruns = 0;
+    nsecs_t active = 0;
+    nsecs_t start{systemTime()};
+
+    // Reset the performance data. This does not represent a thread state change.
+    // Thread info is not reset here because the data is meant to be a continuation of the thread
+    // that struct PerformanceData is associated with.
+    void reset() {
+        workHist.clear();
+        latencyHist.clear();
+        warmupHist.clear();
+        underruns = 0;
+        overruns = 0;
+        active = 0;
+        start = systemTime();
+    }
 };
 
 //------------------------------------------------------------------------------
 
-namespace ReportPerformance {
-
 class PerformanceAnalysis;
 
 // a map of PerformanceAnalysis instances
@@ -204,7 +223,7 @@
     std::deque<timestamp> mPeakTimestamps;
 
     // stores buffer period histograms with timestamp of first sample
-    std::deque<std::pair<timestamp, Histogram>> mHists;
+    std::deque<std::pair<timestamp, Hist>> mHists;
 
     // Parameters used when detecting outliers
     struct BufferPeriod {
diff --git a/media/libnblog/include/media/nblog/ReportPerformance.h b/media/libnblog/include/media/nblog/ReportPerformance.h
index 1b11197..09bb2a0 100644
--- a/media/libnblog/include/media/nblog/ReportPerformance.h
+++ b/media/libnblog/include/media/nblog/ReportPerformance.h
@@ -19,19 +19,38 @@
 
 #include <deque>
 #include <map>
+#include <memory>
 #include <vector>
 
+namespace Json {
+class Value;
+}
+
 namespace android {
 
 namespace ReportPerformance {
 
+struct PerformanceData;
+
+// Dumps performance data to a JSON format.
+// Return by pointer instead of by value to avoid dependency side effects of including
+// the header of an external library.
+std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data);
+
+// Send one thread's data to media metrics, if the performance data is nontrivial (i.e. not
+// all zero values). Return true if data was sent, false if there is nothing to write
+// or an error occurred while writing.
+bool sendToMediaMetrics(const PerformanceData& data);
+
+//------------------------------------------------------------------------------
+
 constexpr int kMsPerSec = 1000;
 constexpr int kSecPerMin = 60;
 
 constexpr int kJiffyPerMs = 10; // time unit for histogram as a multiple of milliseconds
 
 // stores a histogram: key: observed buffer period (multiple of jiffy). value: count
-using Histogram = std::map<int, int>;
+using Hist = std::map<int, int>;
 
 using msInterval = double;
 using jiffyInterval = double;
@@ -54,7 +73,7 @@
 }
 
 // Writes outlier intervals, timestamps, peaks timestamps, and histograms to a file.
-void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
                  const std::deque<std::pair<msInterval, timestamp>> &outlierData,
                  const std::deque<timestamp> &peakTimestamps,
                  const char * kDirectory, bool append, int author, log_hash_t hash);
diff --git a/media/libstagefright/timedtext/Android.bp b/media/libstagefright/timedtext/Android.bp
index a5ad6c6..7c51333 100644
--- a/media/libstagefright/timedtext/Android.bp
+++ b/media/libstagefright/timedtext/Android.bp
@@ -25,3 +25,36 @@
 
     shared_libs: ["libmedia"],
 }
+
+cc_library_static {
+    name: "libstagefright_timedtext2",
+
+    srcs: ["TextDescriptions2.cpp"],
+
+    static_libs: [
+        "libmediaplayer2-protos",
+        "libprotobuf-cpp-lite",
+    ],
+
+    cflags: [
+        "-Wno-multichar",
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    include_dirs: [
+        "frameworks/av/media/libstagefright",
+    ],
+
+    shared_libs: ["libmedia"],
+}
diff --git a/media/libstagefright/timedtext/TextDescriptions2.cpp b/media/libstagefright/timedtext/TextDescriptions2.cpp
new file mode 100644
index 0000000..f48eacc
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TextDescriptions2.h"
+#include <media/stagefright/foundation/ByteUtils.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+TextDescriptions2::TextDescriptions2() {
+}
+
+status_t TextDescriptions2::getPlayerMessageOfDescriptions(
+        const uint8_t *data, ssize_t size,
+        uint32_t flags, int timeMs, PlayerMessage *playerMsg) {
+    if (flags & IN_BAND_TEXT_3GPP) {
+        if (flags & GLOBAL_DESCRIPTIONS) {
+            return extract3GPPGlobalDescriptions(data, size, playerMsg);
+        } else if (flags & LOCAL_DESCRIPTIONS) {
+            return extract3GPPLocalDescriptions(data, size, timeMs, playerMsg);
+        }
+    } else if (flags & OUT_OF_BAND_TEXT_SRT) {
+        if (flags & LOCAL_DESCRIPTIONS) {
+            return extractSRTLocalDescriptions(data, size, timeMs, playerMsg);
+        }
+    }
+
+    return ERROR_UNSUPPORTED;
+}
+
+// Parse the SRT text sample, and store the timing and text sample in a PlayerMessage.
+// The PlayerMessage will be sent to MediaPlayer2.java through event, and will be
+// parsed in TimedText.java.
+status_t TextDescriptions2::extractSRTLocalDescriptions(
+        const uint8_t *data, ssize_t size, int timeMs, PlayerMessage *playerMsg) {
+    playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+    playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+    playerMsg->add_values()->set_int32_value(timeMs);
+
+    playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+    playerMsg->add_values()->set_bytes_value(data, size);
+
+    return OK;
+}
+
+// Extract the local 3GPP display descriptions. 3GPP local descriptions
+// are appended to the text sample if any.
+status_t TextDescriptions2::extract3GPPLocalDescriptions(
+        const uint8_t *data, ssize_t size,
+        int timeMs, PlayerMessage *playerMsg) {
+
+    playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+
+    // write start time to display this text sample
+    playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+    playerMsg->add_values()->set_int32_value(timeMs);
+
+    if (size < 2) {
+        return OK;
+    }
+    ssize_t textLen = (*data) << 8 | (*(data + 1));
+
+    if (size < textLen + 2) {
+        return OK;
+    }
+
+    // write text sample length and text sample itself
+    playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+    playerMsg->add_values()->set_bytes_value(data + 2, textLen);
+
+    if (size > textLen + 2) {
+        data += (textLen + 2);
+        size -= (textLen + 2);
+    } else {
+        return OK;
+    }
+
+    while (size >= 8) {
+        const uint8_t *tmpData = data;
+        ssize_t chunkSize = U32_AT(tmpData);      // size includes size and type
+        uint32_t chunkType = U32_AT(tmpData + 4);
+
+        if (chunkSize <= 8 || chunkSize > size) {
+            return OK;
+        }
+
+        size_t remaining = chunkSize - 8;
+
+        tmpData += 8;
+
+        switch(chunkType) {
+            // 'tbox' box to indicate the position of the text with values
+            // of top, left, bottom and right
+            case FOURCC('t', 'b', 'o', 'x'):
+            {
+                if (remaining < 8) {
+                    return OK;
+                }
+                playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+                remaining -= 8;
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+
+        data += chunkSize;
+        size -= chunkSize;
+    }
+
+    return OK;
+}
+
+// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a PlayerMessage
+status_t TextDescriptions2::extract3GPPGlobalDescriptions(
+        const uint8_t *data, ssize_t size, PlayerMessage *playerMsg) {
+
+    playerMsg->add_values()->set_int32_value(KEY_GLOBAL_SETTING);
+
+    while (size >= 8) {
+        ssize_t chunkSize = U32_AT(data);
+        uint32_t chunkType = U32_AT(data + 4);
+        const uint8_t *tmpData = data;
+        tmpData += 8;
+        size_t remaining = size - 8;
+
+        if (size < chunkSize) {
+            return OK;
+        }
+        switch(chunkType) {
+            case FOURCC('t', 'x', '3', 'g'):
+            {
+                if (remaining < 18) {
+                    return OK;
+                }
+                // Skip DISPLAY_FLAGS, STRUCT_JUSTIFICATION, and BACKGROUND_COLOR_RGBA
+                tmpData += 18;
+                remaining -= 18;
+
+                if (remaining < 8) {
+                    return OK;
+                }
+                playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+                remaining -= 18;
+                // Ignore remaining data.
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+
+        data += chunkSize;
+        size -= chunkSize;
+    }
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/timedtext/TextDescriptions2.h b/media/libstagefright/timedtext/TextDescriptions2.h
new file mode 100644
index 0000000..7c7d2d0
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.h
@@ -0,0 +1,88 @@
+ /*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef TEXT_DESCRIPTIONS2_H_
+
+#define TEXT_DESCRIPTIONS2_H_
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
+namespace android {
+
+class TextDescriptions2 {
+public:
+    enum {
+        IN_BAND_TEXT_3GPP             = 0x01,
+        OUT_OF_BAND_TEXT_SRT          = 0x02,
+
+        GLOBAL_DESCRIPTIONS           = 0x100,
+        LOCAL_DESCRIPTIONS            = 0x200,
+    };
+
+    static status_t getPlayerMessageOfDescriptions(
+            const uint8_t *data, ssize_t size,
+            uint32_t flags, int timeMs, PlayerMessage *playerMsg);
+private:
+    TextDescriptions2();
+
+    enum {
+        // These keys must be in sync with the keys in TimedText.java
+        KEY_DISPLAY_FLAGS                 = 1, // int
+        KEY_STYLE_FLAGS                   = 2, // int
+        KEY_BACKGROUND_COLOR_RGBA         = 3, // int
+        KEY_HIGHLIGHT_COLOR_RGBA          = 4, // int
+        KEY_SCROLL_DELAY                  = 5, // int
+        KEY_WRAP_TEXT                     = 6, // int
+        KEY_START_TIME                    = 7, // int
+        KEY_STRUCT_BLINKING_TEXT_LIST     = 8, // List<CharPos>
+        KEY_STRUCT_FONT_LIST              = 9, // List<Font>
+        KEY_STRUCT_HIGHLIGHT_LIST         = 10, // List<CharPos>
+        KEY_STRUCT_HYPER_TEXT_LIST        = 11, // List<HyperText>
+        KEY_STRUCT_KARAOKE_LIST           = 12, // List<Karaoke>
+        KEY_STRUCT_STYLE_LIST             = 13, // List<Style>
+        KEY_STRUCT_TEXT_POS               = 14, // TextPos
+        KEY_STRUCT_JUSTIFICATION          = 15, // Justification
+        KEY_STRUCT_TEXT                   = 16, // Text
+
+        KEY_GLOBAL_SETTING                = 101,
+        KEY_LOCAL_SETTING                 = 102,
+        KEY_START_CHAR                    = 103,
+        KEY_END_CHAR                      = 104,
+        KEY_FONT_ID                       = 105,
+        KEY_FONT_SIZE                     = 106,
+        KEY_TEXT_COLOR_RGBA               = 107,
+    };
+
+    static status_t extractSRTLocalDescriptions(
+            const uint8_t *data, ssize_t size,
+            int timeMs, PlayerMessage *playerMsg);
+    static status_t extract3GPPGlobalDescriptions(
+            const uint8_t *data, ssize_t size,
+            PlayerMessage *playerMsg);
+    static status_t extract3GPPLocalDescriptions(
+            const uint8_t *data, ssize_t size,
+            int timeMs, PlayerMessage *playerMsg);
+
+    DISALLOW_EVIL_CONSTRUCTORS(TextDescriptions2);
+};
+
+}  // namespace android
+#endif  // TEXT_DESCRIPTIONS2_H_
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index fd784a4..f650b66 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -195,6 +195,9 @@
             //       to avoid blocking here and to prevent possible priority inversion
             mMixer = new AudioMixer(frameCount, mSampleRate);
             // FIXME See the other FIXME at FastMixer::setNBLogWriter()
+            // TODO define an int to thread type mapping for the "2" below.
+            const NBLog::thread_info_t info = { 2 /*FastMixer*/, frameCount, mSampleRate };
+            LOG_THREAD_INFO(info);
             const size_t mixerFrameSize = mSinkChannelCount
                     * audio_bytes_per_sample(mMixerBufferFormat);
             mMixerBufferSize = mixerFrameSize * frameCount;
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 6f223df..09101d9 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -22,6 +22,7 @@
 #include "Configuration.h"
 #include <linux/futex.h>
 #include <sys/syscall.h>
+#include <audio_utils/clock.h>
 #include <cutils/atomic.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
@@ -260,6 +261,9 @@
                         mIsWarm = true;
                         mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs;
                         mDumpState->mWarmupCycles = mWarmupCycles;
+                        const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1e3) +
+                                (mMeasuredWarmupTs.tv_nsec * 1e-6);
+                        LOG_WARMUP_TIME(measuredWarmupMs);
                     }
                 }
                 mSleepNs = -1;
@@ -270,6 +274,7 @@
                         ALOGV("underrun: time since last cycle %d.%03ld sec",
                                 (int) sec, nsec / 1000000L);
                         mDumpState->mUnderruns++;
+                        LOG_UNDERRUN(audio_utils_ns_from_timespec(&newTs));
                         mIgnoreNextOverrun = true;
                     } else if (nsec < mOverrunNs) {
                         if (mIgnoreNextOverrun) {
@@ -279,6 +284,7 @@
                             ALOGV("overrun: time since last cycle %d.%03ld sec",
                                     (int) sec, nsec / 1000000L);
                             mDumpState->mOverruns++;
+                            LOG_OVERRUN(audio_utils_ns_from_timespec(&newTs));
                         }
                         // This forces a minimum cycle time. It:
                         //  - compensates for an audio HAL with jitter due to sample rate conversion
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 6677470..dd2e7c9 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -107,6 +107,23 @@
 #define LOG_LATENCY(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
         x->log<NBLog::EVENT_LATENCY>(ms); } while (0)
 
+// Record thread warmup time in milliseconds. Parameter ms is of type double.
+#define LOG_WARMUP_TIME(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->log<NBLog::EVENT_WARMUP_TIME>(ms); } while (0)
+
+// Record thread underrun event nanosecond timestamp. Parameter ns is an int64_t.
+#define LOG_UNDERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->log<NBLog::EVENT_UNDERRUN>(ns); } while (0)
+
+// Record thread overrun event nanosecond timestamp. Parameter ns is an int64_t.
+#define LOG_OVERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->log<NBLog::EVENT_OVERRUN>(ns); } while (0)
+
+// Record thread info. This currently includes type, frameCount, and sampleRate.
+// Parameter type is thread_info_t as defined in NBLog.h.
+#define LOG_THREAD_INFO(info) do { NBLog::Writer *x = tlNBLogWriter; \
+        if (x != nullptr) x->log<NBLog::EVENT_THREAD_INFO>(info); } while (0)
+
 namespace android {
 extern "C" {
 extern thread_local NBLog::Writer *tlNBLogWriter;
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 4b05395..45da56a 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -483,6 +483,7 @@
 {
     "audiopolicy",
     "audiorecord",
+    "audiothread",
     "audiotrack",
     "codec",
     "extractor",