Merge "NuPlayer: fix track error notification"
diff --git a/drm/mediacas/plugins/mock/Android.mk b/drm/mediacas/plugins/mock/Android.mk
index a97fac6..a1d61da 100644
--- a/drm/mediacas/plugins/mock/Android.mk
+++ b/drm/mediacas/plugins/mock/Android.mk
@@ -28,6 +28,8 @@
 LOCAL_SHARED_LIBRARIES := \
     libutils liblog
 
+LOCAL_HEADER_LIBRARIES := media_plugin_headers
+
 LOCAL_C_INCLUDES += \
     $(TOP)/frameworks/av/include \
     $(TOP)/frameworks/native/include/media \
diff --git a/drm/mediadrm/plugins/mock/Android.bp b/drm/mediadrm/plugins/mock/Android.bp
index 7f44819..abd1884 100644
--- a/drm/mediadrm/plugins/mock/Android.bp
+++ b/drm/mediadrm/plugins/mock/Android.bp
@@ -22,6 +22,8 @@
     vendor: true,
     relative_install_path: "mediadrm",
 
+    header_libs: ["media_plugin_headers"],
+
     shared_libs: [
         "libutils",
         "liblog",
diff --git a/include/media/MmapStreamInterface.h b/include/media/MmapStreamInterface.h
index 7dbc19e..d689e25 100644
--- a/include/media/MmapStreamInterface.h
+++ b/include/media/MmapStreamInterface.h
@@ -18,6 +18,7 @@
 #define ANDROID_AUDIO_MMAP_STREAM_INTERFACE_H
 
 #include <system/audio.h>
+#include <media/AudioClient.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
@@ -37,12 +38,6 @@
         DIRECTION_INPUT,       /**< open a capture mmap stream */
     } stream_direction_t;
 
-    class Client {
-     public:
-        uid_t clientUid;
-        pid_t clientPid;
-        String16 packageName;
-    };
     /**
      * Open a playback or capture stream in MMAP mode at the audio HAL.
      *
@@ -53,13 +48,14 @@
      * \param[in,out] config audio parameters (sampling rate, format ...) for the stream.
      *                       Requested parameters as input,
      *                       Actual parameters as output
-     * \param[in] client a Client struct describing the first client using this stream.
+     * \param[in] client a AudioClient struct describing the first client using this stream.
      * \param[in,out] deviceId audio device the stream should preferably be routed to/from
      *                       Requested as input,
      *                       Actual as output
      * \param[in] callback the MmapStreamCallback interface used by AudioFlinger to notify
      *                     condition changes affecting the stream operation
      * \param[out] interface the MmapStreamInterface interface controlling the created stream
+     * \param[out] same unique handle as the one used for the first client stream started.
      * \return OK if the stream was successfully created.
      *         NO_INIT if AudioFlinger is not properly initialized
      *         BAD_VALUE if the stream cannot be opened because of invalid arguments
@@ -68,10 +64,11 @@
     static status_t openMmapStream(stream_direction_t direction,
                                            const audio_attributes_t *attr,
                                            audio_config_base_t *config,
-                                           const Client& client,
+                                           const AudioClient& client,
                                            audio_port_handle_t *deviceId,
                                            const sp<MmapStreamCallback>& callback,
-                                           sp<MmapStreamInterface>& interface);
+                                           sp<MmapStreamInterface>& interface,
+                                           audio_port_handle_t *handle);
 
     /**
      * Retrieve information on the mmap buffer used for audio samples transfer.
@@ -105,13 +102,13 @@
      * Start a stream operating in mmap mode.
      * createMmapBuffer() must be called before calling start()
      *
-     * \param[in] client a Client struct describing the client starting on this stream.
+     * \param[in] client a AudioClient struct describing the client starting on this stream.
      * \param[out] handle unique handle for this instance. Used with stop().
      * \return OK in case of success.
      *         NO_INIT in case of initialization error
      *         INVALID_OPERATION if called out of sequence
      */
-    virtual status_t start(const Client& client, audio_port_handle_t *handle) = 0;
+    virtual status_t start(const AudioClient& client, audio_port_handle_t *handle) = 0;
 
     /**
      * Stop a stream operating in mmap mode.
diff --git a/include/media/omx/1.0/Conversion.h b/include/media/omx/1.0/Conversion.h
new file mode 120000
index 0000000..06d0ae8
--- /dev/null
+++ b/include/media/omx/1.0/Conversion.h
@@ -0,0 +1 @@
+/usr/local/google/home/pawin/master/frameworks/av/media/libmedia/omx/1.0/include/media/omx/1.0/Conversion.h
\ No newline at end of file
diff --git a/include/media/omx/1.0/WGraphicBufferSource.h b/include/media/omx/1.0/WGraphicBufferSource.h
new file mode 120000
index 0000000..a875e8b
--- /dev/null
+++ b/include/media/omx/1.0/WGraphicBufferSource.h
@@ -0,0 +1 @@
+/usr/local/google/home/pawin/master/frameworks/av/media/libmedia/omx/1.0/include/media/omx/1.0/WGraphicBufferSource.h
\ No newline at end of file
diff --git a/include/media/omx/1.0/WOmx.h b/include/media/omx/1.0/WOmx.h
new file mode 120000
index 0000000..196b306
--- /dev/null
+++ b/include/media/omx/1.0/WOmx.h
@@ -0,0 +1 @@
+/usr/local/google/home/pawin/master/frameworks/av/media/libmedia/omx/1.0/include/media/omx/1.0/WOmx.h
\ No newline at end of file
diff --git a/include/media/omx/1.0/WOmxBufferSource.h b/include/media/omx/1.0/WOmxBufferSource.h
new file mode 120000
index 0000000..8237c6c
--- /dev/null
+++ b/include/media/omx/1.0/WOmxBufferSource.h
@@ -0,0 +1 @@
+/usr/local/google/home/pawin/master/frameworks/av/media/libmedia/omx/1.0/include/media/omx/1.0/WOmxBufferSource.h
\ No newline at end of file
diff --git a/include/media/omx/1.0/WOmxNode.h b/include/media/omx/1.0/WOmxNode.h
new file mode 120000
index 0000000..f30d59c
--- /dev/null
+++ b/include/media/omx/1.0/WOmxNode.h
@@ -0,0 +1 @@
+/usr/local/google/home/pawin/master/frameworks/av/media/libmedia/omx/1.0/include/media/omx/1.0/WOmxNode.h
\ No newline at end of file
diff --git a/include/media/omx/1.0/WOmxObserver.h b/include/media/omx/1.0/WOmxObserver.h
new file mode 120000
index 0000000..1b86143
--- /dev/null
+++ b/include/media/omx/1.0/WOmxObserver.h
@@ -0,0 +1 @@
+/usr/local/google/home/pawin/master/frameworks/av/media/libmedia/omx/1.0/include/media/omx/1.0/WOmxObserver.h
\ No newline at end of file
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
index 1338b86..edf644a 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
@@ -26,30 +26,18 @@
 #include "AAudioExampleUtils.h"
 #include "AAudioSimpleRecorder.h"
 
-#define SAMPLE_RATE        48000
-
-#define NUM_SECONDS        10
-
+// TODO support FLOAT
+#define REQUIRED_FORMAT  AAUDIO_FORMAT_PCM_I16
 #define MIN_FRAMES_TO_READ 48  /* arbitrary, 1 msec at 48000 Hz */
 
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-    (void)argc; // unused
-
+    AAudioArgsParser   argParser;
     aaudio_result_t result;
     AAudioSimpleRecorder recorder;
     int actualSamplesPerFrame;
     int actualSampleRate;
-    const aaudio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
-    aaudio_format_t actualDataFormat;
-
-    const int requestedInputChannelCount = 2; // Can affect whether we get a FAST path.
-
-    //aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
-    const aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
-    //aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
-    //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
-    const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
+    aaudio_format_t       actualDataFormat;
     aaudio_sharing_mode_t actualSharingMode;
 
     AAudioStream *aaudioStream = nullptr;
@@ -70,18 +58,18 @@
 
     printf("%s - Monitor input level using AAudio\n", argv[0]);
 
-    AAudio_setMMapPolicy(AAUDIO_POLICY_ALWAYS);
+    argParser.setFormat(REQUIRED_FORMAT);
+    if (argParser.parseArgs(argc, argv)) {
+        return EXIT_FAILURE;
+    }
 
-    recorder.setPerformanceMode(requestedPerformanceMode);
-    recorder.setSharingMode(requestedSharingMode);
-
-    result = recorder.open(requestedInputChannelCount, 48000, requestedDataFormat,
-                           nullptr, nullptr, nullptr);
+    result = recorder.open(argParser);
     if (result != AAUDIO_OK) {
         fprintf(stderr, "ERROR -  recorder.open() returned %d\n", result);
         goto finish;
     }
     aaudioStream = recorder.getStream();
+    argParser.compareWithStream(aaudioStream);
 
     deviceId = AAudioStream_getDeviceId(aaudioStream);
     printf("deviceId = %d\n", deviceId);
@@ -91,11 +79,6 @@
     actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
     printf("SamplesPerFrame = %d\n", actualSampleRate);
 
-    actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
-    printf("SharingMode: requested = %s, actual = %s\n",
-            getSharingModeText(requestedSharingMode),
-            getSharingModeText(actualSharingMode));
-
     // This is the number of frames that are written in one chunk by a DMA controller
     // or a DSP.
     framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
@@ -110,14 +93,12 @@
     printf("DataFormat: framesPerRead  = %d\n",framesPerRead);
 
     actualDataFormat = AAudioStream_getFormat(aaudioStream);
-    printf("DataFormat: requested      = %d, actual = %d\n", requestedDataFormat, actualDataFormat);
+    printf("DataFormat: requested      = %d, actual = %d\n",
+           REQUIRED_FORMAT, actualDataFormat);
     // TODO handle other data formats
-    assert(actualDataFormat == AAUDIO_FORMAT_PCM_I16);
+    assert(actualDataFormat == REQUIRED_FORMAT);
 
