aaudio: limit number of streams per process

Testing the new max streams restriction revealed the bug
involving the second shared stream.

Bug: 62951298
Bug: 63171495
Test: test_n_streams.cpp can open MAX_STREAMS_PER_PROCESS MMAP streams
Change-Id: Ibea7d9c4716326a37c669954b52f397ed2968caa
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index bd7169f..e4eef06 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -69,3 +69,13 @@
 LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
 LOCAL_MODULE := test_aaudio_recovery
 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_n_streams.cpp
+LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
+LOCAL_MODULE := test_n_streams
+include $(BUILD_NATIVE_TEST)
diff --git a/media/libaaudio/tests/test_n_streams.cpp b/media/libaaudio/tests/test_n_streams.cpp
new file mode 100644
index 0000000..bf4b64e
--- /dev/null
+++ b/media/libaaudio/tests/test_n_streams.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 as many streams as possible and report the maximum.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
+
+//#define MMAP_POLICY              AAUDIO_UNSPECIFIED
+//#define MMAP_POLICY              AAUDIO_POLICY_NEVER
+#define MMAP_POLICY              AAUDIO_POLICY_AUTO
+//#define MMAP_POLICY              AAUDIO_POLICY_ALWAYS
+
+#define MAX_STREAMS   100
+
+int main(int argc, char **argv)
+{
+    (void)argc; // unused
+    (void)argv; // unused
+
+    aaudio_result_t result = AAUDIO_OK;
+    AAudioStreamBuilder *aaudioBuilder = nullptr;
+    AAudioStream *aaudioStream[MAX_STREAMS];
+    int32_t numStreams = 0;
+
+    // Make printf print immediately so that debug info is not stuck
+    // in a buffer if we hang or crash.
+    setvbuf(stdout, NULL, _IONBF, (size_t) 0);
+
+    printf("Try to open a maximum of %d streams.\n", MAX_STREAMS);
+
+    AAudio_setMMapPolicy(MMAP_POLICY);
+    printf("requested MMapPolicy = %d\n", AAudio_getMMapPolicy());
+
+    result = AAudio_createStreamBuilder(&aaudioBuilder);
+    if (result != AAUDIO_OK) {
+        return 1;
+    }
+
+    for (int i = 0; i < MAX_STREAMS; i++) {
+        // Create an AAudioStream using the Builder.
+        result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream[i]);
+        if (result != AAUDIO_OK) {
+            printf("ERROR could not open AAudio stream, %d %s\n",
+                   result, AAudio_convertResultToText(result));
+            break;
+        } else {
+            printf("AAudio stream[%2d] opened successfully. MMAP = %s\n",
+                   i, AAudioStream_isMMapUsed(aaudioStream[i]) ? "YES" : "NO");
+            numStreams++;
+        }
+    }
+
+    printf("Created %d streams!\n", numStreams);
+
+    // Close all the streams.
+    for (int i = 0; i < numStreams; i++) {
+        result = AAudioStream_close(aaudioStream[i]);
+        if (result != AAUDIO_OK) {
+            printf("ERROR could not close AAudio stream, %d %s\n",
+                   result, AAudio_convertResultToText(result));
+            break;
+        } else {
+            printf("AAudio stream[%2d] closed successfully.\n", i);
+        }
+    }
+
+    AAudioStreamBuilder_delete(aaudioBuilder);
+
+finish:
+    return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index da7b80f..c0dfc54 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -68,6 +68,16 @@
     mNotificationClients.erase(pid);
 }
 
+int32_t AAudioClientTracker::getStreamCount(pid_t pid) {
+    std::lock_guard<std::mutex> lock(mLock);
+    auto it = mNotificationClients.find(pid);
+    if (it != mNotificationClients.end()) {
+        return it->second->getStreamCount();
+    } else {
+        return 0; // no existing client
+    }
+}
+
 aaudio_result_t
 AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
     aaudio_result_t result = AAUDIO_OK;
@@ -90,8 +100,7 @@
                                             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);
+    auto it = mNotificationClients.find(pid);
     if (it != mNotificationClients.end()) {
         it->second->unregisterClientStream(serviceStream);
     }
@@ -107,6 +116,11 @@
     //ALOGD("AAudioClientTracker::~NotificationClient() destroyed %p\n", this);
 }
 
