Merge "Implement IOmxStore" into oc-mr1-dev
diff --git a/media/libaaudio/examples/utils/AAudioExampleUtils.h b/media/libaaudio/examples/utils/AAudioExampleUtils.h
index 6cbcc58..9ef62c9 100644
--- a/media/libaaudio/examples/utils/AAudioExampleUtils.h
+++ b/media/libaaudio/examples/utils/AAudioExampleUtils.h
@@ -17,9 +17,14 @@
 #ifndef AAUDIO_EXAMPLE_UTILS_H
 #define AAUDIO_EXAMPLE_UTILS_H
 
-#include <unistd.h>
+#include <atomic>
+#include <linux/futex.h>
 #include <sched.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
 #include <aaudio/AAudio.h>
+#include <utils/Errors.h>
 
 #define NANOS_PER_MICROSECOND ((int64_t)1000)
 #define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
@@ -40,6 +45,12 @@
     return modeText;
 }
 
+static void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
+    time->tv_sec = nanoseconds / NANOS_PER_SECOND;
+    // Calculate the fractional nanoseconds. Avoids expensive % operation.
+    time->tv_nsec = nanoseconds - (time->tv_sec * NANOS_PER_SECOND);
+}
+
 static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
     struct timespec time;
     int result = clock_gettime(clockId, &time);
@@ -79,4 +90,77 @@
     return latencyMillis;
 }
 
+// ================================================================================
+// These Futex calls are common online examples.
+static android::status_t sys_futex(void *addr1, int op, int val1,
+                      struct timespec *timeout, void *addr2, int val3) {
+    android::status_t result = (android::status_t) syscall(SYS_futex, addr1,
+                                                           op, val1, timeout,
+                                                           addr2, val3);
+    return (result == 0) ? 0 : -errno;
+}
+
+static android::status_t futex_wake(void *addr, int numWake) {
+    // Use _PRIVATE because we are just using the futex in one process.
+    return sys_futex(addr, FUTEX_WAKE_PRIVATE, numWake, NULL, NULL, 0);
+}
+
+static android::status_t futex_wait(void *addr, int current, struct timespec *time) {
+    // Use _PRIVATE because we are just using the futex in one process.
+    return sys_futex(addr, FUTEX_WAIT_PRIVATE, current, time, NULL, 0);
+}
+
+// TODO better name?
+/**
+ * The WakeUp class is used to send a wakeup signal to one or more sleeping threads.
+ */
+class WakeUp {
+public:
+    WakeUp() : mValue(0) {}
+    explicit WakeUp(int32_t value) : mValue(value) {}
+
+    /**
+     * Wait until the internal value no longer matches the given value.
+     * Note that this code uses a futex, which is subject to spurious wake-ups.
+     * So check to make sure that the desired condition has been met.
+     *
+     * @return zero if the value changes or various negative errors including
+     *    -ETIMEDOUT if a timeout occurs,
+     *    or -EINTR if interrupted by a signal,
+     *    or -EAGAIN or -EWOULDBLOCK if the internal value does not match the specified value
+     */
+    android::status_t wait(int32_t value, int64_t timeoutNanoseconds) {
+        struct timespec time;
+        convertNanosecondsToTimespec(timeoutNanoseconds, &time);
+        return futex_wait(&mValue, value, &time);
+    }
+
+    /**
+     * Increment value and wake up any threads that need to be woken.
+     *
+     * @return number of waiters woken up
+     */
+    android::status_t wake() {
+        ++mValue;
+        return futex_wake(&mValue, INT_MAX);
+    }
+
+    /**
+     * Set value and wake up any threads that need to be woken.
+     *
+     * @return number of waiters woken up
+     */
+    android::status_t wake(int32_t value) {
+        mValue.store(value);
+        return futex_wake(&mValue, INT_MAX);
+    }
+
+    int32_t get() {
+        return mValue.load();
+    }
+
+private:
+    std::atomic<int32_t>   mValue;
+};
+
 #endif // AAUDIO_EXAMPLE_UTILS_H
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index cc0cb34..d2e7f23 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -23,6 +23,7 @@
 #include <sched.h>
 
 #include <aaudio/AAudio.h>
+#include <atomic>
 #include "AAudioArgsParser.h"
 #include "SineGenerator.h"
 
@@ -219,18 +220,26 @@
     AAudioStream             *mStream = nullptr;
     aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
     aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
+
 };
 
 typedef struct SineThreadedData_s {
+
     SineGenerator  sineOsc1;
     SineGenerator  sineOsc2;
     int64_t        framesTotal = 0;
     int64_t        nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
     int32_t        minNumFrames = INT32_MAX;
     int32_t        maxNumFrames = 0;
-    int            scheduler;
+
+    int            scheduler = 0;
     bool           schedulerChecked = false;
     bool           forceUnderruns = false;
+
+    AAudioSimplePlayer simplePlayer;
+    int32_t            callbackCount = 0;
+    WakeUp             waker{AAUDIO_OK};
+
 } SineThreadedData_t;
 
 // Callback function that fills the audio output buffer.
@@ -247,6 +256,7 @@
         return AAUDIO_CALLBACK_RESULT_STOP;
     }
     SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
+    sineData->callbackCount++;
 
     sineData->framesTotal += numFrames;
 
@@ -304,9 +314,16 @@
 void SimplePlayerErrorCallbackProc(
         AAudioStream *stream __unused,
         void *userData __unused,
-        aaudio_result_t error)
-{
-    printf("Error Callback, error: %d\n",(int)error);
+        aaudio_result_t error) {
+    // should not happen but just in case...
+    if (userData == nullptr) {
+        printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
+        return;
+    }
+    SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
+    android::status_t ret = sineData->waker.wake(error);
+    printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
 }
 
+
 #endif //AAUDIO_SIMPLE_PLAYER_H
diff --git a/media/libaaudio/examples/utils/SineGenerator.h b/media/libaaudio/examples/utils/SineGenerator.h
index 64b772d..a755582 100644
--- a/media/libaaudio/examples/utils/SineGenerator.h
+++ b/media/libaaudio/examples/utils/SineGenerator.h
@@ -58,6 +58,13 @@
         }
     }
 