-    printf("PerformanceMode: requested = %d, actual = %d\n", requestedPerformanceMode,
-           AAudioStream_getPerformanceMode(aaudioStream));
-
-    // Allocate a buffer for the audio data.
+    // Allocate a buffer for the PCM_16 audio data.
     data = new(std::nothrow) int16_t[framesPerRead * actualSamplesPerFrame];
     if (data == nullptr) {
         fprintf(stderr, "ERROR - could not allocate data buffer\n");
@@ -136,7 +117,7 @@
     printf("after start, state = %s\n", AAudio_convertStreamStateToText(state));
 
     // Record for a while.
-    framesToRecord = actualSampleRate * NUM_SECONDS;
+    framesToRecord = actualSampleRate * argParser.getDurationSeconds();
     framesLeft = framesToRecord;
     while (framesLeft > 0) {
         // Read audio data from the stream.
@@ -176,6 +157,8 @@
         goto finish;
     }
 
+    argParser.compareWithStream(aaudioStream);
+
 finish:
     recorder.close();
     delete[] data;
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 9f06ee7..45a3beb 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -20,7 +20,6 @@
 #include <assert.h>
 #include <cctype>
 #include <math.h>
-#include <stdlib.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
new file mode 100644
index 0000000..54217a5
--- /dev/null
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -0,0 +1,288 @@
+/*
+ * 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 AAUDIO_EXAMPLE_ARGS_PARSER_H
+#define AAUDIO_EXAMPLE_ARGS_PARSER_H
+
+#include <cctype>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
+#include <AAudioExampleUtils.h>
+
+// TODO use this as a base class within AAudio
+class AAudioParameters {
+public:
+
+    /**
+     * This is also known as samplesPerFrame.
+     */
+    int32_t getChannelCount() const {
+        return mChannelCount;
+    }
+
+    void setChannelCount(int32_t channelCount) {
+        mChannelCount = channelCount;
+    }
+
+    int32_t getSampleRate() const {
+        return mSampleRate;
+    }
+
+    void setSampleRate(int32_t sampleRate) {
+        mSampleRate = sampleRate;
+    }
+
+    aaudio_format_t getFormat() const {
+        return mFormat;
+    }
+
+    void setFormat(aaudio_format_t format) {
+        mFormat = format;
+    }
+
+    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;
+    }
+
+    int32_t getPerformanceMode() const {
+        return mPerformanceMode;
+    }
+
+    void setPerformanceMode(aaudio_performance_mode_t performanceMode) {
+        mPerformanceMode = performanceMode;
+    }
+
+    int32_t getDeviceId() const {
+        return mDeviceId;
+    }
+
+    void setDeviceId(int32_t deviceId) {
+        mDeviceId = deviceId;
+    }
+
+    int32_t getNumberOfBursts() const {
+        return mNumberOfBursts;
+    }
+
+    void setNumberOfBursts(int32_t numBursts) {
+        mNumberOfBursts = numBursts;
+    }
+
+    /**
+     * Apply these parameters to a stream builder.
+     * @param builder
+     */
+    void applyParameters(AAudioStreamBuilder *builder) const {
+        AAudioStreamBuilder_setChannelCount(builder, mChannelCount);
+        AAudioStreamBuilder_setFormat(builder, mFormat);
+        AAudioStreamBuilder_setSampleRate(builder, mSampleRate);
+        AAudioStreamBuilder_setBufferCapacityInFrames(builder, mBufferCapacity);
+        AAudioStreamBuilder_setDeviceId(builder, mDeviceId);
+        AAudioStreamBuilder_setSharingMode(builder, mSharingMode);
+        AAudioStreamBuilder_setPerformanceMode(builder, mPerformanceMode);
+    }
+
+private:
+    int32_t                    mChannelCount    = AAUDIO_UNSPECIFIED;
+    aaudio_format_t            mFormat          = AAUDIO_FORMAT_UNSPECIFIED;
+    int32_t                    mSampleRate      = AAUDIO_UNSPECIFIED;
+
+    int32_t                    mBufferCapacity  = AAUDIO_UNSPECIFIED;
+    int32_t                    mDeviceId        = AAUDIO_UNSPECIFIED;
+    aaudio_sharing_mode_t      mSharingMode     = AAUDIO_SHARING_MODE_SHARED;
+    aaudio_performance_mode_t  mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
+
+    int32_t                    mNumberOfBursts = AAUDIO_UNSPECIFIED;
+};
+
+class AAudioArgsParser : public AAudioParameters {
+public:
+    AAudioArgsParser() = default;
+    ~AAudioArgsParser() = default;
+
+    enum {
+        DEFAULT_DURATION_SECONDS = 5
+    };
+
+    /**
+     * @param arg
+     * @return true if the argument was not handled
+     */
+    bool parseArg(const char *arg) {
+        bool unrecognized = false;
+        if (arg[0] == '-') {
+            char option = arg[1];
+            switch (option) {
+                case 'b':
+                    setBufferCapacity(atoi(&arg[2]));
+                    break;
+                case 'c':
+                    setChannelCount(atoi(&arg[2]));
+                    break;
+                case 'd':
+                    mDurationSeconds = atoi(&arg[2]);
+                    break;
+                case 'm':
+                    AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
+                    break;
+                case 'n':
+                    setNumberOfBursts(atoi(&arg[2]));
+                    break;
+                case 'p':
+                    setPerformanceMode(parsePerformanceMode(arg[2]));
+                    break;
+                case 'r':
+                    setSampleRate(atoi(&arg[2]));
+                    break;
+                case 'x':
+                    setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+                    break;
+                default:
+                    unrecognized = true;
+                    break;
+            }
+        }
+        return unrecognized;
+    }
+
+    /**
+     *
+     * @param argc
+     * @param argv
+     * @return true if an unrecognized argument was passed
+     */
+    bool parseArgs(int argc, const char **argv) {
+        for (int i = 1; i < argc; i++) {
+            const char *arg = argv[i];
+            if (parseArg(arg)) {
+                usage();
+                return true;
+            }
+
+        }
+        return false;
+    }
+
+    static void usage() {
+        printf("-c{channels} -d{duration} -m -n{burstsPerBuffer} -p{perfMode} -r{rate} -x\n");
+        printf("      Default values are UNSPECIFIED unless otherwise stated.\n");
+        printf("      -b{bufferCapacity} frames\n");
+        printf("      -c{channels} for example 2 for stereo\n");
+        printf("      -d{duration} in seconds, default is %d\n", DEFAULT_DURATION_SECONDS);
+        printf("      -m enable MMAP\n");
+        printf("      -n{numberOfBursts} for setBufferSize\n");
+        printf("      -p{performanceMode} set output AAUDIO_PERFORMANCE_MODE*, default NONE\n");
+        printf("          n for _NONE\n");
+        printf("          l for _LATENCY\n");
+        printf("          p for _POWER_SAVING;\n");
+        printf("      -r{sampleRate} for example 44100\n");
+        printf("      -x to use EXCLUSIVE mode\n");
+    }
+
+    static aaudio_performance_mode_t parsePerformanceMode(char c) {
+        aaudio_performance_mode_t mode = AAUDIO_PERFORMANCE_MODE_NONE;
+        switch (c) {
+            case 'n':
+                mode = AAUDIO_PERFORMANCE_MODE_NONE;
+                break;
+            case 'l':
+                mode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
+                break;
+            case 'p':
+                mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
+                break;
+            default:
+                printf("ERROR invalid performance mode %c\n", c);
+                break;
+        }
+        return mode;
+    }
+
+    /**
+     * Print stream parameters in comparison with requested values.
+     * @param stream
+     */
+    void compareWithStream(AAudioStream *stream) {
+
+        printf("  DeviceId:     requested = %d, actual = %d\n",
+               getDeviceId(), AAudioStream_getDeviceId(stream));
+
+        aaudio_stream_state_t state = AAudioStream_getState(stream);
+        printf("  State:        %s\n", AAudio_convertStreamStateToText(state));
+
+        // Check to see what kind of stream we actually got.
+        printf("  SampleRate:   requested = %d, actual = %d\n",
+               getSampleRate(), AAudioStream_getSampleRate(stream));
+
+        printf("  ChannelCount: requested = %d, actual = %d\n",
+               getChannelCount(), AAudioStream_getChannelCount(stream));
+
+        printf("  DataFormat:   requested = %d, actual = %d\n",
+               getFormat(), AAudioStream_getFormat(stream));
+
+        int32_t framesPerBurst = AAudioStream_getFramesPerBurst(stream);
+        int32_t sizeFrames = AAudioStream_getBufferSizeInFrames(stream);
+        printf("  Buffer:       burst     = %d\n", framesPerBurst);
+        if (framesPerBurst > 0) {
+            printf("  Buffer:       size      = %d = (%d * %d) + %d\n",
+                   sizeFrames,
+                   (sizeFrames / framesPerBurst),
+                   framesPerBurst,
+                   (sizeFrames % framesPerBurst));
+        }
+        printf("  Capacity:     requested = %d, actual = %d\n", getBufferCapacity(),
+               AAudioStream_getBufferCapacityInFrames(stream));
+
+        printf("  SharingMode:  requested = %s, actual = %s\n",
+               getSharingModeText(getSharingMode()),
+               getSharingModeText(AAudioStream_getSharingMode(stream)));
+
+        printf("  PerformanceMode: requested = %d, actual = %d\n",
+               getPerformanceMode(), AAudioStream_getPerformanceMode(stream));
+        printf("  Is MMAP used? %s\n", AAudioStream_isMMapUsed(stream)
+               ? "yes" : "no");
+
+    }
+
+    int32_t getDurationSeconds() const {
+        return mDurationSeconds;
+    }
+
+    void setDurationSeconds(int32_t seconds) {
+        mDurationSeconds = seconds;
+    }
+
+private:
+    int32_t      mDurationSeconds = DEFAULT_DURATION_SECONDS;
+};
+
+#endif // AAUDIO_EXAMPLE_ARGS_PARSER_H
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index aaeb25f..19f8aff 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -23,6 +23,7 @@
 #include <sched.h>
 
 #include <aaudio/AAudio.h>
+#include "AAudioArgsParser.h"
 #include "SineGenerator.h"
 
 //#define SHARING_MODE  AAUDIO_SHARING_MODE_EXCLUSIVE
@@ -55,15 +56,23 @@
         mRequestedPerformanceMode = requestedPerformanceMode;
     }
 
+    // TODO Extract a common base class for record and playback.
     /**
      * Also known as "sample rate"
      * Only call this after open() has been called.
      */
-    int32_t getFramesPerSecond() {
+    int32_t getFramesPerSecond() const {
+        return getSampleRate(); // alias
+    }
+
+    /**
+     * Only call this after open() has been called.
+     */
+    int32_t getSampleRate() const {
         if (mStream == nullptr) {
             return AAUDIO_ERROR_INVALID_STATE;
         }
-        return AAudioStream_getSampleRate(mStream);;
+        return AAudioStream_getSampleRate(mStream);
     }
 
     /**
@@ -73,57 +82,83 @@
         if (mStream == nullptr) {
             return AAUDIO_ERROR_INVALID_STATE;
         }
-        return AAudioStream_getChannelCount(mStream);;
+        return AAudioStream_getChannelCount(mStream);
     }
 
     /**
      * Open a stream
      */
+    aaudio_result_t open(const AAudioParameters &parameters,
+                         AAudioStream_dataCallback dataCallback = nullptr,
+                         AAudioStream_errorCallback errorCallback = nullptr,
+                         void *userContext = nullptr) {
+        aaudio_result_t result = AAUDIO_OK;
+
+        // Use an AAudioStreamBuilder to contain requested parameters.
+        AAudioStreamBuilder *builder = nullptr;
+        result = AAudio_createStreamBuilder(&builder);
+        if (result != AAUDIO_OK) return result;
+
+        parameters.applyParameters(builder); // apply args
+
+        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
+
+        if (dataCallback != nullptr) {
+            AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
+        }
+        if (errorCallback != nullptr) {
+            AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
+        }
+        //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
+        //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
+
+        // Open an AAudioStream using the Builder.
+        result = AAudioStreamBuilder_openStream(builder, &mStream);
+
+        if (result == AAUDIO_OK) {
+            int32_t sizeInBursts = parameters.getNumberOfBursts();
+            if (sizeInBursts > 0) {
+                int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
+                AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
+            }
+        }
+
+        AAudioStreamBuilder_delete(builder);
+        return result;
+    }
+
     aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
-                         AAudioStream_dataCallback dataProc, AAudioStream_errorCallback errorProc,
+                         AAudioStream_dataCallback dataProc,
+                         AAudioStream_errorCallback errorProc,
                          void *userContext) {
         aaudio_result_t result = AAUDIO_OK;
 
         // Use an AAudioStreamBuilder to contain requested parameters.
-        result = AAudio_createStreamBuilder(&mBuilder);
+        AAudioStreamBuilder *builder = nullptr;
+        result = AAudio_createStreamBuilder(&builder);
         if (result != AAUDIO_OK) return result;
 
-        //AAudioStreamBuilder_setSampleRate(mBuilder, 44100);
-        AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequestedPerformanceMode);
-        AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
+        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
+        AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
+        AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
+
+        AAudioStreamBuilder_setChannelCount(builder, channelCount);
+        AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
+        AAudioStreamBuilder_setFormat(builder, format);
+
         if (dataProc != nullptr) {
-            AAudioStreamBuilder_setDataCallback(mBuilder, dataProc, userContext);
+            AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
         }
         if (errorProc != nullptr) {
-            AAudioStreamBuilder_setErrorCallback(mBuilder, errorProc, userContext);
+            AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
         }
-        AAudioStreamBuilder_setChannelCount(mBuilder, channelCount);
-        AAudioStreamBuilder_setSampleRate(mBuilder, sampSampleRate);
-        AAudioStreamBuilder_setFormat(mBuilder, format);
-        //AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
-        AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, 48 * 8);
-
-        //aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_NONE;
-        aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
-        //aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
-        AAudioStreamBuilder_setPerformanceMode(mBuilder, perfMode);
+        //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
+        //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
 
         // Open an AAudioStream using the Builder.
-        result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
-        if (result != AAUDIO_OK) goto finish1;
+        result = AAudioStreamBuilder_openStream(builder, &mStream);
 
-        printf("AAudioStream_getFramesPerBurst() = %d\n",
-               AAudioStream_getFramesPerBurst(mStream));
-        printf("AAudioStream_getBufferSizeInFrames() = %d\n",
-               AAudioStream_getBufferSizeInFrames(mStream));
-        printf("AAudioStream_getBufferCapacityInFrames() = %d\n",
-               AAudioStream_getBufferCapacityInFrames(mStream));
-        printf("AAudioStream_getPerformanceMode() = %d, requested %d\n",
-               AAudioStream_getPerformanceMode(mStream), perfMode);
-
-     finish1:
-        AAudioStreamBuilder_delete(mBuilder);
-        mBuilder = nullptr;
+        AAudioStreamBuilder_delete(builder);
         return result;
     }
 
@@ -132,8 +167,6 @@
             printf("call AAudioStream_close(%p)\n", mStream);  fflush(stdout);
             AAudioStream_close(mStream);
             mStream = nullptr;
-            AAudioStreamBuilder_delete(mBuilder);
-            mBuilder = nullptr;
         }
         return AAUDIO_OK;
     }
@@ -178,7 +211,6 @@
     }
 
 private:
-    AAudioStreamBuilder      *mBuilder = nullptr;
     AAudioStream             *mStream = nullptr;
     aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
     aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
diff --git a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
index b26b2ea..6be9112 100644
--- a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
+++ b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
@@ -20,10 +20,12 @@
 #define AAUDIO_SIMPLE_RECORDER_H
 
 #include <aaudio/AAudio.h>
+#include "AAudioArgsParser.h"
 
 //#define SHARING_MODE  AAUDIO_SHARING_MODE_EXCLUSIVE
 #define SHARING_MODE  AAUDIO_SHARING_MODE_SHARED
 #define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE
+
 /**
  * Simple wrapper for AAudio that opens an input stream either in callback or blocking read mode.
  */
@@ -54,11 +56,18 @@
      * Also known as "sample rate"
      * Only call this after open() has been called.
      */
-    int32_t getFramesPerSecond() {
+    int32_t getFramesPerSecond() const {
+        return getSampleRate(); // alias
+    }
+
+    /**
+     * Only call this after open() has been called.
+     */
+    int32_t getSampleRate() const {
         if (mStream == nullptr) {
             return AAUDIO_ERROR_INVALID_STATE;
         }
-        return AAudioStream_getSampleRate(mStream);;
+        return AAudioStream_getSampleRate(mStream);
     }
 
     /**
@@ -77,53 +86,85 @@
         if (mStream == nullptr) {
             return AAUDIO_ERROR_INVALID_STATE;
         }
-        return AAudioStream_getFramesRead(mStream);;
+        return AAudioStream_getFramesRead(mStream);
+    }
+
+    aaudio_result_t open(const AAudioParameters &parameters,
+                         AAudioStream_dataCallback dataCallback = nullptr,
+                         AAudioStream_errorCallback errorCallback = nullptr,
+                         void *userContext = nullptr) {
+        aaudio_result_t result = AAUDIO_OK;
+
+        // Use an AAudioStreamBuilder to contain requested parameters.
+        AAudioStreamBuilder *builder = nullptr;
+        result = AAudio_createStreamBuilder(&builder);
+        if (result != AAUDIO_OK) return result;
+
+        parameters.applyParameters(builder); // apply args
+
+        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
+
+        if (dataCallback != nullptr) {
+            AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
+        }
+        if (errorCallback != nullptr) {
+            AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
+        }
+
+        // Open an AAudioStream using the Builder.
+        result = AAudioStreamBuilder_openStream(builder, &mStream);
+        if (result != AAUDIO_OK) {
+            fprintf(stderr, "ERROR - AAudioStreamBuilder_openStream() returned %d %s\n",
+                    result, AAudio_convertResultToText(result));
+        }
+
+        if (result == AAUDIO_OK) {
+            int32_t sizeInBursts = parameters.getNumberOfBursts();
+            if (sizeInBursts > 0) {
+                int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
+                AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
+            }
+        }
+
+        AAudioStreamBuilder_delete(builder);
+        return result;
     }
 
     /**
      * Open a stream
      */
     aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
-                         AAudioStream_dataCallback dataProc, AAudioStream_errorCallback errorProc,
+                         AAudioStream_dataCallback dataProc,
+                         AAudioStream_errorCallback errorProc,
                          void *userContext) {
         aaudio_result_t result = AAUDIO_OK;
 
         // Use an AAudioStreamBuilder to contain requested parameters.
-        result = AAudio_createStreamBuilder(&mBuilder);
+        AAudioStreamBuilder *builder = nullptr;
+        result = AAudio_createStreamBuilder(&builder);
         if (result != AAUDIO_OK) return result;
 
-        AAudioStreamBuilder_setDirection(mBuilder, AAUDIO_DIRECTION_INPUT);
-        AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequestedPerformanceMode);
-        AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
+        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
+        AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
+        AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
         if (dataProc != nullptr) {
-            AAudioStreamBuilder_setDataCallback(mBuilder, dataProc, userContext);
+            AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
         }
         if (errorProc != nullptr) {
-            AAudioStreamBuilder_setErrorCallback(mBuilder, errorProc, userContext);
+            AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
         }
