aaudio: close MMAP stream if client dies

Notify client when audio service dies. Clear connection.
Notify AAudio service when client dies. Close client streams.

Use sp<> to track ServiceStreams.

Bug: 38267698
Test: test_no_close.cpp
Change-Id: I5f1699ed3b8b7bd960947c0028a89ca8419ce7a0
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index 3a00f51..d4cb407 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -25,7 +25,7 @@
 #include "AAudioSimplePlayer.h"
 
 #define SAMPLE_RATE           48000
-#define NUM_SECONDS           20
+#define NUM_SECONDS           4
 
 #define MMAP_POLICY              AAUDIO_UNSPECIFIED
 //#define MMAP_POLICY              AAUDIO_POLICY_NEVER
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index 28c4d7f..7131c6c 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -52,6 +52,7 @@
     binding/AAudioBinderClient.cpp \
     binding/AAudioStreamRequest.cpp \
     binding/AAudioStreamConfiguration.cpp \
+    binding/IAAudioClient.cpp \
     binding/IAAudioService.cpp \
     binding/RingBufferParcelable.cpp \
     binding/SharedMemoryParcelable.cpp \
@@ -109,6 +110,7 @@
     binding/AAudioBinderClient.cpp \
     binding/AAudioStreamRequest.cpp \
     binding/AAudioStreamConfiguration.cpp \
+    binding/IAAudioClient.cpp \
     binding/IAAudioService.cpp \
     binding/RingBufferParcelable.cpp \
     binding/SharedMemoryParcelable.cpp \
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index c45bd71..6464334 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -19,75 +19,101 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
+#include <binder/IInterface.h>
 #include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 #include <utils/Mutex.h>
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
+#include <media/AudioSystem.h>
 
 #include <aaudio/AAudio.h>
 
 #include "AudioEndpointParcelable.h"
-#include "binding/AAudioStreamRequest.h"
-#include "binding/AAudioStreamConfiguration.h"
-#include "binding/IAAudioService.h"
-#include "binding/AAudioServiceMessage.h"
+#include "binding/AAudioBinderClient.h"
+//#include "binding/AAudioStreamRequest.h"
+//#include "binding/AAudioStreamConfiguration.h"
+//#include "binding/IAAudioService.h"
+//#include "binding/AAudioServiceMessage.h"
 
-#include "AAudioBinderClient.h"
-#include "AAudioServiceInterface.h"
+//#include "AAudioServiceInterface.h"
 
 using android::String16;
 using android::IServiceManager;
 using android::defaultServiceManager;
 using android::interface_cast;
+using android::IInterface;
 using android::IAAudioService;
 using android::Mutex;
 using android::sp;
+using android::wp;
 
 using namespace aaudio;
 
-static android::Mutex gServiceLock;
-static sp<IAAudioService>  gAAudioService;
-
 ANDROID_SINGLETON_STATIC_INSTANCE(AAudioBinderClient);
 