+    void setAmplitude(double amplitude) {
+        mAmplitude = amplitude;
+    }
+    double getAmplitude() const {
+        return mAmplitude;
+    }
+
 private:
     void advancePhase() {
         mPhase += mPhaseIncrement;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 071ca87..2280b72 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -15,6 +15,7 @@
  */
 
 // Play sine waves using an AAudio callback.
+// If a disconnection occurs then reopen the stream on the new device.
 
 #include <assert.h>
 #include <unistd.h>
@@ -22,33 +23,32 @@
 #include <sched.h>
 #include <stdio.h>
 #include <math.h>
+#include <string.h>
 #include <time.h>
 #include <aaudio/AAudio.h>
 #include "AAudioExampleUtils.h"
 #include "AAudioSimplePlayer.h"
 #include "../../utils/AAudioSimplePlayer.h"
 
-int main(int argc, const char **argv)
+/**
+ * Open stream, play some sine waves, then close the stream.
+ *
+ * @param argParser
+ * @return AAUDIO_OK or negative error code
+ */
+static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser)
 {
-    AAudioArgsParser   argParser;
-    AAudioSimplePlayer player;
     SineThreadedData_t myData;
-    aaudio_result_t result;
-    int32_t actualSampleRate;
+    AAudioSimplePlayer &player = myData.simplePlayer;
+    aaudio_result_t    result = AAUDIO_OK;
+    bool               disconnected = false;
+    int64_t            startedAtNanos;
 
-    // Make printf print immediately so that debug info is not stuck
-    // in a buffer if we hang or crash.
-    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
-
-    printf("%s - Play a sine sweep using an AAudio callback V0.1.3\n", argv[0]);
-
+    printf("----------------------- run complete test --------------------------\n");
     myData.schedulerChecked = false;
+    myData.callbackCount = 0;
     myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount()
 
-    if (argParser.parseArgs(argc, argv)) {
-        return EXIT_FAILURE;
-    }
-
     result = player.open(argParser,
                          SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData);
     if (result != AAUDIO_OK) {
@@ -58,13 +58,19 @@
 
     argParser.compareWithStream(player.getStream());
 
-    actualSampleRate = player.getSampleRate();
-    myData.sineOsc1.setup(440.0, actualSampleRate);
-    myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
-    myData.sineOsc2.setup(660.0, actualSampleRate);
-    myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
+    // Setup sine wave generators.
+    {
+        int32_t actualSampleRate = player.getSampleRate();
+        myData.sineOsc1.setup(440.0, actualSampleRate);
+        myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
+        myData.sineOsc1.setAmplitude(0.2);
+        myData.sineOsc2.setup(660.0, actualSampleRate);
+        myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
+        myData.sineOsc2.setAmplitude(0.2);
+    }
 
 #if 0
+    //  writes not allowed for callback streams
     result = player.prime(); // FIXME crashes AudioTrack.cpp
     if (result != AAUDIO_OK) {
         fprintf(stderr, "ERROR - player.prime() returned %d\n", result);
@@ -78,34 +84,32 @@
         goto error;
     }
 
+    // Play a sine wave in the background.
     printf("Sleep for %d seconds while audio plays in a callback thread.\n",
            argParser.getDurationSeconds());
+    startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
     for (int second = 0; second < argParser.getDurationSeconds(); second++)
     {
-        const struct timespec request = { .tv_sec = 1, .tv_nsec = 0 };
-        (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
-
-        aaudio_stream_state_t state;
-        result = AAudioStream_waitForStateChange(player.getStream(),
-                                                 AAUDIO_STREAM_STATE_CLOSED,
-                                                 &state,
-                                                 0);
+        // Sleep a while. Wake up early if there is an error, for example a DISCONNECT.
+        long ret = myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
+        int64_t millis = (getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND;
+        result = myData.waker.get();
+        printf("wait() returns %ld, aaudio_result = %d, at %6d millis"
+               ", second = %d, framesWritten = %8d, underruns = %d\n",
+               ret, result, (int) millis,
+               second,
+               (int) AAudioStream_getFramesWritten(player.getStream()),
+               (int) AAudioStream_getXRunCount(player.getStream()));
         if (result != AAUDIO_OK) {
-            fprintf(stderr, "ERROR - AAudioStream_waitForStateChange() returned %d\n", result);
-            goto error;
-        }
-        if (state != AAUDIO_STREAM_STATE_STARTING && state != AAUDIO_STREAM_STATE_STARTED) {
-            printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
+            if (result == AAUDIO_ERROR_DISCONNECTED) {
+                disconnected = true;
+            }
             break;
         }
-        printf("framesWritten = %d, underruns = %d\n",
-               (int) AAudioStream_getFramesWritten(player.getStream()),
-               (int) AAudioStream_getXRunCount(player.getStream())
-        );
     }
-    printf("Woke up now.\n");
+    printf("AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
 
-    printf("call stop()\n");
+    printf("call stop() callback # = %d\n", myData.callbackCount);
     result = player.stop();
     if (result != AAUDIO_OK) {
         goto error;
@@ -126,10 +130,28 @@
     printf("max numFrames = %8d\n", (int) myData.maxNumFrames);
 
     printf("SUCCESS\n");
-    return EXIT_SUCCESS;
 error:
     player.close();
-    printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
-    return EXIT_FAILURE;
+    return disconnected ? AAUDIO_ERROR_DISCONNECTED : result;
 }
 
+int main(int argc, const char **argv)
+{
+    AAudioArgsParser   argParser;
+    aaudio_result_t    result;
+
+    // Make printf print immediately so that debug info is not stuck
+    // in a buffer if we hang or crash.
+    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+    printf("%s - Play a sine sweep using an AAudio callback V0.1.2\n", argv[0]);
+
+    if (argParser.parseArgs(argc, argv)) {
+        return EXIT_FAILURE;
+    }
+
+    // Keep looping until we can complete the test without disconnecting.
+    while((result = testOpenPlayClose(argParser)) == AAUDIO_ERROR_DISCONNECTED);
+
+    return (result) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index a5f9ab6..b1cb0e7 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -135,7 +135,11 @@
             &mStatus, &mId, &enabled);
 
     if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
-        ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
+        char typeBuffer[64], uuidBuffer[64];
+        guidToString(type, typeBuffer, sizeof(typeBuffer));
+        guidToString(uuid, uuidBuffer, sizeof(uuidBuffer));
+        ALOGE("set(): AudioFlinger could not create effect %s / %s, status: %d",
+                typeBuffer, uuidBuffer, mStatus);
         if (iEffect == 0) {
             mStatus = NO_INIT;
         }
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 31e1e2b..c212112 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -63,6 +63,14 @@
     return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
 }
 
+// TODO move to audio_utils.
+static inline struct timespec convertNsToTimespec(int64_t ns) {
+    struct timespec tv;
+    tv.tv_sec = static_cast<time_t>(ns / NANOS_PER_SECOND);
+    tv.tv_nsec = static_cast<long>(ns % NANOS_PER_SECOND);
+    return tv;
+}
+
 // current monotonic time in microseconds.
 static int64_t getNowUs()
 {
@@ -539,7 +547,8 @@
     mUpdatePeriod = 0;
     mPosition = 0;
     mReleased = 0;
-    mStartUs = 0;
+    mStartNs = 0;
+    mStartFromZeroUs = 0;
     AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
     mSequence = 1;
     mObservedSequence = mSequence;
@@ -587,6 +596,7 @@
             mStartEts.clear();
         }
     }
+    mStartNs = systemTime(); // save this for timestamp adjustment after starting.
     if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
         // reset current position as seen by client to 0
         mPosition = 0;
@@ -615,7 +625,7 @@
         // since the flush is asynchronous and stop may not fully drain.
         // We save the time when the track is started to later verify whether
         // the counters are realistic (i.e. start from zero after this time).
-        mStartUs = getNowUs();
+        mStartFromZeroUs = mStartNs / 1000;
 
         // force refresh of remaining frames by processAudioBuffer() as last
         // write before stop could be partial.
@@ -2571,8 +2581,7 @@
                     if (at < limit) {
                         ALOGV("timestamp pause lag:%lld adjusting from %lld to %lld",
                                 (long long)lag, (long long)at, (long long)limit);
-                        timestamp.mTime.tv_sec = limit / NANOS_PER_SECOND;
-                        timestamp.mTime.tv_nsec = limit % NANOS_PER_SECOND; // compiler opt.
+                        timestamp.mTime = convertNsToTimespec(limit);
                     }
                 }
                 mPreviousLocation = location;
@@ -2615,18 +2624,18 @@
         // the previous song under gapless playback.
         // However, we sometimes see zero timestamps, then a glitch of
         // the previous song's position, and then correct timestamps afterwards.
-        if (mStartUs != 0 && mSampleRate != 0) {
+        if (mStartFromZeroUs != 0 && mSampleRate != 0) {
             static const int kTimeJitterUs = 100000; // 100 ms
             static const int k1SecUs = 1000000;
 
             const int64_t timeNow = getNowUs();
 
-            if (timeNow < mStartUs + k1SecUs) { // within first second of starting
+            if (timeNow < mStartFromZeroUs + k1SecUs) { // within first second of starting
                 const int64_t timestampTimeUs = convertTimespecToUs(timestamp.mTime);
-                if (timestampTimeUs < mStartUs) {
+                if (timestampTimeUs < mStartFromZeroUs) {
                     return WOULD_BLOCK;  // stale timestamp time, occurs before start.
                 }
-                const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
+                const int64_t deltaTimeUs = timestampTimeUs - mStartFromZeroUs;
                 const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
                         / ((double)mSampleRate * mPlaybackRate.mSpeed);
 
@@ -2649,10 +2658,10 @@
                     return WOULD_BLOCK;
                 }
                 if (deltaPositionByUs != 0) {
-                    mStartUs = 0; // don't check again, we got valid nonzero position.
+                    mStartFromZeroUs = 0; // don't check again, we got valid nonzero position.
                 }
             } else {
-                mStartUs = 0; // don't check again, start time expired.
+                mStartFromZeroUs = 0; // don't check again, start time expired.
             }
             mTimestampStartupGlitchReported = false;
         }
@@ -2690,14 +2699,33 @@
     // Prevent retrograde motion in timestamp.
     // This is sometimes caused by erratic reports of the available space in the ALSA drivers.
     if (status == NO_ERROR) {
+        // previousTimestampValid is set to false when starting after a stop or flush.
         if (previousTimestampValid) {
             const int64_t previousTimeNanos =
                     audio_utils_ns_from_timespec(&mPreviousTimestamp.mTime);
-            const int64_t currentTimeNanos = audio_utils_ns_from_timespec(&timestamp.mTime);
+            int64_t currentTimeNanos = audio_utils_ns_from_timespec(&timestamp.mTime);
+
+            // Fix stale time when checking timestamp right after start().
+            //
+            // For offload compatibility, use a default lag value here.
+            // Any time discrepancy between this update and the pause timestamp is handled
+            // by the retrograde check afterwards.
+            const int64_t lagNs = int64_t(mAfLatency * 1000000LL);
+            const int64_t limitNs = mStartNs - lagNs;
+            if (currentTimeNanos < limitNs) {
+                ALOGD("correcting timestamp time for pause, "
+                        "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld",
+                        (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs);
+                timestamp.mTime = convertNsToTimespec(limitNs);
+                currentTimeNanos = limitNs;
+            }
+
+            // retrograde check
             if (currentTimeNanos < previousTimeNanos) {
                 ALOGW("retrograde timestamp time corrected, %lld < %lld",
                         (long long)currentTimeNanos, (long long)previousTimeNanos);
                 timestamp.mTime = mPreviousTimestamp.mTime;
+                // currentTimeNanos not used below.
             }
 
             // Looking at signed delta will work even when the timestamps
@@ -2907,7 +2935,7 @@
     case STATE_STOPPED:
         if (isOffloadedOrDirect_l()) {
             // check if we have started in the past to return true.
-            return mStartUs > 0;
+            return mStartFromZeroUs > 0;
         }
         // A normal audio track may still be draining, so
         // check if stream has ended.  This covers fasttrack position
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index b168fc9..7ae8ec5 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1085,8 +1085,10 @@
                                                     // reset by stop() but continues monotonically
                                                     // after new IAudioTrack to restore mPosition,
                                                     // and could be easily widened to uint64_t
-    int64_t                 mStartUs;               // the start time after flush or stop.
+    int64_t                 mStartFromZeroUs;       // the start time after flush or stop,
+                                                    // when position should be 0.
                                                     // only used for offloaded and direct tracks.
+    int64_t                 mStartNs;               // the time when start() is called.
     ExtendedTimestamp       mStartEts;              // Extended timestamp at start for normal
                                                     // AudioTracks.
     AudioTimestamp          mStartTs;               // Timestamp at start for offloaded or direct
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 1717b49..ee9406d 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -429,9 +429,7 @@
     size_t ii;
     LVM_INT32 temp;
 
-    src += n-1;
-    dst += n-1;
-    for (ii = n; ii != 0; ii--) {
+    for (ii = 0; ii < n; ii++) {
         temp = (LVM_INT32)((*src) * 32768.0f);
         if (temp >= 32767) {
             *dst = 32767;
@@ -440,8 +438,8 @@
         } else {
             *dst = (LVM_INT16)temp;
         }
-        src--;
-        dst--;
+        src++;
+        dst++;
     }
     return;
 }
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 43881b3..47a147e 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -120,6 +120,8 @@
         // key as part of constructor
         dst->mPid = this->mPid;
         dst->mUid = this->mUid;
+        dst->mPkgName = this->mPkgName;
+        dst->mPkgVersionCode = this->mPkgVersionCode;
         dst->mSessionID = this->mSessionID;
         dst->mTimestamp = this->mTimestamp;
         dst->mFinalized = this->mFinalized;
@@ -201,6 +203,24 @@
     return mUid;
 }
 
+MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(AString pkgName) {
+    mPkgName = pkgName;
+    return *this;
+}
+
+AString MediaAnalyticsItem::getPkgName() const {
+    return mPkgName;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int32_t pkgVersionCode) {
+    mPkgVersionCode = pkgVersionCode;
+    return *this;
+}
+
+int32_t MediaAnalyticsItem::getPkgVersionCode() const {
+    return mPkgVersionCode;
+}
+
 // this key is for the overall record -- "codec", "player", "drm", etc
 MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
     mKey = key;
@@ -263,11 +283,29 @@
         i = mPropCount++;
         prop = &mProps[i];
         prop->setName(name, len);
+        prop->mType = kTypeNone;        // make caller set type info
     }
 
     return prop;
 }
 
+// used within the summarizers; return whether property existed
+bool MediaAnalyticsItem::removeProp(const char *name) {
+    size_t len = strlen(name);
+    size_t i = findPropIndex(name, len);
+    if (i < mPropCount) {
+        Prop *prop = &mProps[i];
+        clearProp(prop);
+        if (i != mPropCount-1) {
+            // in the middle, bring last one down to fill gap
+            mProps[i] = mProps[mPropCount-1];
+        }
+        mPropCount--;
+        return true;
+    }
+    return false;
+}
+
 // set the values
 void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
     Prop *prop = allocateProp(name);
@@ -568,6 +606,10 @@
     // into 'this' object
     // .. we make a copy of the string to put away.
     mKey = data.readCString();
+    mPid = data.readInt32();
+    mUid = data.readInt32();
+    mPkgName = data.readCString();
+    mPkgVersionCode = data.readInt32();
     mSessionID = data.readInt64();
     mFinalized = data.readInt32();
     mTimestamp = data.readInt64();
@@ -611,6 +653,10 @@
 
 
     data->writeCString(mKey.c_str());
+    data->writeInt32(mPid);
+    data->writeInt32(mUid);
+    data->writeCString(mPkgName.c_str());
+    data->writeInt32(mPkgVersionCode);
     data->writeInt64(mSessionID);
     data->writeInt32(mFinalized);
     data->writeInt64(mTimestamp);
@@ -651,21 +697,54 @@
 
 
 AString MediaAnalyticsItem::toString() {
+   return toString(-1);
+}
 
-    AString result = "(";
+AString MediaAnalyticsItem::toString(int version) {
+
+    // v0 : released with 'o'
+    // v1 : bug fix (missing pid/finalized separator),
+    //      adds apk name, apk version code
+
+    if (version <= PROTO_FIRST) {
+        // default to original v0 format, until proper parsers are in place
+        version = PROTO_V0;
+    } else if (version > PROTO_LAST) {
+        version = PROTO_LAST;
+    }
+
+    AString result;
     char buffer[512];
 
+    if (version == PROTO_V0) {
+        result = "(";
+    } else {
+        snprintf(buffer, sizeof(buffer), "[%d:", version);
+        result.append(buffer);
+    }
+
     // same order as we spill into the parcel, although not required
     // key+session are our primary matching criteria
-    //RBE ALOGD("mKey.c_str");
     result.append(mKey.c_str());
-    //RBE ALOGD("post-mKey.c_str");
     result.append(":");
     snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
     result.append(buffer);
 
-    // we need these internally, but don't want to upload them
-    snprintf(buffer, sizeof(buffer), "%d:%d", mUid, mPid);
+    snprintf(buffer, sizeof(buffer), "%d:", mUid);
+    result.append(buffer);
+
+    if (version >= PROTO_V1) {
+        result.append(mPkgName);
+        snprintf(buffer, sizeof(buffer), ":%d:", mPkgVersionCode);
+        result.append(buffer);
+    }
+
+    // in 'o' (v1) , the separator between pid and finalized was omitted
+    if (version <= PROTO_V0) {
+        snprintf(buffer, sizeof(buffer), "%d", mPid);
+    } else {
+        snprintf(buffer, sizeof(buffer), "%d:", mPid);
+    }
     result.append(buffer);
 
     snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
@@ -713,7 +792,11 @@
             result.append(buffer);
     }
 
-    result.append(")");
+    if (version == PROTO_V0) {
+        result.append(")");
+    } else {
+        result.append("]");
+    }
 
     return result;
 }
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index dc501b2..41b9658 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -75,6 +75,14 @@
         typedef const char *Attr;
 
 
+        enum {
+            PROTO_V0 = 0,
+            PROTO_FIRST = PROTO_V0,
+            PROTO_V1 = 1,
+            PROTO_LAST = PROTO_V1,
+        };
+
+
     public:
 
         // access functions for the class
@@ -161,11 +169,18 @@
         MediaAnalyticsItem &setUid(uid_t);
         uid_t getUid() const;
 
+        MediaAnalyticsItem &setPkgName(AString);
+        AString getPkgName() const;
+
+        MediaAnalyticsItem &setPkgVersionCode(int32_t);
+        int32_t getPkgVersionCode() const;
+
         // our serialization code for binder calls
         int32_t writeToParcel(Parcel *);
         int32_t readFromParcel(const Parcel&);
 
         AString toString();
+        AString toString(int version);
 
         // are we collecting analytics data
         static bool isEnabled();
@@ -188,6 +203,8 @@
         // to help validate that A doesn't mess with B's records
         pid_t     mPid;
         uid_t     mUid;
+        AString   mPkgName;
+        int32_t   mPkgVersionCode;
 
         // let's reuse a binder connection
         static sp<IMediaAnalyticsService> sAnalyticsService;
@@ -228,6 +245,7 @@
         size_t findPropIndex(const char *name, size_t len);
         Prop *findProp(const char *name);
         Prop *allocateProp(const char *name);
+        bool removeProp(const char *name);
 
         size_t mPropCount;
         size_t mPropSize;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 0fc1aa7..dc29761 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -69,6 +69,7 @@
       mPlayer(new NuPlayer(pid)),
       mPlayerFlags(0),
       mAnalyticsItem(NULL),
+      mClientUid(-1),
       mAtEOS(false),
       mLooping(false),
       mAutoLoop(false) {
@@ -109,6 +110,10 @@
 
 status_t NuPlayerDriver::setUID(uid_t uid) {
     mPlayer->setUID(uid);
+    mClientUid = uid;
+    if (mAnalyticsItem) {
+        mAnalyticsItem->setUid(mClientUid);
+    }
 
     return OK;
 }
@@ -601,6 +606,7 @@
         mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
         if (mAnalyticsItem) {
             mAnalyticsItem->generateSessionID();
+            mAnalyticsItem->setUid(mClientUid);
         }
     } else {
         ALOGV("did not have anything to record");
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index c5ddcb0..d0cf1dd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -132,11 +132,13 @@
     uint32_t mPlayerFlags;
 
     MediaAnalyticsItem *mAnalyticsItem;
+    uid_t mClientUid;
 
     bool mAtEOS;
     bool mLooping;
     bool mAutoLoop;
 
+
     void updateMetrics(const char *where);
     void logMetrics(const char *where);
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 483a9ff..bd866cb 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1152,7 +1152,18 @@
             return writtenAudioDurationUs - (mediaUs - mAudioFirstAnchorTimeMediaUs);
         }
     }
-    return writtenAudioDurationUs - mAudioSink->getPlayedOutDurationUs(nowUs);
+
+    const int64_t audioSinkPlayedUs = mAudioSink->getPlayedOutDurationUs(nowUs);
+    int64_t pendingUs = writtenAudioDurationUs - audioSinkPlayedUs;
+    if (pendingUs < 0) {
+        // This shouldn't happen unless the timestamp is stale.
+        ALOGW("%s: pendingUs %lld < 0, clamping to zero, potential resume after pause "
+                "writtenAudioDurationUs: %lld, audioSinkPlayedUs: %lld",
+                __func__, (long long)pendingUs,
+                (long long)writtenAudioDurationUs, (long long)audioSinkPlayedUs);
+        pendingUs = 0;
+    }
+    return pendingUs;
 }
 
 int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 801491a..c9394bb 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -4319,9 +4319,14 @@
         h264type.bUseHadamard = OMX_TRUE;
         h264type.nRefFrames = 2;
         h264type.nBFrames = mLatency == 0 ? 1 : std::min(1U, mLatency - 1);
+
+        // disable B-frames until MPEG4Writer can guarantee finalizing files with B-frames
+        h264type.nRefFrames = 1;
+        h264type.nBFrames = 0;
+
         h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
         h264type.nAllowedPictureTypes =
-            OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP | OMX_VIDEO_PictureTypeB;
+            OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
         h264type.nRefIdx10ActiveMinus1 = 0;
         h264type.nRefIdx11ActiveMinus1 = 0;
         h264type.bEntropyCodingCABAC = OMX_TRUE;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 5ef0f56..6f59fac 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -5224,6 +5224,7 @@
 
 void MPEG4Extractor::populateMetrics() {
     ALOGV("MPEG4Extractor::populateMetrics");
+    // write into mAnalyticsItem
 }
 
 static bool LegacySniffMPEG4(
diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
index caceda9..56d2d69 100644
--- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
+++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
@@ -154,6 +154,30 @@
     ALOGV("SoftFlacEncoder::internalGetParameter(index=0x%x)", index);
 
     switch (index) {
+        case OMX_IndexParamAudioPortFormat:
+        {
+            OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+            if (!isValidOMXParam(formatParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex > 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            formatParams->eEncoding =
+                (formatParams->nPortIndex == 0)
+                    ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingFLAC;
+
+            return OMX_ErrorNone;
+        }
+
         case OMX_IndexParamAudioPcm:
         {
             OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
@@ -163,7 +187,7 @@
                 return OMX_ErrorBadParameter;
             }
 
-            if (pcmParams->nPortIndex > 1) {
+            if (pcmParams->nPortIndex != 0) {
                 return OMX_ErrorUndefined;
             }
 
@@ -189,6 +213,10 @@
                 return OMX_ErrorBadParameter;
             }
 
+            if (flacParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
             flacParams->nCompressionLevel = mCompressionLevel;
             flacParams->nChannels = mNumChannels;
             flacParams->nSampleRate = mSampleRate;
@@ -203,6 +231,29 @@
 OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter(
         OMX_INDEXTYPE index, const OMX_PTR params) {
     switch (index) {
+        case OMX_IndexParamAudioPortFormat:
+        {
+            const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+                (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+            if (!isValidOMXParam(formatParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if ((formatParams->nPortIndex == 0
+                        && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
+                || (formatParams->nPortIndex == 1
+                        && formatParams->eEncoding != OMX_AUDIO_CodingFLAC)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
         case OMX_IndexParamAudioPcm:
         {
             ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamAudioPcm)");
@@ -212,7 +263,7 @@
                 return OMX_ErrorBadParameter;
             }
 
-            if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) {
+            if (pcmParams->nPortIndex != 0) {
                 ALOGE("SoftFlacEncoder::internalSetParameter() Error #1");
                 return OMX_ErrorUndefined;
             }
@@ -258,6 +309,10 @@
                 return OMX_ErrorBadParameter;
             }
 
+            if (flacParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
             mCompressionLevel = flacParams->nCompressionLevel; // range clamping done inside encoder
             return OMX_ErrorNone;
         }
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index fcf1092..c4499dc 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -41,7 +41,9 @@
     sp<GraphicBuffer> buf;
     status_t status = mBase->requestBuffer(slot, &buf);
     AnwBuffer anwBuffer;
-    wrapAs(&anwBuffer, *buf);
+    if (buf != nullptr) {
+        wrapAs(&anwBuffer, *buf);
+    }
     _hidl_cb(static_cast<int32_t>(status), anwBuffer);
     return Void();
 }
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
index f319bdc..8d8a2d9 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
@@ -2064,8 +2064,10 @@
     int const* constFds = static_cast<int const*>(baseFds.get());
     numFds = baseNumFds;
     if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
-        native_handle_close(nh);
-        native_handle_delete(nh);
+        if (nh != nullptr) {
+            native_handle_close(nh);
+            native_handle_delete(nh);
+        }
         return false;
     }
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a15b256..13522f5 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2603,7 +2603,7 @@
             while (ec->mEffects.size()) {
                 sp<EffectModule> effect = ec->mEffects[0];
                 effect->unPin();
-                t->removeEffect_l(effect);
+                t->removeEffect_l(effect, /*release*/ true);
                 if (effect->purgeHandles()) {
                     t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
                 }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index f2c1c4f..bd5f146 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -25,6 +25,7 @@
 #include <system/audio_effects/effect_ns.h>
 #include <system/audio_effects/effect_visualizer.h>
 #include <audio_utils/primitives.h>
+#include <media/AudioEffect.h>
 #include <media/audiohal/EffectHalInterface.h>
 #include <media/audiohal/EffectsFactoryHalInterface.h>
 
@@ -109,7 +110,10 @@
 {
     ALOGV("Destructor %p", this);
     if (mEffectInterface != 0) {
-        ALOGW("EffectModule %p destructor called with unreleased interface", this);
+        char uuidStr[64];
+        AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+        ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
+                this, uuidStr);
         release_l();
     }
 
@@ -1081,18 +1085,12 @@
     result.append(buffer);
 
     result.append("\t\tDescriptor:\n");
-    snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
-            mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion,
-            mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1],
-                    mDescriptor.uuid.node[2],
-            mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]);
+    char uuidStr[64];
+    AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+    snprintf(buffer, SIZE, "\t\t- UUID: %s\n", uuidStr);
     result.append(buffer);
-    snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
-                mDescriptor.type.timeLow, mDescriptor.type.timeMid,
-                    mDescriptor.type.timeHiAndVersion,
-                mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1],
-                    mDescriptor.type.node[2],
-                mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]);
+    AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr));
+    snprintf(buffer, SIZE, "\t\t- TYPE: %s\n", uuidStr);
     result.append(buffer);
     snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
             mDescriptor.apiVersion,
@@ -1306,11 +1304,10 @@
     if (thread != 0) {
         thread->disconnectEffectHandle(this, unpinIfLast);
     } else {
-        ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
         // try to cleanup as much as we can
         sp<EffectModule> effect = mEffect.promote();
-        if (effect != 0) {
-            effect->disconnectHandle(this, unpinIfLast);
+        if (effect != 0 && effect->disconnectHandle(this, unpinIfLast) > 0) {
+            ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
         }
     }
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d951e93..297608c 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7633,6 +7633,10 @@
         return NO_ERROR;
     }
 
+    if (!isOutput() && !recordingAllowed(client.packageName, client.clientPid, client.clientUid)) {
+        return PERMISSION_DENIED;
+    }
+
     audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
 
     audio_io_handle_t io = mId;
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index f3bb35c..2836525 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -29,12 +29,15 @@
 #include <unistd.h>
 
 #include <string.h>
+#include <pwd.h>
 
 #include <cutils/atomic.h>
 #include <cutils/properties.h> // for property_get
 
 #include <utils/misc.h>
 
+#include <android/content/pm/IPackageManagerNative.h>
+
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/MemoryHeapBase.h>
@@ -80,27 +83,26 @@
 
 namespace android {
 
+    using namespace android::base;
+    using namespace android::content::pm;
+
 
 
 // summarized records
-// up to 48 sets, each covering an hour -- at least 2 days of coverage
+// up to 36 sets, each covering an hour -- so at least 1.5 days
 // (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 int kMaxRecordSets = 36;
 
+// individual records kept in memory: age or count
+// age: <= 36 hours (1.5 days)
+// count: hard limit of # records
+// (0 for either of these disables that threshold)
+static const nsecs_t kMaxRecordAgeNs =  36 * 3600 * (1000*1000*1000ll);
+static const int kMaxRecords    = 0;
 
 static const char *kServiceName = "media.metrics";
 
-
-//using android::status_t;
-//using android::OK;
-//using android::BAD_VALUE;
-//using android::NOT_ENOUGH_DATA;
-//using android::Parcel;
-
-
 void MediaAnalyticsService::instantiate() {
     defaultServiceManager()->addService(
             String16(kServiceName), new MediaAnalyticsService());
@@ -110,6 +112,7 @@
 MediaAnalyticsService::SummarizerSet::SummarizerSet() {
     mSummarizers = new List<MetricsSummarizer *>();
 }
+
 MediaAnalyticsService::SummarizerSet::~SummarizerSet() {
     // empty the list
     List<MetricsSummarizer *> *l = mSummarizers;
@@ -153,8 +156,10 @@
 
 MediaAnalyticsService::MediaAnalyticsService()
         : mMaxRecords(kMaxRecords),
+          mMaxRecordAgeNs(kMaxRecordAgeNs),
           mMaxRecordSets(kMaxRecordSets),
-          mNewSetInterval(kNewSetIntervalNs) {
+          mNewSetInterval(kNewSetIntervalNs),
+          mDumpProto(MediaAnalyticsItem::PROTO_V0) {
 
     ALOGD("MediaAnalyticsService created");
     // clear our queues
@@ -167,6 +172,8 @@
     mItemsSubmitted = 0;
     mItemsFinalized = 0;
     mItemsDiscarded = 0;
+    mItemsDiscardedExpire = 0;
+    mItemsDiscardedCount = 0;
 
     mLastSessionID = 0;
     // recover any persistency we set up
@@ -177,8 +184,23 @@
         ALOGD("MediaAnalyticsService destroyed");
 
     // clean out mOpen and mFinalized
+    while (mOpen->size() > 0) {
+        MediaAnalyticsItem * oitem = *(mOpen->begin());
+        mOpen->erase(mOpen->begin());
+        delete oitem;
+        mItemsDiscarded++;
+        mItemsDiscardedCount++;
+    }
     delete mOpen;
     mOpen = NULL;
+
+    while (mFinalized->size() > 0) {
+        MediaAnalyticsItem * oitem = *(mFinalized->begin());
+        mFinalized->erase(mFinalized->begin());
+        delete oitem;
+        mItemsDiscarded++;
+        mItemsDiscardedCount++;
+    }
     delete mFinalized;
     mFinalized = NULL;
 
@@ -212,13 +234,15 @@
     //
     bool isTrusted = false;
 
+    ALOGV("caller has uid=%d, embedded uid=%d", uid, uid_given);
+
     switch (uid)  {
         case AID_MEDIA:
         case AID_MEDIA_CODEC:
         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);
             }
@@ -233,6 +257,9 @@
             break;
     }
 
+    item->setPkgName(getPkgName(item->getUid(), true));
+    item->setPkgVersionCode(0);
+    ALOGV("info is from uid %d pkg '%s', version %d", item->getUid(), item->getPkgName().c_str(), item->getPkgVersionCode());
 
     mItemsSubmitted++;
 
@@ -316,6 +343,7 @@
     return id;
 }
 
+
 status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 512;
@@ -333,22 +361,41 @@
     }
 
     // crack any parameters
-    bool clear = false;
-    bool summary = false;
-    nsecs_t ts_since = 0;
     String16 summaryOption("-summary");
+    bool summary = false;
+    String16 protoOption("-proto");
     String16 clearOption("-clear");
+    bool clear = false;
     String16 sinceOption("-since");
+    nsecs_t ts_since = 0;
     String16 helpOption("-help");
     String16 onlyOption("-only");
-    const char *only = NULL;
+    AString only;
     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] == protoOption) {
+            i++;
+            if (i < n) {
+                String8 value(args[i]);
+                int proto = MediaAnalyticsItem::PROTO_V0;       // default to original
+                char *endp;
+                const char *p = value.string();
+                proto = strtol(p, &endp, 10);
+                if (endp != p || *endp == '\0') {
+                    if (proto < MediaAnalyticsItem::PROTO_FIRST) {
+                        proto = MediaAnalyticsItem::PROTO_FIRST;
+                    } else if (proto > MediaAnalyticsItem::PROTO_LAST) {
+                        proto = MediaAnalyticsItem::PROTO_LAST;
+                    }
+                    mDumpProto = proto;
+                }
+            }
         } else if (args[i] == sinceOption) {
             i++;
             if (i < n) {
@@ -368,18 +415,12 @@
             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;
-                }
+                only = value.string();
             }
         } else if (args[i] == helpOption) {
             result.append("Recognized parameters:\n");
             result.append("-help        this help message\n");
+            result.append("-proto X     dump using protocol X (defaults to 1)");
             result.append("-summary     show summary info\n");
             result.append("-clear       clears out saved records\n");
             result.append("-only X      process records for component X\n");
@@ -398,14 +439,12 @@
 
     dumpHeaders(result, ts_since);
 
-    // only want 1, to avoid confusing folks that parse the output
+    // want exactly 1, to avoid confusing folks that parse the output
     if (summary) {
-        dumpSummaries(result, ts_since, only);
+        dumpSummaries(result, ts_since, only.c_str());
     } else {
-        dumpRecent(result, ts_since, only);
+        dumpRecent(result, ts_since, only.c_str());
     }
-    free((void*)only);
-    only=NULL;
 
 
     if (clear) {
@@ -430,6 +469,9 @@
     const size_t SIZE = 512;
     char buffer[SIZE];
 
+    snprintf(buffer, SIZE, "Protocol Version: %d\n", mDumpProto);
+    result.append(buffer);
+
     int enabled = MediaAnalyticsItem::isEnabled();
     if (enabled) {
         snprintf(buffer, SIZE, "Metrics gathering: enabled\n");
@@ -439,10 +481,14 @@
     result.append(buffer);
 
     snprintf(buffer, SIZE,
-        "Since Boot: Submissions: %" PRId64
-            " Finalizations: %" PRId64
-        " Discarded: %" PRId64 "\n",
-        mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
+        "Since Boot: Submissions: %8" PRId64
+            " Finalizations: %8" PRId64 "\n",
+        mItemsSubmitted, mItemsFinalized);
+    result.append(buffer);
+    snprintf(buffer, SIZE,
+        "Records Discarded: %8" PRId64
+            " (by Count: %" PRId64 " by Expiration: %" PRId64 ")\n",
+         mItemsDiscarded, mItemsDiscardedCount, mItemsDiscardedExpire);
     result.append(buffer);
     snprintf(buffer, SIZE,
         "Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded);
@@ -464,6 +510,10 @@
     snprintf(buffer, SIZE, "\nSummarized Metrics:\n");
     result.append(buffer);
 
+    if (only != NULL && *only == '\0') {
+        only = NULL;
+    }
+
     // have each of the distillers dump records
     if (mSummarizerSets != NULL) {
         List<SummarizerSet *>::iterator itSet = mSummarizerSets->begin();
@@ -490,6 +540,10 @@
     const size_t SIZE = 512;
     char buffer[SIZE];
 
+    if (only != NULL && *only == '\0') {
+        only = NULL;
+    }
+
     // show the recently recorded records
     snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
     result.append(buffer);
@@ -526,7 +580,7 @@
                 ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only);
                 continue;
             }
-            AString entry = (*it)->toString();
+            AString entry = (*it)->toString(mDumpProto);
             result.appendFormat("%5d: %s\n", slot, entry.c_str());
             slot++;
         }
@@ -551,13 +605,32 @@
         l->push_back(item);
     }
 
-    // keep removing old records the front until we're in-bounds
+    // keep removing old records the front until we're in-bounds (count)
     if (mMaxRecords > 0) {
         while (l->size() > (size_t) mMaxRecords) {
             MediaAnalyticsItem * oitem = *(l->begin());
             l->erase(l->begin());
             delete oitem;
             mItemsDiscarded++;
+            mItemsDiscardedCount++;
+        }
+    }
+
+    // keep removing old records the front until we're in-bounds (count)
+    if (mMaxRecordAgeNs > 0) {
+        nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+        while (l->size() > 0) {
+            MediaAnalyticsItem * oitem = *(l->begin());
+            nsecs_t when = oitem->getTimestamp();
+            // careful about timejumps too
+            if ((now > when) && (now-when) <= mMaxRecordAgeNs) {
+                // this (and the rest) are recent enough to keep
+                break;
+            }
+            l->erase(l->begin());
+            delete oitem;
+            mItemsDiscarded++;
+            mItemsDiscardedExpire++;
         }
     }
 }
@@ -720,4 +793,85 @@
 
 }
 