-        AAudioStreamBuilder_setChannelCount(mBuilder, channelCount);
-        AAudioStreamBuilder_setSampleRate(mBuilder, sampSampleRate);
-        AAudioStreamBuilder_setFormat(mBuilder, format);
+        AAudioStreamBuilder_setChannelCount(builder, channelCount);
+        AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
+        AAudioStreamBuilder_setFormat(builder, format);
 
         // Open an AAudioStream using the Builder.
-        result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
+        result = AAudioStreamBuilder_openStream(builder, &mStream);
         if (result != AAUDIO_OK) {
             fprintf(stderr, "ERROR - AAudioStreamBuilder_openStream() returned %d %s\n",
                     result, AAudio_convertResultToText(result));
-            goto finish1;
         }
 
-        printf("AAudioStream_getFramesPerBurst() = %d\n",
-               AAudioStream_getFramesPerBurst(mStream));
-        printf("AAudioStream_getBufferSizeInFrames() = %d\n",
-               AAudioStream_getBufferSizeInFrames(mStream));
-        printf("AAudioStream_getBufferCapacityInFrames() = %d\n",
-               AAudioStream_getBufferCapacityInFrames(mStream));
-        return result;
-
-     finish1:
-        AAudioStreamBuilder_delete(mBuilder);
-        mBuilder = nullptr;
+        AAudioStreamBuilder_delete(builder);
         return result;
     }
 
@@ -132,8 +173,6 @@
             printf("call AAudioStream_close(%p)\n", mStream);  fflush(stdout);
             AAudioStream_close(mStream);
             mStream = nullptr;
-            AAudioStreamBuilder_delete(mBuilder);
-            mBuilder = nullptr;
         }
         return AAUDIO_OK;
     }
@@ -186,7 +225,6 @@
     }
 
 private:
-    AAudioStreamBuilder      *mBuilder = nullptr;
     AAudioStream             *mStream = nullptr;
     aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
     aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index b9269e6..0125c0f 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -23,39 +23,22 @@
 #include <aaudio/AAudioTesting.h>
 #include "AAudioExampleUtils.h"
 #include "AAudioSimplePlayer.h"
+#include "AAudioArgsParser.h"
 
-#define SAMPLE_RATE           48000
 #define NUM_SECONDS           4
 
-//#define MMAP_POLICY              AAUDIO_UNSPECIFIED
-//#define MMAP_POLICY              AAUDIO_POLICY_NEVER
-//#define MMAP_POLICY              AAUDIO_POLICY_AUTO
-#define MMAP_POLICY              AAUDIO_POLICY_ALWAYS
-
-#define REQUESTED_FORMAT         AAUDIO_FORMAT_PCM_I16
-
-//#define REQUESTED_SHARING_MODE   AAUDIO_SHARING_MODE_SHARED
-#define REQUESTED_SHARING_MODE   AAUDIO_SHARING_MODE_EXCLUSIVE
-
-
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-    (void)argc; // unused
-
+    AAudioArgsParser   argParser;
     AAudioSimplePlayer player;
     SineThreadedData_t myData;
-    aaudio_result_t result = AAUDIO_OK;
+    aaudio_result_t    result = AAUDIO_OK;
 
-    const int requestedChannelCount = 2;
-    int actualChannelCount = 0;
-    const int requestedSampleRate = SAMPLE_RATE;
-    int actualSampleRate = 0;
-    aaudio_format_t requestedDataFormat = REQUESTED_FORMAT;
+    int32_t         actualChannelCount = 0;
+    int32_t         actualSampleRate = 0;
     aaudio_format_t actualDataFormat = AAUDIO_FORMAT_UNSPECIFIED;
-    aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
 
     AAudioStream *aaudioStream = nullptr;
-    aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
     int32_t  framesPerBurst = 0;
     int32_t  framesPerWrite = 0;
     int32_t  bufferCapacity = 0;
@@ -64,61 +47,37 @@
     int32_t  xRunCount = 0;
     float   *floatData = nullptr;
     int16_t *shortData = nullptr;
-    int32_t deviceId;
 
     // Make printf print immediately so that debug info is not stuck
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
 
-    printf("%s - Play a sine wave using AAudio\n", argv[0]);
+    printf("%s - Play a sine wave using AAudio V0.1.1\n", argv[0]);
 
-    AAudio_setMMapPolicy(MMAP_POLICY);
-    printf("requested MMapPolicy = %d\n", AAudio_getMMapPolicy());
+    if (argParser.parseArgs(argc, argv)) {
+        return EXIT_FAILURE;
+    }
 
-    player.setSharingMode(REQUESTED_SHARING_MODE);
-
-    result = player.open(requestedChannelCount, requestedSampleRate, requestedDataFormat,
-                         nullptr, nullptr, &myData);
+    result = player.open(argParser);
     if (result != AAUDIO_OK) {
         fprintf(stderr, "ERROR -  player.open() returned %d\n", result);
         goto finish;
     }
 
     aaudioStream = player.getStream();
-    // Request stream properties.
 
-    deviceId = AAudioStream_getDeviceId(aaudioStream);
-    printf("deviceId = %d\n", deviceId);
+    argParser.compareWithStream(aaudioStream);
 
-    state = AAudioStream_getState(aaudioStream);
-    printf("after open, state = %s\n", AAudio_convertStreamStateToText(state));
-
-    // Check to see what kind of stream we actually got.
+    actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
     actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
-    printf("SampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate);
+    actualDataFormat = AAudioStream_getFormat(aaudioStream);
 
     myData.sineOsc1.setup(440.0, actualSampleRate);
     myData.sineOsc2.setup(660.0, actualSampleRate);
 
-    actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
-    printf("ChannelCount: requested = %d, actual = %d\n",
-            requestedChannelCount, actualChannelCount);
-
-    actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
-    printf("SharingMode: requested = %s, actual = %s\n",
-            getSharingModeText(REQUESTED_SHARING_MODE),
-            getSharingModeText(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(aaudioStream);
-    printf("Buffer: bufferSize     = %d\n", AAudioStream_getBufferSizeInFrames(aaudioStream));
-    bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream);
-    printf("Buffer: bufferCapacity = %d, remainder = %d\n",
-           bufferCapacity, bufferCapacity % framesPerBurst);
-
     // Some DMA might use very short bursts of 16 frames. We don't need to write such small
     // buffers. But it helps to use a multiple of the burst size for predictable scheduling.
+    framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
     framesPerWrite = framesPerBurst;
     while (framesPerWrite < 48) {
         framesPerWrite *= 2;
@@ -126,13 +85,6 @@
     printf("Buffer: framesPerBurst = %d\n",framesPerBurst);
     printf("Buffer: framesPerWrite = %d\n",framesPerWrite);
 
-    printf("PerformanceMode        = %d\n", AAudioStream_getPerformanceMode(aaudioStream));
-    printf("is MMAP used?          = %s\n", AAudioStream_isMMapUsed(aaudioStream) ? "yes" : "no");
-
-    actualDataFormat = AAudioStream_getFormat(aaudioStream);
-    printf("DataFormat: requested  = %d, actual = %d\n", REQUESTED_FORMAT, actualDataFormat);
-    // TODO handle other data formats
-
     // Allocate a buffer for the audio data.
     if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
         floatData = new float[framesPerWrite * actualChannelCount];
@@ -151,11 +103,11 @@
         goto finish;
     }
 
-    state = AAudioStream_getState(aaudioStream);
-    printf("after start, state = %s\n", AAudio_convertStreamStateToText(state));
+    printf("after start, state = %s\n",
+            AAudio_convertStreamStateToText(AAudioStream_getState(aaudioStream)));
 
     // Play for a while.
-    framesToPlay = actualSampleRate * NUM_SECONDS;
+    framesToPlay = actualSampleRate * argParser.getDurationSeconds();
     framesLeft = framesToPlay;
     while (framesLeft > 0) {
 
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 69145aa..2211b72 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -27,36 +27,42 @@
 #include "AAudioExampleUtils.h"
 #include "AAudioSimplePlayer.h"
 
-#define NUM_SECONDS              5
-
 // Application data that gets passed to the callback.
 #define MAX_FRAME_COUNT_RECORDS    256
 
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-    (void)argc; // unused
+    AAudioArgsParser   argParser;
     AAudioSimplePlayer player;
     SineThreadedData_t myData;
     aaudio_result_t result;
+    int32_t actualSampleRate;
 
     // Make printf print immediately so that debug info is not stuck
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
-    printf("%s - Play a sine sweep using an AAudio callback\n", argv[0]);
+
+    printf("%s - Play a sine sweep using an AAudio callback V0.1.1\n", argv[0]);
 
     myData.schedulerChecked = false;
 
-    result = player.open(2, 44100, AAUDIO_FORMAT_PCM_FLOAT,
+    if (argParser.parseArgs(argc, argv)) {
+        return EXIT_FAILURE;
+    }
+
+    result = player.open(argParser,
                          SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData);
     if (result != AAUDIO_OK) {
         fprintf(stderr, "ERROR -  player.open() returned %d\n", result);
         goto error;
     }
-    printf("player.getFramesPerSecond() = %d\n", player.getFramesPerSecond());
-    printf("player.getChannelCount() = %d\n", player.getChannelCount());
-    myData.sineOsc1.setup(440.0, 48000);
+
+    argParser.compareWithStream(player.getStream());
+
+    actualSampleRate = player.getSampleRate();
+    myData.sineOsc1.setup(440.0, actualSampleRate);
     myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
-    myData.sineOsc2.setup(660.0, 48000);
+    myData.sineOsc2.setup(660.0, actualSampleRate);
     myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
 
 #if 0
@@ -73,8 +79,9 @@
         goto error;
     }
 
-    printf("Sleep for %d seconds while audio plays in a callback thread.\n", NUM_SECONDS);
-    for (int second = 0; second < NUM_SECONDS; second++)
+    printf("Sleep for %d seconds while audio plays in a callback thread.\n",
+           argParser.getDurationSeconds());
+    for (int second = 0; second < argParser.getDurationSeconds(); second++)
     {
         const struct timespec request = { .tv_sec = 1, .tv_nsec = 0 };
         (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index 469f0a8..89ae85c 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -97,6 +97,17 @@
     aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
                                                   pid_t clientThreadId) override;
 
+    aaudio_result_t startClient(aaudio_handle_t streamHandle __unused,
+                                      const android::AudioClient& client __unused,
+                                      audio_port_handle_t *clientHandle) override {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    aaudio_result_t stopClient(aaudio_handle_t streamHandle __unused,
+                               audio_port_handle_t clientHandle __unused)  override {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
     void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {
         // TODO This is just a stub so we can have a client Binder to pass to the service.
         // TODO Implemented in a later CL.
diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h
index 7368062..a64405b 100644
--- a/media/libaaudio/src/binding/AAudioServiceInterface.h
+++ b/media/libaaudio/src/binding/AAudioServiceInterface.h
@@ -18,6 +18,7 @@
 #define ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
 
 #include <utils/StrongPointer.h>
+#include <media/AudioClient.h>
 
 #include "binding/AAudioServiceDefinitions.h"
 #include "binding/AAudioStreamRequest.h"
@@ -86,6 +87,13 @@
 
     virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
                                                   pid_t clientThreadId) = 0;
+
+    virtual aaudio_result_t startClient(aaudio_handle_t streamHandle,
+                                      const android::AudioClient& client,
+                                      audio_port_handle_t *clientHandle) = 0;
+
+    virtual aaudio_result_t stopClient(aaudio_handle_t streamHandle,
+                                       audio_port_handle_t clientHandle) = 0;
 };
 
 } /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index 8a765ad..abdcf5b 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -52,8 +52,12 @@
     status = parcel->writeBool(mSharingModeMatchRequired);
     if (status != NO_ERROR) goto error;
 
+    status = parcel->writeBool(mInService);
+    if (status != NO_ERROR) goto error;
+
     status = mConfiguration.writeToParcel(parcel);
     if (status != NO_ERROR) goto error;
+
     return NO_ERROR;
 
 error:
@@ -74,8 +78,12 @@
     status = parcel->readBool(&mSharingModeMatchRequired);
     if (status != NO_ERROR) goto error;
 
+    status = parcel->readBool(&mInService);
+    if (status != NO_ERROR) goto error;
+
     status = mConfiguration.readFromParcel(parcel);
     if (status != NO_ERROR) goto error;
+
     return NO_ERROR;
 
 error:
@@ -91,5 +99,7 @@
     ALOGD("AAudioStreamRequest mUserId    = %d", mUserId);
     ALOGD("AAudioStreamRequest mProcessId = %d", mProcessId);
     ALOGD("AAudioStreamRequest mDirection = %d", mDirection);
+    ALOGD("AAudioStreamRequest mSharingModeMatchRequired = %d", mSharingModeMatchRequired);
+    ALOGD("AAudioStreamRequest mInService = %d", mInService);
     mConfiguration.dump();
 }
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index 462246b..b0fa96a 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -76,6 +76,14 @@
         return mConfiguration;
     }
 
+    bool isInService() const {
+        return mInService;
+    }
+
+    void setInService(bool inService) {
+        mInService = inService;
+    }
+
     virtual status_t writeToParcel(Parcel* parcel) const override;
 
     virtual status_t readFromParcel(const Parcel* parcel) override;
@@ -90,6 +98,7 @@
     pid_t                      mProcessId;
     aaudio_direction_t         mDirection;
     bool                       mSharingModeMatchRequired = false;
+    bool                       mInService = false; // Stream opened by AAudioservice
 };
 
 } /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index ff13fc2..7b01e44 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -16,7 +16,7 @@
 
 // This file is used in both client and server processes.
 // This is needed to make sense of the logs more easily.
-#define LOG_TAG (mInService ? "AAudioService" : "AAudio")
+#define LOG_TAG (mInService ? "AudioStreamInternal_Service" : "AudioStreamInternal_Client")
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
@@ -93,6 +93,7 @@
     request.setProcessId(getpid());
     request.setDirection(getDirection());
     request.setSharingModeMatchRequired(isSharingModeMatchRequired());
+    request.setInService(mInService);
 
     request.getConfiguration().setDeviceId(getDeviceId());
     request.getConfiguration().setSampleRate(getSampleRate());
