Merge "Add non-treble IOMXStore interface"
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 1dd5139..f906564 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -13,16 +13,23 @@
         "IDrm.cpp",
         "IDrmClient.cpp",
         "IMediaDrmService.cpp",
+        "PluginMetricsReporting.cpp",
         "SharedLibrary.cpp",
         "DrmHal.cpp",
         "CryptoHal.cpp",
+        "protos/plugin_metrics.proto",
     ],
 
+    proto: {
+        type: "lite",
+    },
+
     shared_libs: [
         "libbinder",
         "libcutils",
         "libdl",
         "liblog",
+        "libmediametrics",
         "libmediautils",
         "libstagefright_foundation",
         "libutils",
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 386546f..3150e3c 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -30,6 +30,7 @@
 #include <media/DrmHal.h>
 #include <media/DrmSessionClientInterface.h>
 #include <media/DrmSessionManager.h>
+#include <media/PluginMetricsReporting.h>
 #include <media/drm/DrmAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AString.h>
@@ -194,7 +195,18 @@
      mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {
 }
 
+void DrmHal::closeOpenSessions() {
+    if (mPlugin != NULL) {
+        for (size_t i = 0; i < mOpenSessions.size(); i++) {
+            mPlugin->closeSession(toHidlVec(mOpenSessions[i]));
+            DrmSessionManager::Instance()->removeSession(mOpenSessions[i]);
+        }
+    }
+    mOpenSessions.clear();
+}
+
 DrmHal::~DrmHal() {
+    closeOpenSessions();
     DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
 }
 
@@ -405,11 +417,12 @@
 
 status_t DrmHal::destroyPlugin() {
     Mutex::Autolock autoLock(mLock);
-
     if (mInitCheck != OK) {
         return mInitCheck;
     }
 
+    closeOpenSessions();
+    reportMetrics();
     setListener(NULL);
     if (mPlugin != NULL) {
         mPlugin->setListener(NULL);
@@ -461,6 +474,7 @@
     if (err == OK) {
         DrmSessionManager::Instance()->addSession(getCallingPid(),
                 mDrmSessionClient, sessionId);
+        mOpenSessions.push(sessionId);
     }
     return err;
 }
@@ -475,7 +489,14 @@
     Status status = mPlugin->closeSession(toHidlVec(sessionId));
     if (status == Status::OK) {
         DrmSessionManager::Instance()->removeSession(sessionId);
+        for (size_t i = 0; i < mOpenSessions.size(); i++) {
+            if (mOpenSessions[i] == sessionId) {
+                mOpenSessions.removeAt(i);
+                break;
+            }
+        }
     }
+    reportMetrics();
     return toStatusT(status);
 }
 
@@ -727,6 +748,12 @@
 
 status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const {
     Mutex::Autolock autoLock(mLock);
+    return getPropertyStringInternal(name, value);
+}
+
+status_t DrmHal::getPropertyStringInternal(String8 const &name, String8 &value) const {
+    // This function is internal to the class and should only be called while
+    // mLock is already held.
 
     if (mInitCheck != OK) {
         return mInitCheck;
@@ -748,6 +775,12 @@
 
 status_t DrmHal::getPropertyByteArray(String8 const &name, Vector<uint8_t> &value ) const {
     Mutex::Autolock autoLock(mLock);
+    return getPropertyByteArrayInternal(name, value);
+}
+
+status_t DrmHal::getPropertyByteArrayInternal(String8 const &name, Vector<uint8_t> &value ) const {
+    // This function is internal to the class and should only be called while
+    // mLock is already held.
 
     if (mInitCheck != OK) {
         return mInitCheck;
@@ -962,6 +995,7 @@
 void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused)
 {
     Mutex::Autolock autoLock(mLock);
+    closeOpenSessions();
     setListener(NULL);
     if (mPlugin != NULL) {
         mPlugin->setListener(NULL);
@@ -980,4 +1014,20 @@
     }
 }
 
+void DrmHal::reportMetrics() const
+{
+    Vector<uint8_t> metrics;
+    String8 vendor;
+    String8 description;
+    if (getPropertyStringInternal(String8("vendor"), vendor) == OK &&
+            getPropertyStringInternal(String8("description"), description) == OK &&
+            getPropertyByteArrayInternal(String8("metrics"), metrics) == OK) {
+        status_t res = android::reportDrmPluginMetrics(
+                metrics, vendor, description);
+        if (res != OK) {
+            ALOGE("Metrics were retrieved but could not be reported: %i", res);
+        }
+    }
+}
+
 }  // namespace android
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
new file mode 100644
index 0000000..a9302ea
--- /dev/null
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "PluginMetricsReporting"
+#include <utils/Log.h>
+
+#include <media/PluginMetricsReporting.h>
+
+#include <media/MediaAnalyticsItem.h>
+
+#include "protos/plugin_metrics.pb.h"
+
+namespace android {
+
+namespace {
+
+using android::drm_metrics::MetricsGroup;
+using android::drm_metrics::MetricsGroup_Metric;
+using android::drm_metrics::MetricsGroup_Metric_MetricValue;
+
+const char* const kParentAttribute = "/parent/external";
+
+status_t reportMetricsGroup(const MetricsGroup& metricsGroup,
+                            const String8& batchName,
+                            const int64_t* parentId) {
+    MediaAnalyticsItem analyticsItem(batchName.c_str());
+    analyticsItem.generateSessionID();
+    int64_t sessionId = analyticsItem.getSessionID();
+    if (parentId != NULL) {
+        analyticsItem.setInt64(kParentAttribute, *parentId);
+    }
+
+    for (int i = 0; i < metricsGroup.metric_size(); ++i) {
+        const MetricsGroup_Metric& metric = metricsGroup.metric(i);
+        if (!metric.has_name()) {
+            ALOGE("Metric with no name.");
+            return BAD_VALUE;
+        }
+
+        if (!metric.has_value()) {
+            ALOGE("Metric with no value.");
+            return BAD_VALUE;
+        }
+
+        const MetricsGroup_Metric_MetricValue& value = metric.value();
+        if (value.has_int_value()) {
+            analyticsItem.setInt64(metric.name().c_str(),
+                                   value.int_value());
+        } else if (value.has_double_value()) {
+            analyticsItem.setDouble(metric.name().c_str(),
+                                    value.double_value());
+        } else if (value.has_string_value()) {
+            analyticsItem.setCString(metric.name().c_str(),
+                                     value.string_value().c_str());
+        } else {
+            ALOGE("Metric Value with no actual value.");
+            return BAD_VALUE;
+        }
+    }
+
+    analyticsItem.setFinalized(true);
+    analyticsItem.selfrecord();
+
+    for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) {
+        const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i);
+        status_t res = reportMetricsGroup(subGroup, batchName, &sessionId);
+        if (res != OK) {
+            return res;
+        }
+    }
+
+    return OK;
+}
+
+String8 sanitize(const String8& input) {
+    // Filters the input string down to just alphanumeric characters.
+    String8 output;
+    for (size_t i = 0; i < input.size(); ++i) {
+        char candidate = input[i];
+        if ((candidate >= 'a' && candidate <= 'z') ||
+                (candidate >= 'A' && candidate <= 'Z') ||
+                (candidate >= '0' && candidate <= '9')) {
+            output.append(&candidate, 1);
+        }
+    }
+    return output;
+}
+
+}  // namespace
+
+status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
+                                const String8& vendor,
+                                const String8& description) {
+    MetricsGroup root_metrics_group;
+    if (!root_metrics_group.ParseFromArray(serializedMetrics.array(),
+                                           serializedMetrics.size())) {
+        ALOGE("Failure to parse.");
+        return BAD_VALUE;
+    }
+
+    String8 name = String8::format("drm.vendor.%s.%s",
+                                   sanitize(vendor).c_str(),
+                                   sanitize(description).c_str());
+
+    return reportMetricsGroup(root_metrics_group, name, NULL);
+}
+
+}  // namespace android
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
new file mode 100644
index 0000000..2d26f14
--- /dev/null
+++ b/drm/libmediadrm/protos/plugin_metrics.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+package android.drm_metrics;
+
+// need this if we are using libprotobuf-cpp-2.3.0-lite
+option optimize_for = LITE_RUNTIME;
+
+// The MetricsGroup is a collection of metric name/value pair instances
+// that can be serialized and provided to a caller.
+message MetricsGroup {
+  message Metric {
+    message MetricValue {
+      // Exactly one of the following values must be set.
+      optional int64 int_value = 1;
+      optional double double_value = 2;
+      optional string string_value = 3;
+    }
+
+    // The name of the metric. Must be valid UTF-8. Required.
+    optional string name = 1;
+
+    // The value of the metric. Required.
+    optional MetricValue value = 2;
+  }
+
+  // The list of name/value pairs of metrics.
+  repeated Metric metric = 1;
+
+  // Allow multiple sub groups of metrics.
+  repeated MetricsGroup metric_sub_group = 2;
+}
diff --git a/drm/mediacas/plugins/clearkey/tests/Android.mk b/drm/mediacas/plugins/clearkey/tests/Android.mk
index 5418c1d..e1545af 100644
--- a/drm/mediacas/plugins/clearkey/tests/Android.mk
+++ b/drm/mediacas/plugins/clearkey/tests/Android.mk
@@ -21,6 +21,7 @@
     ClearKeyFetcherTest.cpp
 
 LOCAL_MODULE := ClearKeyFetcherTest
+LOCAL_VENDOR_MODULE := true
 
 # LOCAL_LDFLAGS is needed here for the test to use the plugin, because
 # the plugin is not in standard library search path. Without this .so
diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.bp b/drm/mediadrm/plugins/clearkey/tests/Android.bp
index ac57d65..0fcfc64 100644
--- a/drm/mediadrm/plugins/clearkey/tests/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/tests/Android.bp
@@ -34,4 +34,5 @@
         "libstagefright_foundation",
         "libutils",
     ],
+    header_libs: ["media_plugin_headers"],
 }
diff --git a/include/OWNERS b/include/OWNERS
index 0ec7529..3cb6d9c 100644
--- a/include/OWNERS
+++ b/include/OWNERS
@@ -1,5 +1,6 @@
 elaurent@google.com
 gkasten@android.com
 hunga@google.com
+jtinker@google.com
 lajos@google.com
 marcone@google.com
diff --git a/include/media/PluginMetricsReporting.h b/include/media/PluginMetricsReporting.h
new file mode 120000
index 0000000..7d9a7a0
--- /dev/null
+++ b/include/media/PluginMetricsReporting.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/PluginMetricsReporting.h
\ No newline at end of file
diff --git a/media/OWNERS b/media/OWNERS
index cd4afb4..1605efd 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,5 +1,18 @@
+chz@google.com
+dwkang@google.com
+elaurent@google.com
+essick@google.com
 hunga@google.com
+jmtrivi@google.com
+krocard@google.com
 lajos@google.com
 marcone@google.com
+mnaganov@google.com
+pawin@google.com
+philburk@google.com
+pmclean@google.com
+rachad@google.com
+rago@google.com
 robertshih@google.com
 wjia@google.com
+wonsik@google.com
diff --git a/media/libaaudio/OWNERS b/media/libaaudio/OWNERS
new file mode 100644
index 0000000..f4d51f9
--- /dev/null
+++ b/media/libaaudio/OWNERS
@@ -0,0 +1 @@
+philburk@google.com
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 45a3beb..57d45cd 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -17,6 +17,7 @@
 // Play an impulse and then record it.
 // Measure the round trip latency.
 
+#include <algorithm>
 #include <assert.h>
 #include <cctype>
 #include <math.h>
@@ -25,11 +26,13 @@
 #include <unistd.h>
 
 #include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
 
-#define INPUT_PEAK_THRESHOLD    0.1f
-#define SILENCE_FRAMES          10000
+// Tag for machine readable results as property = value pairs
+#define RESULT_TAG              "RESULT: "
 #define SAMPLE_RATE             48000
-#define NUM_SECONDS             7
+#define NUM_SECONDS             5
+#define NUM_INPUT_CHANNELS      1
 #define FILENAME                "/data/oboe_input.raw"
 
 #define NANOS_PER_MICROSECOND ((int64_t)1000)
@@ -37,12 +40,172 @@
 #define MILLIS_PER_SECOND     1000
 #define NANOS_PER_SECOND      (NANOS_PER_MILLISECOND * MILLIS_PER_SECOND)
 