+AAudioBinderClient::AAudioBinderClient()
+        : AAudioServiceInterface()
+        , Singleton<AAudioBinderClient>() {
+
+    mAAudioClient = new AAudioClient(this);
+    ALOGD("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
+}
+
+AAudioBinderClient::~AAudioBinderClient() {
+    Mutex::Autolock _l(mServiceLock);
+    if (mAAudioService != 0) {
+        IInterface::asBinder(mAAudioService)->unlinkToDeath(mAAudioClient);
+    }
+}
+
 // TODO Share code with other service clients.
 // Helper function to get access to the "AAudioService" service.
 // This code was modeled after frameworks/av/media/libaudioclient/AudioSystem.cpp
-static const sp<IAAudioService> getAAudioService() {
-    sp<IBinder> binder;
-    Mutex::Autolock _l(gServiceLock);
-    if (gAAudioService == 0) {
-        sp<IServiceManager> sm = defaultServiceManager();
-        // Try several times to get the service.
-        int retries = 4;
-        do {
-            binder = sm->getService(String16(AAUDIO_SERVICE_NAME)); // This will wait a while.
+const sp<IAAudioService> AAudioBinderClient::getAAudioService() {
+    sp<IAAudioService> aaudioService;
+    bool needToRegister = false;
+    {
+        Mutex::Autolock _l(mServiceLock);
+        if (mAAudioService == 0) {
+            sp<IBinder> binder;
+            sp<IServiceManager> sm = defaultServiceManager();
+            // Try several times to get the service.
+            int retries = 4;
+            do {
+                binder = sm->getService(String16(AAUDIO_SERVICE_NAME)); // This will wait a while.
+                if (binder != 0) {
+                    break;
+                }
+            } while (retries-- > 0);
+
             if (binder != 0) {
-                break;
+                // Ask for notification if the service dies.
+                status_t status = binder->linkToDeath(mAAudioClient);
+                ALOGD("getAAudioService: linkToDeath(mAAudioClient = %p) returned %d",
+                      mAAudioClient.get(), status);
+                mAAudioService = interface_cast<IAAudioService>(binder);
+                needToRegister = true;
+                // Make sure callbacks can be received by mAAudioClient
+                android::ProcessState::self()->startThreadPool();
+            } else {
+                ALOGE("AAudioBinderClient could not connect to %s", AAUDIO_SERVICE_NAME);
             }
-        } while (retries-- > 0);
-
-        if (binder != 0) {
-            // TODO Add linkToDeath() like in frameworks/av/media/libaudioclient/AudioSystem.cpp
-            // TODO Create a DeathRecipient that disconnects all active streams.
-            gAAudioService = interface_cast<IAAudioService>(binder);
-        } else {
-            ALOGE("AudioStreamInternal could not get %s", AAUDIO_SERVICE_NAME);
         }
+        aaudioService = mAAudioService;
     }
-    return gAAudioService;
+    // Do this outside the mutex lock.
+    if (needToRegister && aaudioService != 0) { // new client?
+        aaudioService->registerClient(mAAudioClient);
+    }
+    return aaudioService;
 }
 
-static void dropAAudioService() {
-    Mutex::Autolock _l(gServiceLock);
-    gAAudioService.clear(); // force a reconnect
+void AAudioBinderClient::dropAAudioService() {
+    Mutex::Autolock _l(mServiceLock);
+    mAAudioService.clear(); // force a reconnect
 }
 
-AAudioBinderClient::AAudioBinderClient()
-        : AAudioServiceInterface()
-        , Singleton<AAudioBinderClient>() {}
-
-AAudioBinderClient::~AAudioBinderClient() {}
 
 /**
 * @param request info needed to create the stream
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index 7cf7bf8..469f0a8 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
 #define ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
 
+#include <utils/RefBase.h>
 #include <utils/Singleton.h>
 
 #include <aaudio/AAudio.h>
@@ -25,14 +26,16 @@
 #include "binding/AAudioStreamRequest.h"
 #include "binding/AAudioStreamConfiguration.h"
 #include "binding/AudioEndpointParcelable.h"
+#include "binding/IAAudioService.h"
 
 /**
- * Implements the AAudioServiceInterface by talking to the actual service through Binder.
+ * Implements the AAudioServiceInterface by talking to the service through Binder.
  */
 
 namespace aaudio {
 
-class AAudioBinderClient : public AAudioServiceInterface
+class AAudioBinderClient : public virtual android::RefBase
+        , public AAudioServiceInterface
         , public android::Singleton<AAudioBinderClient> {
 
 public:
@@ -41,6 +44,12 @@
 
     virtual ~AAudioBinderClient();
 
+    const android::sp<android::IAAudioService> getAAudioService();
+
+    void dropAAudioService();
+
+    void registerClient(const android::sp<android::IAAudioClient>& client __unused) override {}
+
     /**
      * @param request info needed to create the stream
      * @param configuration contains resulting information about the created stream
@@ -87,6 +96,45 @@
 
     aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
                                                   pid_t clientThreadId) override;
+
+    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.
+        ALOGW("onStreamChange called!");
+    }
+
+    class AAudioClient : public android::IBinder::DeathRecipient , public android::BnAAudioClient
+    {
+    public:
+        AAudioClient(android::wp<AAudioBinderClient> aaudioBinderClient)
+            : mBinderClient(aaudioBinderClient) {
+        }
+
+        // implement DeathRecipient
+        virtual void binderDied(const android::wp<android::IBinder>& who __unused) {
+            android::sp<AAudioBinderClient> client = mBinderClient.promote();
+            if (client != 0) {
+                client->dropAAudioService();
+            }
+            ALOGW("AAudio service binderDied()!");
+        }
+
+        // implement BnAAudioClient
+        void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {
+            android::sp<AAudioBinderClient> client = mBinderClient.promote();
+            if (client != 0) {
+                client->onStreamChange(handle, opcode, value);
+            }
+        }
+    private:
+        android::wp<AAudioBinderClient> mBinderClient;
+    };
+
+
+    android::Mutex               mServiceLock;
+    android::sp<android::IAAudioService>  mAAudioService;
+    android::sp<AAudioClient>    mAAudioClient;
+
 };
 
 
diff --git a/media/libaaudio/src/binding/AAudioServiceDefinitions.h b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
index 638544e..8a2303c 100644
--- a/media/libaaudio/src/binding/AAudioServiceDefinitions.h
+++ b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
@@ -29,8 +29,9 @@
 
 namespace android {
 
-enum aaudio_commands_t {
-    OPEN_STREAM = IBinder::FIRST_CALL_TRANSACTION,
+enum aaudio_service_commands_t {
+    REGISTER_CLIENT = IBinder::FIRST_CALL_TRANSACTION,
+    OPEN_STREAM,
     CLOSE_STREAM,
     GET_STREAM_DESCRIPTION,
     START_STREAM,
@@ -41,6 +42,10 @@
     UNREGISTER_AUDIO_THREAD
 };
 
+enum aaudio_client_commands_t {
+    ON_STREAM_CHANGE = IBinder::FIRST_CALL_TRANSACTION
+};
+
 } // namespace android
 
 namespace aaudio {
diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h
index d6a130f..7368062 100644
--- a/media/libaaudio/src/binding/AAudioServiceInterface.h
+++ b/media/libaaudio/src/binding/AAudioServiceInterface.h
@@ -17,10 +17,13 @@
 #ifndef ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
 #define ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
 
+#include <utils/StrongPointer.h>
+
 #include "binding/AAudioServiceDefinitions.h"
 #include "binding/AAudioStreamRequest.h"
 #include "binding/AAudioStreamConfiguration.h"
 #include "binding/AudioEndpointParcelable.h"
+#include "binding/IAAudioClient.h"
 
 /**
  * This has the same methods as IAAudioService but without the Binder features.
@@ -36,6 +39,8 @@
     AAudioServiceInterface() {};
     virtual ~AAudioServiceInterface() = default;
 
+    virtual void registerClient(const android::sp<android::IAAudioClient>& client) = 0;
+
     /**
      * @param request info needed to create the stream
      * @param configuration contains information about the created stream
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index 77138da..462246b 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -68,7 +68,6 @@
         mSharingModeMatchRequired = required;
     }
 
-
     const AAudioStreamConfiguration &getConstantConfiguration() const {
         return mConfiguration;
     }
diff --git a/media/libaaudio/src/binding/IAAudioClient.cpp b/media/libaaudio/src/binding/IAAudioClient.cpp
new file mode 100644
index 0000000..c69c4e8
--- /dev/null
+++ b/media/libaaudio/src/binding/IAAudioClient.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <aaudio/AAudio.h>
+
+#include "binding/AAudioBinderClient.h"
+#include "binding/AAudioServiceDefinitions.h"
+#include "binding/IAAudioClient.h"
+#include "utility/AAudioUtilities.h"
+
+namespace android {
+
+using aaudio::aaudio_handle_t;
+
+/**
+ * This is used by the AAudio Service to talk to an AAudio Client.
+ *
+ * The order of parameters in the Parcels must match with code in AAudioClient.cpp.
+ */
+class BpAAudioClient : public BpInterface<IAAudioClient>
+{
+public:
+    explicit BpAAudioClient(const sp<IBinder>& impl)
+        : BpInterface<IAAudioClient>(impl)
+    {
+    }
+
+    void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAAudioClient::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        data.writeInt32(opcode);
+        data.writeInt32(value);
+        remote()->transact(ON_STREAM_CHANGE, data,  &reply, IBinder::FLAG_ONEWAY);
+    }
+
+};
+
+// Implement an interface to the service.
+IMPLEMENT_META_INTERFACE(AAudioClient, "IAAudioClient");
+
+// The order of parameters in the Parcels must match with code in BpAAudioClient
+
+status_t BnAAudioClient::onTransact(uint32_t code, const Parcel& data,
+                                        Parcel* reply, uint32_t flags) {
+    aaudio_handle_t streamHandle;
+    int32_t opcode = 0;
+    int32_t value = 0;
+    ALOGV("BnAAudioClient::onTransact(%u) %u", code, flags);
+
+    switch(code) {
+        case ON_STREAM_CHANGE: {
+            CHECK_INTERFACE(IAAudioClient, data, reply);
+            data.readInt32(&streamHandle);
+            data.readInt32(&opcode);
+            data.readInt32(&value);
+            onStreamChange(streamHandle, opcode, value);
+            ALOGD("BnAAudioClient onStreamChange(%x, %d, %d)", streamHandle, opcode, value);
+            return NO_ERROR;
+        } break;
+
+        default:
+            // ALOGW("BnAAudioClient::onTransact not handled %u", code);
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+} /* namespace android */
diff --git a/media/libaaudio/src/binding/IAAudioClient.h b/media/libaaudio/src/binding/IAAudioClient.h
new file mode 100644
index 0000000..21cc33b
--- /dev/null
+++ b/media/libaaudio/src/binding/IAAudioClient.h
@@ -0,0 +1,48 @@
+/*
+ * 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_AAUDIO_IAAUDIO_CLIENT_H
+#define ANDROID_AAUDIO_IAAUDIO_CLIENT_H
+
+#include <stdint.h>
+#include <binder/IInterface.h>
+
+#include <aaudio/AAudio.h>
+
+#include "utility/HandleTracker.h"
+
+namespace android {
+
+
+// Interface (our AIDL) - client methods called by service
+class IAAudioClient : public IInterface {
+public:
+
+    DECLARE_META_INTERFACE(AAudioClient);
+
+    virtual void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) = 0;
+
+};
+
+class BnAAudioClient : public BnInterface<IAAudioClient> {
+public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+                                Parcel* reply, uint32_t flags = 0);
+};
+
+} /* namespace android */
+
+#endif //ANDROID_AAUDIO_IAAUDIO_SERVICE_H
diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
index e4bf82a..97fbaaa 100644
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ b/media/libaaudio/src/binding/IAAudioService.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "AAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
 #include <aaudio/AAudio.h>
 #include <binder/IPCThreadState.h>
 
@@ -41,8 +45,16 @@
     {
     }
 
-    virtual aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
-                                     aaudio::AAudioStreamConfiguration &configurationOutput) override {
+    void registerClient(const sp<IAAudioClient>& client) override
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
+        data.writeStrongBinder(IInterface::asBinder(client));
+        remote()->transact(REGISTER_CLIENT, data, &reply);
+    }
+
+    aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
+                               aaudio::AAudioStreamConfiguration &configurationOutput) override {
         Parcel data, reply;
         // send command
         data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
@@ -56,7 +68,6 @@
         // parse reply
         aaudio_handle_t stream;
         err = reply.readInt32(&stream);
-        ALOGD("BpAAudioService::client openStream returned stream = 0x%08x", stream);
         if (err != NO_ERROR) {
             ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err);
             return AAudioConvert_androidToAAudioResult(err);
@@ -233,7 +244,7 @@
 
 status_t BnAAudioService::onTransact(uint32_t code, const Parcel& data,
                                         Parcel* reply, uint32_t flags) {
-    aaudio_handle_t stream;
+    aaudio_handle_t streamHandle;
     aaudio::AAudioStreamRequest request;
     aaudio::AAudioStreamConfiguration configuration;
     pid_t tid;
@@ -242,6 +253,14 @@
     ALOGV("BnAAudioService::onTransact(%i) %i", code, flags);
 
     switch(code) {
+        case REGISTER_CLIENT: {
+            CHECK_INTERFACE(IAAudioService, data, reply);
+            sp<IAAudioClient> client = interface_cast<IAAudioClient>(
+                    data.readStrongBinder());
+            registerClient(client);
+            return NO_ERROR;
+        } break;
+
         case OPEN_STREAM: {
             CHECK_INTERFACE(IAAudioService, data, reply);
             request.readFromParcel(&data);
@@ -250,28 +269,28 @@
             // Override the uid and pid from the client in case they are incorrect.
             request.setUserId(IPCThreadState::self()->getCallingUid());
             request.setProcessId(IPCThreadState::self()->getCallingPid());
-            stream = openStream(request, configuration);
-            //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X ----", stream);
-            reply->writeInt32(stream);
+            streamHandle = openStream(request, configuration);
+            //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", streamHandle);
+            reply->writeInt32(streamHandle);
             configuration.writeToParcel(reply);
             return NO_ERROR;
         } break;
 
         case CLOSE_STREAM: {
             CHECK_INTERFACE(IAAudioService, data, reply);
-            data.readInt32(&stream);
-            result = closeStream(stream);
+            data.readInt32(&streamHandle);
+            result = closeStream(streamHandle);
             //ALOGD("BnAAudioService::onTransact CLOSE_STREAM 0x%08X, result = %d",
-            //      stream, result);
+            //      streamHandle, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
 
         case GET_STREAM_DESCRIPTION: {
             CHECK_INTERFACE(IAAudioService, data, reply);
-            data.readInt32(&stream);
+            data.readInt32(&streamHandle);
             aaudio::AudioEndpointParcelable parcelable;
-            result = getStreamDescription(stream, parcelable);
+            result = getStreamDescription(streamHandle, parcelable);
             if (result != AAUDIO_OK) {
                 return AAudioConvert_aaudioToAndroidStatus(result);
             }
@@ -288,63 +307,63 @@
 
         case START_STREAM: {
             CHECK_INTERFACE(IAAudioService, data, reply);
-            data.readInt32(&stream);
-            result = startStream(stream);
+            data.readInt32(&streamHandle);
+            result = startStream(streamHandle);
             ALOGV("BnAAudioService::onTransact START_STREAM 0x%08X, result = %d",
-                    stream, result);
+                    streamHandle, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
 
         case PAUSE_STREAM: {
             CHECK_INTERFACE(IAAudioService, data, reply);
-            data.readInt32(&stream);
-            result = pauseStream(stream);
+            data.readInt32(&streamHandle);
+            result = pauseStream(streamHandle);
             ALOGV("BnAAudioService::onTransact PAUSE_STREAM 0x%08X, result = %d",
-                  stream, result);
+                  streamHandle, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
 
         case STOP_STREAM: {
             CHECK_INTERFACE(IAAudioService, data, reply);
-            data.readInt32(&stream);
-            result = stopStream(stream);
+            data.readInt32(&streamHandle);
+            result = stopStream(streamHandle);
             ALOGV("BnAAudioService::onTransact STOP_STREAM 0x%08X, result = %d",
-                  stream, result);
+                  streamHandle, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
 
         case FLUSH_STREAM: {
             CHECK_INTERFACE(IAAudioService, data, reply);
-            data.readInt32(&stream);
-            result = flushStream(stream);
+            data.readInt32(&streamHandle);
+            result = flushStream(streamHandle);
             ALOGV("BnAAudioService::onTransact FLUSH_STREAM 0x%08X, result = %d",
-                    stream, result);
+                    streamHandle, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
 
         case REGISTER_AUDIO_THREAD: {
             CHECK_INTERFACE(IAAudioService, data, reply);
-            data.readInt32(&stream);
+            data.readInt32(&streamHandle);
             data.readInt32(&tid);
             data.readInt64(&nanoseconds);
-            result = registerAudioThread(stream, tid, nanoseconds);
+            result = registerAudioThread(streamHandle, tid, nanoseconds);
             ALOGV("BnAAudioService::onTransact REGISTER_AUDIO_THREAD 0x%08X, result = %d",
-                    stream, result);
+                    streamHandle, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
 
         case UNREGISTER_AUDIO_THREAD: {
             CHECK_INTERFACE(IAAudioService, data, reply);
-            data.readInt32(&stream);
+            data.readInt32(&streamHandle);
             data.readInt32(&tid);
-            result = unregisterAudioThread(stream, tid);
+            result = unregisterAudioThread(streamHandle, tid);
             ALOGV("BnAAudioService::onTransact UNREGISTER_AUDIO_THREAD 0x%08X, result = %d",
-                    stream, result);
+                    streamHandle, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
diff --git a/media/libaaudio/src/binding/IAAudioService.h b/media/libaaudio/src/binding/IAAudioService.h
index 071c5b8..30b3ead 100644
--- a/media/libaaudio/src/binding/IAAudioService.h
+++ b/media/libaaudio/src/binding/IAAudioService.h
@@ -28,18 +28,24 @@
 #include "binding/AudioEndpointParcelable.h"
 #include "binding/AAudioStreamRequest.h"
 #include "binding/AAudioStreamConfiguration.h"
+#include "binding/IAAudioClient.h"
 #include "utility/HandleTracker.h"
 
 namespace android {
 
 #define AAUDIO_SERVICE_NAME  "media.aaudio"
 
-// Interface (our AIDL) - Shared by server and client
+// Interface (our AIDL) - service methods called by client
 class IAAudioService : public IInterface {
 public:
 
     DECLARE_META_INTERFACE(AAudioService);
 
+    // Register an object to receive audio input/output change and track notifications.
+    // For a given calling pid, AAudio service disregards any registrations after the first.
+    // Thus the IAAudioClient must be a singleton per process.
+    virtual void registerClient(const sp<IAAudioClient>& client) = 0;
+
     /**
      * @param request info needed to create the stream
      * @param configuration contains information about the created stream
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index e6c779b..bd7169f 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -49,3 +49,23 @@
 LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
 LOCAL_MODULE := test_open_params
 include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, audio-utils) \
+    frameworks/av/media/libaaudio/include \
+    frameworks/av/media/libaaudio/src
+LOCAL_SRC_FILES:= test_no_close.cpp
+LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
+LOCAL_MODULE := test_no_close
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, audio-utils) \
+    frameworks/av/media/libaaudio/include \
+    frameworks/av/media/libaaudio/src
+LOCAL_SRC_FILES:= test_recovery.cpp
+LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
+LOCAL_MODULE := test_aaudio_recovery
+include $(BUILD_NATIVE_TEST)
diff --git a/media/libaaudio/tests/test_no_close.cpp b/media/libaaudio/tests/test_no_close.cpp
new file mode 100644
index 0000000..2dbf153
--- /dev/null
+++ b/media/libaaudio/tests/test_no_close.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+// Try to create a resource leak in the server by opening a stream and then not closing it.
+// Return 0 if the stream opened, 1 if it failed.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <aaudio/AAudio.h>
+
+int main(int argc, char **argv)
+{
+    (void)argc; // unused
+    (void)argv; // unused
+
+    aaudio_result_t result = AAUDIO_OK;
+    AAudioStreamBuilder *aaudioBuilder = nullptr;
+    AAudioStream *aaudioStream = nullptr;
+
+    result = AAudio_createStreamBuilder(&aaudioBuilder);
+    if (result != AAUDIO_OK) {
+        goto finish;
+    }
+
+    // Create an AAudioStream using the Builder.
+    result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+    if (result != AAUDIO_OK) {
+        printf("ERROR could not open AAudio stream, %d\n", result);
+        goto finish;
+    } else {
+        printf("AAudio stream opened successfully.\n");
+    }
+
+    printf("Exit without closing the stream!\n");
+
+finish:
+    return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/media/libaaudio/tests/test_recovery.cpp b/media/libaaudio/tests/test_recovery.cpp
new file mode 100644
index 0000000..7268a30
--- /dev/null
+++ b/media/libaaudio/tests/test_recovery.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+// Play silence and recover from dead servers or disconnected devices.
+
+#include <stdio.h>
+
+#include <aaudio/AAudio.h>
+
+
+#define DEFAULT_TIMEOUT_NANOS  ((int64_t)1000000000)
+
+static const char *getSharingModeText(aaudio_sharing_mode_t mode) {
+    const char *modeText = "unknown";
+    switch (mode) {
+        case AAUDIO_SHARING_MODE_EXCLUSIVE:
+            modeText = "EXCLUSIVE";
+            break;
+        case AAUDIO_SHARING_MODE_SHARED:
+            modeText = "SHARED";
+            break;
+        default:
+            break;
+    }
+    return modeText;
+}
+
+int main(int argc, char **argv) {
+    (void) argc;
+    (void *)argv;
+
+    aaudio_result_t result = AAUDIO_OK;
+
+    int32_t triesLeft = 3;
+    int32_t bufferCapacity;
+    int32_t framesPerBurst = 0;
+    float *buffer = nullptr;
+
+    int32_t actualChannelCount = 0;
+    int32_t actualSampleRate = 0;
+    aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
+    aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
+
+    AAudioStreamBuilder *aaudioBuilder = nullptr;
+    AAudioStream *aaudioStream = nullptr;
+
+    // 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("TestRecovery:\n");
+
+    // Use an AAudioStreamBuilder to contain requested parameters.
+    result = AAudio_createStreamBuilder(&aaudioBuilder);
+    if (result != AAUDIO_OK) {
+        printf("AAudio_createStreamBuilder returned %s",
+               AAudio_convertResultToText(result));
+        goto finish;
+    }
+
+    // Request stream properties.
+    AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT);
+
+    while (triesLeft-- > 0) {
+        // Create an AAudioStream using the Builder.
+        result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+        if (result != AAUDIO_OK) {
+            printf("AAudioStreamBuilder_openStream returned %s",
+                   AAudio_convertResultToText(result));
+            goto finish;
+        }
+
+        // Check to see what kind of stream we actually got.
+        actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
+        actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
+        actualDataFormat = AAudioStream_getFormat(aaudioStream);
+
+        printf("-------- chans = %3d, rate = %6d format = %d\n",
+                actualChannelCount, actualSampleRate, actualDataFormat);
+
+        // 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);
+        bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream);
+        printf("         bufferCapacity = %d, framesPerBurst = %d\n",
+        bufferCapacity, framesPerBurst);
+
+        int samplesPerBurst = framesPerBurst * actualChannelCount;
+        buffer = new float[samplesPerBurst];
+
+        result = AAudioStream_requestStart(aaudioStream);
+        if (result != AAUDIO_OK) {
+            printf("AAudioStream_requestStart returned %s",
+                   AAudio_convertResultToText(result));
+            goto finish;
+        }
+
+        // Play silence for awhile.
+        int32_t framesMax = actualSampleRate * 20;
+        int64_t framesTotal = 0;
+        int64_t printAt = actualSampleRate;
+        while (result == AAUDIO_OK && framesTotal < framesMax) {
+            int32_t framesWritten = AAudioStream_write(aaudioStream,
+                                                       buffer, framesPerBurst,
+                                                       DEFAULT_TIMEOUT_NANOS);
+            if (framesWritten < 0) {
+                result = framesWritten;
+                printf("write() returned %s, frames = %d\n",
+                       AAudio_convertResultToText(result), (int)framesTotal);
+                printf("  frames = %d\n", (int)framesTotal);
+            } else if (framesWritten != framesPerBurst) {
+                printf("write() returned %d, frames = %d\n", framesWritten, (int)framesTotal);
+                result = AAUDIO_ERROR_TIMEOUT;
+            } else {
+                framesTotal += framesWritten;
+                if (framesTotal >= printAt) {
+                    printf("frames = %d\n", (int)framesTotal);
+                    printAt += actualSampleRate;
+                }
+            }
+        }
+        result = AAudioStream_requestStop(aaudioStream);
+        if (result != AAUDIO_OK) {
+            printf("AAudioStream_requestStop returned %s\n",
+                   AAudio_convertResultToText(result));
+        }
+        result = AAudioStream_close(aaudioStream);
+        if (result != AAUDIO_OK) {
+            printf("AAudioStream_close returned %s\n",
+                   AAudio_convertResultToText(result));
+        }
+        aaudioStream = nullptr;
+    }
+
+finish:
+    if (aaudioStream != nullptr) {
+        AAudioStream_close(aaudioStream);
+    }
+    AAudioStreamBuilder_delete(aaudioBuilder);
+    delete[] buffer;
+    printf("          result = %d = %s\n", result, AAudio_convertResultToText(result));
+}
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
new file mode 100644
index 0000000..da7b80f
--- /dev/null
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <assert.h>
+#include <binder/IPCThreadState.h>
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "utility/AAudioUtilities.h"
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
+#include "AAudioClientTracker.h"
+
+using namespace android;
+using namespace aaudio;
+
+ANDROID_SINGLETON_STATIC_INSTANCE(AAudioClientTracker);
+
+AAudioClientTracker::AAudioClientTracker()
+        : Singleton<AAudioClientTracker>() {
+}
+
+// Create a tracker for the client.
+aaudio_result_t AAudioClientTracker::registerClient(pid_t pid,
+                                         const sp<IAAudioClient>& client) {
+    ALOGD("AAudioClientTracker::registerClient(), calling pid = %d, getpid() = %d\n",
+          pid, getpid());
+
+    std::lock_guard<std::mutex> lock(mLock);
+    if (mNotificationClients.count(pid) == 0) {
+        sp<NotificationClient> notificationClient = new NotificationClient(pid);
+        mNotificationClients[pid] = notificationClient;
+
+        sp<IBinder> binder = IInterface::asBinder(client);
+        status_t status = binder->linkToDeath(notificationClient);
+        ALOGW_IF(status != NO_ERROR,
+                 "AAudioClientTracker::registerClient() linkToDeath = %d\n", status);
+        return AAudioConvert_androidToAAudioResult(status);
+    } else {
+        ALOGW("AAudioClientTracker::registerClient(%d) already registered!", pid);
+        return AAUDIO_OK; // TODO should this be considered an error
+    }
+}
+
+void AAudioClientTracker::unregisterClient(pid_t pid) {
+    ALOGD("AAudioClientTracker::unregisterClient(), calling pid = %d, getpid() = %d\n",
+          pid, getpid());
+    std::lock_guard<std::mutex> lock(mLock);
+    mNotificationClients.erase(pid);
+}
+
+aaudio_result_t
+AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
+    aaudio_result_t result = AAUDIO_OK;
+    ALOGV("AAudioClientTracker::registerClientStream(%d, %p)\n", pid, serviceStream.get());
+    std::lock_guard<std::mutex> lock(mLock);
+    sp<NotificationClient> notificationClient = mNotificationClients[pid];
+    if (notificationClient == 0) {
+        // This will get called the first time the audio server registers an internal stream.
+        ALOGV("AAudioClientTracker::registerClientStream(%d,) unrecognized pid\n", pid);
+        notificationClient = new NotificationClient(pid);
+        mNotificationClients[pid] = notificationClient;
+    }
+    notificationClient->registerClientStream(serviceStream);
+    return result;
+}
+
+// Find the tracker for this process and remove it.
+aaudio_result_t
+AAudioClientTracker::unregisterClientStream(pid_t pid,
+                                            sp<AAudioServiceStreamBase> serviceStream) {
+    ALOGV("AAudioClientTracker::unregisterClientStream(%d, %p)\n", pid, serviceStream.get());
+    std::lock_guard<std::mutex> lock(mLock);
+    std::map<pid_t, android::sp<NotificationClient>>::iterator it;
+    it = mNotificationClients.find(pid);
+    if (it != mNotificationClients.end()) {
+        it->second->unregisterClientStream(serviceStream);
+    }
+    return AAUDIO_OK;
+}
+
+AAudioClientTracker::NotificationClient::NotificationClient(pid_t pid)
+        : mProcessId(pid) {
+    //ALOGD("AAudioClientTracker::NotificationClient(%d) created %p\n", pid, this);
+}
+
+AAudioClientTracker::NotificationClient::~NotificationClient() {
+    //ALOGD("AAudioClientTracker::~NotificationClient() destroyed %p\n", this);
+}
+
+aaudio_result_t AAudioClientTracker::NotificationClient::registerClientStream(
+        sp<AAudioServiceStreamBase> serviceStream) {
+    std::lock_guard<std::mutex> lock(mLock);
+    mStreams.insert(serviceStream);
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioClientTracker::NotificationClient::unregisterClientStream(
+        sp<AAudioServiceStreamBase> serviceStream) {
+    std::lock_guard<std::mutex> lock(mLock);
+    mStreams.erase(serviceStream);
+    return AAUDIO_OK;
+}
+
+// Close any open streams for the client.
+void AAudioClientTracker::NotificationClient::binderDied(const wp<IBinder>& who __unused) {
+    AAudioService *aaudioService = AAudioClientTracker::getInstance().getAAudioService();
+    if (aaudioService != nullptr) {
+        // Copy the current list of streams to another vector because closing them below
+        // will cause unregisterClientStream() calls back to this object.
+        std::set<android::sp<AAudioServiceStreamBase>>  streamsToClose;
+
+        {
+            std::lock_guard<std::mutex> lock(mLock);
+            ALOGV("AAudioClientTracker::binderDied() pid = %d, # streams = %d\n",
+                  mProcessId, (int) mStreams.size());
+            for (auto serviceStream : mStreams) {
+                streamsToClose.insert(serviceStream);
+            }
+        }
+
+        for (auto serviceStream : streamsToClose) {
+            aaudio_handle_t handle = serviceStream->getHandle();
+            ALOGW("AAudioClientTracker::binderDied() close abandoned stream 0x%08X\n", handle);
+            aaudioService->closeStream(handle);
+        }
+        // mStreams should be empty now
+    }
+    sp<NotificationClient> keep(this);
+    AAudioClientTracker::getInstance().unregisterClient(mProcessId);
+}
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
new file mode 100644
index 0000000..447665b
--- /dev/null
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -0,0 +1,81 @@
+/*
+ * 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_AAUDIO_AAUDIO_CLIENT_TRACKER_H
+#define ANDROID_AAUDIO_AAUDIO_CLIENT_TRACKER_H
+
+#include <map>
+#include <mutex>
+#include <set>
+
+#include <utils/Singleton.h>
+
+#include <aaudio/AAudio.h>
+#include "binding/IAAudioClient.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+class AAudioClientTracker : public android::Singleton<AAudioClientTracker>{
+public:
+    AAudioClientTracker();
+    ~AAudioClientTracker() = default;
+
+    aaudio_result_t registerClient(pid_t pid, const android::sp<android::IAAudioClient>& client);
+
+    void unregisterClient(pid_t pid);
+
+    aaudio_result_t registerClientStream(pid_t pid,
+                                         android::sp<AAudioServiceStreamBase> serviceStream);
+
+    aaudio_result_t unregisterClientStream(pid_t pid,
+                                           android::sp<AAudioServiceStreamBase> serviceStream);
+
+    android::AAudioService *getAAudioService() const {
+        return mAAudioService;
+    }
+
+    void setAAudioService(android::AAudioService *aaudioService) {
+        mAAudioService = aaudioService;
+    }
+
+private:
+    class NotificationClient : public IBinder::DeathRecipient {
+    public:
+        NotificationClient(pid_t pid);
+        virtual ~NotificationClient();
+
+        aaudio_result_t registerClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
+
+        aaudio_result_t unregisterClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
+
+        // IBinder::DeathRecipient
+        virtual     void    binderDied(const android::wp<IBinder>& who);
+
+    protected:
+        std::mutex                                      mLock;
+        const pid_t                                     mProcessId;
+        std::set<android::sp<AAudioServiceStreamBase>>  mStreams;
+    };
+
+    std::mutex                                       mLock;
+    std::map<pid_t, android::sp<NotificationClient>> mNotificationClients;
+    android::AAudioService                          *mAAudioService = nullptr;
+};
+
+} /* namespace aaudio */
+
+#endif //ANDROID_AAUDIO_AAUDIO_CLIENT_TRACKER_H
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 5f6d599..94577a6 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -69,6 +69,8 @@
 AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService, int32_t deviceId,
                                                            aaudio_direction_t direction) {
     AAudioServiceEndpoint *endpoint = nullptr;
+    AAudioServiceEndpointCapture *capture = nullptr;
+    AAudioServiceEndpointPlay *player = nullptr;
     std::lock_guard<std::mutex> lock(mLock);
 
     // Try to find an existing endpoint.
@@ -83,30 +85,41 @@
             assert(false); // There are only two possible directions.
             break;
     }
+    ALOGD("AAudioEndpointManager::openEndpoint(), found %p for device = %d, dir = %d",
+          endpoint, deviceId, (int)direction);
 
     // If we can't find an existing one then open a new one.
-    if (endpoint != nullptr) {
-        ALOGD("AAudioEndpointManager::openEndpoint(), found %p for device = %d, dir = %d",
-              endpoint, deviceId, (int)direction);
-
-    } else {
-        if (direction == AAUDIO_DIRECTION_INPUT) {
-            AAudioServiceEndpointCapture *capture = new AAudioServiceEndpointCapture(audioService);
-            if (capture->open(deviceId) != AAUDIO_OK) {
-                ALOGE("AAudioEndpointManager::openEndpoint(), open input failed");
-                delete capture;
-            } else {
-                mInputs[deviceId] = capture;
+    if (endpoint == nullptr) {
+        switch(direction) {
+            case AAUDIO_DIRECTION_INPUT:
+                capture = new AAudioServiceEndpointCapture(audioService);
                 endpoint = capture;
-            }
-        } else if (direction == AAUDIO_DIRECTION_OUTPUT) {
-            AAudioServiceEndpointPlay *player = new AAudioServiceEndpointPlay(audioService);
-            if (player->open(deviceId) != AAUDIO_OK) {
-                ALOGE("AAudioEndpointManager::openEndpoint(), open output failed");
-                delete player;
-            } else {
-                mOutputs[deviceId] = player;
+                break;
+            case AAUDIO_DIRECTION_OUTPUT:
+                player = new AAudioServiceEndpointPlay(audioService);
                 endpoint = player;
+                break;
+            default:
+                break;
+        }
+    }
+
+    if (endpoint != nullptr) {
+        aaudio_result_t result = endpoint->open(deviceId);
+        if (result != AAUDIO_OK) {
+            ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
+            delete endpoint;
+            endpoint = nullptr;
+        } else {
+            switch(direction) {
+                case AAUDIO_DIRECTION_INPUT:
+                    mInputs[deviceId] = capture;
+                    break;
+                case AAUDIO_DIRECTION_OUTPUT:
+                    mOutputs[deviceId] = player;
+                    break;
+                default:
+                    break;
             }
         }
         ALOGD("AAudioEndpointManager::openEndpoint(), created %p for device = %d, dir = %d",
@@ -148,6 +161,8 @@
             case AAUDIO_DIRECTION_OUTPUT:
                 mOutputs.erase(deviceId);
                 break;
+            default:
+                break;
         }
 
         serviceEndpoint->close();
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 443f40d..3718bee 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -27,6 +27,7 @@
 #include <utils/String16.h>
 
 #include "binding/AAudioServiceMessage.h"
+#include "AAudioClientTracker.h"
 #include "AAudioEndpointManager.h"
 #include "AAudioService.h"
 #include "AAudioServiceStreamMMAP.h"
@@ -49,6 +50,7 @@
     : BnAAudioService() {
     mCachedProcessId = getpid();
     mCachedUserId = getuid();   // TODO consider using geteuid()
+    AAudioClientTracker::getInstance().setAAudioService(this);
 }
 
 AAudioService::~AAudioService() {
@@ -71,10 +73,15 @@
     return NO_ERROR;
 }
 
+void AAudioService::registerClient(const sp<IAAudioClient>& client) {
+    pid_t pid = IPCThreadState::self()->getCallingPid();
+    AAudioClientTracker::getInstance().registerClient(pid, client);
+}
+
 aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
                                           aaudio::AAudioStreamConfiguration &configurationOutput) {
     aaudio_result_t result = AAUDIO_OK;
-    AAudioServiceStreamBase *serviceStream = nullptr;
+    sp<AAudioServiceStreamBase> serviceStream;
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
     bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
     aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
@@ -89,9 +96,8 @@
         result = serviceStream->open(request, configurationOutput);
         if (result != AAUDIO_OK) {
             // fall back to using a shared stream
-            ALOGD("AAudioService::openStream(), EXCLUSIVE mode failed");
-            delete serviceStream;
-            serviceStream = nullptr;
+            ALOGW("AAudioService::openStream(), could not open in EXCLUSIVE mode");
+            serviceStream.clear();
         } else {
             configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
         }
@@ -106,17 +112,22 @@
     }
 
     if (result != AAUDIO_OK) {
-        delete serviceStream;
-        ALOGE("AAudioService::openStream(): failed, return %d", result);
+        serviceStream.clear();
+        ALOGE("AAudioService::openStream(): failed, return %d = %s",
+              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);
-        ALOGD("AAudioService::openStream(): handle = 0x%08X owned by %d", handle, ownerUserId);
+        aaudio_handle_t handle = mHandleTracker.put(AAUDIO_HANDLE_TYPE_STREAM, serviceStream.get());
         if (handle < 0) {
             ALOGE("AAudioService::openStream(): handle table full");
-            delete serviceStream;
+            serviceStream.clear();
+        } else {
+            ALOGD("AAudioService::openStream(): handle = 0x%08X", handle);
+            serviceStream->setHandle(handle);
+            pid_t pid = request.getProcessId();
+            AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
         }
         return handle;
     }
@@ -126,10 +137,11 @@
     AAudioServiceStreamBase *serviceStream = (AAudioServiceStreamBase *)
             mHandleTracker.remove(AAUDIO_HANDLE_TYPE_STREAM,
                                   streamHandle);
-    ALOGV("AAudioService.closeStream(0x%08X)", streamHandle);
+    ALOGD("AAudioService.closeStream(0x%08X)", streamHandle);
     if (serviceStream != nullptr) {
         serviceStream->close();
-        delete serviceStream;
+        pid_t pid = IPCThreadState::self()->getCallingPid();
+        AAudioClientTracker::getInstance().unregisterClientStream(pid, serviceStream);
         return AAUDIO_OK;
     }
     return AAUDIO_ERROR_INVALID_HANDLE;
@@ -209,8 +221,8 @@
 }
 
 aaudio_result_t AAudioService::registerAudioThread(aaudio_handle_t streamHandle,
-                                                         pid_t clientThreadId,
-                                                         int64_t periodNanoseconds) {
+                                                   pid_t clientThreadId,
+                                                   int64_t periodNanoseconds) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream == nullptr) {
         ALOGE("AAudioService::registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 9b6b342..f84ac4c 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -46,8 +46,10 @@
 
     virtual status_t        dump(int fd, const Vector<String16>& args) override;
 
+    virtual void            registerClient(const sp<IAAudioClient>& client);
+
     virtual aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
-                                     aaudio::AAudioStreamConfiguration &configuration);
+                                     aaudio::AAudioStreamConfiguration &configurationOutput);
 
     virtual aaudio_result_t closeStream(aaudio_handle_t streamHandle);
 
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index cc2cb44..3815711 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -65,20 +65,20 @@
 }
 
 // TODO, maybe use an interface to reduce exposure
-aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
+aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamShared>sharedStream) {
     std::lock_guard<std::mutex> lock(mLockStreams);
     mRegisteredStreams.push_back(sharedStream);
     return AAUDIO_OK;
 }
 
-aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
+aaudio_result_t AAudioServiceEndpoint::unregisterStream(sp<AAudioServiceStreamShared>sharedStream) {
     std::lock_guard<std::mutex> lock(mLockStreams);
     mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
               mRegisteredStreams.end());
     return AAUDIO_OK;
 }
 
-aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
+aaudio_result_t AAudioServiceEndpoint::startStream(sp<AAudioServiceStreamShared> sharedStream) {
     // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
     std::lock_guard<std::mutex> lock(mLockStreams);
     mRunningStreams.push_back(sharedStream);
@@ -88,7 +88,7 @@
     return AAUDIO_OK;
 }
 
-aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
+aaudio_result_t AAudioServiceEndpoint::stopStream(sp<AAudioServiceStreamShared> sharedStream) {
     int numRunningStreams = 0;
     {
         std::lock_guard<std::mutex> lock(mLockStreams);
@@ -130,11 +130,11 @@
 
 void AAudioServiceEndpoint::disconnectRegisteredStreams() {
     std::lock_guard<std::mutex> lock(mLockStreams);
-    for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
-        sharedStream->onStop();
+    for(auto baseStream : mRunningStreams) {
+        baseStream->onStop();
     }
     mRunningStreams.clear();
-    for(AAudioServiceStreamShared *sharedStream : mRegisteredStreams) {
+    for(auto sharedStream : mRegisteredStreams) {
         sharedStream->onDisconnect();
     }
     mRegisteredStreams.clear();
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index c271dbd..090b1dd 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -42,10 +42,10 @@
     int32_t getSamplesPerFrame() const { return mStreamInternal->getSamplesPerFrame();  }
     int32_t getFramesPerBurst() const { return mStreamInternal->getFramesPerBurst();  }
 
-    aaudio_result_t registerStream(AAudioServiceStreamShared *sharedStream);
-    aaudio_result_t unregisterStream(AAudioServiceStreamShared *sharedStream);
-    aaudio_result_t startStream(AAudioServiceStreamShared *sharedStream);
-    aaudio_result_t stopStream(AAudioServiceStreamShared *sharedStream);
+    aaudio_result_t registerStream(android::sp<AAudioServiceStreamShared> sharedStream);
+    aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamShared> sharedStream);
+    aaudio_result_t startStream(android::sp<AAudioServiceStreamShared> sharedStream);
+    aaudio_result_t stopStream(android::sp<AAudioServiceStreamShared> sharedStream);
     aaudio_result_t close();
 
     int32_t getRequestedDeviceId() const { return mRequestedDeviceId; }
@@ -73,8 +73,9 @@
 
     std::mutex               mLockStreams;
 
-    std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
-    std::vector<AAudioServiceStreamShared *> mRunningStreams;
+    std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
+
+    std::vector<android::sp<AAudioServiceStreamShared>> mRunningStreams;
 
 private:
     aaudio_result_t startSharingThread_l();
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 29d6cb9..2bb79bf 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -78,7 +78,7 @@
         // Distribute data to each active stream.
         { // use lock guard
             std::lock_guard <std::mutex> lock(mLockStreams);
-            for (AAudioServiceStreamShared *sharedStream : mRunningStreams) {
+            for (sp<AAudioServiceStreamShared> sharedStream : mRunningStreams) {
                 FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
                 if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
                     getFramesPerBurst()) {
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index cc09cc3..bbe4788 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -79,7 +79,7 @@
         mMixer.clear();
         { // use lock guard
             std::lock_guard <std::mutex> lock(mLockStreams);
-            for (AAudioServiceStreamShared *sharedStream : mRunningStreams) {
+            for (sp<AAudioServiceStreamShared> sharedStream : mRunningStreams) {
                 FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
                 float volume = 0.5; // TODO get from system
                 bool underflowed = mMixer.mix(fifo, volume);
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index ee0e7ed..c415f25 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -42,6 +42,7 @@
 
 AAudioServiceStreamBase::~AAudioServiceStreamBase() {
     close();
+    ALOGD("AAudioServiceStreamBase::~AAudioServiceStreamBase() destroyed %p", this);
 }
 
 aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
@@ -56,6 +57,7 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::close() {
+    stop();
     std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
     delete mUpMessageQueue;
     mUpMessageQueue = nullptr;
@@ -71,28 +73,34 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::pause() {
-    sendCurrentTimestamp();
-    mThreadEnabled.store(false);
-    aaudio_result_t result = mAAudioThread.stop();
-    if (result != AAUDIO_OK) {
-        processFatalError();
-        return result;
+    aaudio_result_t result = AAUDIO_OK;
+    if (isRunning()) {
+        sendCurrentTimestamp();
+        mThreadEnabled.store(false);
+        result = mAAudioThread.stop();
+        if (result != AAUDIO_OK) {
+            processFatalError();
+            return result;
+        }
+        sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
     }
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
     mState = AAUDIO_STREAM_STATE_PAUSED;
     return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::stop() {
-    // TODO wait for data to be played out
-    sendCurrentTimestamp();
-    mThreadEnabled.store(false);
-    aaudio_result_t result = mAAudioThread.stop();
-    if (result != AAUDIO_OK) {
-        processFatalError();
-        return result;
+    aaudio_result_t result = AAUDIO_OK;
+    if (isRunning()) {
+        // TODO wait for data to be played out
+        sendCurrentTimestamp();
+        mThreadEnabled.store(false);
+        result = mAAudioThread.stop();
+        if (result != AAUDIO_OK) {
+            processFatalError();
+            return result;
+        }
+        sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
     }
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
     mState = AAUDIO_STREAM_STATE_STOPPED;
     return result;
 }
@@ -178,4 +186,4 @@
     mUpMessageQueue->fillParcelable(parcelable,
                                     parcelable.mUpMessageQueueParcelable);
     return getDownDataDescription(parcelable);
-}
\ No newline at end of file
+}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index da0b537..d83d5ab 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -20,6 +20,8 @@
 #include <assert.h>
 #include <mutex>
 
+#include <utils/RefBase.h>
+
 #include "fifo/FifoBuffer.h"
 #include "binding/IAAudioService.h"
 #include "binding/AudioEndpointParcelable.h"
@@ -39,7 +41,8 @@
  * Base class for a stream in the AAudio service.
  */
 class AAudioServiceStreamBase
-    : public Runnable  {
+    : public virtual android::RefBase
+    , public Runnable  {
 
 public:
     AAudioServiceStreamBase();
@@ -78,6 +81,9 @@
      */
     virtual aaudio_result_t flush();
 
+    bool isRunning() const {
+        return mState == AAUDIO_STREAM_STATE_STARTED;
+    }
     // -------------------------------------------------------------------
 
     /**
@@ -120,6 +126,13 @@
         mOwnerUserId = uid;
     }
 
+    aaudio_handle_t getHandle() const {
+        return mHandle;
+    }
+    void setHandle(aaudio_handle_t handle) {
+        mHandle = handle;
+    }
+
 protected:
     aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command);
 
@@ -146,6 +159,8 @@
     int32_t            mSampleRate = AAUDIO_UNSPECIFIED;
     int32_t            mCapacityInFrames = AAUDIO_UNSPECIFIED;
     uid_t              mOwnerUserId = -1;
+private:
+    aaudio_handle_t    mHandle = -1;
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 2f3ec27..4083bcc 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -38,7 +38,7 @@
 #define AAUDIO_SAMPLE_RATE_DEFAULT    48000
 
 /**
- * Stream that uses an MMAP buffer.
+ * Service Stream that uses an MMAP buffer.
  */
 
 AAudioServiceStreamMMAP::AAudioServiceStreamMMAP()
@@ -244,7 +244,6 @@
     return AAudioServiceStreamBase::flush();;
 }
 
-
 aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
     struct audio_mmap_position position;
@@ -266,7 +265,7 @@
 }
 
 void AAudioServiceStreamMMAP::onTearDown() {
-    ALOGE("AAudioServiceStreamMMAP::onTearDown() called - TODO");
+    ALOGD("AAudioServiceStreamMMAP::onTearDown() called"); // TODO what is needed here?
 };
 
 void AAudioServiceStreamMMAP::onVolumeChanged(audio_channel_mask_t channels,
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index f246fc02..7515ea7 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -52,7 +52,7 @@
                                                            int32_t framesPerBurst) {
 
     if (requestedCapacityFrames > MAX_FRAMES_PER_BUFFER) {
-        ALOGE("AAudioServiceStreamShared::open(), requested capacity %d > max %d",
+        ALOGE("AAudioServiceStreamShared::calculateBufferCapacity() requested capacity %d > max %d",
               requestedCapacityFrames, MAX_FRAMES_PER_BUFFER);
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
@@ -77,7 +77,7 @@
     }
     // Check for numeric overflow.
     if (numBursts > 0x8000 || framesPerBurst > 0x8000) {
-        ALOGE("AAudioServiceStreamShared::open(), numeric overflow, capacity = %d * %d",
+        ALOGE("AAudioServiceStreamShared::calculateBufferCapacity() overflow, capacity = %d * %d",
               numBursts, framesPerBurst);
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
@@ -85,11 +85,11 @@
 
     // Final sanity check.
     if (capacityInFrames > MAX_FRAMES_PER_BUFFER) {
-        ALOGE("AAudioServiceStreamShared::open(), calculated capacity %d > max %d",
+        ALOGE("AAudioServiceStreamShared::calculateBufferCapacity() calc capacity %d > max %d",
               capacityInFrames, MAX_FRAMES_PER_BUFFER);
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
-    ALOGD("AAudioServiceStreamShared::open(), requested capacity = %d frames, actual = %d",
+    ALOGD("AAudioServiceStreamShared::calculateBufferCapacity() requested %d frames, actual = %d",
           requestedCapacityFrames, capacityInFrames);
     return capacityInFrames;
 }
@@ -97,6 +97,8 @@
 aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request,
                      aaudio::AAudioStreamConfiguration &configurationOutput)  {
 
+    sp<AAudioServiceStreamShared> keep(this);
+
     aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamBase open() returned %d", result);
@@ -110,7 +112,7 @@
     AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
     mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, deviceId, direction);
     if (mServiceEndpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
+        ALOGE("AAudioServiceStreamShared::open() mServiceEndPoint = %p", mServiceEndpoint);
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 
@@ -119,7 +121,7 @@
     if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
         mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
     } else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
-        ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need FLOAT", mAudioFormat);
+        ALOGE("AAudioServiceStreamShared::open() mAudioFormat = %d, need FLOAT", mAudioFormat);
         result = AAUDIO_ERROR_INVALID_FORMAT;
         goto error;
     }
@@ -128,7 +130,7 @@
     if (mSampleRate == AAUDIO_UNSPECIFIED) {
         mSampleRate = mServiceEndpoint->getSampleRate();
     } else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
-        ALOGE("AAudioServiceStreamShared::open(), mSampleRate = %d, need %d",
+        ALOGE("AAudioServiceStreamShared::open() mSampleRate = %d, need %d",
               mSampleRate, mServiceEndpoint->getSampleRate());
         result = AAUDIO_ERROR_INVALID_RATE;
         goto error;
@@ -138,14 +140,14 @@
     if (mSamplesPerFrame == AAUDIO_UNSPECIFIED) {
         mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
     } else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
-        ALOGE("AAudioServiceStreamShared::open(), mSamplesPerFrame = %d, need %d",
+        ALOGE("AAudioServiceStreamShared::open() mSamplesPerFrame = %d, need %d",
               mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
         result = AAUDIO_ERROR_OUT_OF_RANGE;
         goto error;
     }
 
     mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
-    ALOGD("AAudioServiceStreamShared::open(), mSampleRate = %d, mFramesPerBurst = %d",
+    ALOGD("AAudioServiceStreamShared::open() mSampleRate = %d, mFramesPerBurst = %d",
           mSampleRate, mFramesPerBurst);
 
     mCapacityInFrames = calculateBufferCapacity(configurationInput.getBufferCapacity(),
@@ -160,7 +162,7 @@
     mAudioDataQueue = new SharedRingBuffer();
     result = mAudioDataQueue->allocate(calculateBytesPerFrame(), mCapacityInFrames);
     if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamShared::open(), could not allocate FIFO with %d frames",
+        ALOGE("AAudioServiceStreamShared::open() could not allocate FIFO with %d frames",
               mCapacityInFrames);
         result = AAUDIO_ERROR_NO_MEMORY;
         goto error;
@@ -175,7 +177,7 @@
     configurationOutput.setAudioFormat(mAudioFormat);
     configurationOutput.setDeviceId(mServiceEndpoint->getDeviceId());
 
-    result = mServiceEndpoint->registerStream(this);
+    result = mServiceEndpoint->registerStream(keep);
     if (result != AAUDIO_OK) {
         goto error;
     }
@@ -263,7 +265,8 @@
     // TODO wait for pause() to synchronize
     AAudioServiceEndpoint *endpoint = mServiceEndpoint;
     if (endpoint != nullptr) {
-        endpoint->unregisterStream(this);
+        sp<AAudioServiceStreamShared> keep(this);
+        endpoint->unregisterStream(keep);
 
         AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
         mEndpointManager.closeEndpoint(endpoint);
@@ -292,8 +295,7 @@
 }
 
 void AAudioServiceStreamShared::onDisconnect() {
-    mServiceEndpoint->close();
-    mServiceEndpoint = nullptr;
+    processFatalError();
 }
 
 void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index 12f7e04..a896a7a 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -25,6 +25,7 @@
     $(LIBAAUDIO_SRC_DIR)/utility/HandleTracker.cpp \
     SharedMemoryProxy.cpp \
     SharedRingBuffer.cpp \
+    AAudioClientTracker.cpp \
     AAudioEndpointManager.cpp \
     AAudioMixer.cpp \
     AAudioService.cpp \