@@ -326,6 +327,21 @@
     return mServiceInterface.unregisterAudioThread(mServiceStreamHandle, gettid());
 }
 
+aaudio_result_t AudioStreamInternal::startClient(const android::AudioClient& client,
+                                                 audio_port_handle_t *clientHandle) {
+    if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    return mServiceInterface.startClient(mServiceStreamHandle, client, clientHandle);
+}
+
+aaudio_result_t AudioStreamInternal::stopClient(audio_port_handle_t clientHandle) {
+    if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    return mServiceInterface.stopClient(mServiceStreamHandle, clientHandle);
+}
+
 aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId,
                            int64_t *framePosition,
                            int64_t *timeNanoseconds) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 257a702..109e425 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -87,6 +87,11 @@
     //PlayerBase virtuals
     virtual void destroy();
 
+    aaudio_result_t startClient(const android::AudioClient& client,
+                                audio_port_handle_t *clientHandle);
+
+    aaudio_result_t stopClient(audio_port_handle_t clientHandle);
+
 protected:
 
     aaudio_result_t processData(void *buffer,
@@ -170,7 +175,6 @@
 
     AudioEndpointParcelable  mEndPointParcelable; // description of the buffers filled by service
     EndpointDescriptor       mEndpointDescriptor; // buffer description with resolved addresses
-
 };
 
 } /* namespace aaudio */
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index d320320..ceba211 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -323,6 +323,7 @@
             return BAD_VALUE;
         }
         data.write(attr, sizeof(audio_attributes_t));
+        data.writeInt32(*input);
         data.writeInt32(session);
         data.writeInt32(pid);
         data.writeInt32(uid);
@@ -1024,6 +1025,7 @@
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
             audio_attributes_t attr;
             data.read(&attr, sizeof(audio_attributes_t));
+            audio_io_handle_t input = (audio_io_handle_t)data.readInt32();
             audio_session_t session = (audio_session_t)data.readInt32();
             pid_t pid = (pid_t)data.readInt32();
             uid_t uid = (uid_t)data.readInt32();
@@ -1033,7 +1035,6 @@
             audio_input_flags_t flags = (audio_input_flags_t) data.readInt32();
             audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32();
             audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
-            audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
             status_t status = getInputForAttr(&attr, &input, session, pid, uid,
                                               &config,
                                               flags, &selectedDeviceId, &portId);
diff --git a/media/libaudioclient/include/media/AudioClient.h b/media/libaudioclient/include/media/AudioClient.h
new file mode 100644
index 0000000..9efd76d
--- /dev/null
+++ b/media/libaudioclient/include/media/AudioClient.h
@@ -0,0 +1,38 @@
+/*
+ * 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_AUDIO_CLIENT_H
+#define ANDROID_AUDIO_CLIENT_H
+
+#include <system/audio.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class AudioClient {
+ public:
+    AudioClient() :
+        clientUid(-1), clientPid(-1), packageName("") {}
+
+    uid_t clientUid;
+    pid_t clientPid;
+    String16 packageName;
+};
+
+}; // namespace android
+
+#endif  // ANDROID_AUDIO_CLIENT_H
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index bbe97ee..043c84a 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -1,7 +1,7 @@
 cc_library_headers {
     name: "libmedia_headers",
     vendor_available: true,
-    export_include_dirs: ["include"],
+    export_include_dirs: ["include", "omx/1.0/include"],
 }
 
 cc_library {
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 43130eb..a073081 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -29,7 +29,7 @@
 #include <utils/NativeHandle.h>
 #include <gui/IGraphicBufferProducer.h>
 
-#include <omx/1.0/WOmxNode.h>
+#include <media/omx/1.0/WOmxNode.h>
 #include <android/IGraphicBufferSource.h>
 #include <android/IOMXBufferSource.h>
 
diff --git a/include/media/omx/1.0/Conversion.h b/media/libmedia/omx/1.0/include/media/omx/1.0/Conversion.h
similarity index 100%
rename from include/media/omx/1.0/Conversion.h
rename to media/libmedia/omx/1.0/include/media/omx/1.0/Conversion.h
diff --git a/include/media/omx/1.0/WGraphicBufferSource.h b/media/libmedia/omx/1.0/include/media/omx/1.0/WGraphicBufferSource.h
similarity index 100%
rename from include/media/omx/1.0/WGraphicBufferSource.h
rename to media/libmedia/omx/1.0/include/media/omx/1.0/WGraphicBufferSource.h
diff --git a/include/media/omx/1.0/WOmx.h b/media/libmedia/omx/1.0/include/media/omx/1.0/WOmx.h
similarity index 100%
rename from include/media/omx/1.0/WOmx.h
rename to media/libmedia/omx/1.0/include/media/omx/1.0/WOmx.h
diff --git a/include/media/omx/1.0/WOmxBufferSource.h b/media/libmedia/omx/1.0/include/media/omx/1.0/WOmxBufferSource.h
similarity index 100%
rename from include/media/omx/1.0/WOmxBufferSource.h
rename to media/libmedia/omx/1.0/include/media/omx/1.0/WOmxBufferSource.h
diff --git a/include/media/omx/1.0/WOmxNode.h b/media/libmedia/omx/1.0/include/media/omx/1.0/WOmxNode.h
similarity index 100%
rename from include/media/omx/1.0/WOmxNode.h
rename to media/libmedia/omx/1.0/include/media/omx/1.0/WOmxNode.h
diff --git a/include/media/omx/1.0/WOmxObserver.h b/media/libmedia/omx/1.0/include/media/omx/1.0/WOmxObserver.h
similarity index 100%
rename from include/media/omx/1.0/WOmxObserver.h
rename to media/libmedia/omx/1.0/include/media/omx/1.0/WOmxObserver.h
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index 0a9f791..6da1ec1 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -245,8 +245,12 @@
     if (sInitComplete)
         return;
 
-    registerFactory_l(new NuPlayerFactory(), NU_PLAYER);
-    registerFactory_l(new TestPlayerFactory(), TEST_PLAYER);
+    IFactory* factory = new NuPlayerFactory();
+    if (registerFactory_l(factory, NU_PLAYER) != OK)
+        delete factory;
+    factory = new TestPlayerFactory();
+    if (registerFactory_l(factory, TEST_PLAYER) != OK)
+        delete factory;
 
     sInitComplete = true;
 }
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index e3f4876..2b5b4ff 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -892,23 +892,14 @@
     ofs.close();
 }
 
-// converts a time series into a map. key: buffer period length. value: count
-static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
-    // TODO allow buckets of variable resolution
-    std::map<int, int> buckets;
-    for (size_t i = 1; i < samples.size(); ++i) {
-        ++buckets[deltaMs(samples[i - 1], samples[i])];
-    }
-    return buckets;
-}
-
 void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
 {
     //  CallStack cs(LOG_TAG);
     mFd = fd;
     mIndent = indent;
     String8 timestamp, body;
-    PerformanceAnalysis performanceAnalyzer; // used to call analysis functions
+    // FIXME: this is not thread safe
+    static PerformanceAnalysis performanceAnalysis; // used to store data and to call analysis functions
     size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
     if (lost > 0) {
         body.appendFormat("warning: lost %zu bytes worth of events", lost);
@@ -930,29 +921,7 @@
             memcpy(&hash, &(data->hash), sizeof(hash));
             int64_t ts;
             memcpy(&ts, &data->ts, sizeof(ts));
-            const std::pair<log_hash_t, int> key(hash, data->author);
-            // TODO might want to filter excessively high outliers, which are usually caused
-            // by the thread being inactive.
-            mHists[key].push_back(ts);
-            // store time series data for each reader in order to bucket it once there
-            // is enough data. Then, it is written to recentHists as a histogram.
-            mTimeStampSeries[data->author].push_back(ts);
-            // if length of the time series has reached kShortHistSize samples, do 1) and 2):
-            if (mTimeStampSeries[data->author].size() >= kShortHistSize) {
-                // 1) analyze the series to store all outliers and their exact timestamps:
-                performanceAnalyzer.storeOutlierData(data->author, mTimeStampSeries[data->author]);
-                 // 2) compute its histogram, append this to mRecentHists and erase the time series
-                mRecentHists.emplace_front(data->author,
-                                           buildBuckets(mTimeStampSeries[data->author]));
-                // do not let mRecentHists exceed capacity
-                // TODO: turn the FIFO queue into a circular buffer
-                if (mRecentHists.size() >= kRecentHistsCapacity) {
-                    mRecentHists.pop_back();
-                }
-                mTimeStampSeries.erase(data->author);
-            }
-            // if an element in mHists has not grown for a long time, delete
-            // TODO copy histogram data only to mRecentHistsBuffer and pop oldest
+            performanceAnalysis.logTsEntry(data->author, ts);
             ++entry;
             break;
         }
@@ -967,12 +936,10 @@
             break;
         }
     }
-    performanceAnalyzer.reportPerformance(&body, mRecentHists);
+    performanceAnalysis.reportPerformance(&body);
     if (!body.isEmpty()) {
         dumpLine(timestamp, body);
     }
-    // comment in for tests
-    // performanceAnalyzer.testFunction();
 }
 
 void NBLog::Reader::dump(int fd, size_t indent)
diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp
index 84b7635..fa8f47e 100644
--- a/media/libnbaio/PerformanceAnalysis.cpp
+++ b/media/libnbaio/PerformanceAnalysis.cpp
@@ -45,8 +45,21 @@
 
 namespace android {
 
-PerformanceAnalysis::PerformanceAnalysis() : findGlitch(false) {
-    kPeriodMsCPU = static_cast<int>(PerformanceAnalysis::kPeriodMs * kRatio);
+PerformanceAnalysis::PerformanceAnalysis() {
+    // These variables will be (FIXME) learned from the data
+    kPeriodMs = 4; // typical buffer period (mode)
+    // average number of Ms spent processing buffer
+    kPeriodMsCPU = static_cast<int>(kPeriodMs * kRatio);
+}
+
+// converts a time series into a map. key: buffer period length. value: count
+static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
+    // TODO allow buckets of variable resolution
+    std::map<int, int> buckets;
+    for (size_t i = 1; i < samples.size(); ++i) {
+        ++buckets[deltaMs(samples[i - 1], samples[i])];
+    }
+    return buckets;
 }
 
 static int widthOf(int x) {
@@ -58,76 +71,164 @@
     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,
+// 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) {
+    // 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();
+    }
+}
+
+// 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
+// them as long as no peak is detected. When a value is more than 'threshold'
+// standard deviations from the mean, a peak is detected and the mean and sigma
+// are set to the peak value and 0.
+void PerformanceAnalysis::detectPeaks() {
+    if (mOutlierData.empty()) {
+        ALOGD("peak detector called on empty array");
+        return;
+    }
+
+    // compute mean of the distribution. Used to check whether a value is large
+    const double kTypicalDiff = std::accumulate(
+        mOutlierData.begin(), mOutlierData.end(), 0,
+        [](auto &a, auto &b){return a + b.first;}) / mOutlierData.size();
+    // ALOGD("typicalDiff %f", kTypicalDiff);
+
+    // iterator at the beginning of a sequence, or updated to the most recent peak
+    std::deque<std::pair<uint64_t, uint64_t>>::iterator start = mOutlierData.begin();
+    // the mean and standard deviation are updated every time a peak is detected
+    // initialize first time. The mean from the previous sequence is stored
+    // for the next sequence. Here, they are initialized for the first time.
+    if (mPeakDetectorMean < 0) {
+        mPeakDetectorMean = static_cast<double>(start->first);
+        mPeakDetectorSd = 0;
+    }
+    auto sqr = [](auto x){ return x * x; };
+    for (auto it = mOutlierData.begin(); it != mOutlierData.end(); ++it) {
+        // no surprise occurred:
+        // the new element is a small number of standard deviations from the mean
+        if ((fabs(it->first - mPeakDetectorMean) < kStddevThreshold * mPeakDetectorSd) ||
+             // or: right after peak has been detected, the delta is smaller than average
+            (mPeakDetectorSd == 0 && fabs(it->first - mPeakDetectorMean) < kTypicalDiff)) {
+            // update the mean and sd:
+            // count number of elements (distance between start interator and current)
+            const int kN = std::distance(start, it) + 1;
+            // usual formulas for mean and sd
+            mPeakDetectorMean = std::accumulate(start, it + 1, 0.0,
+                                   [](auto &a, auto &b){return a + b.first;}) / kN;
+            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");
+                mPeakTimestamps.pop_front();
+            }
+            mPeakDetectorMean = static_cast<double>(it->first);
+            mPeakDetectorSd = 0;
+            start = it;
+        }
+    }
+    //for (const auto &it : mPeakTimestamps) {
+    //    ALOGE("mPeakTimestamps %f", static_cast<double>(it));
+    //}
+    return;
+}
+
+// Called by LogTsEntry. The input is a vector of timestamps.
+// Finds outliers and writes to mOutlierdata.
+// Each value in mOutlierdata consists of: <outlier timestamp, time elapsed since previous outlier>.
+// e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
+// 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
+    if (mElapsed == 0) {
+        mPrevNs = timestamps[0];
+    }
+    for (const auto &ts: timestamps) {
+        const uint64_t diffMs = static_cast<uint64_t>(deltaMs(mPrevNs, ts));
+        if (diffMs >= static_cast<uint64_t>(kOutlierMs)) {
+            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");
+                mOutlierData.pop_front();
+            }
+            mElapsed = 0;
+        }
+        mElapsed += diffMs;
+        mPrevNs = ts;
+    }
+}
+
+
 // FIXME: delete this temporary test code, recycled for various new functions
 void PerformanceAnalysis::testFunction() {
     // produces values (4: 5000000), (13: 18000000)
     // ns timestamps of buffer periods
     const std::vector<int64_t>kTempTestData = {1000000, 4000000, 5000000,
                                                16000000, 18000000, 28000000};
-    const int kTestAuthor = 1;
-    PerformanceAnalysis::storeOutlierData(kTestAuthor, kTempTestData);
+    PerformanceAnalysis::storeOutlierData(kTempTestData);
     for (const auto &outlier: mOutlierData) {
         ALOGE("PerformanceAnalysis test %lld: %lld",
               static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
     }
+    detectPeaks();
 }
 
-// Each pair consists of: <outlier timestamp, time elapsed since previous outlier>.
-// The timestamp of the beginning of the outlier is recorded.
-// The elapsed time is from the timestamp of the previous outlier
-// e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
-// This function is applied to the time series before it is converted into a histogram.
-void PerformanceAnalysis::storeOutlierData(
-    int author, const std::vector<int64_t> &timestamps) {
-    if (timestamps.size() < 1) {
-        ALOGE("storeOutlierData called on empty vector");
-        return;
-    }
-    author++; // temp to avoid unused error until this value is
-    // either TODO: used or discarded from the arglist
-    author--;
-    uint64_t elapsed = 0;
-    int64_t prev = timestamps.at(0);
-    for (const auto &ts: timestamps) {
-        const uint64_t diff = static_cast<uint64_t>(deltaMs(prev, ts));
-        if (diff >= static_cast<uint64_t>(kOutlierMs)) {
-            mOutlierData.emplace_back(elapsed, static_cast<uint64_t>(prev));
-            elapsed = 0;
-        }
-        elapsed += diff;
-        prev = ts;
-    }
-    // ALOGE("storeOutlierData: result length %zu", outlierData.size());
-    // for (const auto &outlier: OutlierData) {
-    //    ALOGE("PerformanceAnalysis test %lld: %lld",
-    //          static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
-    //}
-}
-
-// TODO: implement peak detector
-/*
-  static void peakDetector() {
-  return;
-  } */
-
-// TODO put this function in separate file. Make it return a std::string instead of modifying body
-// TODO create a subclass of Reader for this and related work
+// 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: build histogram buckets earlier and discard timestamps to save memory
 // TODO consider changing all ints to uint32_t or uint64_t