-class AudioRecorder
+#define MAX_ZEROTH_PARTIAL_BINS   40
+
+static const float s_Impulse[] = {
+        0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // silence on each side of the impulse
+        0.5f, 0.9f, 0.0f, -0.9f, -0.5f, // bipolar
+        0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
+
+
+static double calculateCorrelation(const float *a,
+                                   const float *b,
+                                   int windowSize)
+{
+    double correlation = 0.0;
+    double sumProducts = 0.0;
+    double sumSquares = 0.0;
+
+    // Correlate a against b.
+    for (int i = 0; i < windowSize; i++) {
+        float s1 = a[i];
+        float s2 = b[i];
+        // Use a normalized cross-correlation.
+        sumProducts += s1 * s2;
+        sumSquares += ((s1 * s1) + (s2 * s2));
+    }
+
+    if (sumSquares >= 0.00000001) {
+        correlation = (float) (2.0 * sumProducts / sumSquares);
+    }
+    return correlation;
+}
+
+static int calculateCorrelations(const float *haystack, int haystackSize,
+                                 const float *needle, int needleSize,
+                                 float *results, int resultSize)
+{
+    int ic;
+    int maxCorrelations = haystackSize - needleSize;
+    int numCorrelations = std::min(maxCorrelations, resultSize);
+
+    for (ic = 0; ic < numCorrelations; ic++) {
+        double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
+        results[ic] = correlation;
+    }
+
+    return numCorrelations;
+}
+
+/*==========================================================================================*/
+/**
+ * Scan until we get a correlation of a single scan that goes over the tolerance level,
+ * peaks then drops back down.
+ */
+static double findFirstMatch(const float *haystack, int haystackSize,
+                             const float *needle, int needleSize, double threshold  )
+{
+    int ic;
+    // How many correlations can we calculate?
+    int numCorrelations = haystackSize - needleSize;
+    double maxCorrelation = 0.0;
+    int peakIndex = -1;
+    double location = -1.0;
+
+    for (ic = 0; ic < numCorrelations; ic++) {
+        double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
+
+        if( (correlation > maxCorrelation) ) {
+            maxCorrelation = correlation;
+            peakIndex = ic;
+        }
+
+        //printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n",
+        //    ic, correlation, maxSum );
+        // Are we past what we were looking for?
+        if((maxCorrelation > threshold) && (correlation < 0.5 * maxCorrelation)) {
+            location = peakIndex;
+            break;
+        }
+    }
+
+    return location;
+}
+
+typedef struct LatencyReport_s {
+    double latencyInFrames;
+    double confidence;
+} LatencyReport;
+
+// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
+// Using first echo instead of the original impulse for a better match.
+int measureLatencyFromEchos(const float *haystack, int haystackSize,
+                          const float *needle, int needleSize,
+                          LatencyReport *report) {
+    double threshold = 0.1;
+
+    // Find first peak
+    int first = (int) (findFirstMatch(haystack,
+                                      haystackSize,
+                                      needle,
+                                      needleSize,
+                                      threshold) + 0.5);
+
+    // Use first echo as the needle for the other echos because
+    // it will be more similar.
+    needle = &haystack[first];
+    int again = (int) (findFirstMatch(haystack,
+                                      haystackSize,
+                                      needle,
+                                      needleSize,
+                                      threshold) + 0.5);
+
+    printf("first = %d, again at %d\n", first, again);
+    first = again;
+
+    // Allocate results array
+    int remaining = haystackSize - first;
+    int generous = 48000 * 2;
+    int numCorrelations = std::min(remaining, generous);
+    float *correlations = new float[numCorrelations];
+    float *harmonicSums = new float[numCorrelations](); // cleared to zero
+
+    // Generate correlation for every position.
+    numCorrelations = calculateCorrelations(&haystack[first], remaining,
+                                            needle, needleSize,
+                                            correlations, numCorrelations);
+
+    // Add higher harmonics mapped onto lower harmonics.
+    // This reinforces the "fundamental" echo.
+    const int numEchoes = 10;
+    for (int partial = 1; partial < numEchoes; partial++) {
+        for (int i = 0; i < numCorrelations; i++) {
+            harmonicSums[i / partial] += correlations[i] / partial;
+        }
+    }
+
+    // Find highest peak in correlation array.
+    float maxCorrelation = 0.0;
+    float sumOfPeaks = 0.0;
+    int peakIndex = 0;
+    const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins
+    for (int i = skip; i < numCorrelations; i++) {
+        if (harmonicSums[i] > maxCorrelation) {
+            maxCorrelation = harmonicSums[i];
+            sumOfPeaks += maxCorrelation;
+            peakIndex = i;
+            printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
+        }
+    }
+
+    report->latencyInFrames = peakIndex;
+    if (sumOfPeaks < 0.0001) {
+        report->confidence = 0.0;
+    } else {
+        report->confidence = maxCorrelation / sumOfPeaks;
+    }
+
+    delete[] correlations;
+    delete[] harmonicSums;
+    return 0;
+}
+
+class AudioRecording
 {
 public:
-    AudioRecorder() {
+    AudioRecording() {
     }
-    ~AudioRecorder() {
+    ~AudioRecording() {
         delete[] mData;
     }
 
@@ -52,7 +215,8 @@
         mMaxFrames = maxFrames;
     }
 
-    void record(int16_t *inputData, int inputChannelCount, int numFrames) {
+    // Write SHORT data from the first channel.
+    int write(int16_t *inputData, int inputChannelCount, int numFrames) {
         // stop at end of buffer
         if ((mFrameCounter + numFrames) > mMaxFrames) {
             numFrames = mMaxFrames - mFrameCounter;
@@ -60,9 +224,11 @@
         for (int i = 0; i < numFrames; i++) {
             mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
         }
+        return numFrames;
     }
 
-    void record(float *inputData, int inputChannelCount, int numFrames) {
+    // Write FLOAT data from the first channel.
+    int write(float *inputData, int inputChannelCount, int numFrames) {
         // stop at end of buffer
         if ((mFrameCounter + numFrames) > mMaxFrames) {
             numFrames = mMaxFrames - mFrameCounter;
@@ -70,162 +236,253 @@
         for (int i = 0; i < numFrames; i++) {
             mData[mFrameCounter++] = inputData[i * inputChannelCount];
         }
+        return numFrames;
     }
 
-    int save(const char *fileName) {
+    int size() {
+        return mFrameCounter;
+    }
+
+    float *getData() {
+        return mData;
+    }
+
+    int save(const char *fileName, bool writeShorts = true) {
+        int written = 0;
+        const int chunkSize = 64;
         FILE *fid = fopen(fileName, "wb");
         if (fid == NULL) {
-            return errno;
+            return -errno;
         }
-        int written = fwrite(mData, sizeof(float), mFrameCounter, fid);
+
+        if (writeShorts) {
+            int16_t buffer[chunkSize];
+            int32_t framesLeft = mFrameCounter;
+            int32_t cursor = 0;
+            while (framesLeft) {
+                int32_t framesToWrite = framesLeft < chunkSize ? framesLeft : chunkSize;
+                for (int i = 0; i < framesToWrite; i++) {
+                    buffer[i] = (int16_t) (mData[cursor++] * 32767);
+                }
+                written += fwrite(buffer, sizeof(int16_t), framesToWrite, fid);
+                framesLeft -= framesToWrite;
+            }
+        } else {
+            written = fwrite(mData, sizeof(float), mFrameCounter, fid);
+        }
         fclose(fid);
         return written;
     }
 
 private:
-    float *mData = NULL;
+    float  *mData = nullptr;
     int32_t mFrameCounter = 0;
     int32_t mMaxFrames = 0;
 };
 
 // ====================================================================================
-// ========================= Loopback Processor =======================================
-// ====================================================================================
 class LoopbackProcessor {
 public:
+    virtual ~LoopbackProcessor() = default;
 
-    // Calculate mean and standard deviation.
-    double calculateAverageLatency(double *deviation) {
-        if (mLatencyCount <= 0) {
-            return -1.0;
-        }
-        double sum = 0.0;
-        for (int i = 0; i < mLatencyCount; i++) {
-            sum += mLatencyArray[i];
-        }
-        double average = sum /  mLatencyCount;
-        sum = 0.0;
-        for (int i = 0; i < mLatencyCount; i++) {
-            double error = average - mLatencyArray[i];
-            sum += error * error; // squared
-        }
-        *deviation = sqrt(sum / mLatencyCount);
-        return average;
+    virtual void process(float *inputData, int inputChannelCount,
+                 float *outputData, int outputChannelCount,
+                 int numFrames) = 0;
+
+
+    virtual void report() = 0;
+
+    void setSampleRate(int32_t sampleRate) {
+        mSampleRate = sampleRate;
     }
 
-    float getMaxAmplitude() const { return mMaxAmplitude; }
-    int   getMeasurementCount() const { return mLatencyCount; }
-    float getAverageAmplitude() const { return mAmplitudeTotal / mAmplitudeCount; }
-
-    // TODO Convert this to a feedback circuit and then use auto-correlation to measure the period.
-    void process(float *inputData, int inputChannelCount,
-            float *outputData, int outputChannelCount,
-            int numFrames) {
-        (void) outputChannelCount;
-
-        // Measure peak and average amplitude.
-        for (int i = 0; i < numFrames; i++) {
-            float sample = inputData[i * inputChannelCount];
-            if (sample > mMaxAmplitude) {
-                mMaxAmplitude = sample;
-            }
-            if (sample < 0) {
-                sample = 0 - sample;
-            }
-            mAmplitudeTotal += sample;
-            mAmplitudeCount++;
-        }
-
-        // Clear output.
-        memset(outputData, 0, numFrames * outputChannelCount * sizeof(float));
-
-        // Wait a while between hearing the pulse and starting a new one.
-        if (mState == STATE_SILENT) {
-            mCounter += numFrames;
-            if (mCounter > SILENCE_FRAMES) {
-                //printf("LoopbackProcessor send impulse, burst #%d\n", mBurstCounter);
-                // copy impulse
-                for (float sample : mImpulse) {
-                    *outputData = sample;
-                    outputData += outputChannelCount;
-                }
-                mState = STATE_LISTENING;
-                mCounter = 0;
-            }
-        }
-        // Start listening as soon as we send the impulse.
-        if (mState ==  STATE_LISTENING) {
-            for (int i = 0; i < numFrames; i++) {
-                float sample = inputData[i * inputChannelCount];
-                if (sample >= INPUT_PEAK_THRESHOLD) {
-                    mLatencyArray[mLatencyCount++] = mCounter;
-                    if (mLatencyCount >= MAX_LATENCY_VALUES) {
-                        mState = STATE_DONE;
-                    } else {
-                        mState = STATE_SILENT;
-                    }
-                    mCounter = 0;
-                    break;
-                } else {
-                    mCounter++;
-                }
-            }
-        }
+    int32_t getSampleRate() {
+        return mSampleRate;
     }
 
-    void echo(float *inputData, int inputChannelCount,
-            float *outputData, int outputChannelCount,
-            int numFrames) {
-        int channelsValid = (inputChannelCount < outputChannelCount)
-            ? inputChannelCount : outputChannelCount;
-        for (int i = 0; i < numFrames; i++) {
-            int ic;
-            for (ic = 0; ic < channelsValid; ic++) {
-                outputData[ic] = inputData[ic];
-            }
-            for (ic = 0; ic < outputChannelCount; ic++) {
-                outputData[ic] = 0;
-            }
-            inputData += inputChannelCount;
-            outputData += outputChannelCount;
-        }
-    }
 private:
-    enum {
-        STATE_SILENT,
-        STATE_LISTENING,
-        STATE_DONE
-    };
-
-    enum {
-        MAX_LATENCY_VALUES = 64
-    };
-
-    int     mState = STATE_SILENT;
-    int32_t mCounter = 0;
-    int32_t mLatencyArray[MAX_LATENCY_VALUES];
-    int32_t mLatencyCount = 0;
-    float   mMaxAmplitude = 0;
-    float   mAmplitudeTotal = 0;
-    int32_t mAmplitudeCount = 0;
-    static const float mImpulse[5];
+    int32_t mSampleRate = SAMPLE_RATE;
 };
 
-const float LoopbackProcessor::mImpulse[5] = {0.5f, 0.9f, 0.0f, -0.9f, -0.5f};
+
+// ====================================================================================
+class EchoAnalyzer : public LoopbackProcessor {
+public:
+
+    EchoAnalyzer() : LoopbackProcessor() {
+        audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE);
+    }
+
+    void setGain(float gain) {
+        mGain = gain;
+    }
+
+    float getGain() {
+        return mGain;
+    }
+
+    void report() override {
+
+        const float *needle = s_Impulse;
+        int needleSize = (int)(sizeof(s_Impulse) / sizeof(float));
+        float *haystack = audioRecorder.getData();
+        int haystackSize = audioRecorder.size();
+        int result = measureLatencyFromEchos(haystack, haystackSize,
+                                              needle, needleSize,
+                                              &latencyReport);
+        if (latencyReport.confidence < 0.01) {
+            printf(" ERROR - confidence too low = %f\n", latencyReport.confidence);
+        } else {
+            double latencyMillis = 1000.0 * latencyReport.latencyInFrames / getSampleRate();
+            printf(RESULT_TAG "latency.frames     = %8.2f\n", latencyReport.latencyInFrames);
+            printf(RESULT_TAG "latency.msec       = %8.2f\n", latencyMillis);
+            printf(RESULT_TAG "latency.confidence = %8.6f\n", latencyReport.confidence);
+        }
+    }
+
+    void process(float *inputData, int inputChannelCount,
+                 float *outputData, int outputChannelCount,
+                 int numFrames) override {
+        int channelsValid = std::min(inputChannelCount, outputChannelCount);
+
+        audioRecorder.write(inputData, inputChannelCount, numFrames);
+
+        if (mLoopCounter < mLoopStart) {
+            // Output silence at the beginning.
+            for (int i = 0; i < numFrames; i++) {
+                int ic;
+                for (ic = 0; ic < outputChannelCount; ic++) {
+                    outputData[ic] = 0;
+                }
+                inputData += inputChannelCount;
+                outputData += outputChannelCount;
+            }
+        } else if (mLoopCounter == mLoopStart) {
+            // Send a bipolar impulse that we can easily detect.
+            for (float sample : s_Impulse) {
+                *outputData = sample;
+                outputData += outputChannelCount;
+            }
+        } else {
+            // Echo input to output.
+            for (int i = 0; i < numFrames; i++) {
+                int ic;
+                for (ic = 0; ic < channelsValid; ic++) {
+                    outputData[ic] = inputData[ic] * mGain;
+                }
+                for (; ic < outputChannelCount; ic++) {
+                    outputData[ic] = 0;
+                }
+                inputData += inputChannelCount;
+                outputData += outputChannelCount;
+            }
+        }
+
+        mLoopCounter++;
+    }
+
+private:
+    int   mLoopCounter = 0;
+    int   mLoopStart = 1000;
+    float mGain = 1.0f;
+
+    AudioRecording     audioRecorder;
+    LatencyReport      latencyReport;
+};
+
+
+// ====================================================================================
+class SineAnalyzer : public LoopbackProcessor {
+public:
+
+    void report() override {
+        double magnitude = calculateMagnitude();
+        printf("sine magnitude = %7.5f\n", magnitude);
+        printf("sine frames    = %7d\n", mFrameCounter);
+        printf("sine frequency = %7.1f Hz\n", mFrequency);
+    }
+
+    double calculateMagnitude(double *phasePtr = NULL) {
+        if (mFrameCounter == 0) {
+            return 0.0;
+        }
+        double sinMean = mSinAccumulator / mFrameCounter;
+        double cosMean = mCosAccumulator / mFrameCounter;
+        double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean ));
+        if( phasePtr != NULL )
+        {
+            double phase = atan2( sinMean, cosMean );
+            *phasePtr = phase;
+        }
+        return magnitude;
+    }
+
+    void process(float *inputData, int inputChannelCount,
+                 float *outputData, int outputChannelCount,
+                 int numFrames) override {
+        double phaseIncrement = 2.0 * M_PI * mFrequency / getSampleRate();
+
+        for (int i = 0; i < numFrames; i++) {
+            // Multiply input by sine/cosine
+            float sample = inputData[i * inputChannelCount];
+            float sinOut = sinf(mPhase);
+            mSinAccumulator += sample * sinOut;
+            mCosAccumulator += sample * cosf(mPhase);
+            // Advance and wrap phase
+            mPhase += phaseIncrement;
+            if (mPhase > (2.0 * M_PI)) {
+                mPhase -= (2.0 * M_PI);
+            }
+
+            // Output sine wave so we can measure it.
+            outputData[i * outputChannelCount] = sinOut;
+        }
+        mFrameCounter += numFrames;
+
+        double magnitude = calculateMagnitude();
+        if (mWaiting) {
+            if (magnitude < 0.001) {
+                // discard silence
+                mFrameCounter = 0;
+                mSinAccumulator = 0.0;
+                mCosAccumulator = 0.0;
+            } else {
+                mWaiting = false;
+            }
+        }
+    };
+
+    void setFrequency(int32_t frequency) {
+        mFrequency = frequency;
+    }
+
+    int32_t getFrequency() {
+        return mFrequency;
+    }
+
+private:
+    double  mFrequency = 300.0;
+    double  mPhase = 0.0;
+    int32_t mFrameCounter = 0;
+    double  mSinAccumulator = 0.0;
+    double  mCosAccumulator = 0.0;
+    bool    mWaiting = true;
+};
 
 // TODO make this a class that manages its own buffer allocation
 struct LoopbackData {
-    AAudioStream     *inputStream = nullptr;
-    int32_t           inputFramesMaximum = 0;
-    int16_t          *inputData = nullptr;
-    float            *conversionBuffer = nullptr;
-    int32_t           actualInputChannelCount = 0;
-    int32_t           actualOutputChannelCount = 0;
-    int32_t           inputBuffersToDiscard = 10;
+    AAudioStream      *inputStream = nullptr;
+    int32_t            inputFramesMaximum = 0;
+    int16_t           *inputData = nullptr;
+    float             *conversionBuffer = nullptr;
+    int32_t            actualInputChannelCount = 0;
+    int32_t            actualOutputChannelCount = 0;
+    int32_t            inputBuffersToDiscard = 10;
 
-    aaudio_result_t   inputError;
-    LoopbackProcessor loopbackProcessor;
-    AudioRecorder     audioRecorder;
+    aaudio_result_t    inputError;
+    SineAnalyzer       sineAnalyzer;
+    EchoAnalyzer       echoAnalyzer;
+    LoopbackProcessor *loopbackProcessor;
 };
 
 static void convertPcm16ToFloat(const int16_t *source,
@@ -248,6 +505,7 @@
         int32_t numFrames
 ) {
     (void) outputStream;
+    aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_CONTINUE;
     LoopbackData *myData = (LoopbackData *) userData;
     float  *outputData = (float  *) audioData;
 
@@ -266,6 +524,7 @@
                                        numFrames, 0);
             if (framesRead < 0) {
                 myData->inputError = framesRead;
+                result = AAUDIO_CALLBACK_RESULT_STOP;
             } else if (framesRead > 0) {
                 myData->inputBuffersToDiscard--;
             }
@@ -275,16 +534,13 @@
                                        numFrames, 0);
         if (framesRead < 0) {
             myData->inputError = framesRead;
+            result = AAUDIO_CALLBACK_RESULT_STOP;
         } else if (framesRead > 0) {
-            // Process valid input data.
-            myData->audioRecorder.record(myData->inputData,
-                                         myData->actualInputChannelCount,
-                                         framesRead);
 
             int32_t numSamples = framesRead * myData->actualInputChannelCount;
             convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples);
 
-            myData->loopbackProcessor.process(myData->conversionBuffer,
+            myData->loopbackProcessor->process(myData->conversionBuffer,
                                               myData->actualInputChannelCount,
                                               outputData,
                                               myData->actualOutputChannelCount,
@@ -292,17 +548,25 @@
         }
     }
 
-    return AAUDIO_CALLBACK_RESULT_CONTINUE;
+    return result;
 }
 
+
 static void usage() {
-    printf("loopback: -b{burstsPerBuffer} -p{outputPerfMode} -P{inputPerfMode}\n");
-    printf("          -b{burstsPerBuffer} for example 2 for double buffered\n");
-    printf("          -p{outputPerfMode}  set output AAUDIO_PERFORMANCE_MODE*\n");
-    printf("          -P{inputPerfMode}   set input AAUDIO_PERFORMANCE_MODE*\n");
+    printf("loopback: -n{numBursts} -p{outPerf} -P{inPerf} -t{test} -g{gain} -f{freq}\n");
+    printf("          -c{inputChannels}\n");
+    printf("          -f{freq}  sine frequency\n");
+    printf("          -g{gain}  recirculating loopback gain\n");
+    printf("          -m enable MMAP mode\n");
+    printf("          -n{numBursts} buffer size, for example 2 for double buffered\n");
+    printf("          -p{outPerf}  set output AAUDIO_PERFORMANCE_MODE*\n");
+    printf("          -P{inPerf}   set input AAUDIO_PERFORMANCE_MODE*\n");
     printf("              n for _NONE\n");
     printf("              l for _LATENCY\n");
     printf("              p for _POWER_SAVING;\n");
+    printf("          -t{test}   select test mode\n");
+    printf("              m for sine magnitude\n");
+    printf("              e for echo latency (default)\n");
     printf("For example:  loopback -b2 -pl -Pn\n");
 }
 
@@ -320,12 +584,34 @@
             mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
             break;
         default:
-            printf("ERROR invalue performance mode %c\n", c);
+            printf("ERROR in value performance mode %c\n", c);
             break;
     }
     return mode;
 }
 
+enum {
+    TEST_SINE_MAGNITUDE = 0,
+    TEST_ECHO_LATENCY,
+};
+
+static int parseTestMode(char c) {
+    int testMode = TEST_ECHO_LATENCY;
+    c = tolower(c);
+    switch (c) {
+        case 'm':
+            testMode = TEST_SINE_MAGNITUDE;
+            break;
+        case 'e':
+            testMode = TEST_ECHO_LATENCY;
+            break;
+        default:
+            printf("ERROR in value test mode %c\n", c);
+            break;
+    }
+    return testMode;
+}
+
 // ====================================================================================
 // TODO break up this large main() function into smaller functions
 int main(int argc, const char **argv)
@@ -334,7 +620,7 @@
     LoopbackData loopbackData;
     AAudioStream *outputStream = nullptr;
 
-    const int requestedInputChannelCount = 1;
+    int requestedInputChannelCount = NUM_INPUT_CHANNELS;
     const int requestedOutputChannelCount = AAUDIO_UNSPECIFIED;
     const int requestedSampleRate = SAMPLE_RATE;
     int actualSampleRate = 0;
@@ -342,6 +628,9 @@
     const aaudio_format_t requestedOutputFormat = AAUDIO_FORMAT_PCM_FLOAT;
     aaudio_format_t actualInputFormat;
     aaudio_format_t actualOutputFormat;
+    int testMode = TEST_ECHO_LATENCY;
+    double frequency = 1000.0;
+    double gain = 1.0;
 
     const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
     //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
@@ -363,7 +652,19 @@
         if (arg[0] == '-') {
             char option = arg[1];
             switch (option) {
-                case 'b':
+                case 'c':
+                    requestedInputChannelCount = atoi(&arg[2]);
+                    break;
+                case 'f':
+                    frequency = atof(&arg[2]);
+                    break;
+                case 'g':
+                    gain = atof(&arg[2]);
+                    break;
+                case 'm':
+                    AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
+                    break;
+                case 'n':
                     burstsPerBuffer = atoi(&arg[2]);
                     break;
                 case 'p':
@@ -372,16 +673,35 @@
                 case 'P':
                     inputPerformanceLevel = parsePerformanceMode(arg[2]);
                     break;
+                case 't':
+                    testMode = parseTestMode(arg[2]);
+                    break;
                 default:
                     usage();
+                    exit(0);
                     break;
             }
         } else {
+            usage();
+            exit(0);
             break;
         }
     }
 
-    loopbackData.audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE);
+
+    switch(testMode) {
+        case TEST_SINE_MAGNITUDE:
+            loopbackData.sineAnalyzer.setFrequency(frequency);
+            loopbackData.loopbackProcessor = &loopbackData.sineAnalyzer;
+            break;
+        case TEST_ECHO_LATENCY:
+            loopbackData.echoAnalyzer.setGain(gain);
+            loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer;
+            break;
+        default:
+            exit(1);
+            break;
+    }
 
     // Make printf print immediately so that debug info is not stuck
     // in a buffer if we hang or crash.
@@ -431,47 +751,68 @@
     printf("    channelCount: requested = %d, actual = %d\n", requestedInputChannelCount,
            loopbackData.actualInputChannelCount);
     printf("    framesPerBurst = %d\n", AAudioStream_getFramesPerBurst(loopbackData.inputStream));
+    printf("    bufferSize     = %d\n",
+           AAudioStream_getBufferSizeInFrames(loopbackData.inputStream));
+    printf("    bufferCapacity = %d\n",
+           AAudioStream_getBufferCapacityInFrames(loopbackData.inputStream));
+
+    actualSharingMode = AAudioStream_getSharingMode(loopbackData.inputStream);
+    printf("    sharingMode: requested = %d, actual = %d\n",
+           requestedSharingMode, actualSharingMode);
 
     actualInputFormat = AAudioStream_getFormat(loopbackData.inputStream);
-    printf("    dataFormat: requested = %d, actual = %d\n", requestedInputFormat, actualInputFormat);
+    printf("    dataFormat: requested = %d, actual = %d\n",
+           requestedInputFormat, actualInputFormat);
     assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16);
 
