Merge "Minor updates AHardware accessor in AImageReader" into oc-dev am: 1f31f24466
am: 62be7b944a

Change-Id: I45618caf8ef3659c05a42fa5f5bdd9f061dac8a5
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 5c11bfa..1e327a3 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -70,6 +70,12 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libeffects.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libeffects.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libeffects_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libeffectproxy.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libldnhncr.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libdownmix.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libvisualizer.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libreverbwrapper.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libbundlewrapper.so)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index dfd5df7..d7c2e87 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -162,7 +162,7 @@
                        1,
                        mbuf->range_length(),
                        out),
-                (ssize_t)mbuf->range_length());
+                mbuf->range_length());
 
         mbuf->release();
         mbuf = NULL;
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 80b66c4..e9ec954 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -730,7 +730,7 @@
 
     ssize_t MockDrmPlugin::findSession(Vector<uint8_t> const &sessionId) const
     {
-        ALOGD("findSession: nsessions=%u, size=%u", mSessions.size(), sessionId.size());
+        ALOGD("findSession: nsessions=%zu, size=%zu", mSessions.size(), sessionId.size());
         for (size_t i = 0; i < mSessions.size(); ++i) {
             if (memcmp(mSessions[i].array(), sessionId.array(), sessionId.size()) == 0) {
                 return i;
@@ -741,7 +741,7 @@
 
     ssize_t MockDrmPlugin::findKeySet(Vector<uint8_t> const &keySetId) const
     {
-        ALOGD("findKeySet: nkeySets=%u, size=%u", mKeySets.size(), keySetId.size());
+        ALOGD("findKeySet: nkeySets=%zu, size=%zu", mKeySets.size(), keySetId.size());
         for (size_t i = 0; i < mKeySets.size(); ++i) {
             if (memcmp(mKeySets[i].array(), keySetId.array(), keySetId.size()) == 0) {
                 return i;
diff --git a/include/media/CasImpl.h b/include/media/CasImpl.h
index 0dd506c..726f1ce 100644
--- a/include/media/CasImpl.h
+++ b/include/media/CasImpl.h
@@ -27,7 +27,7 @@
 using namespace media;
 using namespace MediaCas;
 using binder::Status;
-class CasPlugin;
+struct CasPlugin;
 class SharedLibrary;
 
 class CasImpl : public BnCas {
diff --git a/include/media/DescramblerImpl.h b/include/media/DescramblerImpl.h
index c1c79b3..9f212ac 100644
--- a/include/media/DescramblerImpl.h
+++ b/include/media/DescramblerImpl.h
@@ -24,7 +24,7 @@
 using namespace media;
 using namespace MediaDescrambler;
 using binder::Status;
-class DescramblerPlugin;
+struct DescramblerPlugin;
 class SharedLibrary;
 
 class DescramblerImpl : public BnDescrambler {
diff --git a/include/media/Interpolator.h b/include/media/Interpolator.h
index 120a074..dd510de 100644
--- a/include/media/Interpolator.h
+++ b/include/media/Interpolator.h
@@ -278,7 +278,7 @@
             if (res != NO_ERROR) {
                 return res;
             }
-            if (i > 0 && !(x > lastx) /* handle nan */
+            if ((i > 0 && !(x > lastx)) /* handle nan */
                     || y != y /* handle nan */) {
                 // This is a std::map object which imposes sorted order
                 // automatically on emplace.
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index 1282124..7d76f66 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -32,11 +32,8 @@
 #define LOG_TAG "VolumeShaper"
 
 // turn on VolumeShaper logging
-#if 0
-#define VS_LOG ALOGD
-#else
-#define VS_LOG(...)
-#endif
+#define VS_LOGGING 0
+#define VS_LOG(...) ALOGD_IF(VS_LOGGING, __VA_ARGS__)
 
 namespace android {
 
@@ -90,17 +87,17 @@
         Configuration()
             : Interpolator<S, T>()
             , mType(TYPE_SCALE)
+            , mId(-1)
             , mOptionFlags(OPTION_FLAG_NONE)
-            , mDurationMs(1000.)
-            , mId(-1) {
+            , mDurationMs(1000.) {
         }
 
         explicit Configuration(const Configuration &configuration)
             : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
             , mType(configuration.mType)
+            , mId(configuration.mId)
             , mOptionFlags(configuration.mOptionFlags)
-            , mDurationMs(configuration.mDurationMs)
-            , mId(configuration.mId) {
+            , mDurationMs(configuration.mDurationMs) {
         }
 
         Type getType() const {
diff --git a/include/media/omx/1.0/WGraphicBufferSource.h b/include/media/omx/1.0/WGraphicBufferSource.h
index 397e576..bf3be9a 100644
--- a/include/media/omx/1.0/WGraphicBufferSource.h
+++ b/include/media/omx/1.0/WGraphicBufferSource.h
@@ -74,6 +74,7 @@
     BnStatus setTimeLapseConfig(double fps, double captureFps) override;
     BnStatus setStartTimeUs(int64_t startTimeUs) override;
     BnStatus setStopTimeUs(int64_t stopTimeUs) override;
+    BnStatus getStopTimeOffsetUs(int64_t *stopTimeOffsetUs) override;
     BnStatus setColorAspects(int32_t aspects) override;
     BnStatus setTimeOffsetUs(int64_t timeOffsetsUs) override;
     BnStatus signalEndOfInputStream() override;
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 7becf57..9da5ef3 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -172,7 +172,7 @@
                 // client write-only, server read-only
                 uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0
 
-                uint16_t    mPad2;           // unused
+                uint16_t    mPad2 __attribute__((__unused__)); // unused
 
                 // server write-only, client read
                 ExtendedTimestampQueue::Shared mExtendedTimestampQueue;
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index 5193d00..51050cd 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -30,8 +30,8 @@
 class VideoFrame
 {
 public:
-    VideoFrame(): mWidth(0), mHeight(0), mDisplayWidth(0), mDisplayHeight(0), mSize(0), mData(0),
-            mRotationAngle(0) {}
+    VideoFrame(): mWidth(0), mHeight(0), mDisplayWidth(0), mDisplayHeight(0), mSize(0),
+            mRotationAngle(0), mData(0) {}
 
     VideoFrame(const VideoFrame& copy) {
         mWidth = copy.mWidth;
diff --git a/include/radio/Radio.h b/include/radio/Radio.h
index a4dfdd1..fb4dd2f 100644
--- a/include/radio/Radio.h
+++ b/include/radio/Radio.h
@@ -79,7 +79,6 @@
 
             Mutex                   mLock;
             sp<IRadio>              mIRadio;
-            const radio_handle_t    mHandle;
             sp<RadioCallback>       mCallback;
 };
 
diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h
index 9a05cac..7a29e31 100644
--- a/include/soundtrigger/SoundTrigger.h
+++ b/include/soundtrigger/SoundTrigger.h
@@ -72,7 +72,6 @@
 
             Mutex                               mLock;
             sp<ISoundTrigger>                   mISoundTrigger;
-            const sound_trigger_module_handle_t mModule;
             sp<SoundTriggerCallback>            mCallback;
 };
 
diff --git a/media/libaaudio/examples/input_monitor/static/Android.mk b/media/libaaudio/examples/input_monitor/static/Android.mk
index e83f179..16d5092 100644
--- a/media/libaaudio/examples/input_monitor/static/Android.mk
+++ b/media/libaaudio/examples/input_monitor/static/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := examples
+LOCAL_MODULE_TAGS := samples
 LOCAL_C_INCLUDES := \
     $(call include-path-for, audio-utils) \
     frameworks/av/media/libaaudio/include
diff --git a/media/libaaudio/examples/write_sine/static/Android.mk b/media/libaaudio/examples/write_sine/static/Android.mk
index e4da6a8..56986a3 100644
--- a/media/libaaudio/examples/write_sine/static/Android.mk
+++ b/media/libaaudio/examples/write_sine/static/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := examples
+LOCAL_MODULE_TAGS := samples
 LOCAL_C_INCLUDES := \
     $(call include-path-for, audio-utils) \
     frameworks/av/media/libaaudio/include
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 858b5cc..14feada 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -62,7 +62,7 @@
     SET_VOICE_VOLUME,
     GET_RENDER_POSITION,
     GET_INPUT_FRAMES_LOST,
-    NEW_AUDIO_SESSION_ID,
+    NEW_AUDIO_UNIQUE_ID,
     ACQUIRE_AUDIO_SESSION_ID,
     RELEASE_AUDIO_SESSION_ID,
     QUERY_NUM_EFFECTS,
@@ -80,7 +80,7 @@
     RELEASE_AUDIO_PATCH,
     LIST_AUDIO_PATCHES,
     SET_AUDIO_PORT_CONFIG,
-    GET_AUDIO_HW_SYNC,
+    GET_AUDIO_HW_SYNC_FOR_SESSION,
     SYSTEM_READY,
     FRAME_COUNT_HAL,
 };
@@ -628,8 +628,8 @@
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
         data.writeInt32((int32_t) use);
-        status_t status = remote()->transact(NEW_AUDIO_SESSION_ID, data, &reply);
-        audio_unique_id_t id = AUDIO_SESSION_ALLOCATE;
+        status_t status = remote()->transact(NEW_AUDIO_UNIQUE_ID, data, &reply);
+        audio_unique_id_t id = AUDIO_UNIQUE_ID_ALLOCATE;
         if (status == NO_ERROR) {
             id = reply.readInt32();
         }
@@ -912,7 +912,7 @@
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
         data.writeInt32(sessionId);
-        status_t status = remote()->transact(GET_AUDIO_HW_SYNC, data, &reply);
+        status_t status = remote()->transact(GET_AUDIO_HW_SYNC_FOR_SESSION, data, &reply);
         if (status != NO_ERROR) {
             return AUDIO_HW_SYNC_INVALID;
         }
@@ -1262,7 +1262,7 @@
             reply->writeInt32((int32_t) getInputFramesLost(ioHandle));
             return NO_ERROR;
         } break;
-        case NEW_AUDIO_SESSION_ID: {
+        case NEW_AUDIO_UNIQUE_ID: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             reply->writeInt32(newAudioUniqueId((audio_unique_id_use_t) data.readInt32()));
             return NO_ERROR;
@@ -1466,7 +1466,7 @@
             reply->writeInt32(status);
             return NO_ERROR;
         } break;
-        case GET_AUDIO_HW_SYNC: {
+        case GET_AUDIO_HW_SYNC_FOR_SESSION: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             reply->writeInt32(getAudioHwSyncForSession((audio_session_t) data.readInt32()));
             return NO_ERROR;
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index 87ada76..2bd2d01 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -286,7 +286,7 @@
         process_hook_t  hook;   // one of process__*, never NULL
         int32_t         *outputTemp;
         int32_t         *resampleTemp;
-        NBLog::Writer*  mLog;
+        NBLog::Writer*  mNBLogWriter;   // associated NBLog::Writer or &mDummyLog
         int32_t         reserved[1];
         // FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
         track_t         tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
@@ -301,9 +301,11 @@
 
     const uint32_t  mSampleRate;
 
-    NBLog::Writer   mDummyLog;
+    NBLog::Writer   mDummyLogWriter;
 public:
-    void            setLog(NBLog::Writer* log);
+    // Called by FastMixer to inform AudioMixer of it's associated NBLog::Writer.
+    // FIXME It would be safer to use TLS for this, so we don't accidentally use wrong one.
+    void            setNBLogWriter(NBLog::Writer* log);
 private:
     state_t         mState __attribute__((aligned(32)));
 
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index ae1be09..238925d 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -114,7 +114,7 @@
     mState.hook         = process__nop;
     mState.outputTemp   = NULL;
     mState.resampleTemp = NULL;
-    mState.mLog         = &mDummyLog;
+    mState.mNBLogWriter = &mDummyLogWriter;
     // mState.reserved
 
     // FIXME Most of the following initialization is probably redundant since
@@ -145,9 +145,9 @@
     delete [] mState.resampleTemp;
 }
 
-void AudioMixer::setLog(NBLog::Writer *log)
+void AudioMixer::setNBLogWriter(NBLog::Writer *logWriter)
 {
-    mState.mLog = log;
+    mState.mNBLogWriter = logWriter;
 }
 
 static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) {
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk
index 78601d5..09793d1 100644
--- a/media/libeffects/downmix/Android.mk
+++ b/media/libeffects/downmix/Android.mk
@@ -3,6 +3,7 @@
 # Multichannel downmix effect library
 include $(CLEAR_VARS)
 
+LOCAL_VENDOR_MODULE := true
 LOCAL_SRC_FILES:= \
 	EffectDownmix.c
 
diff --git a/media/libeffects/loudness/Android.mk b/media/libeffects/loudness/Android.mk
index 3db4a79..4b1c584 100644
--- a/media/libeffects/loudness/Android.mk
+++ b/media/libeffects/loudness/Android.mk
@@ -3,6 +3,7 @@
 # LoudnessEnhancer library
 include $(CLEAR_VARS)
 
+LOCAL_VENDOR_MODULE := true
 LOCAL_SRC_FILES:= \
 	EffectLoudnessEnhancer.cpp \
 	dsp/core/dynamic_range_compression.cpp
diff --git a/media/libeffects/lvm/lib/Android.mk b/media/libeffects/lvm/lib/Android.mk
index afc87bb..83e8288 100644
--- a/media/libeffects/lvm/lib/Android.mk
+++ b/media/libeffects/lvm/lib/Android.mk
@@ -6,6 +6,7 @@
 
 LOCAL_ARM_MODE := arm
 
+LOCAL_VENDOR_MODULE := true
 LOCAL_SRC_FILES:= \
     StereoWidening/src/LVCS_BypassMix.c \
     StereoWidening/src/LVCS_Control.c \
@@ -131,6 +132,7 @@
 
 LOCAL_ARM_MODE := arm
 
+LOCAL_VENDOR_MODULE := true
 LOCAL_SRC_FILES:= \
     Reverb/src/LVREV_ApplyNewSettings.c \
     Reverb/src/LVREV_ClearAudioBuffers.c \
diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk
index 9051587..f92fb95 100644
--- a/media/libeffects/lvm/wrapper/Android.mk
+++ b/media/libeffects/lvm/wrapper/Android.mk
@@ -6,6 +6,7 @@
 
 LOCAL_ARM_MODE := arm
 
+LOCAL_VENDOR_MODULE := true
 LOCAL_SRC_FILES:= \
 	Bundle/EffectBundle.cpp
 
@@ -37,6 +38,7 @@
 
 LOCAL_ARM_MODE := arm
 
+LOCAL_VENDOR_MODULE := true
 LOCAL_SRC_FILES:= \
     Reverb/EffectReverb.cpp
 
diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk
index 0acf1c0..c4de30d 100644
--- a/media/libeffects/proxy/Android.mk
+++ b/media/libeffects/proxy/Android.mk
@@ -18,7 +18,7 @@
 LOCAL_MODULE_RELATIVE_PATH := soundfx
 LOCAL_MODULE_TAGS := optional
 
-
+LOCAL_VENDOR_MODULE := true
 LOCAL_SRC_FILES := \
         EffectProxy.cpp
 
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index ddcc565..8687e1b 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -3,6 +3,7 @@
 # Visualizer library
 include $(CLEAR_VARS)
 
+LOCAL_VENDOR_MODULE := true
 LOCAL_SRC_FILES:= \
 	EffectVisualizer.cpp
 
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 62a7bdf..1f188f3 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -85,13 +85,13 @@
 }
 
 status_t MediaCodecInfo::Capabilities::writeToParcel(Parcel *parcel) const {
-    CHECK_LE(mProfileLevels.size(), INT32_MAX);
+    CHECK_LE(mProfileLevels.size(), static_cast<size_t>(INT32_MAX));
     parcel->writeInt32(mProfileLevels.size());
     for (size_t i = 0; i < mProfileLevels.size(); i++) {
         parcel->writeInt32(mProfileLevels.itemAt(i).mProfile);
         parcel->writeInt32(mProfileLevels.itemAt(i).mLevel);
     }
-    CHECK_LE(mColorFormats.size(), INT32_MAX);
+    CHECK_LE(mColorFormats.size(), static_cast<size_t>(INT32_MAX));
     parcel->writeInt32(mColorFormats.size());
     for (size_t i = 0; i < mColorFormats.size(); i++) {
         parcel->writeInt32(mColorFormats.itemAt(i));
diff --git a/media/libmedia/aidl/android/IGraphicBufferSource.aidl b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
index f3c7abc..12c2767 100644
--- a/media/libmedia/aidl/android/IGraphicBufferSource.aidl
+++ b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
@@ -31,6 +31,7 @@
     void setTimeLapseConfig(double fps, double captureFps);
     void setStartTimeUs(long startTimeUs);
     void setStopTimeUs(long stopTimeUs);
+    long getStopTimeOffsetUs();
     void setColorAspects(int aspects);
     void setTimeOffsetUs(long timeOffsetsUs);
     void signalEndOfInputStream();
diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h
index 0b0f916..40dd9f9 100644
--- a/media/libmedia/include/media/MediaRecorderBase.h
+++ b/media/libmedia/include/media/MediaRecorderBase.h
@@ -45,7 +45,7 @@
                                const sp<ICameraRecordingProxy>& proxy) = 0;
     virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface) = 0;
     virtual status_t setOutputFile(int fd) = 0;
-    virtual status_t setNextOutputFile(int fd) {return INVALID_OPERATION;}
+    virtual status_t setNextOutputFile(int /*fd*/) {return INVALID_OPERATION;}
     virtual status_t setOutputFileAuxiliary(int /*fd*/) {return INVALID_OPERATION;}
     virtual status_t setParameters(const String8& params) = 0;
     virtual status_t setListener(const sp<IMediaRecorderClient>& listener) = 0;
diff --git a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
index 4c543fa..31d1df9 100644
--- a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
@@ -67,6 +67,14 @@
     return toBinderStatus(mBase->setStopTimeUs(stopTimeUs));
 }
 
+BnStatus LWGraphicBufferSource::getStopTimeOffsetUs(
+        int64_t *stopTimeOffsetUs) {
+    return toBinderStatus(mBase->getStopTimeOffsetUs(
+            [stopTimeOffsetUs](auto, auto offsetUs) {
+                *stopTimeOffsetUs = offsetUs;
+            }));
+}
+
 BnStatus LWGraphicBufferSource::setColorAspects(
         int32_t aspects) {
     return toBinderStatus(mBase->setColorAspects(
diff --git a/media/libmedia/omx/1.0/WOmxNode.cpp b/media/libmedia/omx/1.0/WOmxNode.cpp
index 194378c..edac14f 100644
--- a/media/libmedia/omx/1.0/WOmxNode.cpp
+++ b/media/libmedia/omx/1.0/WOmxNode.cpp
@@ -47,7 +47,7 @@
     status_t transStatus = toStatusT(mBase->getParameter(
             toRawIndexType(index),
             tParams,
-            [&fnStatus, params, size](
+            [&fnStatus, params](
                     Status status, hidl_vec<uint8_t> const& outParams) {
                 fnStatus = toStatusT(status);
                 std::copy(
@@ -145,7 +145,7 @@
     status_t transStatus = toStatusT(mBase->allocateSecureBuffer(
             portIndex,
             static_cast<uint64_t>(size),
-            [&fnStatus, buffer, buffer_data, native_handle](
+            [&fnStatus, buffer, native_handle](
                     Status status,
                     uint32_t outBuffer,
                     hidl_handle const& outNativeHandle) {
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index f050e7f..dc501b2 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -41,6 +41,7 @@
     friend class MediaAnalyticsService;
     friend class IMediaAnalyticsService;
     friend class MediaMetricsJNI;
+    friend class MetricsSummarizer;
 
     public:
 
@@ -231,7 +232,6 @@
         size_t mPropCount;
         size_t mPropSize;
         Prop *mProps;
-
 };
 
 } // namespace android
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index e1d762f..89354d6 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1864,7 +1864,7 @@
         mStartTimeOffsetMs = mEncoderProfiles->getStartTimeOffsetMs(mCameraId);
     } else if (mVideoSource == VIDEO_SOURCE_SURFACE) {
         // surface source doesn't need large initial delay
-        mStartTimeOffsetMs = 200;
+        mStartTimeOffsetMs = 100;
     }
     if (mStartTimeOffsetMs > 0) {
         writer->setStartTimeOffsetMs(mStartTimeOffsetMs);
@@ -1982,10 +1982,12 @@
         mCameraSourceTimeLapse = NULL;
     }
 
-    if (mVideoEncoderSource != NULL) {
-        int64_t stopTimeUs = systemTime() / 1000;
-        sp<MetaData> meta = new MetaData;
-        err = mVideoEncoderSource->setStopStimeUs(stopTimeUs);
+    int64_t stopTimeUs = systemTime() / 1000;
+    for (const auto &source : { mAudioEncoderSource, mVideoEncoderSource }) {
+        if (source != nullptr && OK != source->setStopTimeUs(stopTimeUs)) {
+            ALOGW("Failed to set stopTime %lld us for %s",
+                    (long long)stopTimeUs, source->isVideo() ? "Video" : "Audio");
+        }
     }
 
     if (mWriter != NULL) {
diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h
index a01f7f2..e8d59a7 100644
--- a/media/libmediaplayerservice/include/MediaPlayerInterface.h
+++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h
@@ -286,7 +286,7 @@
     }
 
     // Modular DRM
-    virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId) {
+    virtual status_t prepareDrm(const uint8_t /* uuid */[16], const Vector<uint8_t>& /* drmSessionId */) {
         return INVALID_OPERATION;
     }
     virtual status_t releaseDrm() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 9fe61703..abda34e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1103,7 +1103,7 @@
             // (Case 1)
             // Must be a multiple of the frame size.  If it is not a multiple of a frame size, it
             // needs to fail, as we should not carry over fractional frames between calls.
-            CHECK_EQ(copy % mAudioSink->frameSize(), 0);
+            CHECK_EQ(copy % mAudioSink->frameSize(), 0u);
 
             // (Case 2, 3, 4)
             // Return early to the caller.
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 9264e49..8b3d0dc 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -544,7 +544,7 @@
         return;
     }
 
-    CHECK_EQ(msg->what(), (int)kWhatNotify);
+    CHECK_EQ(msg->what(), kWhatNotify);
 
     int32_t what;
     CHECK(msg->findInt32("what", &what));
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index de38e7f..5413007 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -31,6 +31,7 @@
 #include <utils/String8.h>
 
 #include <queue>
+#include <utility>
 
 namespace android {
 
@@ -51,12 +52,25 @@
 
 // ---------------------------------------------------------------------------
 
-NBLog::FormatEntry::FormatEntry(const uint8_t *entry) : mEntry(entry) {
-    ALOGW_IF(entry[offsetof(struct entry, type)] != EVENT_START_FMT,
-        "Created format entry with invalid event type %d", entry[offsetof(struct entry, type)]);
+/*static*/
+std::unique_ptr<NBLog::AbstractEntry> NBLog::AbstractEntry::buildEntry(const uint8_t *ptr) {
+    uint8_t type = EntryIterator(ptr)->type;
+    switch (type) {
+    case EVENT_START_FMT:
+        return std::make_unique<FormatEntry>(FormatEntry(ptr));
+    case EVENT_HISTOGRAM_FLUSH:
+    case EVENT_HISTOGRAM_ENTRY_TS:
+        return std::make_unique<HistogramEntry>(HistogramEntry(ptr));
+    default:
+        ALOGW("Tried to create AbstractEntry of type %d", type);
+        return nullptr;
+    }
 }
 
-NBLog::FormatEntry::FormatEntry(const NBLog::FormatEntry::iterator &it) : FormatEntry(it.ptr) {}
+NBLog::AbstractEntry::AbstractEntry(const uint8_t *entry) : mEntry(entry) {
+}
+
+// ---------------------------------------------------------------------------
 
 const char *NBLog::FormatEntry::formatString() const {
     return (const char*) mEntry + offsetof(entry, data);
@@ -66,12 +80,14 @@
     return mEntry[offsetof(entry, length)];
 }
 
-NBLog::FormatEntry::iterator NBLog::FormatEntry::args() const {
+NBLog::EntryIterator NBLog::FormatEntry::args() const {
     auto it = begin();
     // skip start fmt
     ++it;
     // skip timestamp
     ++it;
+    // skip hash
+    ++it;
     // Skip author if present
     if (it->type == EVENT_AUTHOR) {
         ++it;
@@ -79,19 +95,33 @@
     return it;
 }
 
-timespec NBLog::FormatEntry::timestamp() const {
+int64_t NBLog::FormatEntry::timestamp() const {
     auto it = begin();
     // skip start fmt
     ++it;
-    return it.payload<timespec>();
+    return it.payload<int64_t>();
 }
 
-pid_t NBLog::FormatEntry::author() const {
+NBLog::log_hash_t NBLog::FormatEntry::hash() const {
     auto it = begin();
     // skip start fmt
     ++it;
     // skip timestamp
     ++it;
+    // unaligned 64-bit read not supported
+    log_hash_t hash;
+    memcpy(&hash, it->data, sizeof(hash));
+    return hash;
+}
+
+int NBLog::FormatEntry::author() const {
+    auto it = begin();
+    // skip start fmt
+    ++it;
+    // skip timestamp
+    ++it;
+    // skip hash
+    ++it;
     // if there is an author entry, return it, return -1 otherwise
     if (it->type == EVENT_AUTHOR) {
         return it.payload<int>();
@@ -99,13 +129,15 @@
     return -1;
 }
 
-NBLog::FormatEntry::iterator NBLog::FormatEntry::copyWithAuthor(
+NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor(
         std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
     auto it = begin();
     // copy fmt start entry
     it.copyTo(dst);
     // copy timestamp
     (++it).copyTo(dst);
+    // copy hash
+    (++it).copyTo(dst);
     // insert author entry
     size_t authorEntrySize = NBLog::Entry::kOverhead + sizeof(author);
     uint8_t authorEntry[authorEntrySize];
@@ -124,71 +156,107 @@
     return it;
 }
 
-void NBLog::FormatEntry::iterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const {
+void NBLog::EntryIterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const {
     size_t length = ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
     dst->write(ptr, length);
 }
 
-void NBLog::FormatEntry::iterator::copyData(uint8_t *dst) const {
+void NBLog::EntryIterator::copyData(uint8_t *dst) const {
     memcpy((void*) dst, ptr + offsetof(entry, data), ptr[offsetof(entry, length)]);
 }
 
-NBLog::FormatEntry::iterator NBLog::FormatEntry::begin() const {
-    return iterator(mEntry);
+NBLog::EntryIterator NBLog::FormatEntry::begin() const {
+    return EntryIterator(mEntry);
 }
 
-NBLog::FormatEntry::iterator::iterator()
+NBLog::EntryIterator::EntryIterator()
     : ptr(nullptr) {}
 
-NBLog::FormatEntry::iterator::iterator(const uint8_t *entry)
+NBLog::EntryIterator::EntryIterator(const uint8_t *entry)
     : ptr(entry) {}
 
-NBLog::FormatEntry::iterator::iterator(const NBLog::FormatEntry::iterator &other)
+NBLog::EntryIterator::EntryIterator(const NBLog::EntryIterator &other)
     : ptr(other.ptr) {}
 
-const NBLog::FormatEntry::entry& NBLog::FormatEntry::iterator::operator*() const {
+const NBLog::entry& NBLog::EntryIterator::operator*() const {
     return *(entry*) ptr;
 }
 
-const NBLog::FormatEntry::entry* NBLog::FormatEntry::iterator::operator->() const {
+const NBLog::entry* NBLog::EntryIterator::operator->() const {
     return (entry*) ptr;
 }
 
-NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator++() {
+NBLog::EntryIterator& NBLog::EntryIterator::operator++() {
     ptr += ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
     return *this;
 }
 
-NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator--() {
+NBLog::EntryIterator& NBLog::EntryIterator::operator--() {
     ptr -= ptr[NBLog::Entry::kPreviousLengthOffset] + NBLog::Entry::kOverhead;
     return *this;
 }
 
-NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::next() const {
-    iterator aux(*this);
+NBLog::EntryIterator NBLog::EntryIterator::next() const {
+    EntryIterator aux(*this);
     return ++aux;
 }
 
-NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::prev() const {
-    iterator aux(*this);
+NBLog::EntryIterator NBLog::EntryIterator::prev() const {
+    EntryIterator aux(*this);
     return --aux;
 }
 
-int NBLog::FormatEntry::iterator::operator-(const NBLog::FormatEntry::iterator &other) const {
+int NBLog::EntryIterator::operator-(const NBLog::EntryIterator &other) const {
     return ptr - other.ptr;
 }
 
-bool NBLog::FormatEntry::iterator::operator!=(const iterator &other) const {
+bool NBLog::EntryIterator::operator!=(const EntryIterator &other) const {
     return ptr != other.ptr;
 }
 
-bool NBLog::FormatEntry::iterator::hasConsistentLength() const {
+bool NBLog::EntryIterator::hasConsistentLength() const {
     return ptr[offsetof(entry, length)] == ptr[ptr[offsetof(entry, length)] +
         NBLog::Entry::kOverhead + NBLog::Entry::kPreviousLengthOffset];
 }
 
 // ---------------------------------------------------------------------------
 
+int64_t NBLog::HistogramEntry::timestamp() const {
+    return EntryIterator(mEntry).payload<HistTsEntry>().ts;
+}
+
+NBLog::log_hash_t NBLog::HistogramEntry::hash() const {
+    return EntryIterator(mEntry).payload<HistTsEntry>().hash;
+}
+
+int NBLog::HistogramEntry::author() const {
+    EntryIterator it(mEntry);
+    if (it->length == sizeof(HistTsEntryWithAuthor)) {
+        return it.payload<HistTsEntryWithAuthor>().author;
+    } else {
+        return -1;
+    }
+}
+
+NBLog::EntryIterator NBLog::HistogramEntry::copyWithAuthor(
+        std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
+    // Current histogram entry has {type, length, struct HistTsEntry, length}.
+    // We now want {type, length, struct HistTsEntryWithAuthor, length}
+    uint8_t buffer[Entry::kOverhead + sizeof(HistTsEntryWithAuthor)];
+    // Copy content until the point we want to add the author
+    memcpy(buffer, mEntry, sizeof(entry) + sizeof(HistTsEntry));
+    // Copy the author
+    *(int*) (buffer + sizeof(entry) + sizeof(HistTsEntry)) = author;
+    // Update lengths
+    buffer[offsetof(entry, length)] = sizeof(HistTsEntryWithAuthor);
+    buffer[sizeof(buffer) + Entry::kPreviousLengthOffset] = sizeof(HistTsEntryWithAuthor);
+    // Write new buffer into FIFO
+    dst->write(buffer, sizeof(buffer));
+    return EntryIterator(mEntry).next();
+}
+
+// ---------------------------------------------------------------------------
+
 #if 0   // FIXME see note in NBLog.h
 NBLog::Timeline::Timeline(size_t size, void *shared)
     : mSize(roundup(size)), mOwn(shared == NULL),
@@ -301,13 +369,15 @@
     if (!mEnabled) {
         return;
     }
-    struct timespec ts;
-    if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
+    int64_t ts = get_monotonic_ns();
+    if (ts > 0) {
         log(EVENT_TIMESTAMP, &ts, sizeof(ts));
+    } else {
+        ALOGE("Failed to get timestamp");
     }
 }
 
-void NBLog::Writer::logTimestamp(const struct timespec &ts)
+void NBLog::Writer::logTimestamp(const int64_t ts)
 {
     if (!mEnabled) {
         return;
@@ -360,19 +430,57 @@
     log(&entry, true);
 }
 
-void NBLog::Writer::logFormat(const char *fmt, ...)
+void NBLog::Writer::logHash(log_hash_t hash)
+{
+    if (!mEnabled) {
+        return;
+    }
+    log(EVENT_HASH, &hash, sizeof(hash));
+}
+
+void NBLog::Writer::logHistTS(log_hash_t hash)
+{
+    if (!mEnabled) {
+        return;
+    }
+    HistTsEntry data;
+    data.hash = hash;
+    data.ts = get_monotonic_ns();
+    if (data.ts > 0) {
+        log(EVENT_HISTOGRAM_ENTRY_TS, &data, sizeof(data));
+    } else {
+        ALOGE("Failed to get timestamp");
+    }
+}
+
+void NBLog::Writer::logHistFlush(log_hash_t hash)
+{
+    if (!mEnabled) {
+        return;
+    }
+    HistTsEntry data;
+    data.hash = hash;
+    data.ts = get_monotonic_ns();
+    if (data.ts > 0) {
+        log(EVENT_HISTOGRAM_FLUSH, &data, sizeof(data));
+    } else {
+        ALOGE("Failed to get timestamp");
+    }
+}
+
+void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...)
 {
     if (!mEnabled) {
         return;
     }
 
     va_list ap;
-    va_start(ap, fmt);
-    Writer::logVFormat(fmt, ap);
+    va_start(ap, hash);
+    Writer::logVFormat(fmt, hash, ap);
     va_end(ap);
 }
 
-void NBLog::Writer::logVFormat(const char *fmt, va_list argp)
+void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp)
 {
     if (!mEnabled) {
         return;
@@ -381,8 +489,9 @@
     int i;
     double f;
     char* s;
-    struct timespec t;
+    int64_t t;
     Writer::logTimestamp();
+    Writer::logHash(hash);
     for (const char *p = fmt; *p != '\0'; p++) {
         // TODO: implement more complex formatting such as %.3f
         if (*p != '%') {
@@ -395,7 +504,7 @@
             break;
 
         case 't': // timestamp
-            t = va_arg(argp, struct timespec);
+            t = va_arg(argp, int64_t);
             Writer::logTimestamp(t);
             break;
 
@@ -440,16 +549,8 @@
         //      a confusion for a programmer debugging their code.
         return;
     }
-    switch (event) {
-    case EVENT_STRING:
-    case EVENT_TIMESTAMP:
-    case EVENT_INTEGER:
-    case EVENT_FLOAT:
-    case EVENT_PID:
-    case EVENT_START_FMT:
-        break;
-    case EVENT_RESERVED:
-    default:
+    // Ignore if invalid event
+    if (event == EVENT_RESERVED || event >= EVENT_UPPER_BOUND) {
         return;
     }
     Entry entry(event, data, length);
@@ -531,7 +632,7 @@
     Writer::logTimestamp();
 }
 
-void NBLog::LockedWriter::logTimestamp(const struct timespec &ts)
+void NBLog::LockedWriter::logTimestamp(const int64_t ts)
 {
     Mutex::Autolock _l(mLock);
     Writer::logTimestamp(ts);
@@ -568,6 +669,12 @@
     Writer::logEnd();
 }
 
+void NBLog::LockedWriter::logHash(log_hash_t hash)
+{
+    Mutex::Autolock _l(mLock);
+    Writer::logHash(hash);
+}
+
 bool NBLog::LockedWriter::isEnabled() const
 {
     Mutex::Autolock _l(mLock);
@@ -582,6 +689,11 @@
 
 // ---------------------------------------------------------------------------
 
+const std::set<NBLog::Event> NBLog::Reader::startingTypes {NBLog::Event::EVENT_START_FMT,
+                                                           NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS};
+const std::set<NBLog::Event> NBLog::Reader::endingTypes   {NBLog::Event::EVENT_END_FMT,
+                                                           NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS,
+                                                           NBLog::Event::EVENT_HISTOGRAM_FLUSH};
 NBLog::Reader::Reader(const void *shared, size_t size)
     : mShared((/*const*/ Shared *) shared), /*mIMemory*/
       mFd(-1), mIndent(0),
@@ -604,16 +716,17 @@
     delete mFifo;
 }
 
-uint8_t *NBLog::Reader::findLastEntryOfType(uint8_t *front, uint8_t *back, uint8_t type) {
+const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
+                                            const std::set<Event> &types) {
     while (back + Entry::kPreviousLengthOffset >= front) {
-        uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
-        if (prev < front || prev + prev[offsetof(FormatEntry::entry, length)] +
+        const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
+        if (prev < front || prev + prev[offsetof(entry, length)] +
                             Entry::kOverhead != back) {
 
             // prev points to an out of limits or inconsistent entry
             return nullptr;
         }
-        if (prev[offsetof(FormatEntry::entry, type)] == type) {
+        if (types.find((const Event) prev[offsetof(entry, type)]) != types.end()) {
             return prev;
         }
         back = prev;
@@ -652,21 +765,21 @@
     // it ends in a complete entry (which is not an END_FMT). So is safe to traverse backwards.
     // TODO: handle client corruption (in the middle of a buffer)
 
-    uint8_t *back = snapshot->mData + availToRead;
-    uint8_t *front = snapshot->mData;
+    const uint8_t *back = snapshot->mData + availToRead;
+    const uint8_t *front = snapshot->mData;
 
     // Find last END_FMT. <back> is sitting on an entry which might be the middle of a FormatEntry.
     // We go backwards until we find an EVENT_END_FMT.
-    uint8_t *lastEnd = findLastEntryOfType(front, back, EVENT_END_FMT);
+    const uint8_t *lastEnd = findLastEntryOfTypes(front, back, endingTypes);
     if (lastEnd == nullptr) {
-        snapshot->mEnd = snapshot->mBegin = FormatEntry::iterator(front);
+        snapshot->mEnd = snapshot->mBegin = EntryIterator(front);
     } else {
         // end of snapshot points to after last END_FMT entry
-        snapshot->mEnd = FormatEntry::iterator(lastEnd + Entry::kOverhead);
+        snapshot->mEnd = EntryIterator(lastEnd).next();
         // find first START_FMT
-        uint8_t *firstStart = nullptr;
-        uint8_t *firstStartTmp = lastEnd;
-        while ((firstStartTmp = findLastEntryOfType(front, firstStartTmp, EVENT_START_FMT))
+        const uint8_t *firstStart = nullptr;
+        const uint8_t *firstStartTmp = snapshot->mEnd;
+        while ((firstStartTmp = findLastEntryOfTypes(front, firstStartTmp, startingTypes))
                 != nullptr) {
             firstStart = firstStartTmp;
         }
@@ -674,7 +787,7 @@
         if (firstStart == nullptr) {
             snapshot->mBegin = snapshot->mEnd;
         } else {
-            snapshot->mBegin = FormatEntry::iterator(firstStart);
+            snapshot->mBegin = EntryIterator(firstStart);
         }
     }
 
@@ -686,6 +799,10 @@
 
 }
 
+inline static int deltaMs(int64_t t1, int64_t t2) {
+    return (t2 - t1) / (1000 * 1000);
+}
+
 void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
 {
 #if 0
@@ -712,7 +829,7 @@
     mFd = fd;
     mIndent = indent;
     String8 timestamp, body;
-    size_t lost = snapshot.lost() + (snapshot.begin() - FormatEntry::iterator(snapshot.data()));
+    size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
     if (lost > 0) {
         body.appendFormat("warning: lost %zu bytes worth of events", lost);
         // TODO timestamp empty here, only other choice to wait for the first timestamp event in the
@@ -730,6 +847,7 @@
     }
     bool deferredTimestamp = false;
 #endif
+
     for (auto entry = snapshot.begin(); entry != snapshot.end();) {
         switch (entry->type) {
 #if 0
@@ -801,6 +919,44 @@
             // right now, this is the only supported case
             entry = handleFormat(FormatEntry(entry), &timestamp, &body);
             break;
+        case EVENT_HISTOGRAM_ENTRY_TS: {
+            HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->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));
+            int64_t ts;
+            memcpy(&ts, &data->ts, sizeof(ts));
+            const std::pair<log_hash_t, int> key(hash, data->author);
+            // TODO might want to filter excessively high outliers, which are usually caused
+            // by the thread being inactive.
+            mHists[key].push_back(ts);
+            ++entry;
+            break;
+        }
+        case EVENT_HISTOGRAM_FLUSH: {
+            HistogramEntry histEntry(entry);
+            // Log timestamp
+            int64_t ts = histEntry.timestamp();
+            timestamp.clear();
+            timestamp.appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
+                            (int) ((ts / (1000 * 1000)) % 1000));
+            // Log histograms
+            body.appendFormat("Histogram flush - ");
+            handleAuthor(histEntry, &body);
+            body.appendFormat("\n");
+            for (auto hist = mHists.begin(); hist != mHists.end();) {
+                if (hist->first.second == histEntry.author()) {
+                    body.appendFormat("Histogram %X", (int)hist->first.first);
+                    drawHistogram(&body, hist->second, true/*logScale*/, indent + timestamp.size());
+                    hist = mHists.erase(hist);
+                } else {
+                    ++hist;
+                }
+            }
+            ++entry;
+            break;
+        }
         case EVENT_END_FMT:
             body.appendFormat("warning: got to end format event");
             ++entry;
@@ -844,11 +1000,13 @@
     return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
 }
 
+// ---------------------------------------------------------------------------
+
 void NBLog::appendTimestamp(String8 *body, const void *data) {
-    struct timespec ts;
-    memcpy(&ts, data, sizeof(struct timespec));
-    body->appendFormat("[%d.%03d]", (int) ts.tv_sec,
-                    (int) (ts.tv_nsec / 1000000));
+    int64_t ts;
+    memcpy(&ts, data, sizeof(ts));
+    body->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
+                    (int) ((ts / (1000 * 1000)) % 1000));
 }
 
 void NBLog::appendInt(String8 *body, const void *data) {
@@ -868,20 +1026,42 @@
     body->appendFormat("<PID: %d, name: %.*s>", id, (int) (length - sizeof(pid_t)), name);
 }
 
-NBLog::FormatEntry::iterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
+String8 NBLog::bufferDump(const uint8_t *buffer, size_t size)
+{
+    String8 str;
+    str.append("[ ");
+    for(size_t i = 0; i < size; i++)
+    {
+        str.appendFormat("%d ", buffer[i]);
+    }
+    str.append("]");
+    return str;
+}
+
+String8 NBLog::bufferDump(const EntryIterator &it)
+{
+    return bufferDump(it, it->length + Entry::kOverhead);
+}
+
+NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
                                                          String8 *timestamp,
                                                          String8 *body) {
     // log timestamp
-    struct timespec ts = fmtEntry.timestamp();
+    int64_t ts = fmtEntry.timestamp();
     timestamp->clear();
-    timestamp->appendFormat("[%d.%03d]", (int) ts.tv_sec,
-                    (int) (ts.tv_nsec / 1000000));
+    timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
+                    (int) ((ts / (1000 * 1000)) % 1000));
+
+    // log unique hash
+    log_hash_t hash = fmtEntry.hash();
+    // print only lower 16bit of hash as hex and line as int to reduce spam in the log
+    body->appendFormat("%.4X-%d ", (int)(hash >> 16) & 0xFFFF, (int) hash & 0xFFFF);
 
     // log author (if present)
     handleAuthor(fmtEntry, body);
 
     // log string
-    NBLog::FormatEntry::iterator arg = fmtEntry.args();
+    NBLog::EntryIterator arg = fmtEntry.args();
 
     const char* fmt = fmtEntry.formatString();
     size_t fmt_length = fmtEntry.formatStringLength();
@@ -954,10 +1134,111 @@
     return arg;
 }
 
+static int widthOf(int x) {
+    int width = 0;
+    while (x > 0) {
+        ++width;
+        x /= 10;
+    }
+    return width;
+}
+
+static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
+    // TODO allow buckets of variable resolution
+    std::map<int, int> buckets;
+    for (size_t i = 1; i < samples.size(); ++i) {
+        ++buckets[deltaMs(samples[i - 1], samples[i])];
+    }
+    return buckets;
+}
+
+static inline uint32_t log2(uint32_t x) {
+    // This works for x > 0
+    return 31 - __builtin_clz(x);
+}
+
+// TODO put this function in separate file. Make it return a std::string instead of modifying body
+/*
+Example output:
+[54.234] Histogram flush - AudioOut_D:
+Histogram 33640BF1
+            [ 1][ 1][ 1][ 3][54][69][ 1][ 2][ 1]
+        64|                      []
+        32|                  []  []
+        16|                  []  []
+         8|                  []  []
+         4|                  []  []
+         2|______________[]__[]__[]______[]____
+              4   5   6   8   9  10  11  13  15
+Notice that all values that fall in the same row have the same height (65 and 127 are displayed
+identically). That's why exact counts are added at the top.
+*/
+void NBLog::Reader::drawHistogram(String8 *body,
+                                  const std::vector<int64_t> &samples,
+                                  bool logScale,
+                                  int indent,
+                                  int maxHeight) {
+    if (samples.size() <= 1) {
+        return;
+    }
+    std::map<int, int> buckets = buildBuckets(samples);
+    // TODO consider changing all ints to uint32_t or uint64_t
+    static const char *underscores = "________________";
+    static const char *spaces = "                ";
+
+    auto it = buckets.begin();
+    int maxLabel = it->first;
+    int maxVal = it->second;
+    // Compute maximum values
+    while (++it != buckets.end()) {
+        if (it->first > maxLabel) {
+            maxLabel = it->first;
+        }
+        if (it->second > maxVal) {
+            maxVal = it->second;
+        }
+    }
+    int height = (logScale) ? log2(maxVal) + 1 : maxVal; // maxVal > 0, safe to call log2
+    int leftPadding = widthOf(maxVal);
+    int colWidth = std::max(std::max(widthOf(maxLabel) + 1, 3), leftPadding + 2);
+    int scalingFactor = 1;
+    // scale data if it exceeds maximum height
+    if (height > maxHeight) {
+        scalingFactor = (height + maxHeight) / maxHeight;
+        height /= scalingFactor;
+    }
+    // write header line with bucket values
+    body->appendFormat("\n%*s", indent, " ");
+    body->appendFormat("%*s", leftPadding + 2, " ");
+    for (auto const &x : buckets)
+    {
+        body->appendFormat("[%*d]", colWidth - 2, x.second);
+    }
+    // write histogram ascii art
+    body->appendFormat("\n%*s", indent, " ");
+    for (int row = height * scalingFactor; row > 0; row -= scalingFactor)
+    {
+        int value = ((logScale) ? (1 << row) : row);
+        body->appendFormat("%*u|", leftPadding, value);
+        for (auto const &x : buckets) {
+            body->appendFormat("%.*s%s", colWidth - 2,
+                   (row <= scalingFactor) ? underscores : spaces,
+                   x.second < value ? ((row <= scalingFactor) ? "__" : "  ") : "[]");
+        }
+        body->appendFormat("\n%*s", indent, " ");
+    }
+    // write footer with bucket labels
+    body->appendFormat("%*s", leftPadding + 1, " ");
+    for (auto const &x : buckets)
+    {
+        body->appendFormat("%*d", colWidth, x.first);
+    }
+    body->appendFormat("\n");
+}
+
 // ---------------------------------------------------------------------------
 
 NBLog::Merger::Merger(const void *shared, size_t size):
-      mBuffer(NULL),
       mShared((Shared *) shared),
       mFifo(mShared != NULL ?
         new audio_utils_fifo(size, sizeof(uint8_t),
@@ -966,6 +1247,8 @@
       {}
 
 void NBLog::Merger::addReader(const NBLog::NamedReader &reader) {
+    // FIXME This is called by binder thread in MediaLogService::registerWriter
+    //       but the access to shared variable mNamedReaders is not yet protected by a lock.
     mNamedReaders.push_back(reader);
 }
 
@@ -973,26 +1256,27 @@
 // composed by a timestamp and the index of the snapshot where the timestamp came from
 struct MergeItem
 {
-    struct timespec ts;
+    int64_t ts;
     int index;
-    MergeItem(struct timespec ts, int index): ts(ts), index(index) {}
+    MergeItem(int64_t ts, int index): ts(ts), index(index) {}
 };
 
 // operators needed for priority queue in merge
-bool operator>(const struct timespec &t1, const struct timespec &t2) {
-    return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec);
-}
+// bool operator>(const int64_t &t1, const int64_t &t2) {
+//     return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec);
+// }
 
 bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) {
-    return i1.ts > i2.ts ||
-        (i1.ts.tv_sec == i2.ts.tv_sec && i1.ts.tv_nsec == i2.ts.tv_nsec && i1.index > i2.index);
+    return i1.ts > i2.ts || (i1.ts == i2.ts && i1.index > i2.index);
 }
 
 // Merge registered readers, sorted by timestamp
 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.
     int nLogs = mNamedReaders.size();
     std::vector<std::unique_ptr<NBLog::Reader::Snapshot>> snapshots(nLogs);
-    std::vector<NBLog::FormatEntry::iterator> offsets(nLogs);
+    std::vector<NBLog::EntryIterator> offsets(nLogs);
     for (int i = 0; i < nLogs; ++i) {
         snapshots[i] = mNamedReaders[i].reader()->getSnapshot();
         offsets[i] = snapshots[i]->begin();
@@ -1004,7 +1288,7 @@
     for (int i = 0; i < nLogs; ++i)
     {
         if (offsets[i] != snapshots[i]->end()) {
-            timespec ts = FormatEntry(offsets[i]).timestamp();
+            int64_t ts = AbstractEntry::buildEntry(offsets[i])->timestamp();
             timestamps.emplace(ts, i);
         }
     }
@@ -1013,30 +1297,36 @@
         // find minimum timestamp
         int index = timestamps.top().index;
         // copy it to the log, increasing offset
-        offsets[index] = FormatEntry(offsets[index]).copyWithAuthor(mFifoWriter, index);
+        offsets[index] = AbstractEntry::buildEntry(offsets[index])->copyWithAuthor(mFifoWriter,
+                                                                                   index);
         // update data structures
         timestamps.pop();
         if (offsets[index] != snapshots[index]->end()) {
-            timespec ts = FormatEntry(offsets[index]).timestamp();
+            int64_t ts = AbstractEntry::buildEntry(offsets[index])->timestamp();
             timestamps.emplace(ts, index);
         }
     }
 }
 
-const std::vector<NBLog::NamedReader> *NBLog::Merger::getNamedReaders() const {
-    return &mNamedReaders;
+const std::vector<NBLog::NamedReader>& NBLog::Merger::getNamedReaders() const {
+    // FIXME This is returning a reference to a shared variable that needs a lock
+    return mNamedReaders;
 }
 
+// ---------------------------------------------------------------------------
+
 NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger)
     : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {}
 
-size_t NBLog::MergeReader::handleAuthor(const NBLog::FormatEntry &fmtEntry, String8 *body) {
-    int author = fmtEntry.author();
-    const char* name = (*mNamedReaders)[author].name();
+void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 *body) {
+    int author = entry.author();
+    // FIXME Needs a lock
+    const char* name = mNamedReaders[author].name();
     body->appendFormat("%s: ", name);
-    return NBLog::Entry::kOverhead + sizeof(author);
 }
 
+// ---------------------------------------------------------------------------
+
 NBLog::MergeThread::MergeThread(NBLog::Merger &merger)
     : mMerger(merger),
       mTimeoutUs(0) {}
diff --git a/media/libnbaio/include/NBLog.h b/media/libnbaio/include/NBLog.h
index 59b77bd..19232ff 100644
--- a/media/libnbaio/include/NBLog.h
+++ b/media/libnbaio/include/NBLog.h
@@ -24,6 +24,8 @@
 #include <utils/Mutex.h>
 #include <utils/threads.h>
 
+#include <map>
+#include <set>
 #include <vector>
 
 namespace android {
@@ -34,12 +36,16 @@
 
 public:
 
+typedef uint64_t log_hash_t;
+
+// FIXME Everything needed for client (writer API and registration) should be isolated
+//       from the rest of the implementation.
 class Writer;
 class Reader;
 
 private:
 
-enum Event {
+enum Event : uint8_t {
     EVENT_RESERVED,
     EVENT_STRING,               // ASCII string, not NUL-terminated
     // TODO: make timestamp optional
@@ -50,7 +56,13 @@
     EVENT_AUTHOR,               // author index (present in merged logs) tracks entry's original log
     EVENT_START_FMT,            // logFormat start event: entry includes format string, following
                                 // entries contain format arguments
+    EVENT_HASH,                 // unique HASH of log origin, originates from hash of file name
+                                // and line number
+    EVENT_HISTOGRAM_ENTRY_TS,   // single datum for timestamp histogram
+    EVENT_HISTOGRAM_FLUSH,      // show histogram on log
     EVENT_END_FMT,              // end of logFormat argument list
+
+    EVENT_UPPER_BOUND,          // to check for invalid events
 };
 
 
@@ -60,93 +72,145 @@
 // 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 {
+// entry representation in memory
+struct entry {
+    const uint8_t type;
+    const uint8_t length;
+    const uint8_t data[0];
+};
+
+// entry tail representation (after data)
+struct ending {
+    uint8_t length;
+    uint8_t next[0];
+};
+
+// entry iterator
+class EntryIterator {
 public:
-    // build a Format Entry starting in the given pointer
-    class iterator;
-    explicit FormatEntry(const uint8_t *entry);
-    explicit FormatEntry(const iterator &it);
+    EntryIterator();
+    explicit EntryIterator(const uint8_t *entry);
+    EntryIterator(const EntryIterator &other);
 
-    // entry representation in memory
-    struct entry {
-        const uint8_t type;
-        const uint8_t length;
-        const uint8_t data[0];
-    };
+    // dereference underlying entry
+    const entry&    operator*() const;
+    const entry*    operator->() const;
+    // advance to next entry
+    EntryIterator&       operator++(); // ++i
+    // back to previous entry
+    EntryIterator&       operator--(); // --i
+    EntryIterator        next() const;
+    EntryIterator        prev() const;
+    bool            operator!=(const EntryIterator &other) const;
+    int             operator-(const EntryIterator &other) const;
 
-    // entry tail representation (after data)
-    struct ending {
-        uint8_t length;
-        uint8_t next[0];
-    };
+    bool            hasConsistentLength() const;
+    void            copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
+    void            copyData(uint8_t *dst) const;
 
-    // entry iterator
-    class iterator {
-    public:
-        iterator();
-        iterator(const uint8_t *entry);
-        iterator(const iterator &other);
+    template<typename T>
+    inline const T& payload() {
+        return *reinterpret_cast<const T *>(ptr + offsetof(entry, data));
+    }
 
-        // dereference underlying entry
-        const entry&    operator*() const;
-        const entry*    operator->() const;
-        // advance to next entry
-        iterator&       operator++(); // ++i
-        // back to previous entry
-        iterator&       operator--(); // --i
-        iterator        next() const;
-        iterator        prev() const;
-        bool            operator!=(const iterator &other) const;
-        int             operator-(const iterator &other) const;
+    inline operator const uint8_t*() const {
+        return ptr;
+    }
 
-        bool            hasConsistentLength() const;
-        void            copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
-        void            copyData(uint8_t *dst) const;
+private:
+    const uint8_t  *ptr;
+};
 
-        template<typename T>
-        inline const T& payload() {
-            return *reinterpret_cast<const T *>(ptr + offsetof(entry, data));
-        }
+class AbstractEntry {
+public:
 
-    private:
-        friend class FormatEntry;
-        const uint8_t  *ptr;
-    };
+    // Entry starting in the given pointer
+    explicit AbstractEntry(const uint8_t *entry);
+    virtual ~AbstractEntry() {}
 
-    // Entry's format string
-    const char* formatString() const;
-
-    // Enrty's format string length
-    size_t      formatStringLength() const;
-
-    // Format arguments (excluding format string, timestamp and author)
-    iterator    args() const;
+    // build concrete entry of appropriate class from pointer
+    static std::unique_ptr<AbstractEntry> buildEntry(const uint8_t *ptr);
 
     // get format entry timestamp
-    timespec    timestamp() const;
+    // TODO consider changing to uint64_t
+    virtual int64_t      timestamp() const = 0;
+
+    // get format entry's unique id
+    virtual log_hash_t   hash() const = 0;
 
     // entry's author index (-1 if none present)
     // a Merger has a vector of Readers, author simply points to the index of the
     // Reader that originated the entry
-    int         author() const;
+    // TODO consider changing to uint32_t
+    virtual int          author() const = 0;
 
-    // copy entry, adding author before timestamp, returns size of original entry
-    iterator    copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const;
+    // copy entry, adding author before timestamp, returns iterator to end of entry
+    virtual EntryIterator    copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+                                       int author) const = 0;
 
-    iterator    begin() const;
-
-private:
+protected:
     // copies ordinary entry from src to dst, and returns length of entry
     // size_t      copyEntry(audio_utils_fifo_writer *dst, const iterator &it);
     const uint8_t  *mEntry;
 };
 
+class FormatEntry : public AbstractEntry {
+public:
+    // explicit FormatEntry(const EntryIterator &it);
+    explicit FormatEntry(const uint8_t *ptr) : AbstractEntry(ptr) {}
+    virtual ~FormatEntry() {}
+
+    // Entry's format string
+    const   char* formatString() const;
+
+    // Enrty's format string length
+            size_t      formatStringLength() const;
+
+    // Format arguments (excluding format string, timestamp and author)
+            EntryIterator    args() const;
+
+    // get format entry timestamp
+    virtual int64_t     timestamp() const override;
+
+    // get format entry's unique id
+    virtual log_hash_t  hash() const override;
+
+    // entry's author index (-1 if none present)
+    // a Merger has a vector of Readers, author simply points to the index of the
+    // Reader that originated the entry
+    virtual int         author() const override;
+
+    // copy entry, adding author before timestamp, returns size of original entry
+    virtual EntryIterator    copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+                                       int author) const override;
+
+            EntryIterator    begin() const;
+};
+
+class HistogramEntry : public AbstractEntry {
+public:
+    explicit HistogramEntry(const uint8_t *ptr) : AbstractEntry(ptr) {
+    }
+    virtual ~HistogramEntry() {}
+
+    virtual int64_t     timestamp() const override;
+
+    virtual log_hash_t  hash() const override;
+
+    virtual int         author() const override;
+
+    virtual EntryIterator    copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+                                       int author) const override;
+
+};
+
 // ---------------------------------------------------------------------------
 
 // representation of a single log entry in private memory
@@ -165,12 +229,28 @@
     static const size_t kMaxLength = 255;
 public:
     // mEvent, mLength, mData[...], duplicate mLength
-    static const size_t kOverhead = sizeof(FormatEntry::entry) + sizeof(FormatEntry::ending);
+    static const size_t kOverhead = sizeof(entry) + sizeof(ending);
     // endind length of previous entry
-    static const size_t kPreviousLengthOffset = - sizeof(FormatEntry::ending) +
-                                                offsetof(FormatEntry::ending, length);
+    static const size_t kPreviousLengthOffset = - sizeof(ending) +
+                                                offsetof(ending, length);
 };
 
+struct HistTsEntry {
+    log_hash_t hash;
+    int64_t ts;
+}; //TODO __attribute__((packed));
+
+struct HistTsEntryWithAuthor {
+    log_hash_t hash;
+    int64_t ts;
+    int author;
+}; //TODO __attribute__((packed));
+
+struct HistIntEntry {
+    log_hash_t hash;
+    int value;
+}; //TODO __attribute__((packed));
+
 // representation of a single log entry in shared memory
 //  byte[0]             mEvent
 //  byte[1]             mLength
@@ -187,7 +267,8 @@
     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.
@@ -244,19 +325,22 @@
 
     virtual ~Writer();
 
+    // FIXME needs comments, and some should be private
     virtual void    log(const char *string);
     virtual void    logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
     virtual void    logvf(const char *fmt, va_list ap);
     virtual void    logTimestamp();
-    virtual void    logTimestamp(const struct timespec &ts);
+    virtual void    logTimestamp(const int64_t ts);
     virtual void    logInteger(const int x);
     virtual void    logFloat(const float x);
     virtual void    logPID();
-    virtual void    logFormat(const char *fmt, ...);
-    virtual void    logVFormat(const char *fmt, va_list ap);
+    virtual void    logFormat(const char *fmt, log_hash_t hash, ...);
+    virtual void    logVFormat(const char *fmt, log_hash_t hash, va_list ap);
     virtual void    logStart(const char *fmt);
     virtual void    logEnd();
-
+    virtual void    logHash(log_hash_t hash);
+    virtual void    logHistTS(log_hash_t hash);
+    virtual void    logHistFlush(log_hash_t hash);
 
     virtual bool    isEnabled() const;
 
@@ -298,12 +382,13 @@
     virtual void    logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
     virtual void    logvf(const char *fmt, va_list ap);
     virtual void    logTimestamp();
-    virtual void    logTimestamp(const struct timespec &ts);
+    virtual void    logTimestamp(const int64_t ts);
     virtual void    logInteger(const int x);
     virtual void    logFloat(const float x);
     virtual void    logPID();
     virtual void    logStart(const char *fmt);
     virtual void    logEnd();
+    virtual void    logHash(log_hash_t hash);
 
     virtual bool    isEnabled() const;
     virtual bool    setEnabled(bool enabled);
@@ -334,18 +419,18 @@
 
         // iterator to beginning of readable segment of snapshot
         // data between begin and end has valid entries
-        FormatEntry::iterator begin() { return mBegin; }
+        EntryIterator begin() { return mBegin; }
 
         // iterator to end of readable segment of snapshot
-        FormatEntry::iterator end() { return mEnd; }
+        EntryIterator end() { return mEnd; }
 
 
     private:
         friend class Reader;
         uint8_t              *mData;
         size_t                mLost;
-        FormatEntry::iterator mBegin;
-        FormatEntry::iterator mEnd;
+        EntryIterator mBegin;
+        EntryIterator mEnd;
     };
 
     // Input parameter 'size' is the desired size of the timeline in byte units.
@@ -364,6 +449,8 @@
     bool     isIMemory(const sp<IMemory>& iMemory) const;
 
 private:
+    static const std::set<Event> startingTypes;
+    static const std::set<Event> endingTypes;
     /*const*/ Shared* const mShared;    // raw pointer to shared memory, actually const but not
                                         // declared as const because audio_utils_fifo() constructor
     sp<IMemory> mIMemory;       // ref-counted version, assigned only in constructor
@@ -374,17 +461,23 @@
     audio_utils_fifo_reader * const mFifoReader;    // used to read from FIFO,
                                                     // non-NULL unless constructor fails
 
+    std::map<std::pair<log_hash_t, int>, std::vector<int64_t>> mHists;
+
     void    dumpLine(const String8& timestamp, String8& body);
 
-    FormatEntry::iterator   handleFormat(const FormatEntry &fmtEntry,
+    EntryIterator   handleFormat(const FormatEntry &fmtEntry,
                                          String8 *timestamp,
                                          String8 *body);
     // dummy method for handling absent author entry
-    virtual size_t handleAuthor(const FormatEntry &fmtEntry, String8 *body) { return 0; }
+    virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {}
+
+    static void drawHistogram(String8 *body, const std::vector<int64_t> &samples,
+                              bool logScale, int indent = 0, int maxHeight = 10);
 
     // 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 uint8_t *findLastEntryOfType(uint8_t *front, uint8_t *back, uint8_t type);
+    static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
+                                         const std::set<Event> &types);
 
     static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps
 };
@@ -417,27 +510,30 @@
     void addReader(const NamedReader &reader);
     // TODO add removeReader
     void merge();
-    const std::vector<NamedReader> *getNamedReaders() const;
+    // FIXME This is returning a reference to a shared variable that needs a lock
+    const std::vector<NamedReader>& getNamedReaders() const;
 private:
     // vector of the readers the merger is supposed to merge from.
     // every reader reads from a writer's buffer
+    // FIXME Needs to be protected by a lock
     std::vector<NamedReader> mNamedReaders;
-    uint8_t *mBuffer;
+
+    // TODO Need comments on all of these
     Shared * const mShared;
     std::unique_ptr<audio_utils_fifo> mFifo;
     std::unique_ptr<audio_utils_fifo_writer> mFifoWriter;
-
-    static struct timespec getTimestamp(const uint8_t *data);
 };
 
 class MergeReader : public Reader {
 public:
     MergeReader(const void *shared, size_t size, Merger &merger);
 private:
-    const std::vector<NamedReader> *mNamedReaders;
+    // FIXME Needs to be protected by a lock,
+    //       because even though our use of it is read-only there may be asynchronous updates
+    const std::vector<NamedReader>& mNamedReaders;
     // handle author entry by looking up the author's name and appending it to the body
     // returns number of bytes read from fmtEntry
-    size_t handleAuthor(const FormatEntry &fmtEntry, String8 *body);
+    void handleAuthor(const AbstractEntry &fmtEntry, String8 *body);
 };
 
 // MergeThread is a thread that contains a Merger. It works as a retriggerable one-shot:
@@ -479,6 +575,15 @@
 
 };  // 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/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2877ba2..fc91da7 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -7212,6 +7212,16 @@
             ALOGE("Failed to set parameter 'stop-time-us' (err %d)", err);
             return err;
         }
+
+        int64_t stopTimeOffsetUs;
+        err = statusFromBinderStatus(
+                mGraphicBufferSource->getStopTimeOffsetUs(&stopTimeOffsetUs));
+
+        if (err != OK) {
+            ALOGE("Failed to get stop time offset (err %d)", err);
+            return err;
+        }
+        mInputFormat->setInt64("android._stop-time-offset-us", stopTimeOffsetUs);
     }
 
     int32_t dummy;
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 4ccd2d0..6a5a229 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -58,6 +58,8 @@
       mOutSampleRate(outSampleRate > 0 ? outSampleRate : sampleRate),
       mTrackMaxAmplitude(false),
       mStartTimeUs(0),
+      mStopSystemTimeUs(-1),
+      mLastFrameTimestampUs(0),
       mMaxAmplitude(0),
       mPrevSampleTimeUs(0),
       mInitialReadTimeUs(0),
@@ -175,6 +177,7 @@
     }
 
     mStarted = false;
+    mStopSystemTimeUs = -1;
     mFrameAvailableCondition.signal();
 
     mRecord->stop();
@@ -286,6 +289,21 @@
     return OK;
 }
 
+status_t AudioSource::setStopTimeUs(int64_t stopTimeUs) {
+    Mutex::Autolock autoLock(mLock);
+    ALOGV("Set stoptime: %lld us", (long long)stopTimeUs);
+
+    if (stopTimeUs < -1) {
+        ALOGE("Invalid stop time %lld us", (long long)stopTimeUs);
+        return BAD_VALUE;
+    } else if (stopTimeUs == -1) {
+        ALOGI("reset stopTime to be -1");
+    }
+
+    mStopSystemTimeUs = stopTimeUs;
+    return OK;
+}
+
 void AudioSource::signalBufferReturned(MediaBuffer *buffer) {
     ALOGV("signalBufferReturned: %p", buffer->data());
     Mutex::Autolock autoLock(mLock);
@@ -338,6 +356,12 @@
         return OK;
     }
 
+    if (mStopSystemTimeUs != -1 && timeUs >= mStopSystemTimeUs) {
+        ALOGV("Drop Audio frame at %lld  stop time: %lld us",
+                (long long)timeUs, (long long)mStopSystemTimeUs);
+        return OK;
+    }
+
     if (mNumFramesReceived == 0 && mPrevSampleTimeUs == 0) {
         mInitialReadTimeUs = timeUs;
         // Initial delay
@@ -346,6 +370,7 @@
         }
         mPrevSampleTimeUs = mStartTimeUs;
     }
+    mLastFrameTimestampUs = timeUs;
 
     size_t numLostBytes = 0;
     if (mNumFramesReceived > 0) {  // Ignore earlier frame lost
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index a569f5d..61a2b5f 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -220,6 +220,7 @@
       mNumFramesEncoded(0),
       mTimeBetweenFrameCaptureUs(0),
       mFirstFrameTimeUs(0),
+      mStopSystemTimeUs(-1),
       mNumFramesDropped(0),
       mNumGlitches(0),
       mGlitchDurationThresholdUs(200000),
@@ -879,6 +880,7 @@
     {
         Mutex::Autolock autoLock(mLock);
         mStarted = false;
+        mStopSystemTimeUs = -1;
         mFrameAvailableCondition.signal();
 
         int64_t token;
@@ -1095,12 +1097,33 @@
     return OK;
 }
 
+status_t CameraSource::setStopTimeUs(int64_t stopTimeUs) {
+    Mutex::Autolock autoLock(mLock);
+    ALOGV("Set stoptime: %lld us", (long long)stopTimeUs);
+
+    if (stopTimeUs < -1) {
+        ALOGE("Invalid stop time %lld us", (long long)stopTimeUs);
+        return BAD_VALUE;
+    } else if (stopTimeUs == -1) {
+        ALOGI("reset stopTime to be -1");
+    }
+
+    mStopSystemTimeUs = stopTimeUs;
+    return OK;
+}
+
 bool CameraSource::shouldSkipFrameLocked(int64_t timestampUs) {
     if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
         ALOGV("Drop frame at %lld/%lld us", (long long)timestampUs, (long long)mStartTimeUs);
         return true;
     }
 
+    if (mStopSystemTimeUs != -1 && timestampUs >= mStopSystemTimeUs) {
+        ALOGV("Drop Camera frame at %lld  stop time: %lld us",
+                (long long)timestampUs, (long long)mStopSystemTimeUs);
+        return true;
+    }
+
     // May need to skip frame or modify timestamp. Currently implemented
     // by the subclass CameraSourceTimeLapse.
     if (skipCurrentFrame(timestampUs)) {
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index b83b0a0..03ea959 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -710,7 +710,7 @@
     uint32_t crc = htonl(crc32(&buffer->data()[5], 12));
     memcpy(&buffer->data()[17], &crc, sizeof(crc));
 
-    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size());
+    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), (ssize_t)buffer->size());
 }
 
 void MPEG2TSWriter::writeProgramMap() {
@@ -786,7 +786,7 @@
     uint32_t crc = htonl(crc32(&buffer->data()[5], 12+mSources.size()*5));
     memcpy(&buffer->data()[17+mSources.size()*5], &crc, sizeof(crc));
 
-    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size());
+    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), (ssize_t)buffer->size());
 }
 
 void MPEG2TSWriter::writeAccessUnit(
@@ -891,7 +891,7 @@
 
     memcpy(ptr, accessUnit->data(), copy);
 
-    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size());
+    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), (ssize_t)buffer->size());
 
     size_t offset = copy;
     while (offset < accessUnit->size()) {
@@ -937,7 +937,7 @@
 
         memcpy(ptr, accessUnit->data() + offset, copy);
         CHECK_EQ(internalWrite(buffer->data(), buffer->size()),
-                 buffer->size());
+                 (ssize_t)buffer->size());
 
         offset += copy;
     }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index cafedba..04f678c 100755
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -138,7 +138,7 @@
             mTotalNumTableEntries(0),
             mNumValuesInCurrEntry(0),
             mCurrTableEntriesElement(NULL) {
-            CHECK_GT(mElementCapacity, 0);
+            CHECK_GT(mElementCapacity, 0u);
             // Ensure no integer overflow on allocation in add().
             CHECK_LT(ENTRY_SIZE, UINT32_MAX / mElementCapacity);
         }
@@ -166,7 +166,7 @@
                 --iterations;
             }
             CHECK(it != mTableEntryList.end());
-            CHECK_EQ(iterations, 0);
+            CHECK_EQ(iterations, 0u);
 
             (*it)[(pos % (mElementCapacity * ENTRY_SIZE))] = value;
         }
@@ -187,7 +187,7 @@
                 --iterations;
             }
             CHECK(it != mTableEntryList.end());
-            CHECK_EQ(iterations, 0);
+            CHECK_EQ(iterations, 0u);
 
             value = (*it)[(pos % (mElementCapacity * ENTRY_SIZE))];
             return true;
@@ -235,12 +235,12 @@
         // 2. followed by the values in the table enties in order
         // @arg writer the writer to actual write to the storage
         void write(MPEG4Writer *writer) const {
-            CHECK_EQ(mNumValuesInCurrEntry % ENTRY_SIZE, 0);
+            CHECK_EQ(mNumValuesInCurrEntry % ENTRY_SIZE, 0u);
             uint32_t nEntries = mTotalNumTableEntries;
             writer->writeInt32(nEntries);
             for (typename List<TYPE *>::iterator it = mTableEntryList.begin();
                 it != mTableEntryList.end(); ++it) {
-                CHECK_GT(nEntries, 0);
+                CHECK_GT(nEntries, 0u);
                 if (nEntries >= mElementCapacity) {
                     writer->write(*it, sizeof(TYPE) * ENTRY_SIZE, mElementCapacity);
                     nEntries -= mElementCapacity;
@@ -1270,7 +1270,7 @@
 
         mOffset += length + 4;
     } else {
-        CHECK_LT(length, 65536);
+        CHECK_LT(length, 65536u);
 
         uint8_t x = length >> 8;
         ::write(mFd, &x, 1);
@@ -1330,7 +1330,7 @@
 }
 
 void MPEG4Writer::beginBox(const char *fourcc) {
-    CHECK_EQ(strlen(fourcc), 4);
+    CHECK_EQ(strlen(fourcc), 4u);
 
     mBoxes.push_back(mWriteMoovBoxToMemory?
             mMoovBoxBufferOffset: mOffset);
@@ -1381,7 +1381,7 @@
 }
 
 void MPEG4Writer::writeFourcc(const char *s) {
-    CHECK_EQ(strlen(s), 4);
+    CHECK_EQ(strlen(s), 4u);
     write(s, 1, 4);
 }
 
@@ -2123,13 +2123,17 @@
     if (mDone) {
         return OK;
     }
-    mDone = true;
+
     if (stopSource) {
         ALOGD("%s track source stopping", getTrackType());
         mSource->stop();
         ALOGD("%s track source stopped", getTrackType());
     }
 
+    // Set mDone to be true after sucessfully stop mSource as mSource may be still outputting
+    // buffers to the writer.
+    mDone = true;
+
     void *dummy;
     pthread_join(mThread, &dummy);
     status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
@@ -3353,10 +3357,10 @@
 void MPEG4Writer::Track::writeMp4aEsdsBox() {
     mOwner->beginBox("esds");
     CHECK(mCodecSpecificData);
-    CHECK_GT(mCodecSpecificDataSize, 0);
+    CHECK_GT(mCodecSpecificDataSize, 0u);
 
     // Make sure all sizes encode to a single byte.
-    CHECK_LT(mCodecSpecificDataSize + 23, 128);
+    CHECK_LT(mCodecSpecificDataSize + 23, 128u);
 
     mOwner->writeInt32(0);     // version=0, flags=0
     mOwner->writeInt8(0x03);   // ES_DescrTag
@@ -3395,10 +3399,10 @@
 
 void MPEG4Writer::Track::writeMp4vEsdsBox() {
     CHECK(mCodecSpecificData);
-    CHECK_GT(mCodecSpecificDataSize, 0);
+    CHECK_GT(mCodecSpecificDataSize, 0u);
 
     // Make sure all sizes encode to a single byte.
-    CHECK_LT(23 + mCodecSpecificDataSize, 128);
+    CHECK_LT(23 + mCodecSpecificDataSize, 128u);
 
     mOwner->beginBox("esds");
 
@@ -3590,7 +3594,7 @@
 
 void MPEG4Writer::Track::writeAvccBox() {
     CHECK(mCodecSpecificData);
-    CHECK_GE(mCodecSpecificDataSize, 5);
+    CHECK_GE(mCodecSpecificDataSize, 5u);
 
     // Patch avcc's lengthSize field to match the number
     // of bytes we use to indicate the size of a nal unit.
@@ -3604,7 +3608,7 @@
 
 void MPEG4Writer::Track::writeHvccBox() {
     CHECK(mCodecSpecificData);
-    CHECK_GE(mCodecSpecificDataSize, 5);
+    CHECK_GE(mCodecSpecificDataSize, 5u);
 
     // Patch avcc's lengthSize field to match the number
     // of bytes we use to indicate the size of a nal unit.
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index bb20850..cfee943 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -54,7 +54,7 @@
     void stopSource();
     void pause();
     void resume();
-
+    status_t setStopTimeUs(int64_t stopTimeUs);
     bool readBuffer(MediaBuffer **buffer);
 
 protected:
@@ -66,6 +66,7 @@
         kWhatStart = 'msta',
         kWhatStop,
         kWhatPull,
+        kWhatSetStopTimeUs,
     };
 
     sp<MediaSource> mSource;
@@ -161,6 +162,12 @@
     return err;
 }
 
+status_t MediaCodecSource::Puller::setStopTimeUs(int64_t stopTimeUs) {
+    sp<AMessage> msg = new AMessage(kWhatSetStopTimeUs, this);
+    msg->setInt64("stop-time-us", stopTimeUs);
+    return postSynchronouslyAndReturnError(msg);
+}
+
 status_t MediaCodecSource::Puller::start(const sp<MetaData> &meta, const sp<AMessage> &notify) {
     ALOGV("puller (%s) start", mIsAudio ? "audio" : "video");
     mLooper->start(
@@ -250,6 +257,20 @@
             break;
         }
 
+        case kWhatSetStopTimeUs:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            int64_t stopTimeUs;
+            CHECK(msg->findInt64("stop-time-us", &stopTimeUs));
+            status_t err = mSource->setStopTimeUs(stopTimeUs);
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
         case kWhatStop:
         {
             mSource->stop();
@@ -364,11 +385,8 @@
 }
 
 
-status_t MediaCodecSource::setStopStimeUs(int64_t stopTimeUs) {
-    if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
-        return OK;
-    }
-    sp<AMessage> msg = new AMessage(kWhatSetStopTimeOffset, mReflector);
+status_t MediaCodecSource::setStopTimeUs(int64_t stopTimeUs) {
+    sp<AMessage> msg = new AMessage(kWhatSetStopTimeUs, mReflector);
     msg->setInt64("stop-time-us", stopTimeUs);
     return postSynchronouslyAndReturnError(msg);
 }
@@ -986,12 +1004,21 @@
 
         mStopping = true;
 
+        int64_t timeoutUs = kStopTimeoutUs;
         // if using surface, signal source EOS and wait for EOS to come back.
         // otherwise, stop puller (which also clears the input buffer queue)
         // and wait for the EOS message. We cannot call source->stop() because
         // the encoder may still be processing input buffers.
         if (mFlags & FLAG_USE_SURFACE_INPUT) {
             mEncoder->signalEndOfInputStream();
+            // Increase the timeout if there is delay in the GraphicBufferSource
+            sp<AMessage> inputFormat;
+            int64_t stopTimeOffsetUs;
+            if (mEncoder->getInputFormat(&inputFormat) == OK &&
+                    inputFormat->findInt64("android._stop-time-offset-us", &stopTimeOffsetUs) &&
+                    stopTimeOffsetUs > 0) {
+                timeoutUs += stopTimeOffsetUs;
+            }
         } else {
             mPuller->stop();
         }
@@ -999,7 +1026,7 @@
         // complete stop even if encoder/puller stalled
         sp<AMessage> timeoutMsg = new AMessage(kWhatStopStalled, mReflector);
         timeoutMsg->setInt32("generation", mGeneration);
-        timeoutMsg->post(kStopTimeoutUs);
+        timeoutMsg->post(timeoutUs);
         break;
     }
 
@@ -1055,7 +1082,7 @@
         response->postReply(replyID);
         break;
     }
-    case kWhatSetStopTimeOffset:
+    case kWhatSetStopTimeUs:
     {
         sp<AReplyToken> replyID;
         CHECK(msg->senderAwaitsResponse(&replyID));
@@ -1063,11 +1090,13 @@
         int64_t stopTimeUs;
         CHECK(msg->findInt64("stop-time-us", &stopTimeUs));
 
-        // Propagate the timestamp offset to GraphicBufferSource.
+        // Propagate the stop time to GraphicBufferSource.
         if (mFlags & FLAG_USE_SURFACE_INPUT) {
             sp<AMessage> params = new AMessage;
             params->setInt64("stop-time-us", stopTimeUs);
             err = mEncoder->setParameters(params);
+        } else {
+            err = mPuller->setStopTimeUs(stopTimeUs);
         }
 
         sp<AMessage> response = new AMessage;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 02d275b..5f9aa01 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -37,6 +37,10 @@
 OMXClient::OMXClient() {
 }
 
+status_t OMXClient::connect() {
+    return connect(nullptr);
+}
+
 status_t OMXClient::connect(bool* trebleFlag) {
     if (property_get_bool("persist.media.treble_omx", true)) {
         if (trebleFlag != nullptr) {
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
index 4134698..4f31c5f 100644
--- a/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/SampleIterator.cpp
@@ -282,7 +282,7 @@
 
         default:
         {
-            CHECK_EQ(mTable->mSampleSizeFieldSize, 4);
+            CHECK_EQ(mTable->mSampleSizeFieldSize, 4u);
 
             uint8_t x;
             if (mTable->mDataSource->readAt(
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index d0d82b3..d14e86b 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -96,13 +96,13 @@
     mFrameAvailableListener = listener;
 }
 
-void SurfaceMediaSource::dump(String8& result) const
+void SurfaceMediaSource::dumpState(String8& result) const
 {
     char buffer[1024];
-    dump(result, "", buffer, 1024);
+    dumpState(result, "", buffer, 1024);
 }
 
-void SurfaceMediaSource::dump(
+void SurfaceMediaSource::dumpState(
         String8& result,
         const char* /* prefix */,
         char* buffer,
@@ -166,7 +166,7 @@
         mMaxAcquiredBufferCount = bufferCount;
     }
 
-    CHECK_GT(mMaxAcquiredBufferCount, 1);
+    CHECK_GT(mMaxAcquiredBufferCount, 1u);
 
     status_t err =
         mConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBufferCount);
@@ -185,7 +185,7 @@
     ALOGV("setMaxAcquiredBufferCount(%zu)", count);
     Mutex::Autolock lock(mMutex);
 
-    CHECK_GT(count, 1);
+    CHECK_GT(count, 1u);
     mMaxAcquiredBufferCount = count;
 
     return OK;
diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
index 9e596ff..5656139 100644
--- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
@@ -296,7 +296,7 @@
     memset(&inputData, 0, sizeof(inputData));
     inputData.Buffer = (unsigned char*) mInputFrame;
     inputData.Length = nSamples * sizeof(int16_t);
-    CHECK(VO_ERR_NONE == mApiHandle->SetInputData(mEncoderHandle,&inputData));
+    CHECK((VO_U32)VO_ERR_NONE == mApiHandle->SetInputData(mEncoderHandle,&inputData));
 
     VO_CODECBUFFER outputData;
     memset(&outputData, 0, sizeof(outputData));
diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp
index 96bbb85..1457242 100644
--- a/media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp
@@ -65,7 +65,7 @@
     onReset();
 
     if (mEncoderHandle) {
-        CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+        CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
         mEncoderHandle = NULL;
     }
 
@@ -524,7 +524,7 @@
         memset(&inputData, 0, sizeof(inputData));
         inputData.Buffer = (unsigned char *)mInputFrame;
         inputData.Length = numBytesPerInputFrame;
-        CHECK(VO_ERR_NONE ==
+        CHECK((VO_U32)VO_ERR_NONE ==
                 mApiHandle->SetInputData(mEncoderHandle, &inputData));
 
         VO_CODECBUFFER outputData;
diff --git a/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp
index 9d50c4e..c7973d6 100644
--- a/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp
+++ b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp
@@ -59,7 +59,7 @@
 
 SoftAMRWBEncoder::~SoftAMRWBEncoder() {
     if (mEncoderHandle != NULL) {
-        CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+        CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
         mEncoderHandle = NULL;
     }
 
@@ -435,7 +435,7 @@
         inputData.Buffer = (unsigned char *) mInputFrame;
         inputData.Length = mInputSize;
 
-        CHECK_EQ(VO_ERR_NONE,
+        CHECK_EQ((VO_U32)VO_ERR_NONE,
                  mApiHandle->SetInputData(mEncoderHandle, &inputData));
 
         VO_CODECBUFFER outputData;
diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h
index 97361fa..6027f76 100644
--- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h
+++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h
@@ -48,13 +48,10 @@
     virtual void onQueueFilled(OMX_U32 portIndex);
 
 private:
-
-    enum {
-        kNumBuffers = 2,
-        kMaxNumSamplesPerFrame = 1152,
-        kMaxInputBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t) * 2,
-        kMaxOutputBufferSize = 65536,    //TODO check if this can be reduced
-    };
+    const unsigned int kNumBuffers = 2;
+    const unsigned int kMaxNumSamplesPerFrame = 1152;
+    const unsigned int kMaxInputBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t) * 2;
+    const unsigned int kMaxOutputBufferSize = 65536;    //TODO check if this can be reduced
 
     bool mSignalledError;
 
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
index 0822c34..3def1f0 100644
--- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
@@ -274,7 +274,7 @@
         mConfig->inputBufferUsedLength = 0;
 
         mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t);
-        if ((int32)outHeader->nAllocLen < mConfig->outputFrameSize) {
+        if ((int32_t)outHeader->nAllocLen < mConfig->outputFrameSize) {
             ALOGE("input buffer too small: got %u, expected %u",
                 outHeader->nAllocLen, mConfig->outputFrameSize);
             android_errorWriteLog(0x534e4554, "27793371");
@@ -363,7 +363,7 @@
             mAnchorTimeUs + (mNumFramesOutput * 1000000ll) / mSamplingRate;
 
         if (inHeader) {
-            CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength);
+            CHECK_GE((int32_t)inHeader->nFilledLen, mConfig->inputBufferUsedLength);
 
             inHeader->nOffset += mConfig->inputBufferUsedLength;
             inHeader->nFilledLen -= mConfig->inputBufferUsedLength;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 536d40d..a07787a 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -30,10 +30,6 @@
 
 namespace android {
 
-static bool runningInEmulator() {
-    char prop[PROPERTY_VALUE_MAX];
-    return (property_get("ro.kernel.qemu", prop, NULL) > 0);
-}
 
 static int ALIGN(int x, int y) {
     // y must be a power of 2.
@@ -108,7 +104,7 @@
     size_t bufHeight = mCropHeight;
 
     // hardware has YUV12 and RGBA8888 support, so convert known formats
-    if (!runningInEmulator()) {
+    {
         switch (mColorFormat) {
             case OMX_COLOR_FormatYUV420Planar:
             case OMX_COLOR_FormatYUV420SemiPlanar:
@@ -205,7 +201,7 @@
 }
 
 std::list<FrameRenderTracker::Info> SoftwareRenderer::render(
-        const void *data, size_t size, int64_t mediaTimeUs, nsecs_t renderTimeNs,
+        const void *data, size_t , int64_t mediaTimeUs, nsecs_t renderTimeNs,
         void* /*platformPrivate*/, const sp<AMessage>& format) {
     resetFormatIfChanged(format);
     FrameRenderTracker::Info *info = NULL;
@@ -244,14 +240,15 @@
                 buf->stride, buf->height,
                 0, 0, mCropWidth - 1, mCropHeight - 1);
     } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar) {
-        if ((size_t)mWidth * mHeight * 3 / 2 > size) {
-            goto skip_copying;
-        }
         const uint8_t *src_y = (const uint8_t *)data;
         const uint8_t *src_u =
                 (const uint8_t *)data + mWidth * mHeight;
         const uint8_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
 
+        src_y +=mCropLeft + mCropTop * mWidth;
+        src_u +=(mCropLeft + mCropTop * mWidth / 2)/2;
+        src_v +=(mCropLeft + mCropTop * mWidth / 2)/2;
+
         uint8_t *dst_y = (uint8_t *)dst;
         size_t dst_y_size = buf->stride * buf->height;
         size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
@@ -259,6 +256,10 @@
         uint8_t *dst_v = dst_y + dst_y_size;
         uint8_t *dst_u = dst_v + dst_c_size;
 
+        dst_y += mCropTop * buf->stride + mCropLeft;
+        dst_v += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+        dst_u += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+
         for (int y = 0; y < mCropHeight; ++y) {
             memcpy(dst_y, src_y, mCropWidth);
 
@@ -277,12 +278,12 @@
         }
     } else if (mColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
             || mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
-        if ((size_t)mWidth * mHeight * 3 / 2 > size) {
-            goto skip_copying;
-        }
         const uint8_t *src_y = (const uint8_t *)data;
         const uint8_t *src_uv = (const uint8_t *)data
-                + mWidth * (mHeight - mCropTop / 2);
+                + mWidth * mHeight;
+
+        src_y += mCropLeft + mCropTop * mWidth;
+        src_uv += (mCropLeft + mCropTop * mWidth) / 2;
 
         uint8_t *dst_y = (uint8_t *)dst;
 
@@ -292,6 +293,10 @@
         uint8_t *dst_v = dst_y + dst_y_size;
         uint8_t *dst_u = dst_v + dst_c_size;
 
+        dst_y += mCropTop * buf->stride + mCropLeft;
+        dst_v += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+        dst_u += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+
         for (int y = 0; y < mCropHeight; ++y) {
             memcpy(dst_y, src_y, mCropWidth);
 
@@ -311,11 +316,8 @@
             dst_v += dst_c_stride;
         }
     } else if (mColorFormat == OMX_COLOR_Format24bitRGB888) {
-        if ((size_t)mWidth * mHeight * 3 > size) {
-            goto skip_copying;
-        }
-        uint8_t* srcPtr = (uint8_t*)data;
-        uint8_t* dstPtr = (uint8_t*)dst;
+        uint8_t* srcPtr = (uint8_t*)data + mWidth * mCropTop * 3 + mCropLeft * 3;
+        uint8_t* dstPtr = (uint8_t*)dst + buf->stride * mCropTop * 3 + mCropLeft * 3;
 
         for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
             memcpy(dstPtr, srcPtr, mCropWidth * 3);
@@ -323,14 +325,11 @@
             dstPtr += buf->stride * 3;
         }
     } else if (mColorFormat == OMX_COLOR_Format32bitARGB8888) {
-        if ((size_t)mWidth * mHeight * 4 > size) {
-            goto skip_copying;
-        }
         uint8_t *srcPtr, *dstPtr;
 
         for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
-            srcPtr = (uint8_t*)data + mWidth * 4 * y;
-            dstPtr = (uint8_t*)dst + buf->stride * 4 * y;
+            srcPtr = (uint8_t*)data + mWidth * 4 * (y + mCropTop) + mCropLeft * 4;
+            dstPtr = (uint8_t*)dst + buf->stride * 4 * (y + mCropTop) + mCropLeft * 4;
             for (size_t x = 0; x < (size_t)mCropWidth; ++x) {
                 uint8_t a = *srcPtr++;
                 for (size_t i = 0; i < 3; ++i) {   // copy RGB
@@ -340,11 +339,8 @@
             }
         }
     } else if (mColorFormat == OMX_COLOR_Format32BitRGBA8888) {
-        if ((size_t)mWidth * mHeight * 4 > size) {
-            goto skip_copying;
-        }
-        uint8_t* srcPtr = (uint8_t*)data;
-        uint8_t* dstPtr = (uint8_t*)dst;
+        uint8_t* srcPtr = (uint8_t*)data + mWidth * mCropTop * 4 + mCropLeft * 4;
+        uint8_t* dstPtr = (uint8_t*)dst + buf->stride * mCropTop * 4 + mCropLeft * 4;
 
         for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
             memcpy(dstPtr, srcPtr, mCropWidth * 4);
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index e144942..143fd59 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -699,7 +699,7 @@
                     }
 
                     if (mContinuation != NULL) {
-                        CHECK_GT(mContinuationCounter, 0);
+                        CHECK_GT(mContinuationCounter, 0u);
                         if (--mContinuationCounter == 0) {
                             mContinuation->post();
                         }
@@ -1550,7 +1550,7 @@
                     mOrigBandwidthIndex, mCurBandwidthIndex);
         }
     }
-    CHECK_LT(mCurBandwidthIndex, mBandwidthItems.size());
+    CHECK_LT((size_t)mCurBandwidthIndex, mBandwidthItems.size());
     const BandwidthItem &item = mBandwidthItems.itemAt(mCurBandwidthIndex);
 
     uint32_t streamMask = 0; // streams that should be fetched by the new fetcher
diff --git a/media/libstagefright/include/AudioSource.h b/media/libstagefright/include/AudioSource.h
index f20c2cd..07a51bf 100644
--- a/media/libstagefright/include/AudioSource.h
+++ b/media/libstagefright/include/AudioSource.h
@@ -53,6 +53,7 @@
 
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
+    virtual status_t setStopTimeUs(int64_t stopTimeUs);
 
     status_t dataCallback(const AudioRecord::Buffer& buffer);
     virtual void signalBufferReturned(MediaBuffer *buffer);
@@ -85,6 +86,8 @@
 
     bool mTrackMaxAmplitude;
     int64_t mStartTimeUs;
+    int64_t mStopSystemTimeUs;
+    int64_t mLastFrameTimestampUs;
     int16_t mMaxAmplitude;
     int64_t mPrevSampleTimeUs;
     int64_t mInitialReadTimeUs;
diff --git a/media/libstagefright/include/CameraSource.h b/media/libstagefright/include/CameraSource.h
index aa56d27..2aaa884 100644
--- a/media/libstagefright/include/CameraSource.h
+++ b/media/libstagefright/include/CameraSource.h
@@ -98,6 +98,7 @@
     virtual status_t stop() { return reset(); }
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
+    virtual status_t setStopTimeUs(int64_t stopTimeUs);
 
     /**
      * Check whether a CameraSource object is properly initialized.
@@ -253,6 +254,7 @@
     List<int64_t> mFrameTimes;
 
     int64_t mFirstFrameTimeUs;
+    int64_t mStopSystemTimeUs;
     int32_t mNumFramesDropped;
     int32_t mNumGlitches;
     int64_t mGlitchDurationThresholdUs;
diff --git a/media/libstagefright/include/CodecBase.h b/media/libstagefright/include/CodecBase.h
index 845146d..0dd77ba 100644
--- a/media/libstagefright/include/CodecBase.h
+++ b/media/libstagefright/include/CodecBase.h
@@ -40,7 +40,7 @@
 namespace android {
 using namespace media;
 class BufferChannelBase;
-class BufferProducerWrapper;
+struct BufferProducerWrapper;
 class MediaCodecBuffer;
 struct PersistentSurface;
 struct RenderedFrameInfo;
diff --git a/media/libstagefright/include/DataSource.h b/media/libstagefright/include/DataSource.h
index 8f2c7eb..63eccea 100644
--- a/media/libstagefright/include/DataSource.h
+++ b/media/libstagefright/include/DataSource.h
@@ -156,7 +156,7 @@
         if (numBytesRead == -1) { // If readAt() returns -1, there is an error.
             return false;
         }
-        if (numBytesRead < numBytesPerChunk) {
+        if (static_cast<size_t>(numBytesRead) < numBytesPerChunk) {
             // This case is triggered when the stream ends before the whole
             // chunk is read.
             x->appendArray(tmp, (size_t)numBytesRead / sizeof(T));
diff --git a/media/libstagefright/include/MPEG4Writer.h b/media/libstagefright/include/MPEG4Writer.h
index 39e7d01..1c4827f 100644
--- a/media/libstagefright/include/MPEG4Writer.h
+++ b/media/libstagefright/include/MPEG4Writer.h
@@ -29,7 +29,7 @@
 
 namespace android {
 
-class AMessage;
+struct AMessage;
 class MediaBuffer;
 class MetaData;
 
diff --git a/media/libstagefright/include/MediaBuffer.h b/media/libstagefright/include/MediaBuffer.h
index 2c0ebe7..e74410d 100644
--- a/media/libstagefright/include/MediaBuffer.h
+++ b/media/libstagefright/include/MediaBuffer.h
@@ -223,7 +223,7 @@
         // Caution: atomic_int_fast32_t is 64 bits on LP64.
         std::atomic_int_least32_t mFlags;
         std::atomic_int_least32_t mRemoteRefcount;
-        int32_t unused[6]; // additional buffer space
+        int32_t unused[6] __attribute__((__unused__)); // additional buffer space
     };
 
     inline SharedControl *getSharedControl() const {
diff --git a/media/libstagefright/include/MediaCodecSource.h b/media/libstagefright/include/MediaCodecSource.h
index 5e99b78..2259d05 100644
--- a/media/libstagefright/include/MediaCodecSource.h
+++ b/media/libstagefright/include/MediaCodecSource.h
@@ -59,6 +59,8 @@
     virtual status_t read(
             MediaBuffer **buffer,
             const ReadOptions *options = NULL);
+    virtual status_t setStopTimeUs(int64_t stopTimeUs);
+
 
     // MediaBufferObserver
     virtual void signalBufferReturned(MediaBuffer *buffer);
@@ -66,11 +68,7 @@
     // for AHandlerReflector
     void onMessageReceived(const sp<AMessage> &msg);
 
-    // Set GraphicBufferSource stop time. GraphicBufferSource will stop
-    // after receiving a buffer with timestamp larger or equal than stopTimeUs.
-    // All the buffers with timestamp larger or equal to stopTimeUs will be
-    // discarded. stopTimeUs uses SYSTEM_TIME_MONOTONIC time base.
-    status_t setStopStimeUs(int64_t stopTimeUs);
+
 
 protected:
     virtual ~MediaCodecSource();
@@ -85,7 +83,7 @@
         kWhatStop,
         kWhatPause,
         kWhatSetInputBufferTimeOffset,
-        kWhatSetStopTimeOffset,
+        kWhatSetStopTimeUs,
         kWhatGetFirstSampleSystemTimeUs,
         kWhatStopStalled,
     };
diff --git a/media/libstagefright/include/MediaExtractor.h b/media/libstagefright/include/MediaExtractor.h
index 073391f..a856b2b 100644
--- a/media/libstagefright/include/MediaExtractor.h
+++ b/media/libstagefright/include/MediaExtractor.h
@@ -70,7 +70,7 @@
     }
     virtual void setUID(uid_t /*uid*/) {
     }
-    virtual status_t setMediaCas(const sp<ICas> &cas) override {
+    virtual status_t setMediaCas(const sp<ICas>& /*cas*/) override {
         return INVALID_OPERATION;
     }
 
diff --git a/media/libstagefright/include/MediaSource.h b/media/libstagefright/include/MediaSource.h
index 1bd3ed0..14adb05 100644
--- a/media/libstagefright/include/MediaSource.h
+++ b/media/libstagefright/include/MediaSource.h
@@ -75,6 +75,23 @@
         return ERROR_UNSUPPORTED;
     }
 
+    // The consumer of this media source requests the source stops sending
+    // buffers with timestamp larger than or equal to stopTimeUs. stopTimeUs
+    // must be in the same time base as the startTime passed in start(). If
+    // source does not support this request, ERROR_UNSUPPORTED will be returned.
+    // If stopTimeUs is invalid, BAD_VALUE will be returned. This could be
+    // called at any time even before source starts and it could be called
+    // multiple times. Setting stopTimeUs to be -1 will effectively cancel the stopTimeUs
+    // set previously. If stopTimeUs is larger than or equal to last buffer's timestamp,
+    // source will start to drop buffer when it gets a buffer with timestamp larger
+    // than or equal to stopTimeUs. If stopTimeUs is smaller than or equal to last
+    // buffer's timestamp, source will drop all the incoming buffers immediately.
+    // After setting stopTimeUs, source may still stop sending buffers with timestamp
+    // less than stopTimeUs if it is stopped by the consumer.
+    virtual status_t setStopTimeUs(int64_t /* stopTimeUs */) {
+        return ERROR_UNSUPPORTED;
+    }
+
 protected:
     virtual ~MediaSource();
 
diff --git a/media/libstagefright/include/MediaWriter.h b/media/libstagefright/include/MediaWriter.h
index 9c30ffa..cd4af4d 100644
--- a/media/libstagefright/include/MediaWriter.h
+++ b/media/libstagefright/include/MediaWriter.h
@@ -50,7 +50,7 @@
 
     virtual void setStartTimeOffsetMs(int /*ms*/) {}
     virtual int32_t getStartTimeOffsetMs() const { return 0; }
-    virtual status_t setNextFd(int fd) { return INVALID_OPERATION; }
+    virtual status_t setNextFd(int /*fd*/) { return INVALID_OPERATION; }
 
 protected:
     virtual ~MediaWriter() {}
diff --git a/media/libstagefright/include/OMXClient.h b/media/libstagefright/include/OMXClient.h
index 315f19b..203a181 100644
--- a/media/libstagefright/include/OMXClient.h
+++ b/media/libstagefright/include/OMXClient.h
@@ -26,7 +26,9 @@
 public:
     OMXClient();
 
-    status_t connect(bool* trebleFlag = nullptr);
+    status_t connect();
+    status_t connect(bool* trebleFlag);
+
     status_t connectLegacy();
     status_t connectTreble();
     void disconnect();
diff --git a/media/libstagefright/include/SurfaceMediaSource.h b/media/libstagefright/include/SurfaceMediaSource.h
index ae19a75..d38c337 100644
--- a/media/libstagefright/include/SurfaceMediaSource.h
+++ b/media/libstagefright/include/SurfaceMediaSource.h
@@ -106,8 +106,8 @@
     void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
 
     // dump our state in a String
-    void dump(String8& result) const;
-    void dump(String8& result, const char* prefix, char* buffer,
+    void dumpState(String8& result) const;
+    void dumpState(String8& result, const char* prefix, char* buffer,
                                                     size_t SIZE) const;
 
     // metaDataStoredInVideoBuffers tells the encoder what kind of metadata
diff --git a/media/libstagefright/include/foundation/ADebug.h b/media/libstagefright/include/foundation/ADebug.h
index 9ad45f3..b498c91 100644
--- a/media/libstagefright/include/foundation/ADebug.h
+++ b/media/libstagefright/include/foundation/ADebug.h
@@ -53,6 +53,9 @@
 #define LITERAL_TO_STRING_INTERNAL(x)    #x
 #define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
 
+#ifdef CHECK
+#undef CHECK
+#endif
 #define CHECK(condition)                                \
     LOG_ALWAYS_FATAL_IF(                                \
             !(condition),                               \
@@ -92,6 +95,15 @@
         }                                                               \
     } while (false)
 
+#ifdef CHECK_EQ
+#undef CHECK_EQ
+#undef CHECK_NE
+#undef CHECK_LE
+#undef CHECK_LT
+#undef CHECK_GE
+#undef CHECK_GT
+#endif
+
 #define CHECK_EQ(x,y)   CHECK_OP(x,y,EQ,==)
 #define CHECK_NE(x,y)   CHECK_OP(x,y,NE,!=)
 #define CHECK_LE(x,y)   CHECK_OP(x,y,LE,<=)
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index e876306..d8540f8 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -206,8 +206,10 @@
 
 Return<void> TWGraphicBufferSource::getStopTimeOffsetUs(
         getStopTimeOffsetUs_cb _hidl_cb) {
-    // TODO: Implement this when needed.
-    _hidl_cb(Status::OK, 0);
+    status_t status;
+    int64_t stopTimeOffsetUs;
+    status = mBase->getStopTimeOffsetUs(&stopTimeOffsetUs);
+    _hidl_cb(toStatus(status), stopTimeOffsetUs);
     return Void();
 }
 
diff --git a/media/libstagefright/omx/1.0/WOmxNode.cpp b/media/libstagefright/omx/1.0/WOmxNode.cpp
index 1a61007..26bc4aa 100644
--- a/media/libstagefright/omx/1.0/WOmxNode.cpp
+++ b/media/libstagefright/omx/1.0/WOmxNode.cpp
@@ -50,7 +50,7 @@
     status_t transStatus = toStatusT(mBase->getParameter(
             toRawIndexType(index),
             tParams,
-            [&fnStatus, params, size](
+            [&fnStatus, params](
                     Status status, hidl_vec<uint8_t> const& outParams) {
                 fnStatus = toStatusT(status);
                 std::copy(
@@ -148,7 +148,7 @@
     status_t transStatus = toStatusT(mBase->allocateSecureBuffer(
             portIndex,
             static_cast<uint64_t>(size),
-            [&fnStatus, buffer, buffer_data, native_handle](
+            [&fnStatus, buffer, native_handle](
                     Status status,
                     uint32_t outBuffer,
                     hidl_handle const& outNativeHandle) {
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp
index f2a454f..12237bf 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/BWGraphicBufferSource.cpp
@@ -160,6 +160,11 @@
     return Status::fromStatusT(mBase->setStopTimeUs(stopTimeUs));
 }
 
+::android::binder::Status BWGraphicBufferSource::getStopTimeOffsetUs(
+        int64_t *stopTimeOffsetUs) {
+    return Status::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs));
+}
+
 ::android::binder::Status BWGraphicBufferSource::setColorAspects(
         int32_t aspects) {
     return Status::fromStatusT(mBase->setColorAspects(aspects));
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/BWGraphicBufferSource.h
index 43763c2..92ee7f7 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.h
+++ b/media/libstagefright/omx/BWGraphicBufferSource.h
@@ -53,6 +53,7 @@
             double fps, double captureFps) override;
     Status setStartTimeUs(int64_t startTimeUs) override;
     Status setStopTimeUs(int64_t stopTimeUs) override;
+    Status getStopTimeOffsetUs(int64_t* stopTimeOffsetUs) override;
     Status setColorAspects(int32_t aspects) override;
     Status setTimeOffsetUs(int64_t timeOffsetsUs) override;
     Status signalEndOfInputStream() override;
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 103a3e9..bca0cee 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -264,6 +264,7 @@
     mLastDataspace(HAL_DATASPACE_UNKNOWN),
     mExecuting(false),
     mSuspended(false),
+    mLastFrameTimestampUs(-1),
     mStopTimeUs(-1),
     mLastActionTimeUs(-1ll),
     mSkipFramesBeforeNs(-1ll),
@@ -649,6 +650,7 @@
         }
         ALOGV("buffer submitted [slot=%d, useCount=%ld] acquired=%d",
                 item.mBuffer->getSlot(), item.mBuffer.use_count(), mNumOutstandingAcquires);
+        mLastFrameTimestampUs = itemTimeUs;
     }
 
     return true;
@@ -1216,10 +1218,20 @@
     return OK;
 }
 
+status_t GraphicBufferSource::getStopTimeOffsetUs(int64_t *stopTimeOffsetUs) {
+    ALOGV("getStopTimeOffsetUs");
+    Mutex::Autolock autoLock(mMutex);
+    if (mStopTimeUs == -1) {
+        ALOGW("Fail to return stopTimeOffsetUs as stop time is not set");
+        return INVALID_OPERATION;
+    }
+    *stopTimeOffsetUs = mStopTimeUs - mLastFrameTimestampUs;
+    return OK;
+}
+
 status_t GraphicBufferSource::setTimeLapseConfig(double fps, double captureFps) {
     ALOGV("setTimeLapseConfig: fps=%lg, captureFps=%lg",
             fps, captureFps);
-
     Mutex::Autolock autoLock(mMutex);
 
     if (mExecuting || !(fps > 0) || !(captureFps > 0)) {
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 3df1aa1..29b51a8 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -172,6 +172,13 @@
     // and not submitted to encoder. timeUs uses SYSTEM_TIME_MONOTONIC time base.
     status_t setStopTimeUs(int64_t stopTimeUs);
 
+    // Gets the stop time offset in us. This is the time offset between latest buffer
+    // time and the stopTimeUs. If stop time is not set, INVALID_OPERATION will be returned.
+    // If return is OK, *stopTimeOffsetUs will contain the valid offset. Otherwise,
+    // *stopTimeOffsetUs will not be modified. Positive stopTimeOffsetUs means buffer time
+    // larger than stopTimeUs.
+    status_t getStopTimeOffsetUs(int64_t *stopTimeOffsetUs);
+
     // Sets the desired color aspects, e.g. to be used when producer does not specify a dataspace.
     status_t setColorAspects(int32_t aspectsPacked);
 
@@ -340,6 +347,8 @@
     // regardless of the metadata of those buffers
     bool areWeDiscardingAvailableBuffers_l();
 
+    int64_t mLastFrameTimestampUs;
+
     // Our BufferQueue interfaces. mProducer is passed to the producer through
     // getIGraphicBufferProducer, and mConsumer is used internally to retrieve
     // the buffers queued by the producer.
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 5505aa4..c6c0245 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -33,6 +33,7 @@
 #include "ASessionDescription.h"
 
 #include <ctype.h>
+#include <cutils/properties.h>
 
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -135,7 +136,7 @@
           mCheckPending(false),
           mCheckGeneration(0),
           mCheckTimeoutGeneration(0),
-          mTryTCPInterleaving(false),
+          mTryTCPInterleaving(property_get_bool("rtp.transport.TCP", false)),
           mTryFakeRTCP(false),
           mReceivedFirstRTCPPacket(false),
           mReceivedFirstRTPPacket(false),
diff --git a/media/libstagefright/webm/WebmFrameThread.cpp b/media/libstagefright/webm/WebmFrameThread.cpp
index 77de3c8..71bfbc9 100644
--- a/media/libstagefright/webm/WebmFrameThread.cpp
+++ b/media/libstagefright/webm/WebmFrameThread.cpp
@@ -124,7 +124,7 @@
 
 void WebmFrameSinkThread::writeCluster(List<sp<WebmElement> >& children) {
     // children must contain at least one simpleblock and its timecode
-    CHECK_GE(children.size(), 2);
+    CHECK_GE(children.size(), 2u);
 
     uint64_t size;
     sp<WebmElement> cluster = new WebmMaster(kMkvCluster, children);
@@ -155,7 +155,7 @@
         // flushing the second to last frame before we check its type. A audio frame
         // should precede the aforementioned video key frame in the next sequence, a video
         // frame should be the last frame in the current (to-be-flushed) sequence.
-        CHECK_GE(n, 2);
+        CHECK_GE(n, 2u);
         n -= 2;
     }
 
diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp
index 25d6821..d6c6930 100644
--- a/media/libstagefright/webm/WebmWriter.cpp
+++ b/media/libstagefright/webm/WebmWriter.cpp
@@ -488,7 +488,7 @@
             params->findInt32(kKeyBitRate, &bitRate);
         }
         mEstimatedCuesSize = estimateCuesSize(bitRate);
-        CHECK_GE(mEstimatedCuesSize, 8);
+        CHECK_GE(mEstimatedCuesSize, 8u);
         cues = new EbmlVoid(mEstimatedCuesSize);
     }
 
diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp
index ae507fc..cc412f5 100644
--- a/media/libstagefright/wifi-display/MediaSender.cpp
+++ b/media/libstagefright/wifi-display/MediaSender.cpp
@@ -420,7 +420,7 @@
             CHECK(accessUnit->meta()->findInt32("rangeLength", &rangeLength));
             CHECK(accessUnit->meta()->findMessage("notify", &notify)
                     && notify != NULL);
-            CHECK_GE(accessUnit->size(), rangeLength);
+            CHECK_GE((int32_t)accessUnit->size(), rangeLength);
 
             sp<GraphicBuffer> grbuf(new GraphicBuffer(
                     rangeOffset + rangeLength /* width */, 1 /* height */,
diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h
index 6178f00..194f1ee 100644
--- a/media/libstagefright/wifi-display/rtp/RTPBase.h
+++ b/media/libstagefright/wifi-display/rtp/RTPBase.h
@@ -36,10 +36,8 @@
         TRANSPORT_TCP_INTERLEAVED,
     };
 
-    enum {
-        // Really UDP _payload_ size
-        kMaxUDPPacketSize = 1472,   // 1472 good, 1473 bad on Android@Home
-    };
+    // Really UDP _payload_ size
+    const unsigned int kMaxUDPPacketSize = 1472;   // 1472 good, 1473 bad on Android@Home
 
     static int32_t PickRandomRTPPort();
 };
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
index 83af393..ca9fdd2 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -247,7 +247,7 @@
 
 status_t RTPSender::queueTSPackets(
         const sp<ABuffer> &tsPackets, uint8_t packetType) {
-    CHECK_EQ(0, tsPackets->size() % 188);
+    CHECK_EQ(0u, tsPackets->size() % 188);
 
     int64_t timeUs;
     CHECK(tsPackets->meta()->findInt64("timeUs", &timeUs));
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h
index fefcab7..bedfd01 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.h
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.h
@@ -66,11 +66,9 @@
         kWhatRTCPNotify,
     };
 
-    enum {
-        kMaxNumTSPacketsPerRTPPacket = (kMaxUDPPacketSize - 12) / 188,
-        kMaxHistorySize              = 1024,
-        kSourceID                    = 0xdeadbeef,
-    };
+    const unsigned int kMaxNumTSPacketsPerRTPPacket = (kMaxUDPPacketSize - 12) / 188;
+    const unsigned int kMaxHistorySize              = 1024;
+    const unsigned int kSourceID                    = 0xdeadbeef;
 
     sp<ANetworkSession> mNetSession;
     sp<AMessage> mNotify;
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
index 4c5ad17..865ba94 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -273,8 +273,8 @@
                 CHECK(mFormat->findInt32("profile-idc", &profileIdc));
                 CHECK(mFormat->findInt32("level-idc", &levelIdc));
                 CHECK(mFormat->findInt32("constraint-set", &constraintSet));
-                CHECK_GE(profileIdc, 0u);
-                CHECK_GE(levelIdc, 0u);
+                CHECK_GE(profileIdc, 0);
+                CHECK_GE(levelIdc, 0);
                 data[2] = profileIdc;    // profile_idc
                 data[3] = constraintSet; // constraint_set*
                 data[4] = levelIdc;      // level_idc
@@ -771,7 +771,7 @@
             program_info_length += mProgramInfoDescriptors.itemAt(i)->size();
         }
 
-        CHECK_LT(program_info_length, 0x400);
+        CHECK_LT(program_info_length, 0x400u);
         *ptr++ = 0xf0 | (program_info_length >> 8);
         *ptr++ = (program_info_length & 0xff);
 
@@ -795,7 +795,7 @@
             for (size_t i = 0; i < track->countDescriptors(); ++i) {
                 ES_info_length += track->descriptorAt(i)->size();
             }
-            CHECK_LE(ES_info_length, 0xfff);
+            CHECK_LE(ES_info_length, 0xfffu);
 
             *ptr++ = 0xf0 | (ES_info_length >> 8);
             *ptr++ = (ES_info_length & 0xff);
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 9cda8dc..4695e5d 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -454,7 +454,7 @@
                 sp<ABuffer> data;
                 CHECK(msg->findBuffer("data", &data));
 
-                CHECK_LE(channel, 0xffu);
+                CHECK_LE(channel, 0xff);
                 CHECK_LE(data->size(), 0xffffu);
 
                 int32_t sessionID;
diff --git a/media/mtp/tests/Android.mk b/media/mtp/tests/Android.mk
index ace0d40..884518c 100644
--- a/media/mtp/tests/Android.mk
+++ b/media/mtp/tests/Android.mk
@@ -4,6 +4,7 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_MODULE := mtp_ffs_handle_test
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_MODULE_TAGS := tests
 
diff --git a/media/mtp/tests/AndroidTest.xml b/media/mtp/tests/AndroidTest.xml
new file mode 100644
index 0000000..c1f4753
--- /dev/null
+++ b/media/mtp/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Config for mtp_ffs_handle_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="mtp_ffs_handle_test->/data/local/tmp/mtp_ffs_handle_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="mtp_ffs_handle_test" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/radio/Radio.cpp b/radio/Radio.cpp
index fa39589..9ddd221 100644
--- a/radio/Radio.cpp
+++ b/radio/Radio.cpp
@@ -117,8 +117,8 @@
 
 
 // Radio
-Radio::Radio(radio_handle_t handle, const sp<RadioCallback>& callback)
-    : mHandle(handle), mCallback(callback)
+Radio::Radio(radio_handle_t /*handle*/, const sp<RadioCallback>& callback)
+    : mCallback(callback)
 {
 }
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2c33fc2..c379c89 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -339,7 +339,6 @@
     AUDIO_HARDWARE_MODULE_ID_A2DP,
     AUDIO_HARDWARE_MODULE_ID_USB,
 };
-#define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0])))
 
 AudioHwDevice* AudioFlinger::findSuitableHwDev_l(
         audio_module_handle_t module,
@@ -349,7 +348,7 @@
     // well known modules
     if (module == 0) {
         ALOGW("findSuitableHwDev_l() loading well know audio hw modules");
-        for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
+        for (size_t i = 0; i < arraysize(audio_interfaces); i++) {
             loadHwModule_l(audio_interfaces[i]);
         }
         // then try to find a module supporting the requested device.
@@ -519,7 +518,7 @@
 #ifdef TEE_SINK
         // dump the serially shared record tee sink
         if (mRecordTeeSource != 0) {
-            dumpTee(fd, mRecordTeeSource);
+            dumpTee(fd, mRecordTeeSource, AUDIO_IO_HANDLE_NONE, 'C');
         }
 #endif
 
@@ -3264,7 +3263,7 @@
 }
 
 #ifdef TEE_SINK
-void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id)
+void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix)
 {
     NBAIO_Source *teeSource = source.get();
     if (teeSource != NULL) {
@@ -3326,7 +3325,8 @@
         struct tm tm;
         localtime_r(&tv.tv_sec, &tm);
         strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm);
-        snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d.wav", teeTime, id);
+        snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d_%c.wav", teeTime, id,
+                suffix);
         // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd
         int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR);
         if (teeFd >= 0) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 75b4e4c..2e0bc66 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -25,6 +25,8 @@
 #include <sys/types.h>
 #include <limits.h>
 
+#include <android-base/macros.h>
+
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
 
@@ -71,12 +73,11 @@
 #include <powermanager/IPowerManager.h>
 
 #include <media/nbaio/NBLog.h>
+#include <private/media/AudioEffectShared.h>
 #include <private/media/AudioTrackShared.h>
 
 namespace android {
 
-struct audio_track_cblk_t;
-struct effect_param_cblk_t;
 class AudioMixer;
 class AudioBuffer;
 class AudioResampler;
@@ -298,7 +299,8 @@
                             const sp<MmapStreamCallback>& callback,
                             sp<MmapStreamInterface>& interface);
 private:
-    static const size_t kLogMemorySize = 40 * 1024;
+    // FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
+    static const size_t kLogMemorySize = 400 * 1024;
     sp<MemoryDealer>    mLogMemoryDealer;   // == 0 when NBLog is disabled
     // When a log writer is unregistered, it is done lazily so that media.log can continue to see it
     // for as long as possible.  The memory is only freed when it is needed for another log writer.
@@ -445,8 +447,8 @@
         sp<AudioFlinger>    audioFlinger() const { return mAudioFlinger; }
 
     private:
-                            Client(const Client&);
-                            Client& operator = (const Client&);
+        DISALLOW_COPY_AND_ASSIGN(Client);
+
         const sp<AudioFlinger> mAudioFlinger;
               sp<MemoryDealer> mMemoryDealer;
         const pid_t         mPid;
@@ -466,8 +468,7 @@
                 virtual     void        binderDied(const wp<IBinder>& who);
 
     private:
-                            NotificationClient(const NotificationClient&);
-                            NotificationClient& operator = (const NotificationClient&);
+        DISALLOW_COPY_AND_ASSIGN(NotificationClient);
 
         const sp<AudioFlinger>  mAudioFlinger;
         const pid_t             mPid;
@@ -824,7 +825,7 @@
 
 #ifdef TEE_SINK
     // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file
-    static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id = 0);
+    static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix);
 
     // whether tee sink is enabled by property
     static bool mTeeSinkInputEnabled;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index b4029c7..f1a55f1 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -22,7 +22,6 @@
 #include "Configuration.h"
 #include <utils/Log.h>
 #include <audio_utils/primitives.h>
-#include <private/media/AudioEffectShared.h>
 #include <media/audiohal/EffectHalInterface.h>
 #include <media/audiohal/EffectsFactoryHalInterface.h>
 #include <system/audio_effects/effect_visualizer.h>
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 0755c52..e37529e 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -135,15 +135,14 @@
 
     void             dump(int fd, const Vector<String16>& args);
 
-protected:
+private:
     friend class AudioFlinger;      // for mHandles
     bool                mPinned;
 
     // Maximum time allocated to effect engines to complete the turn off sequence
     static const uint32_t MAX_DISABLE_TIME_MS = 10000;
 
-    EffectModule(const EffectModule&);
-    EffectModule& operator = (const EffectModule&);
+    DISALLOW_COPY_AND_ASSIGN(EffectModule);
 
     status_t start_l();
     status_t stop_l();
@@ -232,10 +231,9 @@
 
     void dumpToBuffer(char* buffer, size_t size);
 
-protected:
+private:
     friend class AudioFlinger;          // for mEffect, mHasControl, mEnabled
-    EffectHandle(const EffectHandle&);
-    EffectHandle& operator =(const EffectHandle&);
+    DISALLOW_COPY_AND_ASSIGN(EffectHandle);
 
     Mutex mLock;                        // protects IEffect method calls
     wp<EffectModule> mEffect;           // pointer to controlled EffectModule
@@ -366,10 +364,9 @@
 
     void dump(int fd, const Vector<String16>& args);
 
-protected:
+private:
     friend class AudioFlinger;  // for mThread, mEffects
-    EffectChain(const EffectChain&);
-    EffectChain& operator =(const EffectChain&);
+    DISALLOW_COPY_AND_ASSIGN(EffectChain);
 
     class SuspendedEffectDesc : public RefBase {
     public:
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index 873a9ad..d063772 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -57,7 +57,7 @@
     return mSQ.poll();
 }
 
-void FastCapture::setLog(NBLog::Writer *logWriter __unused)
+void FastCapture::setNBLogWriter(NBLog::Writer *logWriter __unused)
 {
 }
 
diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h
index e258a4d..c3817c0 100644
--- a/services/audioflinger/FastCapture.h
+++ b/services/audioflinger/FastCapture.h
@@ -39,7 +39,7 @@
 
     // callouts
     virtual const FastThreadState *poll();
-    virtual void setLog(NBLog::Writer *logWriter);
+    virtual void setNBLogWriter(NBLog::Writer *logWriter);
     virtual void onIdle();
     virtual void onExit();
     virtual bool isSubClassCommand(FastThreadState::Command command);
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 103e7f8..c4f1af3 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -41,6 +41,7 @@
 #include <audio_utils/format.h>
 #include <media/AudioMixer.h>
 #include "FastMixer.h"
+#include "TypedLogger.h"
 
 namespace android {
 
@@ -101,10 +102,12 @@
     return mSQ.poll();
 }
 
-void FastMixer::setLog(NBLog::Writer *logWriter)
+void FastMixer::setNBLogWriter(NBLog::Writer *logWriter)
 {
+    // FIXME If mMixer is set or changed prior to this, we don't inform correctly.
+    //       Should cache logWriter and re-apply it at the assignment to mMixer.
     if (mMixer != NULL) {
-        mMixer->setLog(logWriter);
+        mMixer->setNBLogWriter(logWriter);
     }
 }
 
@@ -135,6 +138,7 @@
 
 void FastMixer::onStateChange()
 {
+    LOG_HIST_FLUSH();
     const FastMixerState * const current = (const FastMixerState *) mCurrent;
     const FastMixerState * const previous = (const FastMixerState *) mPrevious;
     FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
@@ -188,6 +192,7 @@
             //       implementation; it would be better to have normal mixer allocate for us
             //       to avoid blocking here and to prevent possible priority inversion
             mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::sMaxFastTracks);
+            // FIXME See the other FIXME at FastMixer::setNBLogWriter()
             const size_t mixerFrameSize = mSinkChannelCount
                     * audio_bytes_per_sample(mMixerBufferFormat);
             mMixerBufferSize = mixerFrameSize * frameCount;
@@ -330,6 +335,7 @@
 
 void FastMixer::onWork()
 {
+    LOG_HIST_TS();
     const FastMixerState * const current = (const FastMixerState *) mCurrent;
     FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
     const FastMixerState::Command command = mCommand;
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index bdfd8a0..930fa8d 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -46,7 +46,7 @@
 
     // callouts
     virtual const FastThreadState *poll();
-    virtual void setLog(NBLog::Writer *logWriter);
+    virtual void setNBLogWriter(NBLog::Writer *logWriter);
     virtual void onIdle();
     virtual void onExit();
     virtual bool isSubClassCommand(FastThreadState::Command command);
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index cf9fce3..85865b7 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -27,6 +27,7 @@
 #include <utils/Trace.h>
 #include "FastThread.h"
 #include "FastThreadDumpState.h"
+#include "TypedLogger.h"
 
 #define FAST_DEFAULT_NS    999999999L   // ~1 sec: default time to sleep
 #define FAST_HOT_IDLE_NS     1000000L   // 1 ms: time to sleep while hot idling
@@ -64,8 +65,8 @@
     /* mMeasuredWarmupTs({0, 0}), */
     mWarmupCycles(0),
     mWarmupConsecutiveInRangeCycles(0),
-    // mDummyLogWriter
-    mLogWriter(&mDummyLogWriter),
+    // mDummyNBLogWriter
+    mNBLogWriter(&mDummyNBLogWriter),
     mTimestampStatus(INVALID_OPERATION),
 
     mCommand(FastThreadState::INITIAL),
@@ -90,6 +91,9 @@
 
 bool FastThread::threadLoop()
 {
+    // LOGT now works even if tlNBLogWriter is nullptr, but we're considering changing that,
+    // so this initialization permits a future change to remove the check for nullptr.
+    tlNBLogWriter = &mDummyNBLogWriter;
     for (;;) {
 
         // either nanosleep, sched_yield, or busy wait
@@ -119,8 +123,9 @@
 
             // As soon as possible of learning of a new dump area, start using it
             mDumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState;
-            mLogWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &mDummyLogWriter;
-            setLog(mLogWriter);
+            mNBLogWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &mDummyNBLogWriter;
+            setNBLogWriter(mNBLogWriter);   // FastMixer informs its AudioMixer, FastCapture ignores
+            tlNBLogWriter = mNBLogWriter;
 
             // We want to always have a valid reference to the previous (non-idle) state.
             // However, the state queue only guarantees access to current and previous states.
@@ -218,7 +223,6 @@
         struct timespec newTs;
         int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
         if (rc == 0) {
-            //mLogWriter->logTimestamp(newTs);
             if (mOldTsValid) {
                 time_t sec = newTs.tv_sec - mOldTs.tv_sec;
                 long nsec = newTs.tv_nsec - mOldTs.tv_nsec;
diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h
index 816b666..2a71414 100644
--- a/services/audioflinger/FastThread.h
+++ b/services/audioflinger/FastThread.h
@@ -41,7 +41,7 @@
     // callouts to subclass in same lexical order as they were in original FastMixer.cpp
     // FIXME need comments
     virtual const FastThreadState *poll() = 0;
-    virtual void setLog(NBLog::Writer *logWriter __unused) { }
+    virtual void setNBLogWriter(NBLog::Writer *logWriter __unused) { }
     virtual void onIdle() = 0;
     virtual void onExit() = 0;
     virtual bool isSubClassCommand(FastThreadState::Command command) = 0;
@@ -81,8 +81,8 @@
     struct timespec mMeasuredWarmupTs;  // how long did it take for warmup to complete
     uint32_t        mWarmupCycles;  // counter of number of loop cycles during warmup phase
     uint32_t        mWarmupConsecutiveInRangeCycles;    // number of consecutive cycles in range
-    NBLog::Writer   mDummyLogWriter;
-    NBLog::Writer*  mLogWriter;
+    NBLog::Writer   mDummyNBLogWriter;
+    NBLog::Writer*  mNBLogWriter;   // always non-nullptr: real NBLog::Writer* or &mDummyNBLogWriter
     status_t        mTimestampStatus;
 
     FastThreadState::Command mCommand;
diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h
index e4fe8ac..2a27dfd 100644
--- a/services/audioflinger/MmapTracks.h
+++ b/services/audioflinger/MmapTracks.h
@@ -41,11 +41,10 @@
      static void        appendDumpHeader(String8& result);
             void        dump(char* buffer, size_t size);
 
-protected:
+private:
     friend class MmapThread;
 
-                MmapTrack(const MmapTrack&);
-                MmapTrack& operator = (const MmapTrack&);
+    DISALLOW_COPY_AND_ASSIGN(MmapTrack);
 
     // AudioBufferProvider interface
     virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index f84ba08..3f1a0c0 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -94,8 +94,7 @@
     friend class DirectOutputThread;
     friend class OffloadThread;
 
-                        Track(const Track&);
-                        Track& operator = (const Track&);
+    DISALLOW_COPY_AND_ASSIGN(Track);
 
     // AudioBufferProvider interface
     virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 72ebc93..3f83ca8 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -65,8 +65,7 @@
 private:
     friend class AudioFlinger;  // for mState
 
-                        RecordTrack(const RecordTrack&);
-                        RecordTrack& operator = (const RecordTrack&);
+    DISALLOW_COPY_AND_ASSIGN(RecordTrack);
 
     // AudioBufferProvider interface
     virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8d018d1..5a8f045 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -100,10 +100,6 @@
     return a < b ? a : b;
 }
 
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#endif
-
 namespace android {
 
 // retry counts for buffer fill timeout
@@ -2182,7 +2178,7 @@
         }
 
         char buffer[256];
-        track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+        track->dump(buffer, arraysize(buffer), false /* active */);
         mLocalLog.log("addTrack_l    (%p) %s", track.get(), buffer + 4); // log for analysis
 
         status = NO_ERROR;
@@ -2212,7 +2208,7 @@
     track->triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
 
     char buffer[256];
-    track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+    track->dump(buffer, arraysize(buffer), false /* active */);
     mLocalLog.log("removeTrack_l (%p) %s", track.get(), buffer + 4); // log for analysis
 
     mTracks.remove(track);
@@ -2930,7 +2926,7 @@
 
 bool AudioFlinger::PlaybackThread::threadLoop()
 {
-    logWriterTLS = mNBLogWriter.get();
+    tlNBLogWriter = mNBLogWriter.get();
 
     Vector< sp<Track> > tracksToRemove;
 
@@ -2957,15 +2953,17 @@
 
     acquireWakeLock();
 
-    // mNBLogWriter->log can only be called while thread mutex mLock is held.
+    // mNBLogWriter logging APIs can only be called by a single thread, typically the
+    // thread associated with this PlaybackThread.
+    // If you want to share the mNBLogWriter with other threads (for example, binder threads)
+    // then all such threads must agree to hold a common mutex before logging.
     // So if you need to log when mutex is unlocked, set logString to a non-NULL string,
     // and then that string will be logged at the next convenient opportunity.
+    // See reference to logString below.
     const char *logString = NULL;
 
     checkSilentMode_l();
-#if 0
-    int z = 0; // used in logFormat example
-#endif
+
     while (!exitPending())
     {
         // Log merge requests are performed during AudioFlinger binder transactions, but
@@ -2982,6 +2980,7 @@
 
             processConfigEvents_l();
 
+            // See comment at declaration of logString for why this is done under mLock
             if (logString != NULL) {
                 mNBLogWriter->logTimestamp();
                 mNBLogWriter->log(logString);
@@ -3385,7 +3384,7 @@
                 removeTrack_l(track);
             } else { // inactive but not terminated
                 char buffer[256];
-                track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+                track->dump(buffer, arraysize(buffer), false /* active */);
                 mLocalLog.log("removeTracks_l(%p) %s", track.get(), buffer + 4);
             }
         }
@@ -4748,7 +4747,7 @@
 
 #ifdef TEE_SINK
     // Write the tee output to a .wav file
-    dumpTee(fd, mTeeSource, mId);
+    dumpTee(fd, mTeeSource, mId, 'M');
 #endif
 
 }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7469710..b352bd3 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -229,8 +229,7 @@
         virtual     void        binderDied(const wp<IBinder>& who);
 
     private:
-                    PMDeathRecipient(const PMDeathRecipient&);
-                    PMDeathRecipient& operator = (const PMDeathRecipient&);
+        DISALLOW_COPY_AND_ASSIGN(PMDeathRecipient);
 
         wp<ThreadBase> mThread;
     };
@@ -895,7 +894,7 @@
 
     friend class AudioFlinger;      // for numerous
 
-    PlaybackThread& operator = (const PlaybackThread&);
+    DISALLOW_COPY_AND_ASSIGN(PlaybackThread);
 
     status_t    addTrack_l(const sp<Track>& track);
     bool        destroyTrack_l(const sp<Track>& track);
@@ -976,7 +975,7 @@
     sp<NBAIO_Source>        mTeeSource;
 #endif
     uint32_t                mScreenState;   // cached copy of gScreenState
-    static const size_t     kFastMixerLogSize = 4 * 1024;
+    static const size_t     kFastMixerLogSize = 8 * 1024;
     sp<NBLog::Writer>       mFastMixerNBLogWriter;
 
 
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index e0c09f7..cb540ca 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -92,8 +92,7 @@
 
 
 protected:
-                        TrackBase(const TrackBase&);
-                        TrackBase& operator = (const TrackBase&);
+    DISALLOW_COPY_AND_ASSIGN(TrackBase);
 
     // AudioBufferProvider interface
     virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 5480513..dcbbe66 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -234,7 +234,7 @@
 AudioFlinger::ThreadBase::TrackBase::~TrackBase()
 {
 #ifdef TEE_SINK
-    dumpTee(-1, mTeeSource, mId);
+    dumpTee(-1, mTeeSource, mId, 'T');
 #endif
     // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
     mServerProxy.clear();
diff --git a/services/audioflinger/TypedLogger.cpp b/services/audioflinger/TypedLogger.cpp
index e08f6f6..57c206b 100644
--- a/services/audioflinger/TypedLogger.cpp
+++ b/services/audioflinger/TypedLogger.cpp
@@ -23,5 +23,5 @@
 #include "TypedLogger.h"
 
 namespace android {
-thread_local NBLog::Writer *logWriterTLS;
+thread_local NBLog::Writer *tlNBLogWriter;
 }
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 0b23c7c..2d84028 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -19,11 +19,84 @@
 #define ANDROID_TYPED_LOGGER_H
 
 #include <media/nbaio/NBLog.h>
-#define LOGT(fmt, ...) logWriterTLS->logFormat(fmt, ##__VA_ARGS__) // TODO: check null pointer
+#include <algorithm>
+
+/*
+Fowler-Noll-Vo (FNV-1a) hash function for the file name.
+Hashes at compile time. FNV-1a iterative function:
+
+hash = offset_basis
+for each byte to be hashed
+        hash = hash xor byte
+        hash = hash * FNV_prime
+return hash
+
+offset_basis and FNV_prime values depend on the size of the hash output
+Following values are defined by FNV and should not be changed arbitrarily
+*/
+
+template<typename T>
+constexpr T offset_basis();
+
+template<typename T>
+constexpr T FNV_prime();
+
+template<>
+constexpr uint32_t offset_basis<uint32_t>() {
+    return 2166136261u;
+}
+
+template<>
+constexpr uint32_t FNV_prime<uint32_t>() {
+    return 16777619u;
+}
+
+template<>
+constexpr uint64_t offset_basis<uint64_t>() {
+    return 14695981039346656037ull;
+}
+
+template<>
+constexpr uint64_t FNV_prime<uint64_t>() {
+    return 1099511628211ull;
+}
+
+template <typename T, size_t n>
+constexpr T fnv1a(const char (&file)[n], int i = n - 1) {
+    return i == -1 ? offset_basis<T>() : (fnv1a<T>(file, i - 1) ^ file[i]) * FNV_prime<T>();
+}
+
+template <size_t n>
+constexpr uint64_t hash(const char (&file)[n], uint32_t line) {
+    // Line numbers over or equal to 2^16 are clamped to 2^16 - 1. This way increases collisions
+    // compared to wrapping around, but is easy to identify because it doesn't produce aliasing.
+    // It's a very unlikely case anyways.
+    return ((fnv1a<uint64_t>(file) << 16) ^ ((fnv1a<uint64_t>(file) >> 32) & 0xFFFF0000)) |
+           std::min(line, 0xFFFFu);
+}
+
+// TODO Permit disabling of logging at compile-time.
+
+// TODO A non-nullptr dummy implementation that is a nop would be faster than checking for nullptr
+//      in the case when logging is enabled at compile-time and enabled at runtime, but it might be
+//      slower than nullptr check when logging is enabled at compile-time and disabled at runtime.
+
+// Write formatted entry to log
+#define LOGT(fmt, ...) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+                                x->logFormat((fmt), hash(__FILE__, __LINE__), ##__VA_ARGS__); } \
+                                while (0)
+
+// Write histogram timestamp entry
+#define LOG_HIST_TS() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+                                x->logHistTS(hash(__FILE__, __LINE__)); } while(0)
+
+// flush all histogram
+#define LOG_HIST_FLUSH() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+                                x->logHistFlush(hash(__FILE__, __LINE__)); } while(0)
 
 namespace android {
 extern "C" {
-extern thread_local NBLog::Writer *logWriterTLS;
+extern thread_local NBLog::Writer *tlNBLogWriter;
 }
 } // namespace android
 
diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk
index f7197af..9e2813e 100644
--- a/services/mediaanalytics/Android.mk
+++ b/services/mediaanalytics/Android.mk
@@ -5,7 +5,12 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    main_mediametrics.cpp          \
+    main_mediametrics.cpp              \
+    MetricsSummarizerCodec.cpp         \
+    MetricsSummarizerExtractor.cpp     \
+    MetricsSummarizerPlayer.cpp        \
+    MetricsSummarizerRecorder.cpp      \
+    MetricsSummarizer.cpp              \
     MediaAnalyticsService.cpp
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 35c1f5b..876c685 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -20,6 +20,7 @@
 #define LOG_TAG "MediaAnalyticsService"
 #include <utils/Log.h>
 
+#include <stdint.h>
 #include <inttypes.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -70,11 +71,28 @@
 
 #include "MediaAnalyticsService.h"
 
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerCodec.h"
+#include "MetricsSummarizerExtractor.h"
+#include "MetricsSummarizerPlayer.h"
+#include "MetricsSummarizerRecorder.h"
+
 
 namespace android {
 
 
-#define DEBUG_QUEUE     0
+
+// summarized records
+// up to 48 sets, each covering an hour -- at least 2 days of coverage
+// (will be longer if there are hours without any media action)
+static const nsecs_t kNewSetIntervalNs = 3600*(1000*1000*1000ll);
+static const int kMaxRecordSets = 48;
+// individual records kept in memory
+static const int kMaxRecords    = 100;
+
+
+static const char *kServiceName = "media.metrics";
+
 
 //using android::status_t;
 //using android::OK;
@@ -85,18 +103,67 @@
 
 void MediaAnalyticsService::instantiate() {
     defaultServiceManager()->addService(
-            String16("media.metrics"), new MediaAnalyticsService());
+            String16(kServiceName), new MediaAnalyticsService());
 }
 
-// XXX: add dynamic controls for mMaxRecords
+// handle sets of summarizers
+MediaAnalyticsService::SummarizerSet::SummarizerSet() {
+    mSummarizers = new List<MetricsSummarizer *>();
+}
+MediaAnalyticsService::SummarizerSet::~SummarizerSet() {
+    // empty the list
+    List<MetricsSummarizer *> *l = mSummarizers;
+    while (l->size() > 0) {
+        MetricsSummarizer *summarizer = *(l->begin());
+        l->erase(l->begin());
+        delete summarizer;
+    }
+}
+
+void MediaAnalyticsService::newSummarizerSet() {
+    ALOGD("MediaAnalyticsService::newSummarizerSet");
+    MediaAnalyticsService::SummarizerSet *set = new MediaAnalyticsService::SummarizerSet();
+    nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+    set->setStarted(now);
+
+    set->appendSummarizer(new MetricsSummarizerExtractor("extractor"));
+    set->appendSummarizer(new MetricsSummarizerCodec("codec"));
+    set->appendSummarizer(new MetricsSummarizerPlayer("nuplayer"));
+    set->appendSummarizer(new MetricsSummarizerRecorder("recorder"));
+
+    // ALWAYS at the end, since it catches everything
+    set->appendSummarizer(new MetricsSummarizer(NULL));
+
+    // inject this set at the BACK of the list.
+    mSummarizerSets->push_back(set);
+    mCurrentSet = set;
+
+    // limit the # that we have
+    if (mMaxRecordSets > 0) {
+        List<SummarizerSet *> *l = mSummarizerSets;
+        while (l->size() > (size_t) mMaxRecordSets) {
+            ALOGD("Deleting oldest record set....");
+            MediaAnalyticsService::SummarizerSet *oset = *(l->begin());
+            l->erase(l->begin());
+            delete oset;
+            mSetsDiscarded++;
+        }
+    }
+}
+
 MediaAnalyticsService::MediaAnalyticsService()
-        : mMaxRecords(100) {
+        : mMaxRecords(kMaxRecords),
+          mMaxRecordSets(kMaxRecordSets),
+          mNewSetInterval(kNewSetIntervalNs) {
 
     ALOGD("MediaAnalyticsService created");
     // clear our queues
     mOpen = new List<MediaAnalyticsItem *>();
     mFinalized = new List<MediaAnalyticsItem *>();
 
+    mSummarizerSets = new List<MediaAnalyticsService::SummarizerSet *>();
+    newSummarizerSet();
+
     mItemsSubmitted = 0;
     mItemsFinalized = 0;
     mItemsDiscarded = 0;
@@ -109,7 +176,13 @@
 MediaAnalyticsService::~MediaAnalyticsService() {
         ALOGD("MediaAnalyticsService destroyed");
 
-    // XXX: clean out mOpen and mFinalized
+    // clean out mOpen and mFinalized
+    delete mOpen;
+    mOpen = NULL;
+    delete mFinalized;
+    mFinalized = NULL;
+
+    // XXX: clean out the summaries
 }
 
 
@@ -145,7 +218,7 @@
         case AID_MEDIA_EX:
         case AID_MEDIA_DRM:
             // trusted source, only override default values
-            isTrusted = true;
+                isTrusted = true;
             if (uid_given == (-1)) {
                 item->setUid(uid);
             }
@@ -197,10 +270,12 @@
                 oitem = NULL;
             } else {
                 oitem->setFinalized(true);
+                summarize(oitem);
                 saveItem(mFinalized, oitem, 0);
             }
             // new record could itself be marked finalized...
             if (finalizing) {
+                summarize(item);
                 saveItem(mFinalized, item, 0);
                 mItemsFinalized++;
             } else {
@@ -211,6 +286,7 @@
             // combine the records, send it to finalized if appropriate
             oitem->merge(item);
             if (finalizing) {
+                summarize(oitem);
                 saveItem(mFinalized, oitem, 0);
                 mItemsFinalized++;
             }
@@ -229,6 +305,7 @@
                 delete item;
                 item = NULL;
             } else {
+                summarize(item);
                 saveItem(mFinalized, item, 0);
                 mItemsFinalized++;
             }
@@ -239,26 +316,6 @@
     return id;
 }
 
-List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool finished, nsecs_t ts) {
-    // this might never get called; the binder interface maps to the full parm list
-    // on the client side before making the binder call.
-    // but this lets us be sure...
-    List<MediaAnalyticsItem*> *list;
-    list = getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
-    return list;
-}
-
-List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool , nsecs_t , MediaAnalyticsItem::Key ) {
-
-    // XXX: implement the get-item-list semantics
-
-    List<MediaAnalyticsItem *> *list = NULL;
-    // set up our query on the persistent data
-    // slurp in all of the pieces
-    // return that
-    return list;
-}
-
 status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 512;
@@ -277,15 +334,21 @@
 
     // crack any parameters
     bool clear = false;
+    bool summary = false;
     nsecs_t ts_since = 0;
+    String16 summaryOption("-summary");
     String16 clearOption("-clear");
     String16 sinceOption("-since");
     String16 helpOption("-help");
+    String16 onlyOption("-only");
+    const char *only = NULL;
     int n = args.size();
     for (int i = 0; i < n; i++) {
         String8 myarg(args[i]);
         if (args[i] == clearOption) {
             clear = true;
+        } else if (args[i] == summaryOption) {
+            summary = true;
         } else if (args[i] == sinceOption) {
             i++;
             if (i < n) {
@@ -301,12 +364,27 @@
             }
             // command line is milliseconds; internal units are nano-seconds
             ts_since *= 1000*1000;
+        } else if (args[i] == onlyOption) {
+            i++;
+            if (i < n) {
+                String8 value(args[i]);
+                const char *p = value.string();
+                char *q = strdup(p);
+                if (q != NULL) {
+                    if (only != NULL) {
+                        free((void*)only);
+                    }
+                only = q;
+                }
+            }
         } else if (args[i] == helpOption) {
             result.append("Recognized parameters:\n");
             result.append("-help        this help message\n");
+            result.append("-summary     show summary info\n");
             result.append("-clear       clears out saved records\n");
-            result.append("-since XXX   include records since XXX\n");
-            result.append("             (XXX is milliseconds since the UNIX epoch)\n");
+            result.append("-only X      process records for component X\n");
+            result.append("-since X     include records since X\n");
+            result.append("             (X is milliseconds since the UNIX epoch)\n");
             write(fd, result.string(), result.size());
             return NO_ERROR;
         }
@@ -314,9 +392,42 @@
 
     Mutex::Autolock _l(mLock);
 
-    snprintf(buffer, SIZE, "Dump of the mediametrics process:\n");
+    // we ALWAYS dump this piece
+    snprintf(buffer, SIZE, "Dump of the %s process:\n", kServiceName);
     result.append(buffer);
 
+    dumpHeaders(result, ts_since);
+
+    // only want 1, to avoid confusing folks that parse the output
+    if (summary) {
+        dumpSummaries(result, ts_since, only);
+    } else {
+        dumpRecent(result, ts_since, only);
+    }
+
+
+    if (clear) {
+        // remove everything from the finalized queue
+        while (mFinalized->size() > 0) {
+            MediaAnalyticsItem * oitem = *(mFinalized->begin());
+            mFinalized->erase(mFinalized->begin());
+            delete oitem;
+            mItemsDiscarded++;
+        }
+
+        // shall we clear the summary data too?
+
+    }
+
+    write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+// dump headers
+void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since) {
+    const size_t SIZE = 512;
+    char buffer[SIZE];
+
     int enabled = MediaAnalyticsItem::isEnabled();
     if (enabled) {
         snprintf(buffer, SIZE, "Metrics gathering: enabled\n");
@@ -331,50 +442,71 @@
         " Discarded: %" PRId64 "\n",
         mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
     result.append(buffer);
+    snprintf(buffer, SIZE,
+        "Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded);
+    result.append(buffer);
     if (ts_since != 0) {
         snprintf(buffer, SIZE,
             "Dumping Queue entries more recent than: %" PRId64 "\n",
             (int64_t) ts_since);
         result.append(buffer);
     }
+}
+
+// dump summary info
+void MediaAnalyticsService::dumpSummaries(String8 &result, nsecs_t ts_since, const char *only) {
+    const size_t SIZE = 512;
+    char buffer[SIZE];
+    int slot = 0;
+
+    snprintf(buffer, SIZE, "\nSummarized Metrics:\n");
+    result.append(buffer);
+
+    // have each of the distillers dump records
+    if (mSummarizerSets != NULL) {
+        List<SummarizerSet *>::iterator itSet = mSummarizerSets->begin();
+        for (; itSet != mSummarizerSets->end(); itSet++) {
+            nsecs_t when = (*itSet)->getStarted();
+            if (when < ts_since) {
+                continue;
+            }
+            List<MetricsSummarizer *> *list = (*itSet)->getSummarizers();
+            List<MetricsSummarizer *>::iterator it = list->begin();
+            for (; it != list->end(); it++) {
+                if (only != NULL && strcmp(only, (*it)->getKey()) != 0) {
+                    ALOGV("Told to omit '%s'", (*it)->getKey());
+                }
+                AString distilled = (*it)->dumpSummary(slot, only);
+                result.append(distilled.c_str());
+            }
+        }
+    }
+}
+
+// the recent, detailed queues
+void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only) {
+    const size_t SIZE = 512;
+    char buffer[SIZE];
 
     // show the recently recorded records
     snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
     result.append(buffer);
-    result.append(this->dumpQueue(mFinalized, ts_since));
+    result.append(this->dumpQueue(mFinalized, ts_since, only));
 
     snprintf(buffer, sizeof(buffer), "\nIn-Progress Metrics (newest first):\n");
     result.append(buffer);
-    result.append(this->dumpQueue(mOpen, ts_since));
+    result.append(this->dumpQueue(mOpen, ts_since, only));
 
     // show who is connected and injecting records?
     // talk about # records fed to the 'readers'
     // talk about # records we discarded, perhaps "discarded w/o reading" too
-
-    if (clear) {
-        // remove everything from the finalized queue
-        while (mFinalized->size() > 0) {
-            MediaAnalyticsItem * oitem = *(mFinalized->begin());
-            if (DEBUG_QUEUE) {
-                ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
-                    oitem->getKey().c_str(), oitem->getSessionID(),
-                    oitem->getTimestamp());
-            }
-            mFinalized->erase(mFinalized->begin());
-            mItemsDiscarded++;
-        }
-    }
-
-    write(fd, result.string(), result.size());
-    return NO_ERROR;
 }
-
 // caller has locked mLock...
 String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList) {
-    return dumpQueue(theList, (nsecs_t) 0);
+    return dumpQueue(theList, (nsecs_t) 0, NULL);
 }
 
-String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since) {
+String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since, const char * only) {
     String8 result;
     int slot = 0;
 
@@ -387,6 +519,11 @@
             if (when < ts_since) {
                 continue;
             }
+            if (only != NULL &&
+                strcmp(only, (*it)->getKey().c_str()) != 0) {
+                ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only);
+                continue;
+            }
             AString entry = (*it)->toString();
             result.appendFormat("%5d: %s\n", slot, entry.c_str());
             slot++;
@@ -405,13 +542,6 @@
 
     Mutex::Autolock _l(mLock);
 
-    if (DEBUG_QUEUE) {
-        ALOGD("Inject a record: session %" PRId64 " ts %" PRId64 "",
-            item->getSessionID(), item->getTimestamp());
-        String8 before = dumpQueue(l);
-        ALOGD("Q before insert: %s", before.string());
-    }
-
     // adding at back of queue (fifo order)
     if (front)  {
         l->push_front(item);
@@ -419,30 +549,15 @@
         l->push_back(item);
     }
 
-    if (DEBUG_QUEUE) {
-        String8 after = dumpQueue(l);
-        ALOGD("Q after insert: %s", after.string());
-    }
-
     // keep removing old records the front until we're in-bounds
     if (mMaxRecords > 0) {
         while (l->size() > (size_t) mMaxRecords) {
             MediaAnalyticsItem * oitem = *(l->begin());
-            if (DEBUG_QUEUE) {
-                ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
-                    oitem->getKey().c_str(), oitem->getSessionID(),
-                    oitem->getTimestamp());
-            }
             l->erase(l->begin());
             delete oitem;
             mItemsDiscarded++;
         }
     }
-
-    if (DEBUG_QUEUE) {
-        String8 after = dumpQueue(l);
-        ALOGD("Q after cleanup: %s", after.string());
-    }
 }
 
 // are they alike enough that nitem can be folded into oitem?
@@ -515,29 +630,14 @@
 
     Mutex::Autolock _l(mLock);
 
-    if(DEBUG_QUEUE) {
-        String8 before = dumpQueue(l);
-        ALOGD("Q before delete: %s", before.string());
-    }
-
     for (List<MediaAnalyticsItem *>::iterator it = l->begin();
         it != l->end(); it++) {
         if ((*it)->getSessionID() != item->getSessionID())
             continue;
-
-        if (DEBUG_QUEUE) {
-            ALOGD(" --- removing record for SessionID %" PRId64 "", item->getSessionID());
-            ALOGD("drop record at %s:%d", __FILE__, __LINE__);
-        }
         delete *it;
         l->erase(it);
         break;
     }
-
-    if (DEBUG_QUEUE) {
-        String8 after = dumpQueue(l);
-        ALOGD("Q after delete: %s", after.string());
-    }
 }
 
 static AString allowedKeys[] =
@@ -579,5 +679,43 @@
     return false;
 }
 
+// insert into the appropriate summarizer.
+// we make our own copy to save/summarize
+void MediaAnalyticsService::summarize(MediaAnalyticsItem *item) {
+
+    ALOGV("MediaAnalyticsService::summarize()");
+
+    if (item == NULL) {
+        return;
+    }
+
+    nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+    if (mCurrentSet == NULL
+        || (mCurrentSet->getStarted() + mNewSetInterval < now)) {
+        newSummarizerSet();
+    }
+
+    if (mCurrentSet == NULL) {
+        return;
+    }
+
+    List<MetricsSummarizer *> *summarizers = mCurrentSet->getSummarizers();
+    List<MetricsSummarizer *>::iterator it = summarizers->begin();
+    for (; it != summarizers->end(); it++) {
+        if ((*it)->isMine(*item)) {
+            break;
+        }
+    }
+    if (it == summarizers->end()) {
+        ALOGD("no handler for type %s", item->getKey().c_str());
+        return;               // no handler
+    }
+
+    // invoke the summarizer. summarizer will make whatever copies
+    // it wants; the caller retains ownership of item.
+
+    (*it)->handleRecord(item);
+
+}
 
 } // namespace android
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index d2b0f09..6685967 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -28,6 +28,8 @@
 
 #include <media/IMediaAnalyticsService.h>
 
+#include "MetricsSummarizer.h"
+
 
 namespace android {
 
@@ -39,12 +41,6 @@
     // on this side, caller surrenders ownership
     virtual int64_t submit(MediaAnalyticsItem *item, bool forcenew);
 
-    virtual List<MediaAnalyticsItem *>
-            *getMediaAnalyticsItemList(bool finished, int64_t ts);
-    virtual List<MediaAnalyticsItem *>
-            *getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key);
-
-
     static  void            instantiate();
     virtual status_t        dump(int fd, const Vector<String16>& args);
 
@@ -58,6 +54,7 @@
     int64_t mItemsSubmitted;
     int64_t mItemsFinalized;
     int64_t mItemsDiscarded;
+    int64_t mSetsDiscarded;
     MediaAnalyticsItem::SessionID_t mLastSessionID;
 
     // partitioned a bit so we don't over serialize
@@ -67,6 +64,10 @@
     // the most we hold in memory
     // up to this many in each queue (open, finalized)
     int32_t mMaxRecords;
+    // # of sets of summaries
+    int32_t mMaxRecordSets;
+    // nsecs until we start a new record set
+    nsecs_t mNewSetInterval;
 
     // input validation after arrival from client
     bool contentValid(MediaAnalyticsItem *item, bool isTrusted);
@@ -82,12 +83,47 @@
     MediaAnalyticsItem *findItem(List<MediaAnalyticsItem *> *,
                                      MediaAnalyticsItem *, bool removeit);
 
+    // summarizers
+    void summarize(MediaAnalyticsItem *item);
+    class SummarizerSet {
+        nsecs_t mStarted;
+        List<MetricsSummarizer *> *mSummarizers;
+
+      public:
+        void appendSummarizer(MetricsSummarizer *s) {
+            if (s) {
+                mSummarizers->push_back(s);
+            }
+        };
+        nsecs_t getStarted() { return mStarted;}
+        void setStarted(nsecs_t started) {mStarted = started;}
+        List<MetricsSummarizer *> *getSummarizers() { return mSummarizers;}
+
+        SummarizerSet();
+        ~SummarizerSet();
+    };
+    void newSummarizerSet();
+    List<SummarizerSet *> *mSummarizerSets;
+    SummarizerSet *mCurrentSet;
+    List<MetricsSummarizer *> *getFirstSet() {
+        List<SummarizerSet *>::iterator first = mSummarizerSets->begin();
+        if (first != mSummarizerSets->end()) {
+            return (*first)->getSummarizers();
+        }
+        return NULL;
+    }
+
     void saveItem(MediaAnalyticsItem);
     void saveItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *, int);
     void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
 
+    // support for generating output
     String8 dumpQueue(List<MediaAnalyticsItem*> *);
-    String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t);
+    String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
+
+    void dumpHeaders(String8 &result, nsecs_t ts_since);
+    void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);
+    void dumpRecent(String8 &result, nsecs_t ts_since, const char * only);
 
 };
 
diff --git a/services/mediaanalytics/MetricsSummarizer.cpp b/services/mediaanalytics/MetricsSummarizer.cpp
new file mode 100644
index 0000000..3477f1f
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizer.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "MetricsSummarizer"
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+#define DEBUG_SORT      0
+#define DEBUG_QUEUE     0
+
+
+MetricsSummarizer::MetricsSummarizer(const char *key)
+    : mIgnorables(NULL)
+{
+    ALOGV("MetricsSummarizer::MetricsSummarizer");
+
+    if (key == NULL) {
+        mKey = key;
+    } else {
+        mKey = strdup(key);
+    }
+
+    mSummaries = new List<MediaAnalyticsItem *>();
+}
+
+MetricsSummarizer::~MetricsSummarizer()
+{
+    ALOGV("MetricsSummarizer::~MetricsSummarizer");
+    if (mKey) {
+        free((void *)mKey);
+        mKey = NULL;
+    }
+
+    // clear the list of items we have saved
+    while (mSummaries->size() > 0) {
+        MediaAnalyticsItem * oitem = *(mSummaries->begin());
+        if (DEBUG_QUEUE) {
+            ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
+                oitem->getKey().c_str(), oitem->getSessionID(),
+                oitem->getTimestamp());
+        }
+        mSummaries->erase(mSummaries->begin());
+        delete oitem;
+    }
+}
+
+// so we know what summarizer we were using
+const char *MetricsSummarizer::getKey() {
+    const char *value = mKey;
+    if (value == NULL) {
+        value = "unknown";
+    }
+    return value;
+}
+
+// should the record be given to this summarizer
+bool MetricsSummarizer::isMine(MediaAnalyticsItem &item)
+{
+    if (mKey == NULL)
+        return true;
+    AString itemKey = item.getKey();
+    if (strcmp(mKey, itemKey.c_str()) != 0) {
+        return false;
+    }
+    return true;
+}
+
+AString MetricsSummarizer::dumpSummary(int &slot)
+{
+    return dumpSummary(slot, NULL);
+}
+
+AString MetricsSummarizer::dumpSummary(int &slot, const char *only)
+{
+    AString value = "";
+
+    List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
+    if (it != mSummaries->end()) {
+        char buf[16];   // enough for "#####: "
+        for (; it != mSummaries->end(); it++) {
+            if (only != NULL && strcmp(only, (*it)->getKey().c_str()) != 0) {
+                continue;
+            }
+            AString entry = (*it)->toString();
+            snprintf(buf, sizeof(buf), "%5d: ", slot);
+            value.append(buf);
+            value.append(entry.c_str());
+            value.append("\n");
+            slot++;
+        }
+    }
+    return value;
+}
+
+void MetricsSummarizer::setIgnorables(const char **ignorables) {
+    mIgnorables = ignorables;
+}
+
+const char **MetricsSummarizer::getIgnorables() {
+    return mIgnorables;
+}
+
+void MetricsSummarizer::handleRecord(MediaAnalyticsItem *item) {
+
+    ALOGV("MetricsSummarizer::handleRecord() for %s",
+                item == NULL ? "<nothing>" : item->toString().c_str());
+
+    if (item == NULL) {
+        return;
+    }
+
+    List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
+    for (; it != mSummaries->end(); it++) {
+        bool good = sameAttributes((*it), item, getIgnorables());
+        ALOGV("Match against %s says %d",
+              (*it)->toString().c_str(), good);
+        if (good)
+            break;
+    }
+    if (it == mSummaries->end()) {
+            ALOGV("save new record");
+            item = item->dup();
+            if (item == NULL) {
+                ALOGE("unable to save MediaMetrics record");
+            }
+            sortProps(item);
+            item->setInt32("count",1);
+            mSummaries->push_back(item);
+    } else {
+            ALOGV("increment existing record");
+            (*it)->addInt32("count",1);
+            mergeRecord(*(*it), *item);
+    }
+}
+
+void MetricsSummarizer::mergeRecord(MediaAnalyticsItem &/*have*/, MediaAnalyticsItem &/*item*/) {
+    // default is no further massaging.
+    ALOGV("MetricsSummarizer::mergeRecord() [default]");
+    return;
+}
+
+
+//
+// Comparators
+//
+
+// testing that all of 'single' is in 'summ'
+// and that the values match.
+// 'summ' may have extra fields.
+// 'ignorable' is a set of things that we don't worry about matching up
+// (usually time- or count-based values we'll sum elsewhere)
+bool MetricsSummarizer::sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
+
+    if (single == NULL || summ == NULL) {
+        return false;
+    }
+    ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str());
+    ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str());
+
+    // this can be made better.
+    for(size_t i=0;i<single->mPropCount;i++) {
+        MediaAnalyticsItem::Prop *prop1 = &(single->mProps[i]);
+        const char *attrName = prop1->mName;
+        ALOGV("compare on attr '%s'", attrName);
+
+        // is it something we should ignore
+        if (ignorable != NULL) {
+            const char **ig = ignorable;
+            while (*ig) {
+                if (strcmp(*ig, attrName) == 0) {
+                    break;
+                }
+                ig++;
+            }
+            if (*ig) {
+                ALOGV("we don't mind that it has attr '%s'", attrName);
+                continue;
+            }
+        }
+
+        MediaAnalyticsItem::Prop *prop2 = summ->findProp(attrName);
+        if (prop2 == NULL) {
+            ALOGV("summ doesn't have this attr");
+            return false;
+        }
+        if (prop1->mType != prop2->mType) {
+            ALOGV("mismatched attr types");
+            return false;
+        }
+        switch (prop1->mType) {
+            case MediaAnalyticsItem::kTypeInt32:
+                if (prop1->u.int32Value != prop2->u.int32Value)
+                    return false;
+                break;
+            case MediaAnalyticsItem::kTypeInt64:
+                if (prop1->u.int64Value != prop2->u.int64Value)
+                    return false;
+                break;
+            case MediaAnalyticsItem::kTypeDouble:
+                // XXX: watch out for floating point comparisons!
+                if (prop1->u.doubleValue != prop2->u.doubleValue)
+                    return false;
+                break;
+            case MediaAnalyticsItem::kTypeCString:
+                if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0)
+                    return false;
+                break;
+            case MediaAnalyticsItem::kTypeRate:
+                if (prop1->u.rate.count != prop2->u.rate.count)
+                    return false;
+                if (prop1->u.rate.duration != prop2->u.rate.duration)
+                    return false;
+                break;
+            default:
+                return false;
+        }
+    }
+
+    return true;
+}
+
+bool MetricsSummarizer::sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
+
+    // verify same user
+    if (summ->mPid != single->mPid)
+        return false;
+
+    // and finally do the more expensive validation of the attributes
+    return sameAttributes(summ, single, ignorable);
+}
+
+int MetricsSummarizer::PropSorter(const void *a, const void *b) {
+    MediaAnalyticsItem::Prop *ai = (MediaAnalyticsItem::Prop *)a;
+    MediaAnalyticsItem::Prop *bi = (MediaAnalyticsItem::Prop *)b;
+    return strcmp(ai->mName, bi->mName);
+}
+
+// we sort in the summaries so that it looks pretty in the dumpsys
+void MetricsSummarizer::sortProps(MediaAnalyticsItem *item) {
+    if (item->mPropCount != 0) {
+        if (DEBUG_SORT) {
+            ALOGD("sortProps(pre): %s", item->toString().c_str());
+        }
+        qsort(item->mProps, item->mPropCount,
+              sizeof(MediaAnalyticsItem::Prop), MetricsSummarizer::PropSorter);
+        if (DEBUG_SORT) {
+            ALOGD("sortProps(pst): %s", item->toString().c_str());
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizer.h b/services/mediaanalytics/MetricsSummarizer.h
new file mode 100644
index 0000000..0b64eac
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizer.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_METRICSSUMMARIZER_H
+#define ANDROID_METRICSSUMMARIZER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+
+namespace android {
+
+class MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizer(const char *key);
+    virtual ~MetricsSummarizer();
+
+    // show the key
+    const char * getKey();
+
+    // should the record be given to this summarizer
+    bool isMine(MediaAnalyticsItem &item);
+
+    // hand the record to this summarizer
+    void handleRecord(MediaAnalyticsItem *item);
+
+    virtual void mergeRecord(MediaAnalyticsItem &have, MediaAnalyticsItem &incoming);
+
+    // dump the summarized records (for dumpsys)
+    AString dumpSummary(int &slot);
+    AString dumpSummary(int &slot, const char *only);
+
+    void setIgnorables(const char **);
+    const char **getIgnorables();
+
+ protected:
+
+    // various comparators
+    // "do these records have same attributes and values in those attrs"
+    // ditto, but watch for "error" fields
+    bool sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
+    // attributes + from the same app/userid
+    bool sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
+
+    static int PropSorter(const void *a, const void *b);
+    void sortProps(MediaAnalyticsItem *item);
+
+ private:
+    const char *mKey;
+    const char **mIgnorables;
+    List<MediaAnalyticsItem *> *mSummaries;
+
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZER_H
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.cpp b/services/mediaanalytics/MetricsSummarizerCodec.cpp
new file mode 100644
index 0000000..8c74782
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerCodec.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "MetricsSummarizerCodec"
+#include <utils/Log.h>
+
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerCodec.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerCodec::MetricsSummarizerCodec(const char *key)
+    : MetricsSummarizer(key)
+{
+    ALOGV("MetricsSummarizerCodec::MetricsSummarizerCodec");
+}
+
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.h b/services/mediaanalytics/MetricsSummarizerCodec.h
new file mode 100644
index 0000000..c01196f
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerCodec.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_METRICSSUMMARIZERCODEC_H
+#define ANDROID_METRICSSUMMARIZERCODEC_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerCodec : public MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizerCodec(const char *key);
+    virtual ~MetricsSummarizerCodec() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERCODEC_H
diff --git a/services/mediaanalytics/MetricsSummarizerExtractor.cpp b/services/mediaanalytics/MetricsSummarizerExtractor.cpp
new file mode 100644
index 0000000..190f87d
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerExtractor.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "MetricsSummarizerExtractor"
+#include <utils/Log.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerExtractor.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerExtractor::MetricsSummarizerExtractor(const char *key)
+    : MetricsSummarizer(key)
+{
+    ALOGV("MetricsSummarizerExtractor::MetricsSummarizerExtractor");
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerExtractor.h b/services/mediaanalytics/MetricsSummarizerExtractor.h
new file mode 100644
index 0000000..eee052b
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerExtractor.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_METRICSSUMMARIZEREXTRACTOR_H
+#define ANDROID_METRICSSUMMARIZEREXTRACTOR_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerExtractor : public MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizerExtractor(const char *key);
+    virtual ~MetricsSummarizerExtractor() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZEREXTRACTOR_H
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.cpp b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
new file mode 100644
index 0000000..5162059
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "MetricsSummarizerPlayer"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerPlayer.h"
+
+
+
+
+namespace android {
+
+static const char *player_ignorable[] = {
+    "android.media.mediaplayer.durationMs",
+    "android.media.mediaplayer.playingMs",
+    "android.media.mediaplayer.frames",
+    "android.media.mediaplayer.dropped",
+    0
+};
+
+MetricsSummarizerPlayer::MetricsSummarizerPlayer(const char *key)
+    : MetricsSummarizer(key)
+{
+    ALOGV("MetricsSummarizerPlayer::MetricsSummarizerPlayer");
+    setIgnorables(player_ignorable);
+}
+
+void MetricsSummarizerPlayer::mergeRecord(MediaAnalyticsItem &summation, MediaAnalyticsItem &item) {
+
+    ALOGV("MetricsSummarizerPlayer::mergeRecord()");
+
+    //
+    // we sum time & frames.
+    // be careful about our special "-1" values that indicate 'unknown'
+    // treat those as 0 [basically, not summing them into the totals].
+    int64_t duration = 0;
+    if (item.getInt64("android.media.mediaplayer.durationMs", &duration)) {
+        ALOGV("found durationMs of %" PRId64, duration);
+        summation.addInt64("android.media.mediaplayer.durationMs",duration);
+    }
+    int64_t playing = 0;
+    if (item.getInt64("android.media.mediaplayer.playingMs", &playing))
+        ALOGV("found playingMs of %" PRId64, playing);
+        if (playing >= 0) {
+            summation.addInt64("android.media.mediaplayer.playingMs",playing);
+        }
+    int64_t frames = 0;
+    if (item.getInt64("android.media.mediaplayer.frames", &frames))
+        ALOGV("found framess of %" PRId64, frames);
+        if (frames >= 0) {
+            summation.addInt64("android.media.mediaplayer.frames",frames);
+        }
+    int64_t dropped = 0;
+    if (item.getInt64("android.media.mediaplayer.dropped", &dropped))
+        ALOGV("found dropped of %" PRId64, dropped);
+        if (dropped >= 0) {
+            summation.addInt64("android.media.mediaplayer.dropped",dropped);
+        }
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.h b/services/mediaanalytics/MetricsSummarizerPlayer.h
new file mode 100644
index 0000000..ad1bf74
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_METRICSSUMMARIZERPLAYER_H
+#define ANDROID_METRICSSUMMARIZERPLAYER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerPlayer : public MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizerPlayer(const char *key);
+    virtual ~MetricsSummarizerPlayer() {};
+
+    virtual void mergeRecord(MediaAnalyticsItem &have, MediaAnalyticsItem &incoming);
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERPLAYER_H
diff --git a/services/mediaanalytics/MetricsSummarizerRecorder.cpp b/services/mediaanalytics/MetricsSummarizerRecorder.cpp
new file mode 100644
index 0000000..c2919c3
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerRecorder.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "MetricsSummarizerRecorder"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerRecorder.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerRecorder::MetricsSummarizerRecorder(const char *key)
+    : MetricsSummarizer(key)
+{
+    ALOGV("MetricsSummarizerRecorder::MetricsSummarizerRecorder");
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerRecorder.h b/services/mediaanalytics/MetricsSummarizerRecorder.h
new file mode 100644
index 0000000..963baab
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerRecorder.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_METRICSSUMMARIZERRECORDER_H
+#define ANDROID_METRICSSUMMARIZERRECORDER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerRecorder : public MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizerRecorder(const char *key);
+    virtual ~MetricsSummarizerRecorder() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERRECORDER_H
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index aaf1018..a5512e1 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -26,7 +26,7 @@
 
 namespace android {
 
-// static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n";
+ static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n";
 MediaLogService::MediaLogService() :
     BnMediaLogService(),
     mMergerShared((NBLog::Shared*) malloc(NBLog::Timeline::sharedSize(kMergeBufferSize))),
@@ -99,35 +99,38 @@
         return NO_ERROR;
     }
 
-#if 0
-    Vector<NBLog::NamedReader> namedReaders;
-    {
-        bool locked = dumpTryLock(mLock);
+    if (args.size() > 0) {
+        const String8 arg0(args[0]);
+        if (!strcmp(arg0.string(), "-r")) {
+            // needed because mNamedReaders is protected by mLock
+            bool locked = dumpTryLock(mLock);
 
-        // failed to lock - MediaLogService is probably deadlocked
-        if (!locked) {
-            String8 result(kDeadlockedString);
-            if (fd >= 0) {
-                write(fd, result.string(), result.size());
-            } else {
-                ALOGW("%s:", result.string());
+            // failed to lock - MediaLogService is probably deadlocked
+            if (!locked) {
+                String8 result(kDeadlockedString);
+                if (fd >= 0) {
+                    write(fd, result.string(), result.size());
+                } else {
+                    ALOGW("%s:", result.string());
+                }
+                // TODO should we instead proceed to mMergeReader.dump? does it need lock?
+                return NO_ERROR;
             }
-            return NO_ERROR;
-        }
-            // namedReaders = mNamedReaders;
-            // for (size_t i = 0; i < namedReaders.size(); i++) {
-            //     const NBLog::NamedReader& namedReader = namedReaders[i];
-            //     if (fd >= 0) {
-            //         dprintf(fd, "\n%s:\n", namedReader.name());
-            //     } else {
-            //         ALOGI("%s:", namedReader.name());
-            //     }
-            //     namedReader.reader()->dump(fd, 0 /*indent*/);
-            // }
 
-        mLock.unlock();
+            for (const auto& namedReader : mNamedReaders) {
+                if (fd >= 0) {
+                    dprintf(fd, "\n%s:\n", namedReader.name());
+                } else {
+                    ALOGI("%s:", namedReader.name());
+                }
+                // TODO This code is for testing, remove it when done
+                // namedReader.reader()->dump(fd, 0 /*indent*/);
+            }
+
+            mLock.unlock();
+        }
     }
-#endif
+
     // FIXME request merge to make sure log is up to date
     mMergeReader.dump(fd);
     return NO_ERROR;
diff --git a/services/medialog/MediaLogService.h b/services/medialog/MediaLogService.h
index c6b99f1..39d9cc0 100644
--- a/services/medialog/MediaLogService.h
+++ b/services/medialog/MediaLogService.h
@@ -49,12 +49,15 @@
     // Internal dump
     static const int kDumpLockRetries = 50;
     static const int kDumpLockSleepUs = 20000;
-    static const size_t kMergeBufferSize = 16 * 1024; // TODO determine good value for this
+    // Size of merge buffer, in bytes
+    static const size_t kMergeBufferSize = 64 * 1024; // TODO determine good value for this
     static bool dumpTryLock(Mutex& mutex);
 
     Mutex               mLock;
 
-    Vector<NBLog::NamedReader> mNamedReaders;
+    Vector<NBLog::NamedReader> mNamedReaders;   // protected by mLock
+
+    // FIXME Need comments on all of these, especially about locking
     NBLog::Shared *mMergerShared;
     NBLog::Merger mMerger;
     NBLog::MergeReader mMergeReader;
diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp
index e959b83..289b7b1 100644
--- a/soundtrigger/SoundTrigger.cpp
+++ b/soundtrigger/SoundTrigger.cpp
@@ -124,9 +124,9 @@
 }
 
 // SoundTrigger
-SoundTrigger::SoundTrigger(sound_trigger_module_handle_t module,
+SoundTrigger::SoundTrigger(sound_trigger_module_handle_t /*module*/,
                                  const sp<SoundTriggerCallback>& callback)
-    : mModule(module), mCallback(callback)
+    : mCallback(callback)
 {
 }