+// mapping uids to package names
+
+// give me the package name, perhaps going to find it
+AString MediaAnalyticsService::getPkgName(uid_t uid, bool addIfMissing) {
+    ssize_t i = mPkgMappings.indexOfKey(uid);
+    if (i >= 0) {
+        AString pkg = mPkgMappings.valueAt(i);
+        ALOGV("returning pkg '%s' for uid %d", pkg.c_str(), uid);
+        return pkg;
+    }
+
+    AString pkg;
+
+    if (addIfMissing == false) {
+        return pkg;
+    }
+
+    struct passwd *pw = getpwuid(uid);
+    if (pw) {
+        pkg = pw->pw_name;
+    } else {
+        pkg = "-";
+    }
+
+    // find the proper value
+
+    sp<IBinder> binder = NULL;
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) {
+        ALOGE("defaultServiceManager failed");
+    } else {
+        binder = sm->getService(String16("package_native"));
+        if (binder == NULL) {
+            ALOGE("getService package_native failed");
+        }
+    }
+
+    if (binder != NULL) {
+        sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder);
+
+        std::vector<int> uids;
+        std::vector<std::string> names;
+
+        uids.push_back(uid);
+
+        binder::Status status = package_mgr->getNamesForUids(uids, &names);
+        if (!status.isOk()) {
+            ALOGE("package_native::getNamesForUids failed: %s",
+                  status.exceptionMessage().c_str());
+        } else {
+            if (!names[0].empty()) {
+                pkg = names[0].c_str();
+            }
+        }
+    }
+
+    // XXX determine whether package was side-loaded or from playstore.
+    // for privacy, we only list apps loaded from playstore.
+
+    // Sanitize the package name for ":"
+    // as an example, we get "shared:android.uid.systemui"
+    // replace : with something benign (I'm going to use !)
+    if (!pkg.empty()) {
+        int n = pkg.size();
+        char *p = (char *) pkg.c_str();
+        for (int i = 0 ; i < n; i++) {
+            if (p[i] == ':') {
+                p[i] = '!';
+            }
+        }
+    }
+
+    // add it to the map, to save a subsequent lookup
+    if (!pkg.empty()) {
+        ALOGV("Adding uid %d pkg '%s'", uid, pkg.c_str());
+        mPkgMappings.add(uid, pkg);
+    }
+
+    return pkg;
+}
+
 } // namespace android
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 6685967..4fe2fb2 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -54,6 +54,8 @@
     int64_t mItemsSubmitted;
     int64_t mItemsFinalized;
     int64_t mItemsDiscarded;
