1 /*
2 * Copyright 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "AAudioTest"
18
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #include <string>
24
25 #include <android/log.h>
26 #include <gtest/gtest.h>
27
28 #include "test_aaudio.h"
29 #include "utils.h"
30
getNanoseconds(clockid_t clockId)31 int64_t getNanoseconds(clockid_t clockId) {
32 struct timespec time;
33 int result = clock_gettime(clockId, &time);
34 if (result < 0) {
35 return -errno;
36 }
37 return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
38 }
39
performanceModeToString(aaudio_performance_mode_t mode)40 const char* performanceModeToString(aaudio_performance_mode_t mode) {
41 switch (mode) {
42 case AAUDIO_PERFORMANCE_MODE_NONE: return "DEFAULT";
43 case AAUDIO_PERFORMANCE_MODE_POWER_SAVING: return "POWER_SAVING";
44 case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY: return "LOW_LATENCY";
45 }
46 return "UNKNOWN";
47 }
48
sharingModeToString(aaudio_sharing_mode_t mode)49 const char* sharingModeToString(aaudio_sharing_mode_t mode) {
50 switch (mode) {
51 case AAUDIO_SHARING_MODE_SHARED: return "SHARED";
52 case AAUDIO_SHARING_MODE_EXCLUSIVE: return "EXCLUSIVE";
53 }
54 return "UNKNOWN";
55 }
56
57 // Runs "pm list features" and attempts to find the specified feature in its output.
deviceSupportsFeature(const char * feature)58 bool deviceSupportsFeature(const char* feature) {
59 bool hasFeature = false;
60 FILE *p = popen("/system/bin/pm list features", "re");
61 if (p) {
62 char* line = NULL;
63 size_t len = 0;
64 while (getline(&line, &len, p) > 0) {
65 if (strstr(line, feature)) {
66 hasFeature = true;
67 break;
68 }
69 }
70 pclose(p);
71 } else {
72 __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "popen failed: %d", errno);
73 _exit(EXIT_FAILURE);
74 }
75 __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Feature %s: %ssupported",
76 feature, hasFeature ? "" : "not ");
77 return hasFeature;
78 }
79
80 // These periods are quite generous. They are not designed to put
81 // any restrictions on the implementation, but only to ensure sanity.
82 // Use int64_t because 96000 * 30000 is close to int32_t limits.
83 const std::map<aaudio_performance_mode_t, int64_t> StreamBuilderHelper::sMaxFramesPerBurstMs =
84 { { AAUDIO_PERFORMANCE_MODE_NONE, 128 },
85 { AAUDIO_PERFORMANCE_MODE_POWER_SAVING, 30 * 1000 },
86 { AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, 40 } };
87
StreamBuilderHelper(aaudio_direction_t direction,int32_t sampleRate,int32_t channelCount,aaudio_format_t dataFormat,aaudio_sharing_mode_t sharingMode,aaudio_performance_mode_t perfMode)88 StreamBuilderHelper::StreamBuilderHelper(
89 aaudio_direction_t direction, int32_t sampleRate,
90 int32_t channelCount, aaudio_format_t dataFormat,
91 aaudio_sharing_mode_t sharingMode, aaudio_performance_mode_t perfMode)
92 : mDirection{direction},
93 mRequested{sampleRate, channelCount, dataFormat, sharingMode, perfMode},
94 mActual{0, 0, AAUDIO_FORMAT_INVALID, -1, -1}, mFramesPerBurst{-1},
95 mBuilder{nullptr}, mStream{nullptr} {}
96
~StreamBuilderHelper()97 StreamBuilderHelper::~StreamBuilderHelper() {
98 close();
99 }
100
initBuilder()101 void StreamBuilderHelper::initBuilder() {
102 ASSERT_EQ(1U, sMaxFramesPerBurstMs.count(mRequested.perfMode));
103
104 // Use an AAudioStreamBuilder to define the stream.
105 aaudio_result_t result = AAudio_createStreamBuilder(&mBuilder);
106 ASSERT_EQ(AAUDIO_OK, result);
107 ASSERT_TRUE(mBuilder != nullptr);
108
109 // Request stream properties.
110 AAudioStreamBuilder_setDeviceId(mBuilder, AAUDIO_UNSPECIFIED);
111 AAudioStreamBuilder_setDirection(mBuilder, mDirection);
112 AAudioStreamBuilder_setSampleRate(mBuilder, mRequested.sampleRate);
113 AAudioStreamBuilder_setChannelCount(mBuilder, mRequested.channelCount);
114 AAudioStreamBuilder_setFormat(mBuilder, mRequested.dataFormat);
115 AAudioStreamBuilder_setSharingMode(mBuilder, mRequested.sharingMode);
116 AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequested.perfMode);
117 }
118
119 // Needs to be a 'void' function due to ASSERT requirements.
createAndVerifyStream(bool * success)120 void StreamBuilderHelper::createAndVerifyStream(bool *success) {
121 *success = false;
122
123 aaudio_result_t result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
124 if (mRequested.sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE && result != AAUDIO_OK) {
125 __android_log_write(ANDROID_LOG_WARN, LOG_TAG, "Could not open a stream in EXCLUSIVE mode");
126 return;
127 }
128 ASSERT_EQ(AAUDIO_OK, result);
129 ASSERT_TRUE(mStream != nullptr);
130 ASSERT_EQ(AAUDIO_STREAM_STATE_OPEN, AAudioStream_getState(mStream));
131 ASSERT_EQ(mDirection, AAudioStream_getDirection(mStream));
132
133 mActual.sharingMode = AAudioStream_getSharingMode(mStream);
134 if (mActual.sharingMode != mRequested.sharingMode) {
135 // Since we are covering all possible values, the "actual" mode
136 // will also be tested, so no need to run the same test twice.
137 __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Sharing mode %s is not available",
138 sharingModeToString(mRequested.sharingMode));
139 return;
140 }
141
142 // Check to see what kind of stream we actually got.
143 mActual.sampleRate = AAudioStream_getSampleRate(mStream);
144 ASSERT_GE(mActual.sampleRate, 44100);
145 ASSERT_LE(mActual.sampleRate, 96000); // TODO what is min/max?
146
147 mActual.channelCount = AAudioStream_getChannelCount(mStream);
148 ASSERT_GE(mActual.channelCount, 1);
149 ASSERT_LE(mActual.channelCount, 16); // TODO what is min/max?
150
151 mActual.dataFormat = AAudioStream_getFormat(mStream);
152 if (mRequested.dataFormat != AAUDIO_FORMAT_UNSPECIFIED) {
153 ASSERT_EQ(mRequested.dataFormat, mActual.dataFormat);
154 }
155
156 mActual.perfMode = AAudioStream_getPerformanceMode(mStream);
157 if (mRequested.perfMode != AAUDIO_PERFORMANCE_MODE_NONE
158 && mRequested.perfMode != mActual.perfMode) {
159 // Since we are covering all possible values, the "actual" mode
160 // will also be tested, so no need to run the same test twice.
161 __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Performance mode %s is not available",
162 performanceModeToString(mRequested.sharingMode));
163 return;
164 }
165
166 mFramesPerBurst = AAudioStream_getFramesPerBurst(mStream);
167 ASSERT_GE(mFramesPerBurst, 16);
168 const int32_t maxFramesPerBurst =
169 mActual.sampleRate * sMaxFramesPerBurstMs.at(mActual.perfMode) / MILLIS_PER_SECOND;
170 ASSERT_LE(mFramesPerBurst, maxFramesPerBurst);
171
172 int32_t actualBufferSize = AAudioStream_getBufferSizeInFrames(mStream);
173 ASSERT_GT(actualBufferSize, 0);
174 ASSERT_GT(AAudioStream_setBufferSizeInFrames(mStream, actualBufferSize), 0);
175
176 *success = true;
177 }
178
close()179 void StreamBuilderHelper::close() {
180 if (mBuilder != nullptr) {
181 ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(mBuilder));
182 }
183 if (mStream != nullptr) {
184 ASSERT_EQ(AAUDIO_OK, AAudioStream_close(mStream));
185 }
186 }
187
streamCommand(StreamCommand cmd,aaudio_stream_state_t fromState,aaudio_stream_state_t toState)188 void StreamBuilderHelper::streamCommand(
189 StreamCommand cmd, aaudio_stream_state_t fromState, aaudio_stream_state_t toState) {
190 ASSERT_EQ(AAUDIO_OK, cmd(mStream));
191 aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
192 ASSERT_EQ(AAUDIO_OK,
193 AAudioStream_waitForStateChange(mStream, fromState, &state, DEFAULT_STATE_TIMEOUT));
194 ASSERT_EQ(toState, state);
195 }
196
InputStreamBuilderHelper(aaudio_sharing_mode_t requestedSharingMode,aaudio_performance_mode_t requestedPerfMode,aaudio_format_t requestedFormat)197 InputStreamBuilderHelper::InputStreamBuilderHelper(
198 aaudio_sharing_mode_t requestedSharingMode,
199 aaudio_performance_mode_t requestedPerfMode,
200 aaudio_format_t requestedFormat)
201 : StreamBuilderHelper{AAUDIO_DIRECTION_INPUT,
202 48000, 1, requestedFormat, requestedSharingMode, requestedPerfMode} {}
203
204
OutputStreamBuilderHelper(aaudio_sharing_mode_t requestedSharingMode,aaudio_performance_mode_t requestedPerfMode,aaudio_format_t requestedFormat)205 OutputStreamBuilderHelper::OutputStreamBuilderHelper(
206 aaudio_sharing_mode_t requestedSharingMode,
207 aaudio_performance_mode_t requestedPerfMode,
208 aaudio_format_t requestedFormat)
209 : StreamBuilderHelper{AAUDIO_DIRECTION_OUTPUT,
210 48000, 2, requestedFormat, requestedSharingMode, requestedPerfMode} {}
211
initBuilder()212 void OutputStreamBuilderHelper::initBuilder() {
213 StreamBuilderHelper::initBuilder();
214 AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, kBufferCapacityFrames);
215 }
216
createAndVerifyStream(bool * success)217 void OutputStreamBuilderHelper::createAndVerifyStream(bool *success) {
218 StreamBuilderHelper::createAndVerifyStream(success);
219 if (*success) {
220 ASSERT_GE(AAudioStream_getBufferCapacityInFrames(mStream), kBufferCapacityFrames);
221 }
222 }
223
AAudioExtensions()224 AAudioExtensions::AAudioExtensions()
225 : mMMapSupported(isPolicyEnabled(getMMapPolicyProperty()))
226 , mMMapExclusiveSupported(isPolicyEnabled(getIntegerProperty(
227 "aaudio.mmap_exclusive_policy", AAUDIO_POLICY_UNSPECIFIED))) {
228 loadLibrary();
229 }
230
getIntegerProperty(const char * name,int defaultValue)231 int AAudioExtensions::getIntegerProperty(const char *name, int defaultValue) {
232 int result = defaultValue;
233 char valueText[PROP_VALUE_MAX] = {0};
234 if (__system_property_get(name, valueText) != 0) {
235 result = atoi(valueText);
236 }
237 return result;
238 }
239
240 // This should only be called once from the constructor.
loadLibrary()241 bool AAudioExtensions::loadLibrary() {
242 mLibHandle = dlopen(LIB_AAUDIO_NAME, 0);
243 if (mLibHandle == nullptr) {
244 //LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__);
245 return false;
246 }
247
248 mAAudioStream_isMMap = (bool (*)(AAudioStream *stream))
249 dlsym(mLibHandle, FUNCTION_IS_MMAP);
250 if (mAAudioStream_isMMap == nullptr) {
251 //LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__);
252 return false;
253 }
254
255 mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy))
256 dlsym(mLibHandle, FUNCTION_SET_MMAP_POLICY);
257 if (mAAudio_setMMapPolicy == nullptr) {
258 //LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__);
259 return false;
260 }
261
262 mAAudio_getMMapPolicy = (aaudio_policy_t (*)())
263 dlsym(mLibHandle, FUNCTION_GET_MMAP_POLICY);
264 if (mAAudio_getMMapPolicy == nullptr) {
265 //LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__);
266 return false;
267 }
268 mFunctionsLoaded = true;
269 return mFunctionsLoaded;
270 }