+    printf("    is MMAP used?         = %s\n", AAudioStream_isMMapUsed(loopbackData.inputStream)
+                                               ? "yes" : "no");
+
+
     printf("Stream OUTPUT ---------------------\n");
     // Check to see what kind of stream we actually got.
     actualSampleRate = AAudioStream_getSampleRate(outputStream);
     printf("    sampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate);
+    loopbackData.echoAnalyzer.setSampleRate(actualSampleRate);
 
     loopbackData.actualOutputChannelCount = AAudioStream_getChannelCount(outputStream);
     printf("    channelCount: requested = %d, actual = %d\n", requestedOutputChannelCount,
            loopbackData.actualOutputChannelCount);
 
     actualSharingMode = AAudioStream_getSharingMode(outputStream);
-    printf("    sharingMode: requested = %d, actual = %d\n", requestedSharingMode, actualSharingMode);
+    printf("    sharingMode: requested = %d, actual = %d\n",
+           requestedSharingMode, actualSharingMode);
 
     // This is the number of frames that are read in one chunk by a DMA controller
     // or a DSP or a mixer.
     framesPerBurst = AAudioStream_getFramesPerBurst(outputStream);
     printf("    framesPerBurst = %d\n", framesPerBurst);
 
-    printf("    bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream));
-
-    actualOutputFormat = AAudioStream_getFormat(outputStream);
-    printf("    dataFormat: requested = %d, actual = %d\n", requestedOutputFormat, actualOutputFormat);
-    assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
-
-    // Allocate a buffer for the audio data.
-    loopbackData.inputFramesMaximum = 32 * framesPerBurst;
-
-    loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount];
-    loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum *
-                                              loopbackData.actualInputChannelCount];
-
     result = AAudioStream_setBufferSizeInFrames(outputStream, burstsPerBuffer * framesPerBurst);
     if (result < 0) { // may be positive buffer size
         fprintf(stderr, "ERROR - AAudioStream_setBufferSize() returned %d\n", result);
         goto finish;
     }
-    printf("AAudioStream_setBufferSize() actual = %d\n",result);
+    printf("    bufferSize     = %d\n", AAudioStream_getBufferSizeInFrames(outputStream));
+    printf("    bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream));
+
+    actualOutputFormat = AAudioStream_getFormat(outputStream);
+    printf("    dataFormat: requested = %d, actual = %d\n",
+           requestedOutputFormat, actualOutputFormat);
+    assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
+
+    printf("    is MMAP used?         = %s\n", AAudioStream_isMMapUsed(outputStream)
+                                               ? "yes" : "no");
+
+    // Allocate a buffer for the audio data.
+    loopbackData.inputFramesMaximum = 32 * framesPerBurst;
+    loopbackData.inputBuffersToDiscard = 100;
+
+    loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum
+                                         * loopbackData.actualInputChannelCount];
+    loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum *
+                                              loopbackData.actualInputChannelCount];
+
 
     // Start output first so input stream runs low.
     result = AAudioStream_requestStart(outputStream);
@@ -500,18 +841,13 @@
     printf("framesRead    = %d\n", (int) AAudioStream_getFramesRead(outputStream));
     printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(outputStream));
 
-    latency = loopbackData.loopbackProcessor.calculateAverageLatency(&deviation);
-    printf("measured peak    = %8.5f\n", loopbackData.loopbackProcessor.getMaxAmplitude());
-    printf("threshold        = %8.5f\n", INPUT_PEAK_THRESHOLD);
-    printf("measured average = %8.5f\n", loopbackData.loopbackProcessor.getAverageAmplitude());
-    printf("# latency measurements = %d\n", loopbackData.loopbackProcessor.getMeasurementCount());
-    printf("measured latency = %8.2f +/- %4.5f frames\n", latency, deviation);
-    printf("measured latency = %8.2f msec  <===== !!\n", (1000.0 * latency / actualSampleRate));
+    loopbackData.loopbackProcessor->report();
 
-    {
-        int written = loopbackData.audioRecorder.save(FILENAME);
-        printf("wrote %d samples to %s\n", written, FILENAME);
-    }
+//    {
+//        int written = loopbackData.audioRecorder.save(FILENAME);
+//        printf("wrote %d mono samples to %s on Android device\n", written, FILENAME);
+//    }
+
 
 finish:
     AAudioStream_close(outputStream);
@@ -521,7 +857,13 @@
     delete[] outputData;
     AAudioStreamBuilder_delete(builder);
 
-    printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
-    return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+    printf(RESULT_TAG "error = %d = %s\n", result, AAudio_convertResultToText(result));
+    if ((result != AAUDIO_OK)) {
+        printf("error %d = %s\n", result, AAudio_convertResultToText(result));
+        return EXIT_FAILURE;
+    } else {
+        printf("SUCCESS\n");
+        return EXIT_SUCCESS;
+    }
 }
 
diff --git a/media/libaaudio/examples/utils/dummy.cpp b/media/libaaudio/examples/utils/dummy.cpp
new file mode 100644
index 0000000..8ef7e36
--- /dev/null
+++ b/media/libaaudio/examples/utils/dummy.cpp
@@ -0,0 +1,5 @@
+/**
+ * Dummy file needed to get Android Studio to scan this folder.
+ */
+
+int g_DoNotUseThisVariable = 0;
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index 7131c6c..cfcf27a 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -32,6 +32,7 @@
     core/AudioStream.cpp \
     core/AudioStreamBuilder.cpp \
     core/AAudioAudio.cpp \
+    core/AAudioStreamParameters.cpp \
     legacy/AudioStreamLegacy.cpp \
     legacy/AudioStreamRecord.cpp \
     legacy/AudioStreamTrack.cpp \
@@ -90,6 +91,7 @@
 LOCAL_SRC_FILES = core/AudioStream.cpp \
     core/AudioStreamBuilder.cpp \
     core/AAudioAudio.cpp \