+    int64_t mItemsDiscardedExpire;
+    int64_t mItemsDiscardedCount;
     int64_t mSetsDiscarded;
     MediaAnalyticsItem::SessionID_t mLastSessionID;
 
@@ -61,9 +63,12 @@
     mutable Mutex           mLock;
     mutable Mutex           mLock_ids;
 
-    // the most we hold in memory
-    // up to this many in each queue (open, finalized)
+    // limit how many records we'll retain
+    // by count (in each queue (open, finalized))
     int32_t mMaxRecords;
+    // by time (none older than this long agan
+    nsecs_t mMaxRecordAgeNs;
+    //
     // # of sets of summaries
     int32_t mMaxRecordSets;
     // nsecs until we start a new record set
@@ -118,6 +123,7 @@
     void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
 
     // support for generating output
+    int mDumpProto;
     String8 dumpQueue(List<MediaAnalyticsItem*> *);
     String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
 
@@ -125,6 +131,15 @@
     void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);
     void dumpRecent(String8 &result, nsecs_t ts_since, const char * only);
 
+    // mapping uids to package names
+    struct UidToPkgMap {
+        uid_t uid;
+        AString pkg;
+    };
+
+    KeyedVector<uid_t,AString>  mPkgMappings;
+    AString getPkgName(uid_t uid, bool addIfMissing);
+
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/mediaanalytics/MetricsSummarizer.cpp b/services/mediaanalytics/MetricsSummarizer.cpp
index 3477f1f..93fe0ec 100644
--- a/services/mediaanalytics/MetricsSummarizer.cpp
+++ b/services/mediaanalytics/MetricsSummarizer.cpp
@@ -141,23 +141,23 @@
     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);