+int32_t AAudioClientTracker::NotificationClient::getStreamCount() {
+    std::lock_guard<std::mutex> lock(mLock);
+    return mStreams.size();
+}
+
 aaudio_result_t AAudioClientTracker::NotificationClient::registerClientStream(
         sp<AAudioServiceStreamBase> serviceStream) {
     std::lock_guard<std::mutex> lock(mLock);
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
index 447665b..e74c8bf 100644
--- a/services/oboeservice/AAudioClientTracker.h
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -38,6 +38,8 @@
 
     void unregisterClient(pid_t pid);
 
+    int32_t getStreamCount(pid_t pid);
+
     aaudio_result_t registerClientStream(pid_t pid,
                                          android::sp<AAudioServiceStreamBase> serviceStream);
 
@@ -53,11 +55,17 @@
     }
 
 private:
+
+    /**
+     * One per process.
+     */
     class NotificationClient : public IBinder::DeathRecipient {
     public:
         NotificationClient(pid_t pid);
         virtual ~NotificationClient();
 
+        int32_t getStreamCount();
+
         aaudio_result_t registerClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
 
         aaudio_result_t unregisterClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index d8b9101..36e678a 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -115,24 +115,24 @@
             default:
                 break;
         }
-    }
 
-    if (endpoint != nullptr) {
-        aaudio_result_t result = endpoint->open(configuration);
-        if (result != AAUDIO_OK) {
-            ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
-            delete endpoint;
-            endpoint = nullptr;
-        } else {
-            switch(direction) {
-                case AAUDIO_DIRECTION_INPUT:
-                    mInputs.push_back(capture);
-                    break;
-                case AAUDIO_DIRECTION_OUTPUT:
-                    mOutputs.push_back(player);
-                    break;
-                default:
-                    break;
+        if (endpoint != nullptr) {
+            aaudio_result_t result = endpoint->open(configuration);
+            if (result != AAUDIO_OK) {
+                ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
+                delete endpoint;
+                endpoint = nullptr;
+            } else {
+                switch(direction) {
+                    case AAUDIO_DIRECTION_INPUT:
+                        mInputs.push_back(capture);
+                        break;
+                    case AAUDIO_DIRECTION_OUTPUT:
+                        mOutputs.push_back(player);
+                        break;
+                    default:
+                        break;
+                }
             }
         }
         ALOGD("AAudioEndpointManager::openEndpoint(), created %p for device = %d, dir = %d",
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 585341a..91f7885 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -40,6 +40,8 @@
 using namespace android;
 using namespace aaudio;
 
+#define MAX_STREAMS_PER_PROCESS   8
+
 typedef enum
 {
     AAUDIO_HANDLE_TYPE_STREAM
@@ -86,6 +88,17 @@
     bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
     aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
 
+    // Enforce limit on client processes.
+    pid_t pid = request.getProcessId();
+    if (pid != mCachedProcessId) {
+        int32_t count = AAudioClientTracker::getInstance().getStreamCount(pid);
+        if (count >= MAX_STREAMS_PER_PROCESS) {
+            ALOGE("AAudioService::openStream(): exceeded max streams per process %d >= %d",
+                  count,  MAX_STREAMS_PER_PROCESS);
+            return AAUDIO_ERROR_UNAVAILABLE;
+        }
+    }
+
     if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
         ALOGE("AAudioService::openStream(): unrecognized sharing mode = %d", sharingMode);
         return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
@@ -135,7 +148,14 @@
 }
 
 aaudio_result_t AAudioService::closeStream(aaudio_handle_t streamHandle) {
-    AAudioServiceStreamBase *serviceStream = (AAudioServiceStreamBase *)
+    // Check permission first.
+    AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    if (serviceStream == nullptr) {
+        ALOGE("AAudioService::startStream(), illegal stream handle = 0x%0x", streamHandle);
+        return AAUDIO_ERROR_INVALID_HANDLE;
+    }
+
+    serviceStream = (AAudioServiceStreamBase *)
             mHandleTracker.remove(AAUDIO_HANDLE_TYPE_STREAM,
                                   streamHandle);
     ALOGD("AAudioService.closeStream(0x%08X)", streamHandle);