+    core/AAudioStreamParameters.cpp \
     legacy/AudioStreamLegacy.cpp \
     legacy/AudioStreamRecord.cpp \
     legacy/AudioStreamTrack.cpp \
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index 6464334..a268e49 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -57,7 +57,7 @@
         , Singleton<AAudioBinderClient>() {
 
     mAAudioClient = new AAudioClient(this);
-    ALOGD("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
+    ALOGV("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
 }
 
 AAudioBinderClient::~AAudioBinderClient() {
@@ -90,8 +90,11 @@
             if (binder != 0) {
                 // Ask for notification if the service dies.
                 status_t status = binder->linkToDeath(mAAudioClient);
-                ALOGD("getAAudioService: linkToDeath(mAAudioClient = %p) returned %d",
-                      mAAudioClient.get(), status);
+                // TODO review what we should do if this fails
+                if (status != NO_ERROR) {
+                    ALOGE("getAAudioService: linkToDeath(mAAudioClient = %p) returned %d",
+                          mAAudioClient.get(), status);
+                }
                 mAAudioService = interface_cast<IAAudioService>(binder);
                 needToRegister = true;
                 // Make sure callbacks can be received by mAAudioClient
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index 44edb1d..e763934 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -36,17 +36,17 @@
 
 status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const {
     status_t status;
-    status = parcel->writeInt32(mDeviceId);
+    status = parcel->writeInt32(getDeviceId());
     if (status != NO_ERROR) goto error;
-    status = parcel->writeInt32(mSampleRate);
+    status = parcel->writeInt32(getSampleRate());
     if (status != NO_ERROR) goto error;
-    status = parcel->writeInt32(mSamplesPerFrame);
+    status = parcel->writeInt32(getSamplesPerFrame());
     if (status != NO_ERROR) goto error;
-    status = parcel->writeInt32((int32_t) mSharingMode);
+    status = parcel->writeInt32((int32_t) getSharingMode());
     if (status != NO_ERROR) goto error;
-    status = parcel->writeInt32((int32_t) mAudioFormat);
+    status = parcel->writeInt32((int32_t) getFormat());
     if (status != NO_ERROR) goto error;
-    status = parcel->writeInt32(mBufferCapacity);
+    status = parcel->writeInt32(getBufferCapacity());
     if (status != NO_ERROR) goto error;
     return NO_ERROR;
 error:
@@ -55,57 +55,27 @@
 }
 
 status_t AAudioStreamConfiguration::readFromParcel(const Parcel* parcel) {
-    status_t status = parcel->readInt32(&mDeviceId);
+    int32_t value;
+    status_t status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
-    status = parcel->readInt32(&mSampleRate);
+    setDeviceId(value);
+    status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
-    status = parcel->readInt32(&mSamplesPerFrame);
+    setSampleRate(value);
+    status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
-    status = parcel->readInt32(&mSharingMode);
+    setSamplesPerFrame(value);
+    status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
-    status = parcel->readInt32(&mAudioFormat);
+    setSharingMode(value);
+    status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
-    status = parcel->readInt32(&mBufferCapacity);
+    setFormat(value);
+    status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
+    setBufferCapacity(value);
     return NO_ERROR;
 error:
     ALOGE("AAudioStreamConfiguration.readFromParcel(): read failed = %d", status);
     return status;
-}
-
-aaudio_result_t AAudioStreamConfiguration::validate() const {
-    // Validate results of the open.
-    if (mSampleRate < 0 || mSampleRate >= 8 * 48000) { // TODO review limits
-        ALOGE("AAudioStreamConfiguration.validate(): invalid sampleRate = %d", mSampleRate);
-        return AAUDIO_ERROR_INTERNAL;
-    }
-
-    if (mSamplesPerFrame < 1 || mSamplesPerFrame >= 32) { // TODO review limits
-        ALOGE("AAudioStreamConfiguration.validate() invalid samplesPerFrame = %d", mSamplesPerFrame);
-        return AAUDIO_ERROR_INTERNAL;
-    }
-
-    switch (mAudioFormat) {
-    case AAUDIO_FORMAT_PCM_I16:
-    case AAUDIO_FORMAT_PCM_FLOAT:
-        break;
-    default:
-        ALOGE("AAudioStreamConfiguration.validate() invalid audioFormat = %d", mAudioFormat);
-        return AAUDIO_ERROR_INTERNAL;
-    }
-
-    if (mBufferCapacity < 0) {
-        ALOGE("AAudioStreamConfiguration.validate() invalid mBufferCapacity = %d", mBufferCapacity);
-        return AAUDIO_ERROR_INTERNAL;
-    }
-    return AAUDIO_OK;
-}
-
-void AAudioStreamConfiguration::dump() const {
-    ALOGD("AAudioStreamConfiguration mDeviceId        = %d", mDeviceId);
-    ALOGD("AAudioStreamConfiguration mSampleRate      = %d", mSampleRate);
-    ALOGD("AAudioStreamConfiguration mSamplesPerFrame = %d", mSamplesPerFrame);
-    ALOGD("AAudioStreamConfiguration mSharingMode     = %d", (int)mSharingMode);
-    ALOGD("AAudioStreamConfiguration mAudioFormat     = %d", (int)mAudioFormat);
-    ALOGD("AAudioStreamConfiguration mBufferCapacity  = %d", mBufferCapacity);
-}
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.h b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
index 144595a..b324896 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.h
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
@@ -22,6 +22,7 @@
 #include <aaudio/AAudio.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
+#include "core/AAudioStreamParameters.h"
 
 using android::status_t;
 using android::Parcel;
@@ -29,74 +30,14 @@
 
 namespace aaudio {
 
-class AAudioStreamConfiguration : public Parcelable {
+class AAudioStreamConfiguration : public AAudioStreamParameters, public Parcelable {
 public:
     AAudioStreamConfiguration();
     virtual ~AAudioStreamConfiguration();
 
-    int32_t getDeviceId() const {
-        return mDeviceId;
-    }
-
-    void setDeviceId(int32_t deviceId) {
-        mDeviceId = deviceId;
-    }
-
-    int32_t getSampleRate() const {
-        return mSampleRate;
-    }
-
-    void setSampleRate(int32_t sampleRate) {
-        mSampleRate = sampleRate;
-    }
-
-    int32_t getSamplesPerFrame() const {
-        return mSamplesPerFrame;
-    }
-
-    void setSamplesPerFrame(int32_t samplesPerFrame) {
-        mSamplesPerFrame = samplesPerFrame;
-    }
-
-    aaudio_format_t getAudioFormat() const {
-        return mAudioFormat;
-    }
-
-    void setAudioFormat(aaudio_format_t audioFormat) {
-        mAudioFormat = audioFormat;
-    }
-
-    aaudio_sharing_mode_t getSharingMode() const {
-        return mSharingMode;
-    }
-
-    void setSharingMode(aaudio_sharing_mode_t sharingMode) {
-        mSharingMode = sharingMode;
-    }
-
-    int32_t getBufferCapacity() const {
-        return mBufferCapacity;
-    }
-
-    void setBufferCapacity(int32_t frames) {
-        mBufferCapacity = frames;
-    }
-
     virtual status_t writeToParcel(Parcel* parcel) const override;
 
     virtual status_t readFromParcel(const Parcel* parcel) override;
-
-    aaudio_result_t validate() const;
-
-    void dump() const;
-
-private:
-    int32_t               mDeviceId        = AAUDIO_UNSPECIFIED;
-    int32_t               mSampleRate      = AAUDIO_UNSPECIFIED;
-    int32_t               mSamplesPerFrame = AAUDIO_UNSPECIFIED;
-    aaudio_sharing_mode_t mSharingMode     = AAUDIO_SHARING_MODE_SHARED;
-    aaudio_format_t       mAudioFormat     = AAUDIO_FORMAT_UNSPECIFIED;
-    int32_t               mBufferCapacity  = AAUDIO_UNSPECIFIED;
 };
 
 } /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
index 97fbaaa..b3c4934 100644
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ b/media/libaaudio/src/binding/IAAudioService.cpp
@@ -264,13 +264,19 @@
         case OPEN_STREAM: {
             CHECK_INTERFACE(IAAudioService, data, reply);
             request.readFromParcel(&data);
-            //ALOGD("BnAAudioService::client openStream request dump --------------------");
-            //request.dump();
-            // Override the uid and pid from the client in case they are incorrect.
-            request.setUserId(IPCThreadState::self()->getCallingUid());
-            request.setProcessId(IPCThreadState::self()->getCallingPid());
-            streamHandle = openStream(request, configuration);
-            //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", streamHandle);
+            result = request.validate();
+            if (result != AAUDIO_OK) {
+                streamHandle = result;
+            } else {
+                //ALOGD("BnAAudioService::client openStream request dump --------------------");
+                //request.dump();
+                // Override the uid and pid from the client in case they are incorrect.
+                request.setUserId(IPCThreadState::self()->getCallingUid());
+                request.setProcessId(IPCThreadState::self()->getCallingPid());
+                streamHandle = openStream(request, configuration);
+                //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X",
+                //        streamHandle);
+            }
             reply->writeInt32(streamHandle);
             configuration.writeToParcel(reply);
             return NO_ERROR;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 7b01e44..8b14922 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -86,7 +86,7 @@
         setFormat(AAUDIO_FORMAT_PCM_FLOAT);
     }
     // Request FLOAT for the shared mixer.
-    request.getConfiguration().setAudioFormat(AAUDIO_FORMAT_PCM_FLOAT);
+    request.getConfiguration().setFormat(AAUDIO_FORMAT_PCM_FLOAT);
 
     // Build the request to send to the server.
     request.setUserId(getuid());
@@ -119,7 +119,7 @@
         setSharingMode(configuration.getSharingMode());
 
         // Save device format so we can do format conversion and volume scaling together.
-        mDeviceFormat = configuration.getAudioFormat();
+        mDeviceFormat = configuration.getFormat();
 
         result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
         if (result != AAUDIO_OK) {
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index 73f4c1d..6ea1172 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "AAudio"
 //#define LOG_NDEBUG 0
-#include <utils/Log.h>
+#include <log/log.h>
 
 #include <stdint.h>
 
@@ -25,7 +25,6 @@
 
 #define MIN_LATENESS_NANOS (10 * AAUDIO_NANOS_PER_MICROSECOND)
 
-using namespace android;
 using namespace aaudio;
 
 IsochronousClockModel::IsochronousClockModel()
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
new file mode 100644
index 0000000..65c2b46
--- /dev/null
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 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 "AAudio"
+#include <utils/Log.h>
+#include <hardware/audio.h>
+
+#include "AAudioStreamParameters.h"
+
+using namespace aaudio;
+
+// TODO These defines should be moved to a central place in audio.
+#define SAMPLES_PER_FRAME_MIN        1
+// TODO Remove 8 channel limitation.
+#define SAMPLES_PER_FRAME_MAX        FCC_8
+#define SAMPLE_RATE_HZ_MIN           8000
+// HDMI supports up to 32 channels at 1536000 Hz.
+#define SAMPLE_RATE_HZ_MAX           1600000
+
+AAudioStreamParameters::AAudioStreamParameters() {}
+AAudioStreamParameters::~AAudioStreamParameters() {}
+
+aaudio_result_t AAudioStreamParameters::validate() const {
+    if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
+        && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
+        ALOGE("AAudioStreamParameters: channelCount out of range = %d", mSamplesPerFrame);
+        return AAUDIO_ERROR_OUT_OF_RANGE;
+    }
+
+    if (mDeviceId < 0) {
+        ALOGE("AAudioStreamParameters: deviceId out of range = %d", mDeviceId);
+        return AAUDIO_ERROR_OUT_OF_RANGE;
+    }
+
+    switch (mSharingMode) {
+        case AAUDIO_SHARING_MODE_EXCLUSIVE:
+        case AAUDIO_SHARING_MODE_SHARED:
+            break;
+        default:
+            ALOGE("AAudioStreamParameters: illegal sharingMode = %d", mSharingMode);
+            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+            // break;
+    }
+
+    switch (mAudioFormat) {
+        case AAUDIO_FORMAT_UNSPECIFIED:
+        case AAUDIO_FORMAT_PCM_I16:
+        case AAUDIO_FORMAT_PCM_FLOAT:
+            break; // valid
+        default:
+            ALOGE("AAudioStreamParameters: audioFormat not valid = %d", mAudioFormat);
+            return AAUDIO_ERROR_INVALID_FORMAT;
+            // break;
+    }
+
+    if (mSampleRate != AAUDIO_UNSPECIFIED
+        && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) {
+        ALOGE("AAudioStreamParameters: sampleRate out of range = %d", mSampleRate);
+        return AAUDIO_ERROR_INVALID_RATE;
+    }
+
+    if (mBufferCapacity < 0) {
+        ALOGE("AAudioStreamParameters: bufferCapacity out of range = %d", mBufferCapacity);
+        return AAUDIO_ERROR_OUT_OF_RANGE;
+    }
+
+    return AAUDIO_OK;
+}
+
+void AAudioStreamParameters::dump() const {
+    ALOGD("AAudioStreamParameters mDeviceId        = %d", mDeviceId);
+    ALOGD("AAudioStreamParameters mSampleRate      = %d", mSampleRate);
+    ALOGD("AAudioStreamParameters mSamplesPerFrame = %d", mSamplesPerFrame);
+    ALOGD("AAudioStreamParameters mSharingMode     = %d", (int)mSharingMode);
+    ALOGD("AAudioStreamParameters mAudioFormat     = %d", (int)mAudioFormat);
+    ALOGD("AAudioStreamParameters mBufferCapacity  = %d", mBufferCapacity);
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
new file mode 100644
index 0000000..97379cc
--- /dev/null
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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 AAUDIO_STREAM_PARAMETERS_H
+#define AAUDIO_STREAM_PARAMETERS_H
+
+#include <stdint.h>
+
+#include <aaudio/AAudio.h>
+
+namespace aaudio {
+
+class AAudioStreamParameters {
+public:
+    AAudioStreamParameters();
+    virtual ~AAudioStreamParameters();
+
+    int32_t getDeviceId() const {
+        return mDeviceId;
+    }
+
+    void setDeviceId(int32_t deviceId) {
+        mDeviceId = deviceId;
+    }
+
+    int32_t getSampleRate() const {
+        return mSampleRate;
+    }
+
+    void setSampleRate(int32_t sampleRate) {
+        mSampleRate = sampleRate;
+    }
+
+    int32_t getSamplesPerFrame() const {
+        return mSamplesPerFrame;
+    }
+
+    /**
+     * This is also known as channelCount.
+     */
+    void setSamplesPerFrame(int32_t samplesPerFrame) {
+        mSamplesPerFrame = samplesPerFrame;
+    }
+
+    aaudio_format_t getFormat() const {
+        return mAudioFormat;
+    }
+
+    void setFormat(aaudio_format_t audioFormat) {
+        mAudioFormat = audioFormat;
+    }
+
+    aaudio_sharing_mode_t getSharingMode() const {
+        return mSharingMode;
+    }
+
+    void setSharingMode(aaudio_sharing_mode_t sharingMode) {
+        mSharingMode = sharingMode;
+    }
+
+    int32_t getBufferCapacity() const {
+        return mBufferCapacity;
+    }
+
+    void setBufferCapacity(int32_t frames) {
+        mBufferCapacity = frames;
+    }
+
+    virtual aaudio_result_t validate() const;
+
+    void dump() const;
+
+private:
+    int32_t                    mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+    int32_t                    mSampleRate      = AAUDIO_UNSPECIFIED;
+    int32_t                    mDeviceId        = AAUDIO_UNSPECIFIED;
+    aaudio_sharing_mode_t      mSharingMode     = AAUDIO_SHARING_MODE_SHARED;
+    aaudio_format_t            mAudioFormat     = AAUDIO_FORMAT_UNSPECIFIED;
+    int32_t                    mBufferCapacity  = AAUDIO_UNSPECIFIED;
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_STREAM_PARAMETERS_H
\ No newline at end of file
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 6c4aa59..43a1ef1 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -179,36 +179,9 @@
 
     // Check for values that are ridiculously out of range to prevent math overflow exploits.
     // The service will do a better check.
-    if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
-        && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
-        ALOGE("AudioStreamBuilder: channelCount out of range = %d", mSamplesPerFrame);
-        return AAUDIO_ERROR_OUT_OF_RANGE;
-    }
-
-    if (mDeviceId < 0) {
-        ALOGE("AudioStreamBuilder: deviceId out of range = %d", mDeviceId);
-        return AAUDIO_ERROR_OUT_OF_RANGE;
-    }
-
-    switch (mSharingMode) {
-        case AAUDIO_SHARING_MODE_EXCLUSIVE:
-        case AAUDIO_SHARING_MODE_SHARED:
-            break;
-        default:
-            ALOGE("AudioStreamBuilder: illegal sharingMode = %d", mSharingMode);
-            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-            // break;
-    }
-
-    switch (mFormat) {
-        case AAUDIO_FORMAT_UNSPECIFIED:
-        case AAUDIO_FORMAT_PCM_I16:
-        case AAUDIO_FORMAT_PCM_FLOAT:
-            break; // valid
-        default:
-            ALOGE("AudioStreamBuilder: audioFormat not valid = %d", mFormat);
-            return AAUDIO_ERROR_INVALID_FORMAT;
-            // break;
+    aaudio_result_t result = AAudioStreamParameters::validate();
+    if (result != AAUDIO_OK) {
+        return result;
     }
 
     switch (mDirection) {
@@ -221,17 +194,6 @@
             // break;
     }
 
-    if (mSampleRate != AAUDIO_UNSPECIFIED
-        && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) {
-        ALOGE("AudioStreamBuilder: sampleRate out of range = %d", mSampleRate);
-        return AAUDIO_ERROR_INVALID_RATE;
-    }
-
-    if (mBufferCapacity < 0) {
-        ALOGE("AudioStreamBuilder: bufferCapacity out of range = %d", mBufferCapacity);
-        return AAUDIO_ERROR_OUT_OF_RANGE;
-    }
-
     switch (mPerformanceMode) {
         case AAUDIO_PERFORMANCE_MODE_NONE:
         case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index d757592..6e548b1 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -21,6 +21,7 @@
 
 #include <aaudio/AAudio.h>
 
+#include "AAudioStreamParameters.h"
 #include "AudioStream.h"
 
 namespace aaudio {
@@ -28,24 +29,12 @@
 /**
  * Factory class for an AudioStream.
  */
-class AudioStreamBuilder {
+class AudioStreamBuilder : public AAudioStreamParameters {
 public:
     AudioStreamBuilder();
 
     ~AudioStreamBuilder();
 
-    int getSamplesPerFrame() const {
-        return mSamplesPerFrame;
-    }
-
-    /**
-     * This is also known as channelCount.
-     */
-    AudioStreamBuilder* setSamplesPerFrame(int samplesPerFrame) {
-        mSamplesPerFrame = samplesPerFrame;
-        return this;
-    }
-
     aaudio_direction_t getDirection() const {
         return mDirection;
     }
@@ -55,33 +44,6 @@
         return this;
     }
 
-    int32_t getSampleRate() const {
-        return mSampleRate;
-    }
-
-    AudioStreamBuilder* setSampleRate(int32_t sampleRate) {
-        mSampleRate = sampleRate;
-        return this;
-    }
-
-    aaudio_format_t getFormat() const {
-        return mFormat;
-    }
-
-    AudioStreamBuilder *setFormat(aaudio_format_t format) {
-        mFormat = format;
-        return this;
-    }
-
-    aaudio_sharing_mode_t getSharingMode() const {
-        return mSharingMode;
-    }
-
-    AudioStreamBuilder* setSharingMode(aaudio_sharing_mode_t sharingMode) {
-        mSharingMode = sharingMode;
-        return this;
-    }
-
     bool isSharingModeMatchRequired() const {
         return mSharingModeMatchRequired;
     }
@@ -91,15 +53,6 @@
         return this;
     }
 
-    int32_t getBufferCapacity() const {
-        return mBufferCapacity;
-    }
-
-    AudioStreamBuilder* setBufferCapacity(int32_t frames) {
-        mBufferCapacity = frames;
-        return this;
-    }
-
     int32_t getPerformanceMode() const {
         return mPerformanceMode;
     }
@@ -109,15 +62,6 @@
         return this;
     }
 
-    int32_t getDeviceId() const {
-        return mDeviceId;
-    }
-
-    AudioStreamBuilder* setDeviceId(int32_t deviceId) {
-        mDeviceId = deviceId;
-        return this;
-    }
-
     AAudioStream_dataCallback getDataCallbackProc() const {
         return mDataCallbackProc;
     }
@@ -165,17 +109,11 @@
 
     aaudio_result_t build(AudioStream **streamPtr);
 
-    aaudio_result_t validate() const;
+    virtual aaudio_result_t validate() const override;
 
 private:
-    int32_t                    mSamplesPerFrame = AAUDIO_UNSPECIFIED;
-    int32_t                    mSampleRate = AAUDIO_UNSPECIFIED;
-    int32_t                    mDeviceId = AAUDIO_UNSPECIFIED;
-    aaudio_sharing_mode_t      mSharingMode = AAUDIO_SHARING_MODE_SHARED;
     bool                       mSharingModeMatchRequired = false; // must match sharing mode requested
-    aaudio_format_t            mFormat = AAUDIO_FORMAT_UNSPECIFIED;
     aaudio_direction_t         mDirection = AAUDIO_DIRECTION_OUTPUT;
-    int32_t                    mBufferCapacity = AAUDIO_UNSPECIFIED;
     aaudio_performance_mode_t  mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
 
     AAudioStream_dataCallback  mDataCallbackProc = nullptr;  // external callback functions
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 2d8ac6e..a3198d7 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -229,10 +229,13 @@
     case AAUDIO_ERROR_NULL:
         status = UNEXPECTED_NULL;
         break;
+    case AAUDIO_ERROR_UNAVAILABLE:
+        status = NOT_ENOUGH_DATA;
+        break;
+
     // TODO translate these result codes
     case AAUDIO_ERROR_INTERNAL:
     case AAUDIO_ERROR_UNIMPLEMENTED:
-    case AAUDIO_ERROR_UNAVAILABLE:
     case AAUDIO_ERROR_NO_FREE_HANDLES:
     case AAUDIO_ERROR_NO_MEMORY:
     case AAUDIO_ERROR_TIMEOUT:
@@ -268,6 +271,9 @@
     case WOULD_BLOCK:
         result = AAUDIO_ERROR_WOULD_BLOCK;
         break;
+    case NOT_ENOUGH_DATA:
+        result = AAUDIO_ERROR_UNAVAILABLE;
+        break;
     default:
         result = AAUDIO_ERROR_INTERNAL;
         break;
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index 97f0dfd..e49945b 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -36,7 +36,7 @@
 // a value between "other" + 1 and "other" + INT32_MAX, the choice of
 // which needs to be the "least recently used" sequence value for "self".
 // In general, this means (new_self) returned is max(self, other) + 1.
-
+__attribute__((no_sanitize("integer")))
 static uint32_t incrementSequence(uint32_t self, uint32_t other) {
     int32_t diff = (int32_t) self - (int32_t) other;
     if (diff >= 0 && diff < INT32_MAX) {
@@ -389,6 +389,7 @@
 
 // ---------------------------------------------------------------------------
 
+__attribute__((no_sanitize("integer")))
 void AudioTrackClientProxy::flush()
 {
     // This works for mFrameCountP2 <= 2^30
@@ -862,6 +863,7 @@
     return old;
 }
 
+__attribute__((no_sanitize("integer")))
 void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
 {
     audio_track_cblk_t* cblk = mCblk;
@@ -1017,6 +1019,7 @@
     return (ssize_t) mState.mPosition;
 }
 
+__attribute__((no_sanitize("integer")))
 status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
 {
     if (mIsShutdown) {
@@ -1061,6 +1064,7 @@
     return NO_ERROR;
 }
 
+__attribute__((no_sanitize("integer")))
 void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
 {
     size_t stepCount = buffer->mFrameCount;
diff --git a/media/libaudioclient/OWNERS b/media/libaudioclient/OWNERS
new file mode 100644
index 0000000..482b9fb
--- /dev/null
+++ b/media/libaudioclient/OWNERS
@@ -0,0 +1,3 @@
+gkasten@google.com
+jmtrivi@google.com
+mnaganov@google.com
diff --git a/media/libaudiohal/OWNERS b/media/libaudiohal/OWNERS
new file mode 100644
index 0000000..1456ab6
--- /dev/null
+++ b/media/libaudiohal/OWNERS
@@ -0,0 +1,2 @@
+krocard@google.com
+mnaganov@google.com
diff --git a/media/libeffects/OWNERS b/media/libeffects/OWNERS
new file mode 100644
index 0000000..7e3de13
--- /dev/null
+++ b/media/libeffects/OWNERS
@@ -0,0 +1,3 @@
+krocard@google.com
+mnaganov@google.com
+rago@google.com
diff --git a/media/libeffects/config/Android.bp b/media/libeffects/config/Android.bp
index 5666d14..4398a91 100644
--- a/media/libeffects/config/Android.bp
+++ b/media/libeffects/config/Android.bp
@@ -10,5 +10,8 @@
         "libtinyxml2",
     ],
 
+    header_libs: ["libaudio_system_headers"],
+    export_header_lib_headers: ["libaudio_system_headers"],
+
     export_include_dirs: ["include"],
 }
diff --git a/media/libeffects/factory/Android.bp b/media/libeffects/factory/Android.bp
index 21be587..ddbfdd8 100644
--- a/media/libeffects/factory/Android.bp
+++ b/media/libeffects/factory/Android.bp
@@ -10,10 +10,11 @@
 cc_library_shared {
     name: "libeffects",
     vendor: true,
-    srcs: ["EffectsFactory.c",
-           "EffectsConfigLoader.c",
-           "EffectsFactoryState.c",
-           "EffectsXmlConfigLoader.cpp",
+    srcs: [
+         "EffectsFactory.c",
+         "EffectsConfigLoader.c",
+         "EffectsFactoryState.c",
+         "EffectsXmlConfigLoader.cpp",
     ],
 
     shared_libs: [
@@ -24,11 +25,12 @@
     ],
     cflags: ["-fvisibility=hidden"],
 
-    include_dirs: ["system/media/audio_effects/include"],
-
     local_include_dirs:["include/media"],
 
-    header_libs: ["libeffects_headers"],
+    header_libs: [
+        "libaudioeffects",
+        "libeffects_headers",
+    ],
     export_header_lib_headers: ["libeffects_headers"],
 }
 
diff --git a/media/libeffects/factory/EffectsConfigLoader.c b/media/libeffects/factory/EffectsConfigLoader.c
index 5c973b4..fcef36f 100644
--- a/media/libeffects/factory/EffectsConfigLoader.c
+++ b/media/libeffects/factory/EffectsConfigLoader.c
@@ -19,6 +19,7 @@
 
 #include <dlfcn.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #include <cutils/config_utils.h>
 #include <cutils/misc.h>
diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h
index cdba362..29dbc9c 100644
--- a/media/libeffects/factory/EffectsFactory.h
+++ b/media/libeffects/factory/EffectsFactory.h
@@ -20,7 +20,6 @@
 #include <dirent.h>
 #include <pthread.h>
 
-#include <android/log.h>
 #include <cutils/compiler.h>
 #include <hardware/audio_effect.h>
 
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
index 42e00f8..438b787 100644
--- a/media/libeffects/factory/EffectsXmlConfigLoader.cpp
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
@@ -281,7 +281,7 @@
                 ++nbSkippedElement;
                 continue;
             }
-            listPush(&effectLoadResult.effectDesc, subEffectList);
+            listPush(effectLoadResult.effectDesc.get(), subEffectList);
 
             // Since we return a dummy descriptor for the proxy during
             // get_descriptor call, we replace it with the corresponding
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index e031765..5d25e4d 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -39,6 +39,11 @@
 
 struct DrmSessionClientInterface;
 
+inline bool operator==(const Vector<uint8_t> &l, const Vector<uint8_t> &r) {
+    if (l.size() != r.size()) return false;
+    return memcmp(l.array(), r.array(), l.size()) == 0;
+}
+
 struct DrmHal : public BnDrm,
              public IBinder::DeathRecipient,
              public IDrmPluginListener {
@@ -161,6 +166,9 @@
     const Vector<sp<IDrmFactory>> mFactories;
     sp<IDrmPlugin> mPlugin;
 
+    Vector<Vector<uint8_t>> mOpenSessions;
+    void closeOpenSessions();
+
     /**
      * mInitCheck is:
      *   NO_INIT if a plugin hasn't been created yet
@@ -175,6 +183,11 @@
 
     void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& array);
 
+    void reportMetrics() const;
+    status_t getPropertyStringInternal(String8 const &name, String8 &value) const;
+    status_t getPropertyByteArrayInternal(String8 const &name,
+                                          Vector<uint8_t> &value) const;
+
     DISALLOW_EVIL_CONSTRUCTORS(DrmHal);
 };
 
diff --git a/media/libmedia/include/media/PluginMetricsReporting.h b/media/libmedia/include/media/PluginMetricsReporting.h
new file mode 100644
index 0000000..4a5a363
--- /dev/null
+++ b/media/libmedia/include/media/PluginMetricsReporting.h
@@ -0,0 +1,33 @@
+/*
+ * 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 PLUGIN_METRICS_REPORTING_H_
+
+#define PLUGIN_METRICS_REPORTING_H_
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
+                                const String8& vendorName,
+                                const String8& description);
+
+}  // namespace android
+
+#endif  // PLUGIN_METRICS_REPORTING_H_
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 18fd857..77f0cab 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -83,6 +83,9 @@
 #include "HTTPBase.h"
 #include "RemoteDisplay.h"
 
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleepUs = 20000;
+
 namespace {
 using android::media::Metadata;
 using android::status_t;
@@ -392,12 +395,32 @@
     snprintf(buffer, 255, "  pid(%d), connId(%d), status(%d), looping(%s)\n",
             mPid, mConnId, mStatus, mLoop?"true": "false");
     result.append(buffer);
-    write(fd, result.string(), result.size());
-    if (mPlayer != NULL) {
-        mPlayer->dump(fd, args);
+
+    sp<MediaPlayerBase> p;
+    sp<AudioOutput> audioOutput;
+    bool locked = false;
+    for (int i = 0; i < kDumpLockRetries; ++i) {
+        if (mLock.tryLock() == NO_ERROR) {
+            locked = true;
+            break;
+        }
+        usleep(kDumpLockSleepUs);
     }
-    if (mAudioOutput != 0) {
-        mAudioOutput->dump(fd, args);
+
+    if (locked) {
+        p = mPlayer;
+        audioOutput = mAudioOutput;
+        mLock.unlock();
+    } else {
+        result.append("  lock is taken, no dump from player and audio output\n");
+    }
+    write(fd, result.string(), result.size());
+
+    if (p != NULL) {
+        p->dump(fd, args);
+    }
+    if (audioOutput != 0) {
+        audioOutput->dump(fd, args);
     }
     write(fd, "\n", 1);
     return NO_ERROR;
@@ -577,7 +600,10 @@
 MediaPlayerService::Client::~Client()
 {
     ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
-    mAudioOutput.clear();
+    {
+        Mutex::Autolock l(mLock);
+        mAudioOutput.clear();
+    }
     wp<Client> client(this);
     disconnect();
     mService->removeClient(client);
@@ -597,10 +623,9 @@
         Mutex::Autolock l(mLock);
         p = mPlayer;
         mClient.clear();
+        mPlayer.clear();
     }
 
-    mPlayer.clear();
-
     // clear the notification to prevent callbacks to dead client
     // and reset the player. We assume the player will serialize
     // access to itself if necessary.
@@ -621,7 +646,7 @@
 sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
 {
     // determine if we have the right player type
-    sp<MediaPlayerBase> p = mPlayer;
+    sp<MediaPlayerBase> p = getPlayer();
     if ((p != NULL) && (p->playerType() != playerType)) {
         ALOGV("delete player");
         p.clear();
@@ -772,6 +797,7 @@
     }
 
     if (mStatus == OK) {
+        Mutex::Autolock l(mLock);
         mPlayer = p;
     }
 }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 758db1f..483a9ff 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -840,7 +840,7 @@
         return;
     }
 
-    notifyEOS(true /* audio */, ERROR_END_OF_STREAM);
+    notifyEOS_l(true /* audio */, ERROR_END_OF_STREAM);
 }
 
 size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
@@ -917,10 +917,10 @@
             if (mAudioSink->needsTrailingPadding()) {
                 postEOSDelayUs = getPendingAudioPlayoutDurationUs(ALooper::GetNowUs());
             }
-            ALOGV("fillAudioBuffer: notifyEOS "
+            ALOGV("fillAudioBuffer: notifyEOS_l "
                     "mNumFramesWritten:%u  finalResult:%d  postEOSDelay:%lld",
                     mNumFramesWritten, entry->mFinalResult, (long long)postEOSDelayUs);
-            notifyEOS(true /* audio */, entry->mFinalResult, postEOSDelayUs);
+            notifyEOS_l(true /* audio */, entry->mFinalResult, postEOSDelayUs);
         }
     }
     return sizeCopied;
@@ -1408,6 +1408,11 @@
 }
 
 void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult, int64_t delayUs) {
+    Mutex::Autolock autoLock(mLock);
+    notifyEOS_l(audio, finalResult, delayUs);
+}
+
+void NuPlayer::Renderer::notifyEOS_l(bool audio, status_t finalResult, int64_t delayUs) {
     if (audio && delayUs > 0) {
         sp<AMessage> msg = new AMessage(kWhatEOS, this);
         msg->setInt32("audioEOSGeneration", mAudioEOSGeneration);
@@ -1420,6 +1425,11 @@
     notify->setInt32("audio", static_cast<int32_t>(audio));
     notify->setInt32("finalResult", finalResult);
     notify->post(delayUs);
+
+    if (audio) {
+        // Video might outlive audio. Clear anchor to enable video only case.
+        mAnchorTimeMediaUs = -1;
+    }
 }
 
 void NuPlayer::Renderer::notifyAudioTearDown(AudioTearDownReason reason) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index e6850b5..f58b79c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -275,6 +275,7 @@
     void onChangeAudioFormat(const sp<AMessage> &meta, const sp<AMessage> &notify);
 
     void notifyEOS(bool audio, status_t finalResult, int64_t delayUs = 0);
+    void notifyEOS_l(bool audio, status_t finalResult, int64_t delayUs = 0);
     void notifyFlushComplete(bool audio);
     void notifyPosition();
     void notifyVideoLateBy(int64_t lateByUs);
diff --git a/media/libnbaio/Android.bp b/media/libnbaio/Android.bp
index 02f7529..4220b77 100644
--- a/media/libnbaio/Android.bp
+++ b/media/libnbaio/Android.bp
@@ -45,6 +45,7 @@
         "PerformanceAnalysis.cpp",
         "Pipe.cpp",
         "PipeReader.cpp",
+        "ReportPerformance.cpp",
         "SourceAudioBufferProvider.cpp",
     ],
 
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 2b5b4ff..2f639d2 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -24,8 +24,8 @@
 *     Calls LOG_HIST_TS
 * LOG_HIST_TS
 *     Hashes file name and line number, and writes single timestamp to buffer
-*     calls NBLOG::Writer::logHistTS once
-* NBLOG::Writer::logHistTS
+*     calls NBLOG::Writer::logEventHistTS once
+* NBLOG::Writer::logEventHistTS
 *     calls NBLOG::Writer::log on hash and current timestamp
 *     time is in CLOCK_MONOTONIC converted to ns
 * NBLOG::Writer::log(Event, const void*, size_t)
@@ -44,6 +44,8 @@
 * ssize_t audio_utils_fifo_reader::obtain
 *     Determines readable buffer section via pointer arithmetic on reader
 *     and writer pointers
+* Similarly, LOG_AUDIO_STATE() is called by onStateChange whenever audio is
+* turned on or off, and writes this notification to the FIFO.
 *
 * 2) reading the data from shared memory
 * Thread::threadloop()
@@ -106,6 +108,7 @@
 #include <audio_utils/roundup.h>
 #include <media/nbaio/NBLog.h>
 #include <media/nbaio/PerformanceAnalysis.h>
+#include <media/nbaio/ReportPerformance.h>
 // #include <utils/CallStack.h> // used to print callstack
 #include <utils/Log.h>
 #include <utils/String8.h>
@@ -138,6 +141,7 @@
     switch (type) {
     case EVENT_START_FMT:
         return std::make_unique<FormatEntry>(FormatEntry(ptr));
+    case EVENT_AUDIO_STATE:
     case EVENT_HISTOGRAM_ENTRY_TS:
         return std::make_unique<HistogramEntry>(HistogramEntry(ptr));
     default:
@@ -516,7 +520,7 @@
     log(EVENT_HASH, &hash, sizeof(hash));
 }
 
-void NBLog::Writer::logHistTS(log_hash_t hash)
+void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash)
 {
     if (!mEnabled) {
         return;
@@ -525,7 +529,7 @@
     data.hash = hash;
     data.ts = get_monotonic_ns();
     if (data.ts > 0) {
-        log(EVENT_HISTOGRAM_ENTRY_TS, &data, sizeof(data));
+        log(event, &data, sizeof(data));
     } else {
         ALOGE("Failed to get timestamp");
     }
@@ -758,7 +762,9 @@
 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_ENTRY_TS,
+                                                           NBLog::Event::EVENT_AUDIO_STATE};
+
 NBLog::Reader::Reader(const void *shared, size_t size)
     : mShared((/*const*/ Shared *) shared), /*mIMemory*/
       mFd(-1), mIndent(0),
@@ -864,42 +870,17 @@
 
 }
 
-// writes sample deltas to file, either truncating or appending
-inline void writeHistToFile(const std::vector<int64_t> &samples, bool append) {
-    // name of file on audioserver
-    static const char* const kName = (char *)"/data/misc/audioserver/sample_results.txt";
-    // stores deltas between the samples
-    std::vector<int64_t> intervals;
-    if (samples.size() == 0) return;
-    for (size_t i = 1; i < samples.size(); ++i) {
-        intervals.push_back(deltaMs(samples[i - 1], samples[i]));
-    }
-    // Deletes maximum value in a histogram. Temp quick fix.
-    // FIXME: need to find root cause of approx. 35th element from the end
-    // consistently being an outlier in the first histogram of a flush
-    // ALOGW("%" PRId64 "before", (int64_t) *(std::max_element(intervals.begin(), intervals.end())));
-    intervals.erase(std::max_element(intervals.begin(), intervals.end()));
-    // ALOGW("%" PRId64 "after", (int64_t) *(std::max_element(intervals.begin(), intervals.end())));
-    std::ofstream ofs;
-    ofs.open(kName, append ? std::ios::app : std::ios::trunc);
-    if (!ofs) {
-        ALOGW("couldn't open file %s", kName);
-        return;
-    }
-    for (size_t i = 0; i < intervals.size(); ++i) {
-        ofs << intervals[i] << "\n";
-    }
-    ofs.close();
-}
-
+// TODO: move this to PerformanceAnalysis
+// TODO: make call to dump periodic so that data in shared FIFO does not get overwritten
 void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
 {
-    //  CallStack cs(LOG_TAG);
     mFd = fd;
     mIndent = indent;
     String8 timestamp, body;
     // FIXME: this is not thread safe
-    static PerformanceAnalysis performanceAnalysis; // used to store data and to call analysis functions
+    // TODO: need a separate instance of performanceAnalysis for each thread
+    // used to store data and to call analysis functions
+    static ReportPerformance::PerformanceAnalysis performanceAnalysis;
     size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
     if (lost > 0) {
         body.appendFormat("warning: lost %zu bytes worth of events", lost);
@@ -921,7 +902,12 @@
             memcpy(&hash, &(data->hash), sizeof(hash));
             int64_t ts;
             memcpy(&ts, &data->ts, sizeof(ts));
-            performanceAnalysis.logTsEntry(data->author, ts);
+            performanceAnalysis.logTsEntry(ts);
+            ++entry;
+            break;
+        }
+        case EVENT_AUDIO_STATE: {
+            performanceAnalysis.handleStateChange();
             ++entry;
             break;
         }
diff --git a/media/libnbaio/OWNERS b/media/libnbaio/OWNERS
new file mode 100644
index 0000000..f9cb567
--- /dev/null
+++ b/media/libnbaio/OWNERS
@@ -0,0 +1 @@
+gkasten@google.com
diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp
index fa8f47e..fb3bddc 100644
--- a/media/libnbaio/PerformanceAnalysis.cpp
+++ b/media/libnbaio/PerformanceAnalysis.cpp
@@ -21,7 +21,6 @@
 #include <algorithm>
 #include <climits>
 #include <deque>
-#include <fstream>
 #include <iostream>
 #include <math.h>
 #include <numeric>
@@ -36,6 +35,7 @@
 #include <audio_utils/roundup.h>
 #include <media/nbaio/NBLog.h>
 #include <media/nbaio/PerformanceAnalysis.h>
+#include <media/nbaio/ReportPerformance.h>
 // #include <utils/CallStack.h> // used to print callstack
 #include <utils/Log.h>
 #include <utils/String8.h>
@@ -45,6 +45,8 @@
 
 namespace android {
 
+namespace ReportPerformance {
+
 PerformanceAnalysis::PerformanceAnalysis() {
     // These variables will be (FIXME) learned from the data
     kPeriodMs = 4; // typical buffer period (mode)
@@ -71,39 +73,92 @@
     return width;
 }
 
-// Takes a single buffer period timestamp entry with author information and stores it
-// in a temporary series of timestamps. Once the series is full, the data is analyzed,
+// Given a series of audio processing wakeup timestamps,
+// buckets the time intervals into a histogram, searches for
+// outliers, analyzes the outlier series for unexpectedly
+// small or large values and stores these as peaks, and flushes
+// the timestamp series from memory.
+void PerformanceAnalysis::processAndFlushTimeStampSeries() {
+    // 1) analyze the series to store all outliers and their exact timestamps:
+    storeOutlierData(mTimeStampSeries);
+
+    // 2) detect peaks in the outlier series
+    detectPeaks();
+
+    // 3) compute its histogram, append to mRecentHists and clear the time series
+    mRecentHists.emplace_back(static_cast<timestamp>(mTimeStampSeries[0]),
+                              buildBuckets(mTimeStampSeries));
+    // do not let mRecentHists exceed capacity
+    // ALOGD("mRecentHists size: %d", static_cast<int>(mRecentHists.size()));
+    if (mRecentHists.size() >= kRecentHistsCapacity) {
+        //  ALOGD("popped back mRecentHists");
+        mRecentHists.pop_front();
+    }
+    mTimeStampSeries.clear();
+}
+
+// forces short-term histogram storage to avoid adding idle audio time interval
+// to buffer period data
+void PerformanceAnalysis::handleStateChange() {
+    ALOGD("handleStateChange");
+    processAndFlushTimeStampSeries();
+    return;
+}
+
+// Takes a single buffer period timestamp entry information and stores it in a
+// temporary series of timestamps. Once the series is full, the data is analyzed,
 // stored, and emptied.
-// TODO: decide whether author or file location information is more important to store
-// for now, only stores author (thread)
-void PerformanceAnalysis::logTsEntry(int author, int64_t ts) {
+void PerformanceAnalysis::logTsEntry(int64_t ts) {
     // TODO might want to filter excessively high outliers, which are usually caused
     // by the thread being inactive.
     // Store time series data for each reader in order to bucket it once there
     // is enough data. Then, write to recentHists as a histogram.
-    mTimeStampSeries[author].push_back(ts);
-    // if length of the time series has reached kShortHistSize samples, do 1) and 2):
-    if (mTimeStampSeries[author].size() >= kShortHistSize) {
-        // 1) analyze the series to store all outliers and their exact timestamps:
-        storeOutlierData(mTimeStampSeries[author]);
-        // 2) detect peaks in the outlier series
-        detectPeaks();
-        // 3) compute its histogram, append this to mRecentHists and erase the time series
-        // FIXME: need to store the timestamp of the beginning of each histogram
-        // FIXME: Restore LOG_HIST_FLUSH to separate histograms at every end-of-stream event
-        // A histogram should not span data between audio off/on timespans
-        mRecentHists.emplace_back(author,
-                                   buildBuckets(mTimeStampSeries[author]));
-        // do not let mRecentHists exceed capacity
-        // ALOGD("mRecentHists size: %d", static_cast<int>(mRecentHists.size()));
-        if (mRecentHists.size() >= kRecentHistsCapacity) {
-            //  ALOGD("popped back mRecentHists");
-            mRecentHists.pop_front();
-        }
-        mTimeStampSeries[author].clear();
+    mTimeStampSeries.push_back(ts);
+    // if length of the time series has reached kShortHistSize samples,
+    // analyze the data and flush the timestamp series from memory
+    if (mTimeStampSeries.size() >= kShortHistSize) {
+        processAndFlushTimeStampSeries();
     }
 }
 
+// When the short-term histogram array mRecentHists has reached capacity,
+// merge histograms for data compression and store them in mLongTermHists
+// clears mRecentHists
+// TODO: have logTsEntry write directly to mLongTermHists, discard mRecentHists,
+// start a new histogram when a peak occurs
+void PerformanceAnalysis::processAndFlushRecentHists() {
+
+    // Buckets is used to aggregate short-term histograms.
+    Histogram buckets;
+    timestamp startingTs = mRecentHists[0].first;
+
+    for (const auto &shortHist: mRecentHists) {
+        // If the time between starting and ending timestamps has reached the maximum,
+        // add the current histogram (buckets) to the long-term histogram buffer,
+        // clear buckets, and start a new long-term histogram aggregation process.
+        if (deltaMs(startingTs, shortHist.first) >= kMaxHistTimespanMs) {
+            mLongTermHists.emplace_back(startingTs, std::move(buckets));
+            buckets.clear();
+            startingTs = shortHist.first;
+            // When memory is full, delete oldest histogram
+            // TODO use a circular buffer
+            if (mLongTermHists.size() >= kLongTermHistsCapacity) {
+                mLongTermHists.pop_front();
+            }
+        }
+
+        // add current histogram to buckets
+        for (const auto &countPair : shortHist.second) {
+            buckets[countPair.first] += countPair.second;
+        }
+    }
+    mRecentHists.clear();
+    // TODO: decide when/where to call writeToFile
+    // TODO: add a thread-specific extension to the file name
+    static const char* const kName = (const char *) "/data/misc/audioserver/sample_results.txt";
+    writeToFile(mOutlierData, mLongTermHists, kName, false);
+}
+
 // Given a series of outlier intervals (mOutlier data),
 // looks for changes in distribution (peaks), which can be either positive or negative.
 // The function sets the mean to the starting value and sigma to 0, and updates
@@ -112,7 +167,6 @@
 // are set to the peak value and 0.
 void PerformanceAnalysis::detectPeaks() {
     if (mOutlierData.empty()) {
-        ALOGD("peak detector called on empty array");
         return;
     }
 
@@ -147,14 +201,12 @@
             mPeakDetectorSd = sqrt(std::accumulate(start, it + 1, 0.0,
                       [=](auto &a, auto &b){ return a + sqr(b.first - mPeakDetectorMean);})) /
                       ((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance
-            // ALOGD("value, mean, sd: %f, %f, %f", static_cast<double>(it->first), mean, sd);
         }
         // surprising value: store peak timestamp and reset mean, sd, and start iterator
         else {
             mPeakTimestamps.emplace_back(it->second);
             // TODO: remove pop_front once a circular buffer is in place
-            if (mPeakTimestamps.size() >= kShortHistSize) {
-                ALOGD("popped back mPeakTimestamps");
+            if (mPeakTimestamps.size() >= kPeakSeriesSize) {
                 mPeakTimestamps.pop_front();
             }
             mPeakDetectorMean = static_cast<double>(it->first);
@@ -162,9 +214,6 @@
             start = it;
         }
     }
-    //for (const auto &it : mPeakTimestamps) {
-    //    ALOGE("mPeakTimestamps %f", static_cast<double>(it));
-    //}
     return;
 }
 
@@ -175,7 +224,6 @@
 // This function is applied to the time series before it is converted into a histogram.
 void PerformanceAnalysis::storeOutlierData(const std::vector<int64_t> &timestamps) {
     if (timestamps.size() < 1) {
-        ALOGE("storeOutlierData called on empty vector");
         return;
     }
     // first pass: need to initialize
@@ -188,10 +236,9 @@
             mOutlierData.emplace_back(mElapsed, static_cast<uint64_t>(mPrevNs));
             // Remove oldest value if the vector is full
             // TODO: remove pop_front once circular buffer is in place
-            // FIXME: change kShortHistSize to some other constant. Make sure it is large
-            // enough that data will never be lost before being written to a long-term FIFO
-            if (mOutlierData.size() >= kShortHistSize) {
-                ALOGD("popped back mOutlierData");
+            // FIXME: make sure kShortHistSize is large enough that that data will never be lost
+            // before being written to file or to a FIFO
+            if (mOutlierData.size() >= kOutlierSeriesSize) {
                 mOutlierData.pop_front();
             }
             mElapsed = 0;
@@ -217,9 +264,8 @@
 }
 
 // TODO Make it return a std::string instead of modifying body --> is this still relevant?
-// FIXME: as can be seen when printing the values, the outlier timestamps typically occur
-// in the first histogram 35 to 38 indices from the end (most often 35).
 // TODO consider changing all ints to uint32_t or uint64_t
+// TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis
 void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) {
     if (mRecentHists.size() < 1) {
         ALOGD("reportPerformance: mRecentHists is empty");
@@ -323,4 +369,6 @@
     return;
 }
 
+} // namespace ReportPerformance
+
 }   // namespace android
diff --git a/media/libnbaio/ReportPerformance.cpp b/media/libnbaio/ReportPerformance.cpp
new file mode 100644
index 0000000..dc50ada
--- /dev/null
+++ b/media/libnbaio/ReportPerformance.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "ReportPerformance"
+
+#include <fstream>
+#include <iostream>
+#include <queue>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <utility>
+#include <media/nbaio/NBLog.h>
+#include <media/nbaio/PerformanceAnalysis.h>
+#include <media/nbaio/ReportPerformance.h>
+// #include <utils/CallStack.h> // used to print callstack
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+namespace android {
+
+namespace ReportPerformance {
+
+// Writes outlier intervals, timestamps, and histograms spanning long time intervals to a file.
+// TODO: format the data efficiently and write different types of data to different files
+void writeToFile(std::deque<std::pair<outlierInterval, timestamp>> &outlierData,
+                                    std::deque<std::pair<timestamp, Histogram>> &hists,
+                                    const char * kName,
+                                    bool append) {
+    ALOGD("writing performance data to file");
+    if (outlierData.empty() || hists.empty()) {
+        return;
+    }
+
+    std::ofstream ofs;
+    ofs.open(kName, append ? std::ios::app : std::ios::trunc);
+    if (!ofs.is_open()) {
+        ALOGW("couldn't open file %s", kName);
+        return;
+    }
+    ofs << "Outlier data: interval and timestamp\n";
+    for (const auto &outlier : outlierData) {
+        ofs << outlier.first << ": " << outlier.second << "\n";
+    }
+    ofs << "Histogram data\n";
+    for (const auto &hist : hists) {
+        ofs << "\ttimestamp\n";
+        ofs << hist.first << "\n";
+        ofs << "\tbuckets and counts\n";
+        for (const auto &bucket : hist.second) {
+            ofs << bucket.first << ": " << bucket.second << "\n";
+        }
+    }
+    ofs.close();
+}
+
+} // namespace ReportPerformance
+
+}   // namespace android
diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h
index 0b5e24d..3e48ee1 100644
--- a/media/libnbaio/include/media/nbaio/NBLog.h
+++ b/media/libnbaio/include/media/nbaio/NBLog.h
@@ -44,8 +44,6 @@
 class Writer;
 class Reader;
 
-private:
-
 enum Event : uint8_t {
     EVENT_RESERVED,
     EVENT_STRING,               // ASCII string, not NUL-terminated
@@ -60,11 +58,13 @@
     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_AUDIO_STATE,          // audio on/off event: logged upon FastMixer::onStateChange() call
     EVENT_END_FMT,              // end of logFormat argument list
 
     EVENT_UPPER_BOUND,          // to check for invalid events
 };
 
+private:
 
 // ---------------------------------------------------------------------------
 // API for handling format entry operations
@@ -248,6 +248,8 @@
     int author;
 }; //TODO __attribute__((packed));
 
+using StateTsEntryWithAuthor = HistTsEntryWithAuthor;
+
 struct HistIntEntry {
     log_hash_t hash;
     int value;
@@ -341,7 +343,7 @@
     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    logEventHistTs(Event event, log_hash_t hash);
 
     virtual bool    isEnabled() const;
 
diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
index 89699bf..b0dc148 100644
--- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
+++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
@@ -23,10 +23,11 @@
 #include <deque>
 #include <vector>
 #include "NBLog.h"
+#include "ReportPerformance.h"
 
 namespace android {
 
-class String8;
+namespace ReportPerformance {
 
 class PerformanceAnalysis {
     // This class stores and analyzes audio processing wakeup timestamps from NBLog
@@ -37,31 +38,25 @@
 
     PerformanceAnalysis();
 
-    // FIXME: decide whether to use 64 or 32 bits
-    typedef uint64_t log_hash_t;
+    // Given a series of audio processing wakeup timestamps,
+    // compresses and and analyzes the data, and flushes
+    // the timestamp series from memory.
+    void processAndFlushTimeStampSeries();
 
-    // stores a short-term histogram of size determined by kShortHistSize
-    // key: observed buffer period. value: count
-    // TODO: unsigned, unsigned
-    // TODO: change this name to histogram
-    using short_histogram = std::map<int, int>;
+    // Called when an audio on/off event is read from the buffer,
+    // e.g. EVENT_AUDIO_STATE.
+    // calls flushTimeStampSeries on the data up to the event,
+    // effectively discarding the idle audio time interval
+    void handleStateChange();
 
-    using outlierInterval = uint64_t;
-    // int64_t timestamps are converted to uint64_t in PerformanceAnalysis::storeOutlierData,
-    // and all further analysis functions use uint64_t.
-    using timestamp = uint64_t;
-    using timestamp_raw = int64_t;
+    // When the short-term histogram array mRecentHists has reached capacity,
+    // merges histograms for data compression and stores them in mLongTermHists
+    void processAndFlushRecentHists();
 
     // Writes wakeup timestamp entry to log and runs analysis
-    // author is the thread ID
-    // TODO: check. if the thread has multiple histograms, is author info correct
-    // FIXME: remove author from arglist. Want to call these function separately on
-    // each thread’s data.
-    // FIXME: decide whether to store the hash (source file location) instead
-    // FIXME: If thread has multiple histograms, check that code works and correct
-    // author is stored (test with multiple threads). Need to check that the current
-    // code is not receiving data from multiple threads. This could cause odd values.
-    void logTsEntry(int author, timestamp_raw ts);
+    // TODO: make this thread safe. Each thread should have its own instance
+    // of PerformanceAnalysis.
+    void logTsEntry(timestamp_raw ts);
 
     // FIXME: make peakdetector and storeOutlierData a single function
     // Input: mOutlierData. Looks at time elapsed between outliers
@@ -84,9 +79,7 @@
 
     // This function used to detect glitches in a time series
     // TODO incorporate this into the analysis (currently unused)
-    void     alertIfGlitch(const std::vector<timestamp_raw> &samples);
-
-    ~PerformanceAnalysis() {}
+    void alertIfGlitch(const std::vector<timestamp_raw> &samples);
 
 private:
 
@@ -97,23 +90,24 @@
     // a peak is a moment at which the average outlier interval changed significantly
     std::deque<timestamp> mPeakTimestamps;
 
+    // TODO: turn these into circular buffers for better data flow
     // FIFO of small histograms
-    // stores fixed-size short buffer period histograms with hash and thread data
-    // TODO: Turn it into a circular buffer for better data flow
-    std::deque<std::pair<int, short_histogram>> mRecentHists;
+    // stores fixed-size short buffer period histograms with timestamp of first sample
+    std::deque<std::pair<timestamp, Histogram>> mRecentHists;
 
-    // map from author to vector of timestamps, collected from NBLog
-    // when a vector reaches its maximum size, analysis is run and the data is deleted
-    std::map<int, std::vector<timestamp_raw>> mTimeStampSeries;
+    // FIFO of small histograms
+    // stores fixed-size long-term buffer period histograms with timestamp of first sample
+    std::deque<std::pair<timestamp, Histogram>> mLongTermHists;
 
-    // TODO: measure these from the data (e.g., mode) as they may change.
-    // const int kGlitchThreshMs = 7;
-    // const int kMsPerSec = 1000;
+    // vector of timestamps, collected from NBLog for a (TODO) specific thread
+    // when a vector reaches its maximum size, the data is processed and flushed
+    std::vector<timestamp_raw> mTimeStampSeries;
+
+    static const int kMsPerSec = 1000;
 
     // Parameters used when detecting outliers
     // TODO: learn some of these from the data, delete unused ones
     // FIXME: decide whether to make kPeriodMs static.
-    // The non-const values are (TODO: will be) learned from the data
     static const int kNumBuff = 3; // number of buffers considered in local history
     int kPeriodMs; // current period length is ideally 4 ms
     static const int kOutlierMs = 7; // values greater or equal to this cause glitches
@@ -124,8 +118,15 @@
     // Peak detection: number of standard deviations from mean considered a significant change
     static const int kStddevThreshold = 5;
 
+    // capacity allocated to data structures
+    // TODO: adjust all of these values
     static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
     static const int kShortHistSize = 50; // number of samples in a short-term histogram
+    static const int kOutlierSeriesSize = 100; // number of values stored in outlier array
+    static const int kPeakSeriesSize = 100; // number of values stored in peak array
+    static const int kLongTermHistsCapacity = 20; // number of long-term histogram stored in memory
+    // maximum elapsed time between first and last timestamp of a long-term histogram
+    static const int kMaxHistTimespanMs = 5 * kMsPerSec;
 
     // these variables are stored in-class to ensure continuity while analyzing the timestamp
     // series one short sequence at a time: the variables are not re-initialized every time.
@@ -138,14 +139,7 @@
 
 };
 
-static inline int deltaMs(int64_t ns1, int64_t ns2) {
-    return (ns2 - ns1) / (1000 * 1000);
-}
-
-static inline uint32_t log2(uint32_t x) {
-    // This works for x > 0
-    return 31 - __builtin_clz(x);
-}
+} // namespace ReportPerformance
 
 }   // namespace android
 
diff --git a/media/libnbaio/include/media/nbaio/ReportPerformance.h b/media/libnbaio/include/media/nbaio/ReportPerformance.h
new file mode 100644
index 0000000..27d2810
--- /dev/null
+++ b/media/libnbaio/include/media/nbaio/ReportPerformance.h
@@ -0,0 +1,66 @@
+/*
+ * 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_MEDIA_REPORTPERFORMANCE_H
+#define ANDROID_MEDIA_REPORTPERFORMANCE_H
+
+#include <deque>
+#include <map>
+#include <vector>
+
+namespace android {
+
+// This class is used by reportPerformance function
+// TODO move reportPerformance function to ReportPerformance.cpp
+class String8;
+
+namespace ReportPerformance {
+
+// stores a histogram: key: observed buffer period. value: count
+// TODO: unsigned, unsigned
+using Histogram = std::map<int, int>;
+
+using outlierInterval = uint64_t;
+// int64_t timestamps are converted to uint64_t in PerformanceAnalysis::storeOutlierData,
+// and all analysis functions use uint64_t.
+using timestamp = uint64_t;
+using timestamp_raw = int64_t;
+
+// FIXME: decide whether to use 64 or 32 bits
+// TODO: the code has a mix of typedef and using. Standardize to one or the other.
+typedef uint64_t log_hash_t;
+
+static inline int deltaMs(int64_t ns1, int64_t ns2) {
+    return (ns2 - ns1) / (1000 * 1000);
+}
+
+static inline uint32_t log2(uint32_t x) {
+    // This works for x > 0
+    return 31 - __builtin_clz(x);
+}
+
+// Writes outlier intervals, timestamps, and histograms spanning long time
+// intervals to a file.
+void writeToFile(std::deque<std::pair<outlierInterval, timestamp>> &outlierData,
+                 std::deque<std::pair<timestamp, Histogram>> &hists,
+                 const char * kName,
+                 bool append);
+
+} // namespace ReportPerformance
+
+}   // namespace android
+
+#endif  // ANDROID_MEDIA_REPORTPERFORMANCE_H
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 27c121f..92399f1 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -311,8 +311,14 @@
     int64_t mMinCttsOffsetTicks;
     int64_t mMaxCttsOffsetTicks;
 
-    // Save the last 10 frames' timestamp for debug.
-    std::list<std::pair<int64_t, int64_t>> mTimestampDebugHelper;
+    // Save the last 10 frames' timestamp and frame type for debug.
+    struct TimestampDebugHelperEntry {
+        int64_t pts;
+        int64_t dts;
+        std::string frameType;
+    };
+
+    std::list<TimestampDebugHelperEntry> mTimestampDebugHelper;
 
     // Sequence parameter set or picture parameter set
     struct AVCParamSet {
@@ -2543,12 +2549,12 @@
 }
 
 void MPEG4Writer::Track::dumpTimeStamps() {
-    ALOGE("Dumping %s track's last 10 frames timestamp ", getTrackType());
+    ALOGE("Dumping %s track's last 10 frames timestamp and frame type ", getTrackType());
     std::string timeStampString;
-    for (std::list<std::pair<int64_t, int64_t>>::iterator num = mTimestampDebugHelper.begin();
-            num != mTimestampDebugHelper.end(); ++num) {
-        timeStampString += "(" + std::to_string(num->first)+
-                "us, " + std::to_string(num->second) + "us) ";
+    for (std::list<TimestampDebugHelperEntry>::iterator entry = mTimestampDebugHelper.begin();
+            entry != mTimestampDebugHelper.end(); ++entry) {
+        timeStampString += "(" + std::to_string(entry->pts)+
+                "us, " + std::to_string(entry->dts) + "us " + entry->frameType + ") ";
     }
     ALOGE("%s", timeStampString.c_str());
 }
@@ -2758,9 +2764,9 @@
             previousPausedDurationUs += pausedDurationUs - lastDurationUs;
             mResumed = false;
         }
-        std::pair<int64_t, int64_t> timestampPair;
+        TimestampDebugHelperEntry timestampDebugEntry;
         timestampUs -= previousPausedDurationUs;
-        timestampPair.first = timestampUs;
+        timestampDebugEntry.pts = timestampUs;
         if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) {
             copy->release();
             mSource->stop();
@@ -2790,6 +2796,14 @@
             }
 
             mLastDecodingTimeUs = decodingTimeUs;
+            timestampDebugEntry.dts = decodingTimeUs;
+            timestampDebugEntry.frameType = isSync ? "Key frame" : "Non-Key frame";
+            // Insert the timestamp into the mTimestampDebugHelper
+            if (mTimestampDebugHelper.size() >= kTimestampDebugCount) {
+                mTimestampDebugHelper.pop_front();
+            }
+            mTimestampDebugHelper.push_back(timestampDebugEntry);
+
             cttsOffsetTimeUs =
                     timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
             if (WARN_UNLESS(cttsOffsetTimeUs >= 0ll, "for %s track", trackName)) {
@@ -2919,12 +2933,6 @@
         lastDurationUs = timestampUs - lastTimestampUs;
         lastDurationTicks = currDurationTicks;
         lastTimestampUs = timestampUs;
-        timestampPair.second = timestampUs;
-        // Insert the timestamp into the mTimestampDebugHelper
-        if (mTimestampDebugHelper.size() >= kTimestampDebugCount) {
-            mTimestampDebugHelper.pop_front();
-        }
-        mTimestampDebugHelper.push_back(timestampPair);
 
         if (isSync != 0) {
             addOneStssTableEntry(mStszTableEntries->count());
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
index 6e70ded..9a69226 100644
--- a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
@@ -89,10 +89,10 @@
 }
 
 
-static size_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) {
+static ssize_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) {
     OMX_S64 minTimeStamp = LLONG_MAX;
-    int idx = -1;
-    for (size_t i = 0; i < MAX_TIME_STAMPS; i++) {
+    ssize_t idx = -1;
+    for (ssize_t i = 0; i < MAX_TIME_STAMPS; i++) {
         if (pIsTimeStampValid[i]) {
             if (pNTimeStamp[i] < minTimeStamp) {
                 minTimeStamp = pNTimeStamp[i];
@@ -788,10 +788,15 @@
             }
 
             if (s_dec_op.u4_output_present) {
-                size_t timeStampIdx;
+                ssize_t timeStampIdx;
                 outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
 
                 timeStampIdx = getMinTimestampIdx(mTimeStamps, mTimeStampsValid);
+                if (timeStampIdx < 0) {
+                    ALOGE("b/62872863, Invalid timestamp index!");
+                    android_errorWriteLog(0x534e4554, "62872863");
+                    return;
+                }
                 outHeader->nTimeStamp = mTimeStamps[timeStampIdx];
                 mTimeStampsValid[timeStampIdx] = false;
 
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index f1b44ae..1cf9744 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -892,6 +892,11 @@
         bits.skipBits(2);
 
         unsigned aac_frame_length = bits.getBits(13);
+        if (aac_frame_length == 0){
+            ALOGE("b/62673179, Invalid AAC frame length!");
+            android_errorWriteLog(0x534e4554, "62673179");
+            return NULL;
+        }
 
         bits.skipBits(11);  // adts_buffer_fullness
 
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index fccb12b..0bc65e1 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -85,7 +85,21 @@
         libName.append(kComponents[i].mLibNameSuffix);
         libName.append(".so");
 
-        void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
+        // RTLD_NODELETE means we keep the shared library around forever.
+        // this eliminates thrashing during sequences like loading soundpools.
+        // It also leaves the rest of the logic around the dlopen()/dlclose()
+        // calls in this file unchanged.
+        //
+        // Implications of the change:
+        // -- the codec process (where this happens) will have a slightly larger
+        //    long-term memory footprint as it accumulates the loaded shared libraries.
+        //    This is expected to be a small amount of memory.
+        // -- plugin codecs can no longer (and never should have) depend on a
+        //    free reset of any static data as the library would have crossed
+        //    a dlclose/dlopen cycle.
+        //
+
+        void *libHandle = dlopen(libName.c_str(), RTLD_NOW|RTLD_NODELETE);
 
         if (libHandle == NULL) {
             ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror());
diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h
index 637bf9b..7e7e81e 100644
--- a/media/ndk/include/media/NdkMediaCodec.h
+++ b/media/ndk/include/media/NdkMediaCodec.h
@@ -27,6 +27,7 @@
 #ifndef _NDK_MEDIA_CODEC_H
 #define _NDK_MEDIA_CODEC_H
 
+#include <stdint.h>
 #include <sys/cdefs.h>
 
 #include "NdkMediaCrypto.h"
@@ -130,17 +131,45 @@
  */
 ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec*, int64_t timeoutUs);
 
-/**
- * Send the specified buffer to the codec for processing.
+/*
+ * __USE_FILE_OFFSET64 changes the type of off_t in LP32, which changes the ABI
+ * of these declarations to  not match the platform. In that case, define these
+ * APIs in terms of int32_t instead. Passing an off_t in this situation will
+ * result in silent truncation unless the user builds with -Wconversion, but the
+ * only alternative it to not expose them at all for this configuration, which
+ * makes the whole API unusable.
+ *
+ * https://github.com/android-ndk/ndk/issues/459
  */
-media_status_t AMediaCodec_queueInputBuffer(AMediaCodec*,
-        size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags);
+#if defined(__USE_FILE_OFFSET64) && !defined(__LP64__)
+#define _off_t_compat int32_t
+#else
+#define _off_t_compat off_t
+#endif  /* defined(__USE_FILE_OFFSET64) && !defined(__LP64__) */
+
+#if (defined(__cplusplus) && __cplusplus >= 201103L) || \
+    __STDC_VERSION__ >= 201112L
+static_assert(sizeof(_off_t_compat) == sizeof(long),
+              "_off_t_compat does not match the NDK ABI. See "
+              "https://github.com/android-ndk/ndk/issues/459.");
+#endif
 
 /**
  * Send the specified buffer to the codec for processing.
  */
-media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*,
-        size_t idx, off_t offset, AMediaCodecCryptoInfo*, uint64_t time, uint32_t flags);
+media_status_t AMediaCodec_queueInputBuffer(AMediaCodec*, size_t idx,
+                                            _off_t_compat offset, size_t size,
+                                            uint64_t time, uint32_t flags);
+
+/**
+ * Send the specified buffer to the codec for processing.
+ */
+media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*, size_t idx,
+                                                  _off_t_compat offset,
+                                                  AMediaCodecCryptoInfo*,
+                                                  uint64_t time, uint32_t flags);
+
+#undef _off_t_compat
 
 /**
  * Get the index of the next available buffer of processed data.
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 38c9687..0df9a39 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1193,25 +1193,10 @@
         String8 value;
         if (param.get(String8(AudioParameter::keyBtNrec), value) == NO_ERROR) {
             bool btNrecIsOff = (value == AudioParameter::valueOff);
-            if (mBtNrecIsOff != btNrecIsOff) {
+            if (mBtNrecIsOff.exchange(btNrecIsOff) != btNrecIsOff) {
                 for (size_t i = 0; i < mRecordThreads.size(); i++) {
-                    sp<RecordThread> thread = mRecordThreads.valueAt(i);
-                    audio_devices_t device = thread->inDevice();
-                    bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff;
-                    // collect all of the thread's session IDs
-                    KeyedVector<audio_session_t, bool> ids = thread->sessionIds();
-                    // suspend effects associated with those session IDs
-                    for (size_t j = 0; j < ids.size(); ++j) {
-                        audio_session_t sessionId = ids.keyAt(j);
-                        thread->setEffectSuspended(FX_IID_AEC,
-                                                   suspend,
-                                                   sessionId);
-                        thread->setEffectSuspended(FX_IID_NS,
-                                                   suspend,
-                                                   sessionId);
-                    }
+                    mRecordThreads.valueAt(i)->checkBtNrec();
                 }
-                mBtNrecIsOff = btNrecIsOff;
             }
         }
         String8 screenState;
@@ -3214,6 +3199,11 @@
 
 status_t AudioFlinger::putOrphanEffectChain_l(const sp<AudioFlinger::EffectChain>& chain)
 {
+    // clear possible suspended state before parking the chain so that it starts in default state
+    // when attached to a new record thread
+    chain->setEffectSuspended_l(FX_IID_AEC, false);
+    chain->setEffectSuspended_l(FX_IID_NS, false);
+
     audio_session_t session = chain->sessionId();
     ssize_t index = mOrphanEffectChains.indexOfKey(session);
     ALOGV("putOrphanEffectChain_l session %d index %zd", session, index);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 8a96c1d..9023b2d 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -350,12 +350,13 @@
                                         sync_event_callback_t callBack,
                                         const wp<RefBase>& cookie);
 
+    bool        btNrecIsOff() const { return mBtNrecIsOff.load(); }
+
+
 private:
 
                audio_mode_t getMode() const { return mMode; }
 
-                bool        btNrecIsOff() const { return mBtNrecIsOff; }
-
                             AudioFlinger() ANDROID_API;
     virtual                 ~AudioFlinger();
 
@@ -781,7 +782,7 @@
                 volatile atomic_uint_fast32_t       mNextUniqueIds[AUDIO_UNIQUE_ID_USE_MAX];
 
                 audio_mode_t                        mMode;
-                bool                                mBtNrecIsOff;
+                std::atomic_bool                    mBtNrecIsOff;
 
                 // protected by mLock
                 Vector<AudioSessionRef*> mAudioSessionRefs;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index f1a55f1..f2c1c4f 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -21,10 +21,12 @@
 
 #include "Configuration.h"
 #include <utils/Log.h>
+#include <system/audio_effects/effect_aec.h>
+#include <system/audio_effects/effect_ns.h>
+#include <system/audio_effects/effect_visualizer.h>
 #include <audio_utils/primitives.h>
 #include <media/audiohal/EffectHalInterface.h>
 #include <media/audiohal/EffectsFactoryHalInterface.h>
-#include <system/audio_effects/effect_visualizer.h>
 
 #include "AudioFlinger.h"
 #include "ServiceUtilities.h"
@@ -1333,6 +1335,24 @@
     ALOGVV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p",
             cmdCode, mHasControl, mEffect.unsafe_get());
 
+    // reject commands reserved for internal use by audio framework if coming from outside
+    // of audioserver
+    switch(cmdCode) {
+        case EFFECT_CMD_ENABLE:
+        case EFFECT_CMD_DISABLE:
+        case EFFECT_CMD_SET_PARAM:
+        case EFFECT_CMD_SET_PARAM_DEFERRED:
+        case EFFECT_CMD_SET_PARAM_COMMIT:
+        case EFFECT_CMD_GET_PARAM:
+            break;
+        default:
+            if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY) {
+                break;
+            }
+            android_errorWriteLog(0x534e4554, "62019992");
+            return BAD_VALUE;
+    }
+
     if (cmdCode == EFFECT_CMD_ENABLE) {
         if (*replySize < sizeof(int)) {
             android_errorWriteLog(0x534e4554, "32095713");
@@ -1791,6 +1811,7 @@
                 idx_insert);
     }
     effect->configure();
+
     return NO_ERROR;
 }
 
@@ -2012,6 +2033,7 @@
             mSuspendedEffects.add(type->timeLow, desc);
             ALOGV("setEffectSuspended_l() add entry for %08x", type->timeLow);
         }
+
         if (desc->mRefCount++ == 0) {
             sp<EffectModule> effect = getEffectIfEnabled(type);
             if (effect != 0) {
@@ -2027,7 +2049,8 @@
         desc = mSuspendedEffects.valueAt(index);
         if (desc->mRefCount <= 0) {
             ALOGW("setEffectSuspended_l() restore refcount should not be 0 %d", desc->mRefCount);
-            desc->mRefCount = 1;
+            desc->mRefCount = 0;
+            return;
         }
         if (--desc->mRefCount == 0) {
             ALOGV("setEffectSuspended_l() remove entry for %08x", mSuspendedEffects.keyAt(index));
@@ -2105,6 +2128,17 @@
 const effect_uuid_t * const SL_IID_VOLUME = &SL_IID_VOLUME_;
 #endif //OPENSL_ES_H_
 
+/* static */
+bool AudioFlinger::EffectChain::isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type)
+{
+    // Only NS and AEC are suspended when BtNRec is off
+    if ((memcmp(type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) ||
+        (memcmp(type, FX_IID_NS, sizeof(effect_uuid_t)) == 0)) {
+        return true;
+    }
+    return false;
+}
+
 bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descriptor_t& desc)
 {
     // auxiliary effects and visualizer are never suspended on output mix
@@ -2159,7 +2193,7 @@
         ALOGV("checkSuspendOnEffectEnabled() enable suspending fx %08x",
             effect->desc().type.timeLow);
         sp<SuspendedEffectDesc> desc = mSuspendedEffects.valueAt(index);
-        // if effect is requested to suspended but was not yet enabled, supend it now.
+        // if effect is requested to suspended but was not yet enabled, suspend it now.
         if (desc->mEffect == 0) {
             desc->mEffect = effect;
             effect->setEnabled(false);
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index e37529e..e29798b 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -331,7 +331,8 @@
     void setStrategy(uint32_t strategy)
             { mStrategy = strategy; }
 
-    // suspend effect of the given type
+    // suspend or restore effects of the specified type. The number of suspend requests is counted
+    // and restore occurs once all suspend requests are cancelled.
     void setEffectSuspended_l(const effect_uuid_t *type,
                               bool suspend);
     // suspend all eligible effects
@@ -372,7 +373,7 @@
     public:
         SuspendedEffectDesc() : mRefCount(0) {}
 
-        int mRefCount;
+        int mRefCount;   // > 0 when suspended
         effect_uuid_t mType;
         wp<EffectModule> mEffect;
     };
@@ -388,6 +389,8 @@
     // types or implementations from the suspend/restore mechanism.
     bool isEffectEligibleForSuspend(const effect_descriptor_t& desc);
 
+    static bool isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type);
+
     void clearInputBuffer_l(const sp<ThreadBase>& thread);
 
     void setThread(const sp<ThreadBase>& thread);
@@ -414,6 +417,6 @@
              // mSuspendedEffects lists all effects currently suspended in the chain.
              // Use effect type UUID timelow field as key. There is no real risk of identical
              // timeLow fields among effect type UUIDs.
-             // Updated by updateSuspendedSessions_l() only.
+             // Updated by setEffectSuspended_l() and setEffectSuspendedAll_l() only.
              KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
 };
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index c4a0c0d..c10fa05 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -138,6 +138,8 @@
 
 void FastMixer::onStateChange()
 {
+    // log that audio was turned on/off
+    LOG_AUDIO_STATE();
     const FastMixerState * const current = (const FastMixerState *) mCurrent;
     const FastMixerState * const previous = (const FastMixerState *) mPrevious;
     FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
diff --git a/services/audioflinger/OWNERS b/services/audioflinger/OWNERS
new file mode 100644
index 0000000..703e4d2
--- /dev/null
+++ b/services/audioflinger/OWNERS
@@ -0,0 +1,3 @@
+hunga@google.com
+jmtrivi@google.com
+mnaganov@google.com
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7871ae6..8efcce6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -991,13 +991,6 @@
     ALOGW("power manager service died !!!");
 }
 
-void AudioFlinger::ThreadBase::setEffectSuspended(
-        const effect_uuid_t *type, bool suspend, audio_session_t sessionId)
-{
-    Mutex::Autolock _l(mLock);
-    setEffectSuspended_l(type, suspend, sessionId);
-}
-
 void AudioFlinger::ThreadBase::setEffectSuspended_l(
         const effect_uuid_t *type, bool suspend, audio_session_t sessionId)
 {
@@ -1176,6 +1169,11 @@
         return BAD_VALUE;
     }
 
+    // always allow effects without processing load or latency
+    if ((desc->flags & EFFECT_FLAG_NO_PROCESS_MASK) == EFFECT_FLAG_NO_PROCESS) {
+        return NO_ERROR;
+    }
+
     switch (mType) {
     case MIXER: {
         // Reject any effect on mixer multichannel sinks.
@@ -1206,10 +1204,6 @@
                 }
             }
 
-            // always allow effects without processing load or latency
-            if ((desc->flags & EFFECT_FLAG_NO_PROCESS_MASK) == EFFECT_FLAG_NO_PROCESS) {
-                break;
-            }
             if (flags & AUDIO_OUTPUT_FLAG_RAW) {
                 ALOGW("checkEffectCompatibility_l(): effect %s on playback thread in raw mode",
                       desc->name);
@@ -1451,6 +1445,7 @@
     effect->setDevice(mInDevice);
     effect->setMode(mAudioFlinger->getMode());
     effect->setAudioSource(mAudioSource);
+
     return NO_ERROR;
 }
 
@@ -5956,6 +5951,7 @@
     // mPipeMemory
     // mFastCaptureNBLogWriter
     , mFastTrackAvail(false)
+    , mBtNrecSuspended(false)
 {
     snprintf(mThreadName, kThreadNameLength, "AudioIn_%X", id);
     mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
@@ -6721,12 +6717,6 @@
         }
         mTracks.add(track);
 
-        // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
-        bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
-                        mAudioFlinger->btNrecIsOff();
-        setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
-        setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
-
         if ((*flags & AUDIO_INPUT_FLAG_FAST) && (tid != -1)) {
             pid_t callingPid = IPCThreadState::self()->getCallingPid();
             // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
@@ -7100,6 +7090,26 @@
     buffer->frameCount = 0;
 }
 
+void AudioFlinger::RecordThread::checkBtNrec()
+{
+    Mutex::Autolock _l(mLock);
+    checkBtNrec_l();
+}
+
+void AudioFlinger::RecordThread::checkBtNrec_l()
+{
+    // disable AEC and NS if the device is a BT SCO headset supporting those
+    // pre processings
+    bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+                        mAudioFlinger->btNrecIsOff();
+    if (mBtNrecSuspended.exchange(suspend) != suspend) {
+        for (size_t i = 0; i < mEffectChains.size(); i++) {
+            setEffectSuspended_l(FX_IID_AEC, suspend, mEffectChains[i]->sessionId());
+            setEffectSuspended_l(FX_IID_NS, suspend, mEffectChains[i]->sessionId());
+        }
+    }
+}
+
 
 bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
                                                         status_t& status)
@@ -7172,17 +7182,7 @@
             if (value != AUDIO_DEVICE_NONE) {
                 mPrevInDevice = value;
             }
-            // disable AEC and NS if the device is a BT SCO headset supporting those
-            // pre processings
-            if (mTracks.size() > 0) {
-                bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
-                                    mAudioFlinger->btNrecIsOff();
-                for (size_t i = 0; i < mTracks.size(); i++) {
-                    sp<RecordTrack> track = mTracks[i];
-                    setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
-                    setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
-                }
-            }
+            checkBtNrec_l();
         }
     }
     if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR &&
@@ -7415,17 +7415,7 @@
         mEffectChains[i]->setDevice_l(mInDevice);
     }
 
-    // disable AEC and NS if the device is a BT SCO headset supporting those
-    // pre processings
-    if (mTracks.size() > 0) {
-        bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
-                            mAudioFlinger->btNrecIsOff();
-        for (size_t i = 0; i < mTracks.size(); i++) {
-            sp<RecordTrack> track = mTracks[i];
-            setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
-            setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
-        }
-    }
+    checkBtNrec_l();
 
     // store new source and send to effects
     if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 22d78eb..32f2bc4 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -361,11 +361,6 @@
                 virtual uint32_t getStrategyForSession_l(audio_session_t sessionId __unused)
                         { return 0; }
 
-                // suspend or restore effect according to the type of effect passed. a NULL
-                // type pointer means suspend all effects in the session
-                void setEffectSuspended(const effect_uuid_t *type,
-                                        bool suspend,
-                                        audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX);
                 // check if some effects must be suspended/restored when an effect is enabled
                 // or disabled
                 void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
@@ -417,10 +412,13 @@
                 void        releaseWakeLock_l();
                 void        updateWakeLockUids_l(const SortedVector<uid_t> &uids);
                 void        getPowerManager_l();
+                // suspend or restore effects of the specified type (or all if type is NULL)
+                // on a given session. The number of suspend requests is counted and restore
+                // occurs when all suspend requests are cancelled.
                 void setEffectSuspended_l(const effect_uuid_t *type,
                                           bool suspend,
                                           audio_session_t sessionId);
-                // updated mSuspendedSessions when an effect suspended or restored
+                // updated mSuspendedSessions when an effect is suspended or restored
                 void        updateSuspendedSessions_l(const effect_uuid_t *type,
                                                       bool suspend,
                                                       audio_session_t sessionId);
@@ -484,6 +482,7 @@
                 const sp<PMDeathRecipient> mDeathRecipient;
                 // list of suspended effects per session and per type. The first (outer) vector is
                 // keyed by session ID, the second (inner) by type UUID timeLow field
+                // Updated by updateSuspendedSessions_l() only.
                 KeyedVector< audio_session_t, KeyedVector< int, sp<SuspendedSessionDesc> > >
                                         mSuspendedSessions;
                 static const size_t     kLogSize = 4 * 1024;
@@ -1391,6 +1390,8 @@
                         }
     virtual bool        isOutput() const override { return false; }
 