+        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) {
+            MediaAnalyticsItem *nitem = item->dup();
+            if (nitem == NULL) {
                 ALOGE("unable to save MediaMetrics record");
             }
-            sortProps(item);
-            item->setInt32("count",1);
-            mSummaries->push_back(item);
+            sortProps(nitem);
+            nitem->setInt32("aggregated",1);
+            mergeRecord(*nitem, *item);
+            mSummaries->push_back(nitem);
     } else {
             ALOGV("increment existing record");
-            (*it)->addInt32("count",1);
+            (*it)->addInt32("aggregated",1);
             mergeRecord(*(*it), *item);
     }
 }
@@ -168,6 +168,71 @@
     return;
 }
 
+// keep some stats for things: sums, counts, standard deviation
+// the integer version -- all of these pieces are in 64 bits
+void MetricsSummarizer::minMaxVar64(MediaAnalyticsItem &summation, const char *key, int64_t value) {
+    if (key == NULL)
+        return;
+    int len = strlen(key) + 32;
+    char *tmpKey = (char *)malloc(len);
+
+    if (tmpKey == NULL) {
+        return;
+    }
+
+    // N - count of samples
+    snprintf(tmpKey, len, "%s.n", key);
+    summation.addInt64(tmpKey, 1);
+
+    // zero - count of samples that are zero
+    if (value == 0) {
+        snprintf(tmpKey, len, "%s.zero", key);
+        int64_t zero = 0;
+        (void) summation.getInt64(tmpKey,&zero);
+        zero++;
+        summation.setInt64(tmpKey, zero);
+    }
+
+    // min
+    snprintf(tmpKey, len, "%s.min", key);
+    int64_t min = value;
+    if (summation.getInt64(tmpKey,&min)) {
+        if (min > value) {
+            summation.setInt64(tmpKey, value);
+        }
+    } else {
+        summation.setInt64(tmpKey, value);
+    }
+
+    // max
+    snprintf(tmpKey, len, "%s.max", key);
+    int64_t max = value;
+    if (summation.getInt64(tmpKey,&max)) {
+        if (max < value) {
+            summation.setInt64(tmpKey, value);
+        }
+    } else {
+        summation.setInt64(tmpKey, value);
+    }
+
+    // components for mean, stddev;
+    // stddev = sqrt(1/4*(sumx2 - (2*sumx*sumx/n) + ((sumx/n)^2)))
+    // sum x
+    snprintf(tmpKey, len, "%s.sumX", key);
+    summation.addInt64(tmpKey, value);
+    // sum x^2
+    snprintf(tmpKey, len, "%s.sumX2", key);
+    summation.addInt64(tmpKey, value*value);
+
+
+    // last thing we do -- remove the base key from the summation
+    // record so we won't get confused about it having both individual
+    // and summary information in there.
+    summation.removeProp(key);
+
+    free(tmpKey);
+}
+
 
 //
 // Comparators