-void PerformanceAnalysis::reportPerformance(String8 *body,
-                                            const std::deque<std::pair
-                                            <int, short_histogram>> &shortHists,
-                                            int maxHeight) {
-    if (shortHists.size() < 1) {
+void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) {
+    if (mRecentHists.size() < 1) {
+        ALOGD("reportPerformance: mRecentHists is empty");
         return;
     }
-    // this is temporary code, which only prints out one histogram
-    // of all data stored in buffer. The data is not erased, only overwritten.
+    ALOGD("reportPerformance: hists size %d", static_cast<int>(mRecentHists.size()));
     // TODO: more elaborate data analysis
     std::map<int, int> buckets;
-    for (const auto &shortHist: shortHists) {
+    for (const auto &shortHist: mRecentHists) {
         for (const auto &countPair : shortHist.second) {
             buckets[countPair.first] += countPair.second;
         }
@@ -222,14 +323,4 @@
     return;
 }
 
-bool PerformanceAnalysis::isFindGlitch() const
-{
-    return findGlitch;
-}
-
-void PerformanceAnalysis::setFindGlitch(bool s)
-{
-    findGlitch = s;
-}
-
 }   // namespace android
diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h
index 6b08e94..0b5e24d 100644
--- a/media/libnbaio/include/media/nbaio/NBLog.h
+++ b/media/libnbaio/include/media/nbaio/NBLog.h
@@ -406,6 +406,7 @@
 public:
 
     // A snapshot of a readers buffer
+    // This is raw data. No analysis has been done on it
     class Snapshot {
     public:
         Snapshot() : mData(NULL), mLost(0) {}
@@ -445,6 +446,7 @@
     // get snapshot of readers fifo buffer, effectively consuming the buffer
     std::unique_ptr<Snapshot> getSnapshot();
     // dump a particular snapshot of the reader
+    // TODO: move dump to PerformanceAnalysis. Model/view/controller design
     void     dump(int fd, size_t indent, Snapshot & snap);
     // dump the current content of the reader's buffer (call getSnapshot() and previous dump())
     void     dump(int fd, size_t indent = 0);
@@ -452,9 +454,6 @@
 
 private:
 
-    // TODO: decide whether these belong in NBLog::Reader or in PerformanceAnalysis
-    static const int kShortHistSize = 50; // number of samples in a short-term histogram
-    static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
     static const std::set<Event> startingTypes;
     static const std::set<Event> endingTypes;
     /*const*/ Shared* const mShared;    // raw pointer to shared memory, actually const but not
@@ -467,23 +466,6 @@
     audio_utils_fifo_reader * const mFifoReader;    // used to read from FIFO,
                                                     // non-NULL unless constructor fails
 
-    // stores a short-term histogram of size determined by kShortHistSize
-    // TODO: unsigned, unsigned
-    using short_histogram = std::map<int, int>;
-
-    // each pair contains a sequence of timestamps (one histogram's worth)
-    // pair's log_hash_t is the hash of the source code location where the timestamp was taken
-    // pair's int points to the Reader that originated the entry
-    std::map<std::pair<log_hash_t, int>, std::vector<int64_t>> mHists;
-
-    // mHistsCopy stores timestamp vectors whose key is the reader thread index.
-    // TODO remove old mHists after changing the code
-    std::map<int, std::vector<int64_t>> mTimeStampSeries;
-
-    // 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;
-
     // TODO: it might be clearer, instead of a direct map from source location to vector of
     // timestamps, if we instead first mapped from source location to an object that
     // represented that location. And one_of its fields would be a vector of timestamps.
diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
index 32ae38b..89699bf 100644
--- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
+++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
@@ -22,79 +22,119 @@
 #include <map>
 #include <deque>
 #include <vector>
+#include "NBLog.h"
 
 namespace android {
 
 class String8;
 
 class PerformanceAnalysis {
-
+    // This class stores and analyzes audio processing wakeup timestamps from NBLog
+    // FIXME: currently, all performance data is stored in deques. Need to add a mutex.
+    // FIXME: continue this way until analysis is done in a separate thread. Then, use
+    // the fifo writer utilities.
 public:
 
-PerformanceAnalysis();
+    PerformanceAnalysis();
 
-// stores a short-term histogram of size determined by kShortHistSize
-// TODO: unsigned, unsigned
-// CHECK: is there a better way to use short_histogram than to write 'using'
-// both in this header file and in NBLog.h?
-using short_histogram = std::map<int, int>;
+    // FIXME: decide whether to use 64 or 32 bits
+    typedef uint64_t log_hash_t;
 
-// returns a vector of pairs <outlier timestamp, time elapsed since previous outlier
-// called by NBLog::Reader::dump before data is converted into histogram
-// TODO: currently, the elapsed time
-// The resolution is only as good as the ms duration of one shortHist
-void storeOutlierData(int author, const std::vector<int64_t> &timestamps);
+    // 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>;
 
-// TODO: delete this. temp for testing
-void testFunction();
+    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;
 
-// Given a series, looks for changes in distribution (peaks)
-// Returns a 'signal' array of the same length as the series, where each
-// value is mapped to -1, 0, or 1 based on whether a negative or positive peak
-// was detected, or no significant change occurred.
-// The function sets the mean to the starting value and sigma to 0, and updates
-// them as long as no peak is detected. When a value is more than 'threshold'
-// standard deviations from the mean, a peak is detected and the mean and sigma
-// are set to the peak value and 0.
-// static void peakDetector();
+    // 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);
 
-// input: series of short histograms. output: prints an analysis of the
-// data to the console
-// TODO: change this so that it writes the analysis to the long-term
-// circular buffer and prints an analyses both for the short and long-term
-void reportPerformance(String8 *body,
-                       const std::deque<std::pair
-                       <int, short_histogram>> &shortHists,
-                       int maxHeight = 10);
+    // FIXME: make peakdetector and storeOutlierData a single function
+    // Input: mOutlierData. Looks at time elapsed between outliers
+    // finds significant changes in the distribution
+    // writes timestamps of significant changes to mPeakTimestamps
+    void detectPeaks();
 
-// if findGlitch is true, log warning when buffer periods caused glitch
-// TODO adapt this to the analysis in reportPerformance instead of logging
-void     alertIfGlitch(const std::vector<int64_t> &samples);
-bool     isFindGlitch() const;
-void     setFindGlitch(bool s);
+    // runs analysis on timestamp series before it is converted to a histogram
+    // finds outliers
+    // writes to mOutlierData <time elapsed since previous outlier, outlier timestamp>
+    void storeOutlierData(const std::vector<timestamp_raw> &timestamps);
 
-~PerformanceAnalysis() {}
+    // input: series of short histograms. Generates a string of analysis of the buffer periods
+    // TODO: WIP write more detailed analysis
+    // FIXME: move this data visualization to a separate class. Model/view/controller
+    void reportPerformance(String8 *body, int maxHeight = 10);
+
+    // TODO: delete this. temp for testing
+    void testFunction();
+
+    // 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() {}
 
 private:
 
-// stores outlier analysis
-std::vector<std::pair<uint64_t, uint64_t>> mOutlierData;
+    // stores outlier analysis: <elapsed time between outliers in ms, outlier timestamp>
+    std::deque<std::pair<outlierInterval, timestamp>> mOutlierData;
 
-// stores long-term audio performance data
-// TODO: Turn it into a circular buffer
-std::deque<std::pair<int, int>> mPerformanceAnalysis;
+    // stores each timestamp at which a peak was detected
+    // a peak is a moment at which the average outlier interval changed significantly
+    std::deque<timestamp> mPeakTimestamps;
 
-// alert if a local buffer period sequence caused an audio glitch
-bool findGlitch;
-//TODO: measure these from the data (e.g., mode) as they may change.
-//const int kGlitchThreshMs = 7;
-// const int kMsPerSec = 1000;
-const int kNumBuff = 3; // number of buffers considered in local history
-const int kPeriodMs = 4; // current period length is ideally 4 ms
-const int kOutlierMs = 7; // values greater or equal to this cause glitches every time
-// DAC processing time for 4 ms buffer
-static constexpr double kRatio = 0.75; // estimate of CPU time as ratio of period length
-int kPeriodMsCPU; //compute based on kPeriodLen and kRatio
+    // 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;
+
+    // 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;
+
+    // TODO: measure these from the data (e.g., mode) as they may change.
+    // const int kGlitchThreshMs = 7;
+    // 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
+    // DAC processing time for 4 ms buffer
+    static constexpr double kRatio = 0.75; // estimate of CPU time as ratio of period length
+    int kPeriodMsCPU; // compute based on kPeriodLen and kRatio
+
+    // Peak detection: number of standard deviations from mean considered a significant change
+    static const int kStddevThreshold = 5;
+
+    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
+
+    // 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.
+    // FIXME: create inner class for these variables and decide which other ones to add to it
+    double mPeakDetectorMean = -1;
+    double mPeakDetectorSd = -1;
+    // variables for storeOutlierData
+    uint64_t mElapsed = 0;
+    int64_t mPrevNs = -1;
 
 };
 
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 142ae07..6491ceb 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -960,7 +960,9 @@
                             return NO_MEMORY;
                         }
                         hidlMem = mapMemory(hidlMemToken);
-
+                        if (hidlMem == nullptr) {
+                            return NO_MEMORY;
+                        }
                         err = mOMXNode->useBuffer(
                                 portIndex, hidlMemToken, &info.mBufferID);
                     } else {
@@ -1008,6 +1010,9 @@
                                 return NO_MEMORY;
                             }
                             hidlMem = mapMemory(hidlMemToken);
+                            if (hidlMem == nullptr) {
+                                return NO_MEMORY;
+                            }
                             info.mData = new SharedMemoryBuffer(format, hidlMem);
                             info.mMemRef = hidlMem;
                         } else {
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 61a2b5f..6ed0d0e 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -972,6 +972,14 @@
         }
 
         if (handle != nullptr) {
+            ssize_t offset;
+            size_t size;
+            sp<IMemoryHeap> heap = frame->getMemory(&offset, &size);
+            if (heap->getHeapID() != mMemoryHeapBase->getHeapID()) {
+                ALOGE("%s: Mismatched heap ID, ignoring release (got %x, expected %x)",
+		     __FUNCTION__, heap->getHeapID(), mMemoryHeapBase->getHeapID());
+                return;
+            }
             uint32_t batchSize = 0;
             {
                 Mutex::Autolock autoLock(mBatchLock);
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
index f89688c..cff4a33 100644
--- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
@@ -68,7 +68,7 @@
     def.eDir = OMX_DirInput;
     def.nBufferCountMin = kNumInputBuffers;
     def.nBufferCountActual = def.nBufferCountMin;
-    def.nBufferSize = 8192;
+    def.nBufferSize = 32768;
     def.bEnabled = OMX_TRUE;
     def.bPopulated = OMX_FALSE;
     def.eDomain = OMX_PortDomainAudio;
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index b0faee1..c6fa4ae 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -1052,6 +1052,10 @@
 
         case OMXBuffer::kBufferTypeHidlMemory: {
                 sp<IHidlMemory> hidlMemory = mapMemory(omxBuffer.mHidlMemory);
+                if (hidlMemory == nullptr) {
+                    ALOGE("OMXNodeInstance useBuffer() failed to map memory");
+                    return NO_MEMORY;
+                }
                 return useBuffer_l(portIndex, NULL, hidlMemory, buffer);
             }
         default:
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index d850aa9..38c9687 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -260,10 +260,11 @@
 status_t MmapStreamInterface::openMmapStream(MmapStreamInterface::stream_direction_t direction,
                                              const audio_attributes_t *attr,
                                              audio_config_base_t *config,
-                                             const MmapStreamInterface::Client& client,
+                                             const AudioClient& client,
                                              audio_port_handle_t *deviceId,
                                              const sp<MmapStreamCallback>& callback,
-                                             sp<MmapStreamInterface>& interface)
+                                             sp<MmapStreamInterface>& interface,
+                                             audio_port_handle_t *handle)
 {
     sp<AudioFlinger> af;
     {
@@ -273,7 +274,7 @@
     status_t ret = NO_INIT;
     if (af != 0) {
         ret = af->openMmapStream(
-                direction, attr, config, client, deviceId, callback, interface);
+                direction, attr, config, client, deviceId, callback, interface, handle);
     }
     return ret;
 }
@@ -281,10 +282,11 @@
 status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t direction,
                                       const audio_attributes_t *attr,
                                       audio_config_base_t *config,
-                                      const MmapStreamInterface::Client& client,
+                                      const AudioClient& client,
                                       audio_port_handle_t *deviceId,
                                       const sp<MmapStreamCallback>& callback,
-                                      sp<MmapStreamInterface>& interface)
+                                      sp<MmapStreamInterface>& interface,
+                                      audio_port_handle_t *handle)
 {
     status_t ret = initCheck();
     if (ret != NO_ERROR) {
@@ -293,7 +295,7 @@
 
     audio_session_t sessionId = (audio_session_t) newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
     audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT;
-    audio_io_handle_t io;
+    audio_io_handle_t io = AUDIO_IO_HANDLE_NONE;
     audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
     if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
         audio_config_t fullConfig = AUDIO_CONFIG_INITIALIZER;
@@ -325,6 +327,7 @@
     if (thread != 0) {
         interface = new MmapThreadHandle(thread);
         thread->configure(attr, streamType, sessionId, callback, portId);
+        *handle = portId;
     } else {
         ret = NO_INIT;
     }
@@ -1279,7 +1282,7 @@
         if (thread == NULL) {
             thread = (ThreadBase *)checkMmapThread_l(ioHandle);
             if (thread == NULL) {
-                String8("");
+                return String8("");
             }
         }
     }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 2e0bc66..8a96c1d 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -294,10 +294,11 @@
     status_t openMmapStream(MmapStreamInterface::stream_direction_t direction,
                             const audio_attributes_t *attr,
                             audio_config_base_t *config,
-                            const MmapStreamInterface::Client& client,
+                            const AudioClient& client,
                             audio_port_handle_t *deviceId,
                             const sp<MmapStreamCallback>& callback,
-                            sp<MmapStreamInterface>& interface);
+                            sp<MmapStreamInterface>& interface,
+                            audio_port_handle_t *handle);
 private:
     // FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
     static const size_t kLogMemorySize = 400 * 1024;