+            void        checkBtNrec();
+
 private:
             // Enter standby if not already in standby, and set mStandby flag
             void    standbyIfNotAlreadyInStandby();
@@ -1398,6 +1399,8 @@
             // Call the HAL standby method unconditionally, and don't change mStandby flag
             void    inputStandBy();
 
+            void    checkBtNrec_l();
+
             AudioStreamIn                       *mInput;
             SortedVector < sp<RecordTrack> >    mTracks;
             // mActiveTracks has dual roles:  it indicates the current active track(s), and
@@ -1457,6 +1460,8 @@
             sp<NBLog::Writer>                   mFastCaptureNBLogWriter;
 
             bool                                mFastTrackAvail;    // true if fast track available
+            // common state to all record threads
+            std::atomic_bool                    mBtNrecSuspended;
 };
 
 class MmapThread : public ThreadBase
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 83aa6a1..909af09 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -88,7 +88,11 @@
 
 // Write histogram timestamp entry
 #define LOG_HIST_TS() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
-                                x->logHistTS(hash(__FILE__, __LINE__)); } while(0)
+        x->logEventHistTs(NBLog::EVENT_HISTOGRAM_ENTRY_TS, hash(__FILE__, __LINE__)); } while(0)
+
+// Record that audio was turned on/off
+#define LOG_AUDIO_STATE() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->logEventHistTs(NBLog::EVENT_AUDIO_STATE, hash(__FILE__, __LINE__)); } while(0)
 
 namespace android {
 extern "C" {
diff --git a/services/audiopolicy/OWNERS b/services/audiopolicy/OWNERS
new file mode 100644
index 0000000..a8483fa
--- /dev/null
+++ b/services/audiopolicy/OWNERS
@@ -0,0 +1,3 @@
+jmtrivi@google.com
+krocard@google.com
+mnaganov@google.com
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index e022057..49423a0 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -138,13 +138,15 @@
                 requestQueueRet.description().c_str());
         return DEAD_OBJECT;
     }
