cts: ensure AAudio MMAP cannot be turned on by app

MMAP can only be enabled by the system.
The app can then turn it off if needed for testing.

Bug: 65280854
Bug: 65262790
Test: this is a test
Merged-In: Ia094306d060013f3a233b3eda6242ea85e63f39d
Change-Id: Ia094306d060013f3a233b3eda6242ea85e63f39d
diff --git a/tests/tests/nativemedia/aaudio/Android.mk b/tests/tests/nativemedia/aaudio/Android.mk
index f1fc7fd..d69f66e 100644
--- a/tests/tests/nativemedia/aaudio/Android.mk
+++ b/tests/tests/nativemedia/aaudio/Android.mk
@@ -25,8 +25,9 @@
 
 LOCAL_SRC_FILES := \
     src/test_aaudio.cpp \
-    src/test_aaudio_misc.cpp \
     src/test_aaudio_callback.cpp \
+    src/test_aaudio_misc.cpp \
+    src/test_aaudio_mmap.cpp \
     src/test_aaudio_stream_builder.cpp \
     src/utils.cpp \
 
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_mmap.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio_mmap.cpp
new file mode 100644
index 0000000..6e6dc2f
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio_mmap.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "AAudioTest"
+
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <sys/system_properties.h>
+
+#include "test_aaudio.h"
+#include "utils.h"
+
+/* These definitions are from aaudio/AAudioTesting.h */
+enum {
+    AAUDIO_POLICY_NEVER = 1,
+    AAUDIO_POLICY_AUTO,
+    AAUDIO_POLICY_ALWAYS
+};
+typedef int32_t aaudio_policy_t;
+
+static aaudio_result_t (*s_setMMapPolicy)(aaudio_policy_t policy) = nullptr;
+static aaudio_policy_t (*s_getMMapPolicy)() = nullptr;
+
+/**
+ * @return integer value or -1 on error
+ */
+static int getSystemPropertyInt(const char *propName, int defaultValue) {
+    char valueText[PROP_VALUE_MAX] = {'\0'};
+    if (__system_property_get(propName, valueText) <= 0) {
+        return defaultValue;
+    }
+    char *endptr = nullptr;
+    int value = strtol(valueText, &endptr, 10);
+    if (endptr == nullptr || *endptr != '\0') {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "getSystemPropertyInt() - non-integer value = %s", valueText);
+        return -1;
+    } else {
+        return value;
+    }
+}
+
+static int getSystemMMapPolicy() {
+    return getSystemPropertyInt("aaudio.mmap_policy", AAUDIO_UNSPECIFIED);
+}
+
+// Test allowed values of policy.
+TEST(test_aaudio_mmap, testCurrentPolicy) {
+    aaudio_policy_t policy = getSystemMMapPolicy();
+
+    // It must be one of these defined enum values.
+    EXPECT_TRUE(policy == AAUDIO_UNSPECIFIED
+        || policy == AAUDIO_POLICY_NEVER
+        || policy == AAUDIO_POLICY_AUTO
+        || policy == AAUDIO_POLICY_ALWAYS);
+    // But some defined values are not allowed in some versions of the SDK.
+
+    // AAUDIO_POLICY_ALWAYS is only for testing during development.
+    // It forces MMAP mode for all streams, which will fail for some stream settings.
+    EXPECT_NE(AAUDIO_POLICY_ALWAYS, policy);
+
+    // TODO This part should be removed in OC-MR1 or later.
+    // MMAP should not be enabled before OC-MR1
+    EXPECT_NE(AAUDIO_POLICY_AUTO, policy);
+}
+
+// Link to test functions in shared library.
+static void loadMMapTestFunctions() {
+    if (s_setMMapPolicy != nullptr) return; // already loaded
+
+    void *handle;
+    handle = dlopen("libaaudio.so", RTLD_NOW);
+    EXPECT_NE(nullptr, handle);
+    s_setMMapPolicy = (int (*)(int)) dlsym(handle, "AAudio_setMMapPolicy");
+    EXPECT_NE(nullptr, s_setMMapPolicy);
+    s_getMMapPolicy = (int (*)()) dlsym(handle, "AAudio_getMMapPolicy");
+    EXPECT_NE(nullptr, s_getMMapPolicy);
+}
+
+// An application should not be able to create an MMAP stream
+// by enabling MMAP when the system "aaudio.mmap_policy" says not to.
+TEST(test_aaudio_mmap, testElevatingMMapPolicy) {
+    aaudio_result_t result = AAUDIO_OK;
+    AAudioStreamBuilder *builder = nullptr;
+    AAudioStream *stream = nullptr;
+
+    aaudio_policy_t policy = getSystemMMapPolicy();
+    bool mmapAllowed = (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS);
+    if (mmapAllowed) return;
+    // Try to enable MMAP when not allowed.
+
+    loadMMapTestFunctions();
+
+    EXPECT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&builder));
+
+    // LOW_LATENCY required for MMAP
+    AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
+    // Force policy to create an MMAP stream or fail.
+    aaudio_policy_t originalPolicy = s_getMMapPolicy();
+    s_setMMapPolicy(AAUDIO_POLICY_ALWAYS); // try to enable MMAP mode
+    result = AAudioStreamBuilder_openStream(builder, &stream);
+    s_setMMapPolicy(originalPolicy);
+
+    // openStream should have failed.
+    EXPECT_NE(AAUDIO_OK, result);
+
+    // These should not crash if NULL.
+    AAudioStream_close(stream);
+    AAudioStreamBuilder_delete(builder);
+}