@@ -596,7 +597,7 @@
         virtual status_t createMmapBuffer(int32_t minSizeFrames,
                                           struct audio_mmap_buffer_info *info);
         virtual status_t getMmapPosition(struct audio_mmap_position *position);
-        virtual status_t start(const MmapStreamInterface::Client& client,
+        virtual status_t start(const AudioClient& client,
                                          audio_port_handle_t *handle);
         virtual status_t stop(audio_port_handle_t handle);
         virtual status_t standby();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 6329f20..7871ae6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7544,7 +7544,7 @@
     return mThread->getMmapPosition(position);
 }
 
-status_t AudioFlinger::MmapThreadHandle::start(const MmapStreamInterface::Client& client,
+status_t AudioFlinger::MmapThreadHandle::start(const AudioClient& client,
         audio_port_handle_t *handle)
 
 {
@@ -7638,77 +7638,75 @@
     return mHalStream->getMmapPosition(position);
 }
 
-status_t AudioFlinger::MmapThread::start(const MmapStreamInterface::Client& client,
+status_t AudioFlinger::MmapThread::start(const AudioClient& client,
                                          audio_port_handle_t *handle)
 {
-    ALOGV("%s clientUid %d mStandby %d", __FUNCTION__, client.clientUid, mStandby);
+    ALOGV("%s clientUid %d mStandby %d mPortId %d *handle %d", __FUNCTION__,
+          client.clientUid, mStandby, mPortId, *handle);
     if (mHalStream == 0) {
         return NO_INIT;
     }
 
     status_t ret;
-    audio_session_t sessionId;
-    audio_port_handle_t portId;
 
-    if (mActiveTracks.size() == 0) {
+    if (*handle == mPortId) {
         // for the first track, reuse portId and session allocated when the stream was opened
         ret = mHalStream->start();
         if (ret != NO_ERROR) {
             ALOGE("%s: error mHalStream->start() = %d for first track", __FUNCTION__, ret);
             return ret;
         }
-        portId = mPortId;
-        sessionId = mSessionId;
         mStandby = false;
+        return NO_ERROR;
+    }
+
+    audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
+
+    audio_io_handle_t io = mId;
+    if (isOutput()) {
+        audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+        config.sample_rate = mSampleRate;
+        config.channel_mask = mChannelMask;
+        config.format = mFormat;
+        audio_stream_type_t stream = streamType();
+        audio_output_flags_t flags =
+                (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
+        audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+        ret = AudioSystem::getOutputForAttr(&mAttr, &io,
+                                            mSessionId,
+                                            &stream,
+                                            client.clientUid,
+                                            &config,
+                                            flags,
+                                            &deviceId,
+                                            &portId);
     } else {
-        // for other tracks than first one, get a new port ID from APM.
-        sessionId = (audio_session_t)mAudioFlinger->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
-        audio_io_handle_t io;
-        if (isOutput()) {
-            audio_config_t config = AUDIO_CONFIG_INITIALIZER;
-            config.sample_rate = mSampleRate;
-            config.channel_mask = mChannelMask;
-            config.format = mFormat;
-            audio_stream_type_t stream = streamType();
-            audio_output_flags_t flags =
-                    (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
-            audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
-            ret = AudioSystem::getOutputForAttr(&mAttr, &io,
-                                                sessionId,
-                                                &stream,
-                                                client.clientUid,
-                                                &config,
-                                                flags,
-                                                &deviceId,
-                                                &portId);
-        } else {
-            audio_config_base_t config;
-            config.sample_rate = mSampleRate;
-            config.channel_mask = mChannelMask;
-            config.format = mFormat;
-            audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
-            ret = AudioSystem::getInputForAttr(&mAttr, &io,
-                                                  sessionId,
-                                                  client.clientPid,
-                                                  client.clientUid,
-                                                  &config,
-                                                  AUDIO_INPUT_FLAG_MMAP_NOIRQ,
-                                                  &deviceId,
-                                                  &portId);
-        }
-        // APM should not chose a different input or output stream for the same set of attributes
-        // and audo configuration
-        if (ret != NO_ERROR || io != mId) {
-            ALOGE("%s: error getting output or input from APM (error %d, io %d expected io %d)",
-                  __FUNCTION__, ret, io, mId);
-            return BAD_VALUE;
-        }
+        audio_config_base_t config;
+        config.sample_rate = mSampleRate;
+        config.channel_mask = mChannelMask;
+        config.format = mFormat;
+        audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+        ret = AudioSystem::getInputForAttr(&mAttr, &io,
+                                              mSessionId,
+                                              client.clientPid,
+                                              client.clientUid,
+                                              &config,
+                                              AUDIO_INPUT_FLAG_MMAP_NOIRQ,
+                                              &deviceId,
+                                              &portId);
+    }
+    // APM should not chose a different input or output stream for the same set of attributes
+    // and audo configuration
+    if (ret != NO_ERROR || io != mId) {
+        ALOGE("%s: error getting output or input from APM (error %d, io %d expected io %d)",
+              __FUNCTION__, ret, io, mId);
+        return BAD_VALUE;
     }
 
     if (isOutput()) {
-        ret = AudioSystem::startOutput(mId, streamType(), sessionId);
+        ret = AudioSystem::startOutput(mId, streamType(), mSessionId);
     } else {
-        ret = AudioSystem::startInput(mId, sessionId);
+        ret = AudioSystem::startInput(mId, mSessionId);
     }
 
     // abort if start is rejected by audio policy manager
@@ -7716,9 +7714,9 @@
         ALOGE("%s: error start rejected by AudioPolicyManager = %d", __FUNCTION__, ret);
         if (mActiveTracks.size() != 0) {
             if (isOutput()) {
-                AudioSystem::releaseOutput(mId, streamType(), sessionId);
+                AudioSystem::releaseOutput(mId, streamType(), mSessionId);
             } else {
-                AudioSystem::releaseInput(mId, sessionId);
+                AudioSystem::releaseInput(mId, mSessionId);
             }
         } else {
             mHalStream->stop();
@@ -7726,12 +7724,11 @@
         return PERMISSION_DENIED;
     }
 
-    sp<MmapTrack> track = new MmapTrack(this, mSampleRate, mFormat, mChannelMask, sessionId,
-                                        client.clientUid, client.clientPid,
-                                        portId);
+    sp<MmapTrack> track = new MmapTrack(this, mSampleRate, mFormat, mChannelMask, mSessionId,
+                                        client.clientUid, client.clientPid, portId);
 
     mActiveTracks.add(track);
-    sp<EffectChain> chain = getEffectChain_l(sessionId);
+    sp<EffectChain> chain = getEffectChain_l(mSessionId);
     if (chain != 0) {
         chain->setStrategy(AudioSystem::getStrategyForStream(streamType()));
         chain->incTrackCnt();
@@ -7739,10 +7736,9 @@
     }
 
     *handle = portId;
-
     broadcast_l();
 
-    ALOGV("%s DONE handle %d stream %p", __FUNCTION__, portId, mHalStream.get());
+    ALOGV("%s DONE handle %d stream %p", __FUNCTION__, *handle, mHalStream.get());
 
     return NO_ERROR;
 }
@@ -7755,6 +7751,11 @@
         return NO_INIT;
     }
 
+    if (handle == mPortId) {
+        mHalStream->stop();
+        return NO_ERROR;
+    }
+
     sp<MmapTrack> track;
     for (const sp<MmapTrack> &t : mActiveTracks) {
         if (handle == t->portId()) {
@@ -7770,14 +7771,10 @@
 
     if (isOutput()) {
         AudioSystem::stopOutput(mId, streamType(), track->sessionId());
-        if (mActiveTracks.size() != 0) {
-            AudioSystem::releaseOutput(mId, streamType(), track->sessionId());
-        }
+        AudioSystem::releaseOutput(mId, streamType(), track->sessionId());
     } else {
         AudioSystem::stopInput(mId, track->sessionId());
-        if (mActiveTracks.size() != 0) {
-            AudioSystem::releaseInput(mId, track->sessionId());
-        }
+        AudioSystem::releaseInput(mId, track->sessionId());
     }
 
     sp<EffectChain> chain = getEffectChain_l(track->sessionId());
@@ -7788,9 +7785,6 @@
 
     broadcast_l();
 
-    if (mActiveTracks.size() == 0) {
-        mHalStream->stop();
-    }
     return NO_ERROR;
 }
 
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index cc11ddb..22d78eb 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1482,7 +1482,7 @@
     status_t createMmapBuffer(int32_t minSizeFrames,
                                       struct audio_mmap_buffer_info *info);
     status_t getMmapPosition(struct audio_mmap_position *position);
-    status_t start(const MmapStreamInterface::Client& client, audio_port_handle_t *handle);
+    status_t start(const AudioClient& client, audio_port_handle_t *handle);
     status_t stop(audio_port_handle_t handle);
     status_t standby();
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index ded2285..4f79ed2 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -71,7 +71,7 @@
 
     virtual void toAudioPort(struct audio_port *port) const;
 
-    virtual void importAudioPort(const sp<AudioPort>& port);
+    virtual void importAudioPort(const sp<AudioPort>& port, bool force = false);
 
     void addAudioProfile(const sp<AudioProfile> &profile) { mProfiles.add(profile); }
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
index 2e653e2..cedf22d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
@@ -52,6 +52,7 @@
     audio_channel_mask_t channelMask() const { return mConfig.channel_mask; }
     audio_input_flags_t flags() const { return mFlags; }
     uid_t uid() const { return mRecordClientInfo.uid; }
+    void setUid(uid_t uid) { mRecordClientInfo.uid = uid; }
     bool matches(const sp<AudioSession> &other) const;
     bool isSoundTrigger() const { return mIsSoundTrigger; }
     uint32_t openCount() const { return mOpenCount; } ;
@@ -65,7 +66,7 @@
     virtual void onSessionInfoUpdate() const;
 
 private:
-    const record_client_info_t mRecordClientInfo;
+    record_client_info_t mRecordClientInfo;
     const struct audio_config_base mConfig;
     const audio_input_flags_t mFlags;
     bool  mIsSoundTrigger;
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index 9a52d22..1a644d7 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -48,7 +48,7 @@
     // AudioPort
     virtual void attach(const sp<HwModule>& module);
     virtual void toAudioPort(struct audio_port *port) const;
-    virtual void importAudioPort(const sp<AudioPort>& port);
+    virtual void importAudioPort(const sp<AudioPort>& port, bool force = false);
 
     audio_port_handle_t getId() const;
     status_t dump(int fd, int spaces, int index, bool verbose = true) const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index 6ed2cb7..fcf9070 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -128,7 +128,7 @@
     port->num_gains = i;
 }
 
-void AudioPort::importAudioPort(const sp<AudioPort>& port)
+void AudioPort::importAudioPort(const sp<AudioPort>& port, bool force __unused)
 {
     size_t indexToImport;
     for (indexToImport = 0; indexToImport < port->mProfiles.size(); indexToImport++) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index f0e48b6..a2c1165 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -263,7 +263,10 @@
     strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
 }
 
-void DeviceDescriptor::importAudioPort(const sp<AudioPort>& port) {
+void DeviceDescriptor::importAudioPort(const sp<AudioPort>& port, bool force) {
+    if (!force && !port->hasDynamicAudioProfile()) {
+        return;
+    }
     AudioPort::importAudioPort(port);
     port->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
 }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 77b0182..55c364f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1495,6 +1495,43 @@
             "session %d, flags %#x",
           attr->source, config->sample_rate, config->format, config->channel_mask, session, flags);
 
+    // special case for mmap capture: if an input IO handle is specified, we reuse this input if
+    // possible
+    if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) == AUDIO_INPUT_FLAG_MMAP_NOIRQ &&
+            *input != AUDIO_IO_HANDLE_NONE) {
+        ssize_t index = mInputs.indexOfKey(*input);
+        if (index < 0) {
+            ALOGW("getInputForAttr() unknown MMAP input %d", *input);
+            return BAD_VALUE;
+        }
+        sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
+        sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
+        if (audioSession == 0) {
+            ALOGW("getInputForAttr() unknown session %d on input %d", session, *input);
+            return BAD_VALUE;
+        }
+        // For MMAP mode, the first call to getInputForAttr() is made on behalf of audioflinger.
+        // The second call is for the first active client and sets the UID. Any further call
+        // corresponds to a new client and is only permitted from the same UId.
+        if (audioSession->openCount() == 1) {
+            audioSession->setUid(uid);
+        } else if (audioSession->uid() != uid) {
+            ALOGW("getInputForAttr() bad uid %d for session %d uid %d",
+                  uid, session, audioSession->uid());
+            return INVALID_OPERATION;
+        }
+        audioSession->changeOpenCount(1);
+        *inputType = API_INPUT_LEGACY;
+        if (*portId == AUDIO_PORT_HANDLE_NONE) {
+            *portId = AudioPort::getNextUniqueId();
+        }
+        DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(inputDesc->mDevice);
+        *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
+                : AUDIO_PORT_HANDLE_NONE;
+        ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
+        return NO_ERROR;
+    }
+
     *input = AUDIO_IO_HANDLE_NONE;
     *inputType = API_INPUT_INVALID;
 
@@ -1898,6 +1935,11 @@
                 continue;
             }
 
+            if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
+                    activeDesc->getId() == inputDesc->getId()) {
+                continue;
+            }
+
             audio_source_t activeSource = activeDesc->inputSource(true);
             if (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) {
                 if (activeSource == AUDIO_SOURCE_HOTWORD) {
@@ -3724,7 +3766,7 @@
                         sp<DeviceDescriptor> devDesc = mAvailableInputDevices[index];
                         if (!devDesc->isAttached()) {
                             devDesc->attach(mHwModules[i]);
-                            devDesc->importAudioPort(inProfile);
+                            devDesc->importAudioPort(inProfile, true);
                         }
                     }
                 }