+
+    std::unique_ptr<ResultMetadataQueue>& resQueue = mResultMetadataQueue;
     auto resultQueueRet = session->getCaptureResultMetadataQueue(
-        [&queue = mResultMetadataQueue](const auto& descriptor) {
-            queue = std::make_unique<ResultMetadataQueue>(descriptor);
-            if (!queue->isValid() ||  queue->availableToWrite() <= 0) {
+        [&resQueue](const auto& descriptor) {
+            resQueue = std::make_unique<ResultMetadataQueue>(descriptor);
+            if (!resQueue->isValid() || resQueue->availableToWrite() <= 0) {
                 ALOGE("HAL returns empty result metadata fmq, not use it");
-                queue = nullptr;
-                // Don't use the queue onwards.
+                resQueue = nullptr;
+                // Don't use the resQueue onwards.
             }
         });
     if (!resultQueueRet.isOk()) {
@@ -153,7 +155,7 @@
         return DEAD_OBJECT;
     }
 
-    mInterface = std::make_unique<HalInterface>(session, queue);
+    mInterface = new HalInterface(session, queue);
     std::string providerType;
     mVendorTagId = manager->getProviderTagIdLocked(mId.string());
 
@@ -182,7 +184,7 @@
     mTagMonitor.initialize(mVendorTagId);
 
     /** Start up request queue thread */
-    mRequestThread = new RequestThread(this, mStatusTracker, mInterface.get());
+    mRequestThread = new RequestThread(this, mStatusTracker, mInterface);
     res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
     if (res != OK) {
         SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -237,7 +239,7 @@
     ALOGI("%s: E", __FUNCTION__);
 
     status_t res = OK;
-
+    std::vector<wp<Camera3StreamInterface>> streams;
     {
         Mutex::Autolock l(mLock);
         if (mStatus == STATUS_UNINITIALIZED) return res;
@@ -269,8 +271,13 @@
             mRequestThread->requestExit();
         }
 
-        mOutputStreams.clear();
-        mInputStream.clear();
+        streams.reserve(mOutputStreams.size() + (mInputStream != nullptr ? 1 : 0));
+        for (size_t i = 0; i < mOutputStreams.size(); i++) {
+            streams.push_back(mOutputStreams[i]);
+        }
+        if (mInputStream != nullptr) {
+            streams.push_back(mInputStream);
+        }
     }
 
     // Joining done without holding mLock, otherwise deadlocks may ensue
@@ -289,11 +296,8 @@
     HalInterface* interface;
     {
         Mutex::Autolock l(mLock);
-
         mRequestThread.clear();
         mStatusTracker.clear();
-        mBufferManager.clear();
-
         interface = mInterface.get();
     }
 
@@ -301,12 +305,25 @@
     // wait on assorted callbacks,etc, to complete before it can return.
     interface->close();
 
+    flushInflightRequests();
+
     {
         Mutex::Autolock l(mLock);
         mInterface->clear();
+        mOutputStreams.clear();
+        mInputStream.clear();
+        mBufferManager.clear();
         internalUpdateStatusLocked(STATUS_UNINITIALIZED);
     }
 
+    for (auto& weakStream : streams) {
+        sp<Camera3StreamInterface> stream = weakStream.promote();
+        if (stream != nullptr) {
+            ALOGE("%s: Stream %d leaked! strong reference (%d)!",
+                    __FUNCTION__, stream->getId(), stream->getStrongCount() - 1);
+        }
+    }
+
     ALOGI("%s: X", __FUNCTION__);
     return res;
 }
@@ -834,6 +851,15 @@
 hardware::Return<void> Camera3Device::processCaptureResult(
         const hardware::hidl_vec<
                 hardware::camera::device::V3_2::CaptureResult>& results) {
+    // Ideally we should grab mLock, but that can lead to deadlock, and
+    // it's not super important to get up to date value of mStatus for this
+    // warning print, hence skipping the lock here
+    if (mStatus == STATUS_ERROR) {
+        // Per API contract, HAL should act as closed after device error
+        // But mStatus can be set to error by framework as well, so just log
+        // a warning here.
+        ALOGW("%s: received capture result in error state.", __FUNCTION__);
+    }
 
     if (mProcessCaptureResultLock.tryLock() != OK) {
         // This should never happen; it indicates a wrong client implementation
@@ -965,6 +991,16 @@
 
 hardware::Return<void> Camera3Device::notify(
         const hardware::hidl_vec<hardware::camera::device::V3_2::NotifyMsg>& msgs) {
+    // Ideally we should grab mLock, but that can lead to deadlock, and
+    // it's not super important to get up to date value of mStatus for this
+    // warning print, hence skipping the lock here
+    if (mStatus == STATUS_ERROR) {
+        // Per API contract, HAL should act as closed after device error
+        // But mStatus can be set to error by framework as well, so just log
+        // a warning here.
+        ALOGW("%s: received notify message in error state.", __FUNCTION__);
+    }
+
     for (const auto& msg : msgs) {
         notify(msg);
     }
@@ -2434,6 +2470,53 @@
     }
 }
 
+void Camera3Device::flushInflightRequests() {
+    { // First return buffers cached in mInFlightMap
+        Mutex::Autolock l(mInFlightLock);
+        for (size_t idx = 0; idx < mInFlightMap.size(); idx++) {
+            const InFlightRequest &request = mInFlightMap.valueAt(idx);
+            returnOutputBuffers(request.pendingOutputBuffers.array(),
+                request.pendingOutputBuffers.size(), 0);
+        }
+        mInFlightMap.clear();
+    }
+
+    // Then return all inflight buffers not returned by HAL
+    std::vector<std::pair<int32_t, int32_t>> inflightKeys;
+    mInterface->getInflightBufferKeys(&inflightKeys);
+
+    int32_t inputStreamId = (mInputStream != nullptr) ? mInputStream->getId() : -1;
+    for (auto& pair : inflightKeys) {
+        int32_t frameNumber = pair.first;
+        int32_t streamId = pair.second;
+        buffer_handle_t* buffer;
+        status_t res = mInterface->popInflightBuffer(frameNumber, streamId, &buffer);
+        if (res != OK) {
+            ALOGE("%s: Frame %d: No in-flight buffer for stream %d",
+                    __FUNCTION__, frameNumber, streamId);
+            continue;
+        }
+
+        camera3_stream_buffer_t streamBuffer;
+        streamBuffer.buffer = buffer;
+        streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+        streamBuffer.acquire_fence = -1;
+        streamBuffer.release_fence = -1;
+        if (streamId == inputStreamId) {
+            streamBuffer.stream = mInputStream->asHalStream();
+            res = mInputStream->returnInputBuffer(streamBuffer);
+            if (res != OK) {
+                ALOGE("%s: Can't return input buffer for frame %d to"
+                      "  its stream:%s (%d)",  __FUNCTION__,
+                      frameNumber, strerror(-res), res);
+            }
+        } else {
+            streamBuffer.stream = mOutputStreams.valueFor(streamId)->asHalStream();
+            returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
+        }
+    }
+}
+
 void Camera3Device::insertResultLocked(CaptureResult *result,
         uint32_t frameNumber) {
     if (result == nullptr) return;
@@ -3349,6 +3432,20 @@
     return res;
 }
 
+void Camera3Device::HalInterface::getInflightBufferKeys(
+        std::vector<std::pair<int32_t, int32_t>>* out) {
+    std::lock_guard<std::mutex> lock(mInflightLock);
+    out->clear();
+    out->reserve(mInflightBufferMap.size());
+    for (auto& pair : mInflightBufferMap) {
+        uint64_t key = pair.first;
+        int32_t streamId = key & 0xFFFFFFFF;
+        int32_t frameNumber = (key >> 32) & 0xFFFFFFFF;
+        out->push_back(std::make_pair(frameNumber, streamId));
+    }
+    return;
+}
+
 status_t Camera3Device::HalInterface::pushInflightBufferLocked(
         int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer, int acquireFence) {
     uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId);
@@ -3423,7 +3520,7 @@
 
 Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
         sp<StatusTracker> statusTracker,
-        HalInterface* interface) :
+        sp<HalInterface> interface) :
         Thread(/*canCallJava*/false),
         mParent(parent),
         mStatusTracker(statusTracker),
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 5549dd1..0251d62 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -265,6 +265,10 @@
         status_t popInflightBuffer(int32_t frameNumber, int32_t streamId,
                 /*out*/ buffer_handle_t **buffer);
 