@@ -186,20 +251,23 @@
     ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str());
     ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str());
 
+    // keep different sources/users separate
+    if (single->mUid != summ->mUid) {
+        return false;
+    }
+
     // 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) {
+            for (;*ig; ig++) {
                 if (strcmp(*ig, attrName) == 0) {
                     break;
                 }
-                ig++;
             }
             if (*ig) {
                 ALOGV("we don't mind that it has attr '%s'", attrName);
@@ -218,29 +286,42 @@
         }
         switch (prop1->mType) {
             case MediaAnalyticsItem::kTypeInt32:
-                if (prop1->u.int32Value != prop2->u.int32Value)
+                if (prop1->u.int32Value != prop2->u.int32Value) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             case MediaAnalyticsItem::kTypeInt64:
-                if (prop1->u.int64Value != prop2->u.int64Value)
+                if (prop1->u.int64Value != prop2->u.int64Value) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             case MediaAnalyticsItem::kTypeDouble:
                 // XXX: watch out for floating point comparisons!
-                if (prop1->u.doubleValue != prop2->u.doubleValue)
+                if (prop1->u.doubleValue != prop2->u.doubleValue) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             case MediaAnalyticsItem::kTypeCString:
-                if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0)
+                if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             case MediaAnalyticsItem::kTypeRate:
-                if (prop1->u.rate.count != prop2->u.rate.count)
+                if (prop1->u.rate.count != prop2->u.rate.count) {
+                    ALOGV("mismatch values");
                     return false;
-                if (prop1->u.rate.duration != prop2->u.rate.duration)
+                }
+                if (prop1->u.rate.duration != prop2->u.rate.duration) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             default:
+                ALOGV("mismatch values in default type");
                 return false;
         }
     }