@@ -4087,8 +4129,8 @@
                 continue;
             }
 
-            ALOGV("opening output for device %08x with params %s profile %p",
-                                                      device, address.string(), profile.get());
+            ALOGV("opening output for device %08x with params %s profile %p name %s",
+                  device, address.string(), profile.get(), profile->getName().string());
             desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
             desc->mDevice = device;
             audio_config_t config = AUDIO_CONFIG_INITIALIZER;
@@ -4337,6 +4379,10 @@
             config.channel_mask = desc->mChannelMask;
             config.format = desc->mFormat;
             audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+
+            ALOGV("opening inputput for device %08x with params %s profile %p name %s",
+                  desc->mDevice, address.string(), profile.get(), profile->getName().string());
+
             status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
                                                            &input,
                                                            &config,
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 02d4a19..ec2f5b9 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioEndpointManager"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
@@ -104,6 +104,8 @@
 
     // If we can't find an existing one then open a new one.
     if (endpoint == nullptr) {
+        // we must call openStream with audioserver identity
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
         switch(direction) {
             case AAUDIO_DIRECTION_INPUT:
                 capture = new AAudioServiceEndpointCapture(audioService);
@@ -138,6 +140,7 @@
         }
         ALOGD("AAudioEndpointManager::openEndpoint(), created %p for device = %d, dir = %d",
               endpoint, configuration.getDeviceId(), (int)direction);
+        IPCThreadState::self()->restoreCallingIdentity(token);
     }
 
     if (endpoint != nullptr) {
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 669bb54..3992719 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -50,8 +50,9 @@
 
 android::AAudioService::AAudioService()
     : BnAAudioService() {
-    mCachedProcessId = getpid();
-    mCachedUserId = getuid();   // TODO consider using geteuid()
+    mAudioClient.clientUid = getuid();   // TODO consider using geteuid()
+    mAudioClient.clientPid = getpid();
+    mAudioClient.packageName = String16("");
     AAudioClientTracker::getInstance().setAAudioService(this);
 }
 
@@ -92,7 +93,7 @@
 
     // Enforce limit on client processes.
     pid_t pid = request.getProcessId();
-    if (pid != mCachedProcessId) {
+    if (pid != mAudioClient.clientPid) {
         int32_t count = AAudioClientTracker::getInstance().getStreamCount(pid);
         if (count >= MAX_STREAMS_PER_PROCESS) {
             ALOGE("AAudioService::openStream(): exceeded max streams per process %d >= %d",
@@ -107,7 +108,13 @@
     }
 
     if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
-        serviceStream = new AAudioServiceStreamMMAP(mCachedUserId);
+        // only trust audioserver for in service indication
+        bool inService = false;
+        if (mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
+                mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
+            inService = request.isInService();
+        }
+        serviceStream = new AAudioServiceStreamMMAP(mAudioClient, inService);
         result = serviceStream->open(request, configurationOutput);
         if (result != AAUDIO_OK) {
             // fall back to using a shared stream
@@ -132,8 +139,6 @@
               result, AAudio_convertResultToText(result));
         return result;
     } else {
-        const uid_t ownerUserId = request.getUserId(); // only set by service, not by client
-        serviceStream->setOwnerUserId(ownerUserId);
         aaudio_handle_t handle = mHandleTracker.put(AAUDIO_HANDLE_TYPE_STREAM, serviceStream.get());
         if (handle < 0) {
             ALOGE("AAudioService::openStream(): handle table full");
@@ -143,7 +148,6 @@
             ALOGD("AAudioService::openStream(): handle = 0x%08X", handle);
             serviceStream->setHandle(handle);
             pid_t pid = request.getProcessId();
-            serviceStream->setOwnerProcessId(pid);
             AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
         }
         return handle;
@@ -181,8 +185,8 @@
         const uid_t callingUserId = IPCThreadState::self()->getCallingUid();
         const uid_t ownerUserId = serviceStream->getOwnerUserId();
         bool callerOwnsIt = callingUserId == ownerUserId;
-        bool serverCalling = callingUserId == mCachedUserId;
-        bool serverOwnsIt = ownerUserId == mCachedUserId;
+        bool serverCalling = callingUserId == mAudioClient.clientUid;
+        bool serverOwnsIt = ownerUserId == mAudioClient.clientUid;
         bool allowed = callerOwnsIt || serverCalling || serverOwnsIt;
         if (!allowed) {
             ALOGE("AAudioService: calling uid %d cannot access stream 0x%08X owned by %d",
@@ -212,6 +216,7 @@
         ALOGE("AAudioService::startStream(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
+
     aaudio_result_t result = serviceStream->start();
     return result;
 }
@@ -286,3 +291,26 @@
     serviceStream->setRegisteredThread(0);
     return AAUDIO_OK;
 }
+
+aaudio_result_t AAudioService::startClient(aaudio_handle_t streamHandle,
+                                  const android::AudioClient& client,
+                                  audio_port_handle_t *clientHandle) {
+    AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    if (serviceStream == nullptr) {
+        ALOGE("AAudioService::startClient(), illegal stream handle = 0x%0x",
+              streamHandle);
+        return AAUDIO_ERROR_INVALID_HANDLE;
+    }
+    return serviceStream->startClient(client, clientHandle);
+}
+
+aaudio_result_t AAudioService::stopClient(aaudio_handle_t streamHandle,
+                                          audio_port_handle_t clientHandle) {
+    AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    if (serviceStream == nullptr) {
+        ALOGE("AAudioService::stopClient(), illegal stream handle = 0x%0x",
+              streamHandle);
+        return AAUDIO_ERROR_INVALID_HANDLE;
+    }
+    return serviceStream->stopClient(clientHandle);
+}
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index f84ac4c..8421efc 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -21,6 +21,7 @@
 #include <pthread.h>
 
 #include <binder/BinderService.h>
+#include <media/AudioClient.h>
 
 #include <aaudio/AAudio.h>
 #include "utility/HandleTracker.h"
@@ -72,14 +73,20 @@
     virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
                                                   pid_t tid);
 
+    virtual aaudio_result_t startClient(aaudio_handle_t streamHandle,
+                                      const android::AudioClient& client,
+                                      audio_port_handle_t *clientHandle);
+
+    virtual aaudio_result_t stopClient(aaudio_handle_t streamHandle,
+                                       audio_port_handle_t clientHandle);
+
 private:
 
     aaudio::AAudioServiceStreamBase *convertHandleToServiceStream(aaudio_handle_t streamHandle) const;
 
     HandleTracker mHandleTracker;
 
-    uid_t   mCachedUserId = -1;
-    pid_t   mCachedProcessId = -1;
+    android::AudioClient mAudioClient;
 
     enum constants {
         DEFAULT_AUDIO_PRIORITY = 2
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index b519829..5895974 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceEndpoint"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
@@ -94,7 +94,7 @@
 }
 
 aaudio_result_t AAudioServiceEndpoint::close() {
-    return getStreamInternal()->close();
+     return getStreamInternal()->close();
 }
 
 // TODO, maybe use an interface to reduce exposure
@@ -113,26 +113,25 @@
 
 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);
-    mRunningStreams.push_back(sharedStream);
-    if (mRunningStreams.size() == 1) {
+    if (++mRunningStreams == 1) {
+        result = getStreamInternal()->requestStart();
         startSharingThread_l();
     }
-    return AAUDIO_OK;
+    return result;
 }
 
 aaudio_result_t AAudioServiceEndpoint::stopStream(sp<AAudioServiceStreamShared> sharedStream) {
     int numRunningStreams = 0;
     {
         std::lock_guard<std::mutex> lock(mLockStreams);
-        mRunningStreams.erase(
-                std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
-                mRunningStreams.end());
-        numRunningStreams = mRunningStreams.size();
+        numRunningStreams = --mRunningStreams;
     }
     if (numRunningStreams == 0) {
         // Don't call this under a lock because the callbackLoop also uses the lock.
         stopSharingThread();
+        getStreamInternal()->requestStop();
     }
     return AAUDIO_OK;
 }
@@ -163,11 +162,8 @@
 
 void AAudioServiceEndpoint::disconnectRegisteredStreams() {
     std::lock_guard<std::mutex> lock(mLockStreams);
-    for(auto baseStream : mRunningStreams) {
-        baseStream->onStop();
-    }
-    mRunningStreams.clear();
     for(auto sharedStream : mRegisteredStreams) {
+        sharedStream->stop();
         sharedStream->disconnect();
     }
     mRegisteredStreams.clear();
@@ -189,3 +185,4 @@
 
     return true;
 }
+
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index a78d3fa..ed995e5 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -79,7 +79,7 @@
 
     std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
 
-    std::vector<android::sp<AAudioServiceStreamShared>> mRunningStreams;
+    size_t                   mRunningStreams = 0;
 
 private:
     aaudio_result_t startSharingThread_l();
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index a144c54..6a37330 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -57,9 +57,7 @@
 void *AAudioServiceEndpointCapture::callbackLoop() {
     ALOGD("AAudioServiceEndpointCapture(): callbackLoop() entering");
     int32_t underflowCount = 0;
-
-    aaudio_result_t result = getStreamInternal()->requestStart();
-
+    aaudio_result_t result = AAUDIO_OK;
     int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
 
     // result might be a frame count
@@ -78,21 +76,21 @@
         // Distribute data to each active stream.
         { // use lock guard
             std::lock_guard <std::mutex> lock(mLockStreams);
-            for (sp<AAudioServiceStreamShared> sharedStream : mRunningStreams) {
-                FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
-                if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
-                    getFramesPerBurst()) {
-                    underflowCount++;
-                } else {
-                    fifo->write(mDistributionBuffer, getFramesPerBurst());
+            for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
+                if (sharedStream->isRunning()) {
+                    FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+                    if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
+                        getFramesPerBurst()) {
+                        underflowCount++;
+                    } else {
+                        fifo->write(mDistributionBuffer, getFramesPerBurst());
+                    }
+                    sharedStream->markTransferTime(AudioClock::getNanoseconds());
                 }
-                sharedStream->markTransferTime(AudioClock::getNanoseconds());
             }
         }
     }
 
-    result = getStreamInternal()->requestStop();
-
     ALOGD("AAudioServiceEndpointCapture(): callbackLoop() exiting, %d underflows", underflowCount);
     return NULL; // TODO review
 }
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index 1afcc1e..86ccde0 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -68,9 +68,7 @@
 void *AAudioServiceEndpointPlay::callbackLoop() {
     ALOGD("AAudioServiceEndpointPlay(): callbackLoop() entering");
     int32_t underflowCount = 0;
-
-    aaudio_result_t result = getStreamInternal()->requestStart();
-
+    aaudio_result_t result = AAUDIO_OK;
     int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
 
     // result might be a frame count
@@ -79,13 +77,15 @@
         mMixer.clear();
         { // use lock guard
             std::lock_guard <std::mutex> lock(mLockStreams);
-            for (sp<AAudioServiceStreamShared> sharedStream : mRunningStreams) {
-                FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
-                float volume = 1.0; // to match legacy volume
-                bool underflowed = mMixer.mix(fifo, volume);
-                underflowCount += underflowed ? 1 : 0;
-                // TODO log underflows in each stream
-                sharedStream->markTransferTime(AudioClock::getNanoseconds());
+            for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
+                if (sharedStream->isRunning()) {
+                    FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+                    float volume = 1.0; // to match legacy volume
+                    bool underflowed = mMixer.mix(fifo, volume);
+                    underflowCount += underflowed ? 1 : 0;
+                    // TODO log underflows in each stream
+                    sharedStream->markTransferTime(AudioClock::getNanoseconds());
+                }
             }
         }
 
@@ -102,8 +102,6 @@
         }
     }
 
-    result = getStreamInternal()->requestStop();
-
     ALOGD("AAudioServiceEndpointPlay(): callbackLoop() exiting, %d underflows", underflowCount);
     return NULL; // TODO review
 }
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 52b1801..e0f6ad4 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceStreamBase"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
@@ -38,6 +38,9 @@
 AAudioServiceStreamBase::AAudioServiceStreamBase()
         : mUpMessageQueue(nullptr)
         , mAAudioThread() {
+    mMmapClient.clientUid = -1;
+    mMmapClient.clientPid = -1;
+    mMmapClient.packageName = String16("");
 }
 
 AAudioServiceStreamBase::~AAudioServiceStreamBase() {
@@ -45,7 +48,8 @@
     // If the stream is deleted when OPEN or in use then audio resources will leak.
     // This would indicate an internal error. So we want to find this ASAP.
     LOG_ALWAYS_FATAL_IF(!(mState == AAUDIO_STREAM_STATE_CLOSED
-                        || mState == AAUDIO_STREAM_STATE_UNINITIALIZED),
+                        || mState == AAUDIO_STREAM_STATE_UNINITIALIZED
+                        || mState == AAUDIO_STREAM_STATE_DISCONNECTED),
                         "service stream still open, state = %d", mState);
 }
 
@@ -58,13 +62,18 @@
     result << "      framesPerBurst = " << mFramesPerBurst << "\n";
     result << "      channelCount   = " << mSamplesPerFrame << "\n";
     result << "      capacityFrames = " << mCapacityInFrames << "\n";