+        // Get a vector of (frameNumber, streamId) pair of currently inflight
+        // buffers
+        void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out);
+
       private:
         camera3_device_t *mHal3Device;
         sp<hardware::camera::device::V3_2::ICameraDeviceSession> mHidlSession;
@@ -333,7 +337,7 @@
         std::vector<std::pair<int, uint64_t>> mFreedBuffers;
     };
 
-    std::unique_ptr<HalInterface> mInterface;
+    sp<HalInterface> mInterface;
 
     CameraMetadata             mDeviceInfo;
 
@@ -624,7 +628,7 @@
 
         RequestThread(wp<Camera3Device> parent,
                 sp<camera3::StatusTracker> statusTracker,
-                HalInterface* interface);
+                sp<HalInterface> interface);
         ~RequestThread();
 
         void     setNotificationListener(wp<NotificationListener> listener);
@@ -774,7 +778,7 @@
 
         wp<Camera3Device>  mParent;
         wp<camera3::StatusTracker>  mStatusTracker;
-        HalInterface*      mInterface;
+        sp<HalInterface>   mInterface;
 
         wp<NotificationListener> mListener;
 
@@ -1023,6 +1027,10 @@
     // Remove the in-flight request of the given index from mInFlightMap
     // if it's no longer needed. It must only be called with mInFlightLock held.
     void removeInFlightRequestIfReadyLocked(int idx);