@@ -248,15 +329,6 @@
     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;
@@ -267,14 +339,8 @@
 // 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());
-        }
     }
 }
 
diff --git a/services/mediaanalytics/MetricsSummarizer.h b/services/mediaanalytics/MetricsSummarizer.h
index 0b64eac..a9f0786 100644
--- a/services/mediaanalytics/MetricsSummarizer.h
+++ b/services/mediaanalytics/MetricsSummarizer.h
@@ -59,10 +59,9 @@
 
     // 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);
+
+    void minMaxVar64(MediaAnalyticsItem &summ, const char *key, int64_t value);
 
     static int PropSorter(const void *a, const void *b);
     void sortProps(MediaAnalyticsItem *item);
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.cpp b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
index 5162059..f882cb9 100644
--- a/services/mediaanalytics/MetricsSummarizerPlayer.cpp
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
@@ -51,37 +51,43 @@
     setIgnorables(player_ignorable);
 }
 
+// NB: this is also called for the first time -- so summation == item
+// Not sure if we need a flag for that or not.
+// In this particular mergeRecord() code -- we're' ok for this.
 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);
+        minMaxVar64(summation, "android.media.mediaplayer.durationMs", duration);
     }
+
     int64_t playing = 0;
-    if (item.getInt64("android.media.mediaplayer.playingMs", &playing))
+    if (item.getInt64("android.media.mediaplayer.playingMs", &playing)) {
         ALOGV("found playingMs of %" PRId64, playing);
-        if (playing >= 0) {
-            summation.addInt64("android.media.mediaplayer.playingMs",playing);
-        }
+    }
+    if (playing >= 0) {
+        minMaxVar64(summation,"android.media.mediaplayer.playingMs",playing);
+    }
+
     int64_t frames = 0;