-    result << "      owner uid      = " << mOwnerUserId << "\n";
+    result << "      owner uid      = " << mMmapClient.clientUid << "\n";
 
     return result.str();
 }
 
 aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
                      aaudio::AAudioStreamConfiguration &configurationOutput) {
+
+    mMmapClient.clientUid = request.getUserId();
+    mMmapClient.clientPid = request.getProcessId();
+    mMmapClient.packageName.setTo(String16("")); // FIXME what should we do here?
+
     std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
     if (mUpMessageQueue != nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
@@ -86,6 +95,9 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::start() {
+    if (isRunning()) {
+        return AAUDIO_OK;
+    }
     sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
     mState = AAUDIO_STREAM_STATE_STARTED;
     mThreadEnabled.store(true);
@@ -94,32 +106,34 @@
 
 aaudio_result_t AAudioServiceStreamBase::pause() {
     aaudio_result_t result = AAUDIO_OK;
-    if (isRunning()) {
-        sendCurrentTimestamp();
-        mThreadEnabled.store(false);
-        result = mAAudioThread.stop();
-        if (result != AAUDIO_OK) {
-            disconnect();
-            return result;
-        }
-        sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
+    if (!isRunning()) {
+        return result;
     }
+    sendCurrentTimestamp();
+    mThreadEnabled.store(false);
+    result = mAAudioThread.stop();
+    if (result != AAUDIO_OK) {
+        disconnect();
+        return result;
+    }
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
     mState = AAUDIO_STREAM_STATE_PAUSED;
     return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::stop() {
     aaudio_result_t result = AAUDIO_OK;
-    if (isRunning()) {
-        // TODO wait for data to be played out
-        sendCurrentTimestamp(); // warning - this calls a virtual function
-        result = stopTimestampThread();
-        if (result != AAUDIO_OK) {
-            disconnect();
-            return result;
-        }
-        sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
+    if (!isRunning()) {
+        return result;
     }
+    // TODO wait for data to be played out
+    sendCurrentTimestamp(); // warning - this calls a virtual function
+    result = stopTimestampThread();
+    if (result != AAUDIO_OK) {
+        disconnect();
+        return result;
+    }
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
     mState = AAUDIO_STREAM_STATE_STOPPED;
     return result;
 }
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index c7df6f3..93a522e 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -27,6 +27,7 @@
 #include "binding/AudioEndpointParcelable.h"
 #include "binding/AAudioServiceMessage.h"
 #include "utility/AAudioUtilities.h"
+#include <media/AudioClient.h>
 
 #include "SharedRingBuffer.h"
 #include "AAudioThread.h"
@@ -85,9 +86,19 @@
      */
     virtual aaudio_result_t flush();
 
+    virtual aaudio_result_t startClient(const android::AudioClient& client __unused,
+                                        audio_port_handle_t *clientHandle __unused) {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle __unused) {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
     bool isRunning() const {
         return mState == AAUDIO_STREAM_STATE_STARTED;
     }
+
     // -------------------------------------------------------------------
 
     /**
@@ -124,17 +135,11 @@
     void disconnect();
 
     uid_t getOwnerUserId() const {
-        return mOwnerUserId;
-    }
-    void setOwnerUserId(uid_t uid) {
-        mOwnerUserId = uid;
+        return mMmapClient.clientUid;
     }
 
     pid_t getOwnerProcessId() const {
-        return mOwnerProcessId;
-    }
-    void setOwnerProcessId(pid_t pid) {
-        mOwnerProcessId = pid;
+        return mMmapClient.clientPid;
     }
 
     aaudio_handle_t getHandle() const {
@@ -164,24 +169,25 @@
 
     aaudio_stream_state_t   mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
 
-    pid_t              mRegisteredClientThread = ILLEGAL_THREAD_ID;
+    pid_t                   mRegisteredClientThread = ILLEGAL_THREAD_ID;
 
-    SharedRingBuffer*  mUpMessageQueue;
-    std::mutex         mLockUpMessageQueue;
+    SharedRingBuffer*       mUpMessageQueue;
+    std::mutex              mLockUpMessageQueue;
 
-    AAudioThread       mAAudioThread;
+    AAudioThread            mAAudioThread;
     // This is used by one thread to tell another thread to exit. So it must be atomic.
-    std::atomic<bool>  mThreadEnabled;
+    std::atomic<bool>       mThreadEnabled;
 
-    aaudio_format_t    mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
-    int32_t            mFramesPerBurst = 0;
-    int32_t            mSamplesPerFrame = AAUDIO_UNSPECIFIED;
-    int32_t            mSampleRate = AAUDIO_UNSPECIFIED;
-    int32_t            mCapacityInFrames = AAUDIO_UNSPECIFIED;
-    uid_t              mOwnerUserId = -1;
-    pid_t              mOwnerProcessId = -1;
+    aaudio_format_t         mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
+    int32_t                 mFramesPerBurst = 0;
+    int32_t                 mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+    int32_t                 mSampleRate = AAUDIO_UNSPECIFIED;
+    int32_t                 mCapacityInFrames = AAUDIO_UNSPECIFIED;
+    android::AudioClient    mMmapClient;
+    audio_port_handle_t     mClientHandle = AUDIO_PORT_HANDLE_NONE;
+
 private:
-    aaudio_handle_t    mHandle = -1;
+    aaudio_handle_t         mHandle = -1;
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamExclusive.h b/services/oboeservice/AAudioServiceStreamExclusive.h
deleted file mode 100644
index db382a3..0000000
--- a/services/oboeservice/AAudioServiceStreamExclusive.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 AAUDIO_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
-#define AAUDIO_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
-
-#include "AAudioServiceStreamMMAP.h"
-
-namespace aaudio {
-
-/**
- * Exclusive mode stream in the AAudio service.
- *
- * This is currently a stub.
- * We may move code from AAudioServiceStreamMMAP into this class.
- * If not, then it will be removed.
- */
-class AAudioServiceStreamExclusive : public AAudioServiceStreamMMAP {
-
-public:
-    AAudioServiceStreamExclusive() {};
-    virtual ~AAudioServiceStreamExclusive() = default;
-};
-
-} /* namespace aaudio */
-
-#endif //AAUDIO_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 1b80486..68be3c3 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceStreamMMAP"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
@@ -41,19 +41,21 @@
  * Service Stream that uses an MMAP buffer.
  */
 
-AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(uid_t serviceUid)
+AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(const android::AudioClient& serviceClient,
+                                                 bool inService)
         : AAudioServiceStreamBase()
         , mMmapStreamCallback(new MyMmapStreamCallback(*this))
         , mPreviousFrameCounter(0)
         , mMmapStream(nullptr)
-        , mCachedUserId(serviceUid) {
+        , mServiceClient(serviceClient)
+        , mInService(inService) {
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::close() {
     if (mState == AAUDIO_STREAM_STATE_CLOSED) {
         return AAUDIO_OK;
     }
-
+    stop();
     if (mMmapStream != 0) {
         mMmapStream.clear(); // TODO review. Is that all we have to do?
         // Apparently the above close is asynchronous. An attempt to open a new device
@@ -90,9 +92,6 @@
 
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
     audio_port_handle_t deviceId = configurationInput.getDeviceId();
-
-    mMmapClient.clientUid = request.getUserId();
-    mMmapClient.clientPid = request.getProcessId();
     aaudio_direction_t direction = request.getDirection();
 
     // Fill in config
@@ -123,8 +122,6 @@
         return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
     }
 
-    mMmapClient.packageName.setTo(String16("aaudio_service")); // FIXME what should we do here?
-
     MmapStreamInterface::stream_direction_t streamDirection = (direction == AAUDIO_DIRECTION_OUTPUT)
         ? MmapStreamInterface::DIRECTION_OUTPUT : MmapStreamInterface::DIRECTION_INPUT;
 
@@ -135,7 +132,8 @@
                                                           mMmapClient,
                                                           &deviceId,
                                                           mMmapStreamCallback,
-                                                          mMmapStream);
+                                                          mMmapStream,
+                                                          &mPortHandle);
     if (status != OK) {
         ALOGE("openMmapStream returned status %d", status);
         return AAUDIO_ERROR_UNAVAILABLE;
@@ -172,7 +170,7 @@
         mCapacityInFrames = -mCapacityInFrames;
     } else {
         // exclusive mode is only possible if the final fd destination is inside audioserver
-        if ((mMmapClient.clientUid != mCachedUserId) &&
+        if ((mMmapClient.clientUid != mServiceClient.clientUid) &&
                 configurationInput.getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
             // Fallback is handled by caller but indicate what is possible in case
             // this is used in the future
@@ -223,15 +221,21 @@
  * Start the flow of data.
  */
 aaudio_result_t AAudioServiceStreamMMAP::start() {
+    if (isRunning()) {
+        return AAUDIO_OK;
+    }
     if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
     aaudio_result_t result;
-    status_t status = mMmapStream->start(mMmapClient, &mPortHandle);
+    status_t status = mMmapStream->start(mServiceClient, &mPortHandle);
     if (status != OK) {
         ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", status);
         disconnect();
         result = AAudioConvert_androidToAAudioResult(status);
     } else {
         result = AAudioServiceStreamBase::start();
+        if (!mInService && result == AAUDIO_OK) {
+            startClient(mMmapClient, &mClientHandle);
+        }
     }
     return result;
 }
@@ -240,18 +244,28 @@
  * Stop the flow of data such that start() can resume with loss of data.
  */
 aaudio_result_t AAudioServiceStreamMMAP::pause() {
+    if (!isRunning()) {
+        return AAUDIO_OK;
+    }
     if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-
     aaudio_result_t result1 = AAudioServiceStreamBase::pause();
+    if (!mInService) {
+        stopClient(mClientHandle);
+    }
     status_t status = mMmapStream->stop(mPortHandle);
     mFramesRead.reset32();
     return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::stop() {
+    if (!isRunning()) {
+        return AAUDIO_OK;
+    }
     if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-
     aaudio_result_t result1 = AAudioServiceStreamBase::stop();
+    if (!mInService) {
+        stopClient(mClientHandle);
+    }
     aaudio_result_t status = mMmapStream->stop(mPortHandle);
     mFramesRead.reset32();
     return (result1 != AAUDIO_OK) ? result1 :  AAudioConvert_androidToAAudioResult(status);
@@ -266,6 +280,15 @@
     return AAudioServiceStreamBase::flush();;
 }
 
+aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
+                                                     audio_port_handle_t *clientHandle) {
+    return AAudioConvert_androidToAAudioResult(mMmapStream->start(client, clientHandle));
+}
+
+aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
+    return AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
+}
+
 aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
     struct audio_mmap_position position;
@@ -301,11 +324,11 @@
 
 void AAudioServiceStreamMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
     ALOGD("AAudioServiceStreamMMAP::onRoutingChanged() called with %d, old = %d",
-          deviceId, mPortHandle);
-    if (mPortHandle > 0 && mPortHandle != deviceId) {
+          deviceId, mDeviceId);
+    if (mDeviceId != AUDIO_PORT_HANDLE_NONE  && mDeviceId != deviceId) {
         disconnect();
     }
-    mPortHandle = deviceId;
+    mDeviceId = deviceId;
 };
 
 /**
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index 257bea9..533e5a8 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -43,7 +43,7 @@
     , public android::MmapStreamCallback {
 
 public:
-    AAudioServiceStreamMMAP(uid_t serviceUid);
+    AAudioServiceStreamMMAP(const android::AudioClient& serviceClient, bool inService);
     virtual ~AAudioServiceStreamMMAP() = default;
 
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
@@ -77,6 +77,11 @@
 
     aaudio_result_t close() override;
 
+    virtual aaudio_result_t startClient(const android::AudioClient& client,
+                                        audio_port_handle_t *clientHandle);
+
+    virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle);
+
     /**
      * Send a MMAP/NOIRQ buffer timestamp to the client.
      */
@@ -131,9 +136,10 @@
     // Interface to the AudioFlinger MMAP support.
     android::sp<android::MmapStreamInterface> mMmapStream;
     struct audio_mmap_buffer_info             mMmapBufferinfo;
-    android::MmapStreamInterface::Client      mMmapClient;
-    audio_port_handle_t                       mPortHandle = -1; // TODO review best default
-    uid_t                                     mCachedUserId = -1;
+    audio_port_handle_t                       mPortHandle = AUDIO_PORT_HANDLE_NONE;
+    audio_port_handle_t                       mDeviceId = AUDIO_PORT_HANDLE_NONE;
+    android::AudioClient                      mServiceClient;
+    bool                                      mInService = false;
 };
 
 } // namespace aaudio
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 8bb34d1..fe488cb 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceStreamShared"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
@@ -191,6 +191,9 @@
  * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
  */
 aaudio_result_t AAudioServiceStreamShared::start()  {
+    if (isRunning()) {
+        return AAUDIO_OK;
+    }
     AAudioServiceEndpoint *endpoint = mServiceEndpoint;
     if (endpoint == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
@@ -201,7 +204,10 @@
         ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
         disconnect();
     } else {
-        result = AAudioServiceStreamBase::start();
+        result = endpoint->getStreamInternal()->startClient(mMmapClient, &mClientHandle);
+        if (result == AAUDIO_OK) {
+            result = AAudioServiceStreamBase::start();
+        }
     }
     return result;
 }
@@ -212,10 +218,14 @@
  * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
 */
 aaudio_result_t AAudioServiceStreamShared::pause()  {
+    if (!isRunning()) {
+        return AAUDIO_OK;
+    }
     AAudioServiceEndpoint *endpoint = mServiceEndpoint;
     if (endpoint == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
+    endpoint->getStreamInternal()->stopClient(mClientHandle);
     aaudio_result_t result = endpoint->stopStream(this);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
@@ -225,10 +235,14 @@
 }
 
 aaudio_result_t AAudioServiceStreamShared::stop()  {
+    if (!isRunning()) {
+        return AAUDIO_OK;
+    }
     AAudioServiceEndpoint *endpoint = mServiceEndpoint;
     if (endpoint == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
+    endpoint->getStreamInternal()->stopClient(mClientHandle);
     aaudio_result_t result = endpoint->stopStream(this);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
@@ -248,7 +262,7 @@
         return AAUDIO_ERROR_INVALID_STATE;
     }
     if (mState != AAUDIO_STREAM_STATE_PAUSED) {
-        ALOGE("AAudioServiceStreamShared::flush() stream not paused, state = %s",
+         ALOGE("AAudioServiceStreamShared::flush() stream not paused, state = %s",
             AAudio_convertStreamStateToText(mState));
         return AAUDIO_ERROR_INVALID_STATE;
     }
@@ -261,11 +275,12 @@
         return AAUDIO_OK;
     }
 
+    stop();
+
     AAudioServiceEndpoint *endpoint = mServiceEndpoint;
     if (endpoint == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
-    endpoint->stopStream(this);
 
     endpoint->unregisterStream(this);
 
@@ -277,7 +292,6 @@
         delete mAudioDataQueue;
         mAudioDataQueue = nullptr;
     }
-
     return AAudioServiceStreamBase::close();
 }
 
@@ -293,9 +307,6 @@
     return AAUDIO_OK;
 }
 
-void AAudioServiceStreamShared::onStop() {
-}
-
 void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
     mMarkedPosition = mAudioDataQueue->getFifoBuffer()->getReadCounter();
     mMarkedTime = nanoseconds;
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 742c5af..6b67337 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -87,8 +87,6 @@
      */
     void markTransferTime(int64_t nanoseconds);
 
-    void onStop();
-
 protected:
 
     aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) override;