+    // Remove all in-flight requests and return all buffers.
+    // This is used after HAL interface is closed to cleanup any request/buffers
+    // not returned by HAL.
+    void flushInflightRequests();
 
     /**** End scope for mInFlightLock ****/
 
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 4eb15ad..35096eb 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -293,8 +293,9 @@
 void Camera3InputStream::onBufferFreed(const wp<GraphicBuffer>& gb) {
     const sp<GraphicBuffer> buffer = gb.promote();
     if (buffer != nullptr) {
-        if (mBufferFreedListener != nullptr) {
-            mBufferFreedListener->onBufferFreed(mId, buffer->handle);
+        sp<Camera3StreamBufferFreedListener> callback = mBufferFreedListener.promote();
+        if (callback != nullptr) {
+            callback->onBufferFreed(mId, buffer->handle);
         }
     } else {
         ALOGE("%s: GraphicBuffer is freed before onBufferFreed callback finishes!", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index e15aa43..b02cd6a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -727,7 +727,7 @@
 
 void Camera3OutputStream::onBuffersRemovedLocked(
         const std::vector<sp<GraphicBuffer>>& removedBuffers) {
-    Camera3StreamBufferFreedListener* callback = mBufferFreedListener;
+    sp<Camera3StreamBufferFreedListener> callback = mBufferFreedListener.promote();
     if (callback != nullptr) {
         for (auto gb : removedBuffers) {
             callback->onBufferFreed(mId, gb->handle);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 9297ac8..e77421a 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -523,6 +523,8 @@
         return BAD_VALUE;
     }
 
+    removeOutstandingBuffer(buffer);
+
     /**
      * TODO: Check that the state is valid first.
      *
@@ -540,7 +542,6 @@
     // buffer to be returned.
     mOutputBufferReturnedSignal.signal();
 
-    removeOutstandingBuffer(buffer);
     return res;
 }
 
@@ -591,13 +592,14 @@
         return BAD_VALUE;
     }
 
+    removeOutstandingBuffer(buffer);
+
     status_t res = returnInputBufferLocked(buffer);
     if (res == OK) {
         fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/false);
         mInputBufferReturnedSignal.signal();
     }
 
-    removeOutstandingBuffer(buffer);
     return res;
 }
 
@@ -744,7 +746,7 @@
 }
 
 void Camera3Stream::setBufferFreedListener(
-        Camera3StreamBufferFreedListener* listener) {
+        wp<Camera3StreamBufferFreedListener> listener) {
     Mutex::Autolock l(mLock);
     // Only allow set listener during stream configuration because stream is guaranteed to be IDLE
     // at this state, so setBufferFreedListener won't collide with onBufferFreed callbacks
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index b6c8396..0940d62 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -371,7 +371,7 @@
 
     // Setting listener will remove previous listener (if exists)
     virtual void     setBufferFreedListener(
-            Camera3StreamBufferFreedListener* listener) override;
+            wp<Camera3StreamBufferFreedListener> listener) override;
 
     /**
      * Return if the buffer queue of the stream is abandoned.
@@ -416,7 +416,7 @@
             android_dataspace dataSpace, camera3_stream_rotation_t rotation,
             int setId);
 
-    Camera3StreamBufferFreedListener* mBufferFreedListener;
+    wp<Camera3StreamBufferFreedListener> mBufferFreedListener;
 
     /**
      * Interface to be implemented by derived classes
diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
index 478a752..104cd22 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
@@ -24,7 +24,7 @@
 
 namespace camera3 {
 
-class Camera3StreamBufferFreedListener {
+class Camera3StreamBufferFreedListener : public virtual RefBase {
 public:
     // onBufferFreed is called when a buffer is no longer being managed
     // by this stream. This will not be called in events when all
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index c695a10..0544a1b 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -298,7 +298,7 @@
      * Client is responsible to keep the listener object alive throughout the lifecycle of this
      * Camera3Stream.
      */
-    virtual void setBufferFreedListener(Camera3StreamBufferFreedListener* listener) = 0;
+    virtual void setBufferFreedListener(wp<Camera3StreamBufferFreedListener> listener) = 0;
 };
 
 } // namespace camera3
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index c0bea13..75392bd 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -64,7 +64,7 @@
 // Create a tracker for the client.
 aaudio_result_t AAudioClientTracker::registerClient(pid_t pid,
                                          const sp<IAAudioClient>& client) {
-    ALOGD("AAudioClientTracker::registerClient(), calling pid = %d, getpid() = %d\n",
+    ALOGV("AAudioClientTracker::registerClient(), calling pid = %d, getpid() = %d\n",
           pid, getpid());
 
     std::lock_guard<std::mutex> lock(mLock);
@@ -84,7 +84,7 @@
 }
 
 void AAudioClientTracker::unregisterClient(pid_t pid) {
-    ALOGD("AAudioClientTracker::unregisterClient(), calling pid = %d, getpid() = %d\n",
+    ALOGV("AAudioClientTracker::unregisterClient(), calling pid = %d, getpid() = %d\n",
           pid, getpid());
     std::lock_guard<std::mutex> lock(mLock);
     mNotificationClients.erase(pid);
@@ -103,12 +103,12 @@
 aaudio_result_t
 AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
     aaudio_result_t result = AAUDIO_OK;
-    ALOGD("AAudioClientTracker::registerClientStream(%d, %p)\n", pid, serviceStream.get());
+    ALOGV("AAudioClientTracker::registerClientStream(%d, %p)\n", pid, serviceStream.get());
     std::lock_guard<std::mutex> lock(mLock);
     sp<NotificationClient> notificationClient = mNotificationClients[pid];
     if (notificationClient == 0) {
         // This will get called the first time the audio server registers an internal stream.
-        ALOGD("AAudioClientTracker::registerClientStream(%d,) unrecognized pid\n", pid);
+        ALOGV("AAudioClientTracker::registerClientStream(%d,) unrecognized pid\n", pid);
         notificationClient = new NotificationClient(pid);
         mNotificationClients[pid] = notificationClient;
     }
@@ -120,11 +120,11 @@
 aaudio_result_t
 AAudioClientTracker::unregisterClientStream(pid_t pid,
                                             sp<AAudioServiceStreamBase> serviceStream) {
-    ALOGD("AAudioClientTracker::unregisterClientStream(%d, %p)\n", pid, serviceStream.get());
+    ALOGV("AAudioClientTracker::unregisterClientStream(%d, %p)\n", pid, serviceStream.get());
     std::lock_guard<std::mutex> lock(mLock);
     auto it = mNotificationClients.find(pid);
     if (it != mNotificationClients.end()) {
-        ALOGD("AAudioClientTracker::unregisterClientStream(%d, %p) found NotificationClient\n",
+        ALOGV("AAudioClientTracker::unregisterClientStream(%d, %p) found NotificationClient\n",
               pid, serviceStream.get());
         it->second->unregisterClientStream(serviceStream);
     } else {
@@ -158,11 +158,7 @@
 aaudio_result_t AAudioClientTracker::NotificationClient::unregisterClientStream(
         sp<AAudioServiceStreamBase> serviceStream) {
     std::lock_guard<std::mutex> lock(mLock);
-    ALOGD("AAudioClientTracker::NotificationClient() before erase() count = %d\n",
-          (int)mStreams.size());
     mStreams.erase(serviceStream);
-    ALOGD("AAudioClientTracker::NotificationClient() after erase() count = %d\n",
-          (int)mStreams.size());
     return AAUDIO_OK;
 }
 
@@ -176,8 +172,6 @@
 
         {
             std::lock_guard<std::mutex> lock(mLock);
-            ALOGV("AAudioClientTracker::binderDied() pid = %d, # streams = %d\n",
-                  mProcessId, (int) mStreams.size());
             for (auto serviceStream : mStreams) {
                 streamsToClose.insert(serviceStream);
             }
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 5895974..0f863fe 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -84,7 +84,7 @@
     // Don't fall back to SHARED because that would cause recursion.
     builder.setSharingModeMatchRequired(true);
     builder.setDeviceId(mRequestedDeviceId);
-    builder.setFormat(configuration.getAudioFormat());
+    builder.setFormat(configuration.getFormat());
     builder.setSampleRate(configuration.getSampleRate());
     builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
     builder.setDirection(getDirection());
@@ -112,10 +112,10 @@
 }
 
 aaudio_result_t AAudioServiceEndpoint::startStream(sp<AAudioServiceStreamShared> sharedStream) {
-    // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
     aaudio_result_t result = AAUDIO_OK;
-    std::lock_guard<std::mutex> lock(mLockStreams);
     if (++mRunningStreams == 1) {
+        // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
+        std::lock_guard<std::mutex> lock(mLockStreams);
         result = getStreamInternal()->requestStart();
         startSharingThread_l();
     }
@@ -123,13 +123,8 @@
 }
 
 aaudio_result_t AAudioServiceEndpoint::stopStream(sp<AAudioServiceStreamShared> sharedStream) {
-    int numRunningStreams = 0;
-    {
-        std::lock_guard<std::mutex> lock(mLockStreams);
-        numRunningStreams = --mRunningStreams;
-    }
-    if (numRunningStreams == 0) {
-        // Don't call this under a lock because the callbackLoop also uses the lock.
+    // Don't lock here because the disconnectRegisteredStreams also uses the lock.
+    if (--mRunningStreams == 0) { // atomic
         stopSharingThread();
         getStreamInternal()->requestStop();
     }
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index ed995e5..e40a670 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -73,13 +73,13 @@
 
     virtual AudioStreamInternal *getStreamInternal() = 0;
 
-    std::atomic<bool>        mCallbackEnabled;
+    std::atomic<bool>        mCallbackEnabled{false};
 
     mutable std::mutex       mLockStreams;
 
     std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
 
-    size_t                   mRunningStreams = 0;
+    std::atomic<int>         mRunningStreams{0};
 
 private:
     aaudio_result_t startSharingThread_l();
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index 86ccde0..e609ab5 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -57,7 +57,6 @@
             mLatencyTuningEnabled = true;
             burstsPerBuffer = BURSTS_PER_BUFFER_DEFAULT;
         }
-        ALOGD("AAudioServiceEndpoint(): burstsPerBuffer = %d", burstsPerBuffer);
         int32_t desiredBufferSize = burstsPerBuffer * getStreamInternal()->getFramesPerBurst();
         getStreamInternal()->setBufferSize(desiredBufferSize);
     }
@@ -66,7 +65,6 @@
 
 // Mix data from each application stream and write result to the shared MMAP stream.
 void *AAudioServiceEndpointPlay::callbackLoop() {
-    ALOGD("AAudioServiceEndpointPlay(): callbackLoop() entering");
     int32_t underflowCount = 0;
     aaudio_result_t result = AAUDIO_OK;
     int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
@@ -102,6 +100,8 @@
         }
     }
 
-    ALOGD("AAudioServiceEndpointPlay(): callbackLoop() exiting, %d underflows", underflowCount);
+    ALOGW_IF((underflowCount > 0),
+             "AAudioServiceEndpointPlay(): callbackLoop() had %d underflows", underflowCount);
+
     return NULL; // TODO review
 }
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index e0f6ad4..5f7d179 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -219,6 +219,8 @@
     //          (long long) command.timestamp.timestamp);
         command.what = AAudioServiceMessage::code::TIMESTAMP;
         result = writeUpMessageQueue(&command);
+    } else if (result == AAUDIO_ERROR_UNAVAILABLE) {
+        result = AAUDIO_OK; // just not available yet, try again later
     }
     return result;
 }
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 93a522e..cebefec 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -163,6 +163,11 @@
 
     aaudio_result_t sendCurrentTimestamp();
 
+    /**
+     * @param positionFrames
+     * @param timeNanos
+     * @return AAUDIO_OK or AAUDIO_ERROR_UNAVAILABLE or other negative error
+     */
     virtual aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) = 0;
 
     virtual aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) = 0;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 68be3c3..afa2ff0 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -95,7 +95,7 @@
     aaudio_direction_t direction = request.getDirection();
 
     // Fill in config
-    aaudio_format_t aaudioFormat = configurationInput.getAudioFormat();
+    aaudio_format_t aaudioFormat = configurationInput.getFormat();
     if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
         aaudioFormat = AAUDIO_FORMAT_PCM_I16;
     }
@@ -154,9 +154,9 @@
               status);
         return AAUDIO_ERROR_UNAVAILABLE;
     } else {
-        ALOGD("createMmapBuffer status %d shared_address = %p buffer_size %d burst_size %d"
-                "Sharable FD: %s",
-              status, mMmapBufferinfo.shared_memory_address,
+        ALOGD("createMmapBuffer status %d, buffer_size, %d burst_size %d"
+                ", Sharable FD: %s",
+              status,
               abs(mMmapBufferinfo.buffer_size_frames),
               mMmapBufferinfo.burst_size_frames,
               mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
@@ -210,7 +210,7 @@
     // Fill in AAudioStreamConfiguration
     configurationOutput.setSampleRate(mSampleRate);
     configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
-    configurationOutput.setAudioFormat(mAudioFormat);
+    configurationOutput.setFormat(mAudioFormat);
     configurationOutput.setDeviceId(deviceId);
 
     setState(AAUDIO_STREAM_STATE_OPEN);
@@ -297,16 +297,18 @@
         return AAUDIO_ERROR_NULL;
     }
     status_t status = mMmapStream->getMmapPosition(&position);
-    if (status != OK) {
-        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned %d", status);
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+    if (result == AAUDIO_ERROR_UNAVAILABLE) {
+        ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data yet");
+    } else if (result != AAUDIO_OK) {
+        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
         disconnect();
-        return AAudioConvert_androidToAAudioResult(status);
     } else {
         mFramesRead.update32(position.position_frames);
         *positionFrames = mFramesRead.get();
         *timeNanos = position.time_nanoseconds;
     }
-    return AAUDIO_OK;
+    return result;
 }
 
 void AAudioServiceStreamMMAP::onTearDown() {
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index fe488cb..5654113 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -112,7 +112,7 @@
     }
 
     // Is the request compatible with the shared endpoint?
-    mAudioFormat = configurationInput.getAudioFormat();
+    mAudioFormat = configurationInput.getFormat();
     if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
         mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
     } else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
@@ -169,7 +169,7 @@
     // Fill in configuration for client.
     configurationOutput.setSampleRate(mSampleRate);
     configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
-    configurationOutput.setAudioFormat(mAudioFormat);
+    configurationOutput.setFormat(mAudioFormat);
     configurationOutput.setDeviceId(mServiceEndpoint->getDeviceId());
 
     result = mServiceEndpoint->registerStream(keep);
diff --git a/services/oboeservice/OWNERS b/services/oboeservice/OWNERS
new file mode 100644
index 0000000..f4d51f9
--- /dev/null
+++ b/services/oboeservice/OWNERS
@@ -0,0 +1 @@
+philburk@google.com
diff --git a/services/oboeservice/SharedMemoryProxy.cpp b/services/oboeservice/SharedMemoryProxy.cpp
index fc4532c..c31557e 100644
--- a/services/oboeservice/SharedMemoryProxy.cpp
+++ b/services/oboeservice/SharedMemoryProxy.cpp
@@ -16,12 +16,11 @@
 
 #define LOG_TAG "AAudioService"
 //#define LOG_NDEBUG 0
-#include <utils/Log.h>
+#include <log/log.h>
 
 #include <aaudio/AAudio.h>
 #include "SharedMemoryProxy.h"
 
-using namespace android;
 using namespace aaudio;
 
 SharedMemoryProxy::~SharedMemoryProxy()