-    if (item.getInt64("android.media.mediaplayer.frames", &frames))
+    if (item.getInt64("android.media.mediaplayer.frames", &frames)) {
         ALOGV("found framess of %" PRId64, frames);
-        if (frames >= 0) {
-            summation.addInt64("android.media.mediaplayer.frames",frames);
-        }
+    }
+    if (frames >= 0) {
+        minMaxVar64(summation,"android.media.mediaplayer.frames",frames);
+    }
+
     int64_t dropped = 0;
-    if (item.getInt64("android.media.mediaplayer.dropped", &dropped))
+    if (item.getInt64("android.media.mediaplayer.dropped", &dropped)) {
         ALOGV("found dropped of %" PRId64, dropped);
-        if (dropped >= 0) {
-            summation.addInt64("android.media.mediaplayer.dropped",dropped);
-        }
+    }
+    if (dropped >= 0) {
+        minMaxVar64(summation,"android.media.mediaplayer.dropped",dropped);
+    }
 }
 
 } // namespace android
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 43595a4..47041c5 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -92,7 +92,7 @@
 
     aaudio_result_t result = AAudioServiceStreamBase::start();
     if (!mInService && result == AAUDIO_OK) {
-        startClient(mMmapClient, &mClientHandle);
+        result = startClient(mMmapClient, &mClientHandle);
     }
     return result;
 }
@@ -107,7 +107,7 @@
     aaudio_result_t result = AAudioServiceStreamBase::pause();
     // TODO put before base::pause()?
     if (!mInService) {
-        stopClient(mClientHandle);
+        (void) stopClient(mClientHandle);
     }
     return result;
 }
@@ -119,7 +119,7 @@
     aaudio_result_t result = AAudioServiceStreamBase::stop();
     // TODO put before base::stop()?
     if (!mInService) {
-        stopClient(mClientHandle);
+        (void) stopClient(mClientHandle);
     }
     return result;
 }
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 8891aba..952acd7 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -840,7 +840,7 @@
         }
 
         const bool supports_stop_all =
-                (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() == ENOSYS);
+                (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() != -ENOSYS);
 
         for (size_t i = 0; i < mModels.size(); i++) {
             sp<Model> model = mModels.valueAt(i);