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/binder_ibinder_jni.h>
26 #include <android/binder_status.h>
27 #include <android/log.h>
28 #include <gtest/gtest.h>
29 #include <nativetesthelper_jni/utils.h>
30
31 #include "test_aaudio.h"
32 #include "utils.h"
33
34 using ::ndk::SpAIBinder;
35 using ::ndk::ScopedAIBinder_DeathRecipient;
36
getNanoseconds(clockid_t clockId)37 int64_t getNanoseconds(clockid_t clockId) {
38 struct timespec time;
39 int result = clock_gettime(clockId, &time);
40 if (result < 0) {
41 return -errno;
42 }
43 return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
44 }
45
performanceModeToString(aaudio_performance_mode_t mode)46 const char* performanceModeToString(aaudio_performance_mode_t mode) {
47 switch (mode) {
48 case AAUDIO_PERFORMANCE_MODE_NONE: return "DEFAULT";
49 case AAUDIO_PERFORMANCE_MODE_POWER_SAVING: return "POWER_SAVING";
50 case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY: return "LOW_LATENCY";
51 }
52 return "UNKNOWN";
53 }
54
sharingModeToString(aaudio_sharing_mode_t mode)55 const char* sharingModeToString(aaudio_sharing_mode_t mode) {
56 switch (mode) {
57 case AAUDIO_SHARING_MODE_SHARED: return "SHARED";
58 case AAUDIO_SHARING_MODE_EXCLUSIVE: return "EXCLUSIVE";
59 }
60 return "UNKNOWN";
61 }
62
63 // Runs "pm list features" and attempts to find the specified feature in its output.
deviceSupportsFeature(const char * feature)64 bool deviceSupportsFeature(const char* feature) {
65 bool hasFeature = false;
66 FILE *p = popen("/system/bin/pm list features", "re");
67 if (p) {
68 char* line = NULL;
69 size_t len = 0;
70 while (getline(&line, &len, p) > 0) {
71 if (strstr(line, feature)) {
72 hasFeature = true;
73 break;
74 }
75 }
76 pclose(p);
77 } else {
78 __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "popen failed: %d", errno);
79 _exit(EXIT_FAILURE);
80 }
81 __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Feature %s: %ssupported",
82 feature, hasFeature ? "" : "not ");
83 return hasFeature;
84 }
85
86 // These periods are quite generous. They are not designed to put
87 // any restrictions on the implementation, but only to ensure sanity.
88 // Use int64_t because 96000 * 30000 is close to int32_t limits.
89 const std::map<aaudio_performance_mode_t, int64_t> StreamBuilderHelper::sMaxFramesPerBurstMs =
90 { { AAUDIO_PERFORMANCE_MODE_NONE, 128 },
91 { AAUDIO_PERFORMANCE_MODE_POWER_SAVING, 30 * 1000 },
92 { AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, 40 } };
93
94 const std::unordered_set<aaudio_format_t> StreamBuilderHelper::sValidStreamFormats =
95 {AAUDIO_FORMAT_PCM_I16, AAUDIO_FORMAT_PCM_FLOAT, AAUDIO_FORMAT_PCM_I24_PACKED,
96 AAUDIO_FORMAT_PCM_I32, AAUDIO_FORMAT_IEC61937};
97
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)98 StreamBuilderHelper::StreamBuilderHelper(
99 aaudio_direction_t direction, int32_t sampleRate,
100 int32_t channelCount, aaudio_format_t dataFormat,
101 aaudio_sharing_mode_t sharingMode, aaudio_performance_mode_t perfMode)
102 : mDirection{direction},
103 mRequested{sampleRate, channelCount, dataFormat, sharingMode, perfMode},
104 mActual{0, 0, AAUDIO_FORMAT_INVALID, -1, -1}, mFramesPerBurst{-1},
105 mBuilder{nullptr}, mStream{nullptr} {}
106
~StreamBuilderHelper()107 StreamBuilderHelper::~StreamBuilderHelper() {
108 close();
109 }
110
initBuilder()111 void StreamBuilderHelper::initBuilder() {
112 ASSERT_EQ(1U, sMaxFramesPerBurstMs.count(mRequested.perfMode));
113
114 // Use an AAudioStreamBuilder to define the stream.
115 aaudio_result_t result = AAudio_createStreamBuilder(&mBuilder);
116 ASSERT_EQ(AAUDIO_OK, result);
117 ASSERT_TRUE(mBuilder != nullptr);
118
119 // Request stream properties.
120 AAudioStreamBuilder_setDeviceId(mBuilder, AAUDIO_UNSPECIFIED);
121 AAudioStreamBuilder_setDirection(mBuilder, mDirection);
122 AAudioStreamBuilder_setSampleRate(mBuilder, mRequested.sampleRate);
123 AAudioStreamBuilder_setChannelCount(mBuilder, mRequested.channelCount);
124 AAudioStreamBuilder_setFormat(mBuilder, mRequested.dataFormat);
125 AAudioStreamBuilder_setSharingMode(mBuilder, mRequested.sharingMode);
126 AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequested.perfMode);
127 }
128
129 // Needs to be a 'void' function due to ASSERT requirements.
createAndVerifyStream(bool * success)130 void StreamBuilderHelper::createAndVerifyStream(bool *success) {
131 *success = false;
132
133 aaudio_result_t result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
134 if (mRequested.sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE && result != AAUDIO_OK) {
135 __android_log_write(ANDROID_LOG_WARN, LOG_TAG, "Could not open a stream in EXCLUSIVE mode");
136 return;
137 }
138 ASSERT_EQ(AAUDIO_OK, result);
139 ASSERT_TRUE(mStream != nullptr);
140 ASSERT_EQ(AAUDIO_STREAM_STATE_OPEN, AAudioStream_getState(mStream));
141 ASSERT_EQ(mDirection, AAudioStream_getDirection(mStream));
142
143 mActual.sharingMode = AAudioStream_getSharingMode(mStream);
144 if (mActual.sharingMode != mRequested.sharingMode) {
145 // Since we are covering all possible values, the "actual" mode
146 // will also be tested, so no need to run the same test twice.
147 __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Sharing mode %s is not available",
148 sharingModeToString(mRequested.sharingMode));
149 return;
150 }
151
152 // Check to see what kind of stream we actually got.
153 mActual.sampleRate = AAudioStream_getSampleRate(mStream);
154 ASSERT_GE(mActual.sampleRate, kMinValidSampleRate);
155 ASSERT_LE(mActual.sampleRate, kMaxValidSampleRate);
156
157 ASSERT_GE(AAudioStream_getHardwareSampleRate(mStream), kMinValidSampleRate);
158 ASSERT_LE(AAudioStream_getHardwareSampleRate(mStream), kMaxValidSampleRate);
159
160 mActual.channelCount = AAudioStream_getChannelCount(mStream);
161 ASSERT_GE(mActual.channelCount, kMinValidChannelCount);
162 ASSERT_LE(mActual.channelCount, kMaxValidChannelCount);
163
164 ASSERT_GE(AAudioStream_getHardwareChannelCount(mStream), kMinValidChannelCount);
165 ASSERT_LE(AAudioStream_getHardwareChannelCount(mStream), kMaxValidChannelCount);
166
167 mActual.dataFormat = AAudioStream_getFormat(mStream);
168 if (mRequested.dataFormat != AAUDIO_FORMAT_UNSPECIFIED) {
169 ASSERT_EQ(mRequested.dataFormat, mActual.dataFormat);
170 }
171
172 ASSERT_NE(AAudioStream_getHardwareFormat(mStream), AAUDIO_FORMAT_UNSPECIFIED);
173 ASSERT_NE(AAudioStream_getHardwareFormat(mStream), AAUDIO_FORMAT_INVALID);
174 ASSERT_TRUE(sValidStreamFormats.find(AAudioStream_getHardwareFormat(mStream)) !=
175 sValidStreamFormats.end());
176
177 mActual.perfMode = AAudioStream_getPerformanceMode(mStream);
178 if (mRequested.perfMode != AAUDIO_PERFORMANCE_MODE_NONE
179 && mRequested.perfMode != mActual.perfMode) {
180 // Since we are covering all possible values, the "actual" mode
181 // will also be tested, so no need to run the same test twice.
182 __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Performance mode %s is not available",
183 performanceModeToString(mRequested.sharingMode));
184 return;
185 }
186
187 mFramesPerBurst = AAudioStream_getFramesPerBurst(mStream);
188 ASSERT_GE(mFramesPerBurst, 16);
189 const int32_t maxFramesPerBurst =
190 mActual.sampleRate * sMaxFramesPerBurstMs.at(mActual.perfMode) / MILLIS_PER_SECOND;
191 ASSERT_LE(mFramesPerBurst, maxFramesPerBurst);
192
193 int32_t actualBufferSize = AAudioStream_getBufferSizeInFrames(mStream);
194 ASSERT_GT(actualBufferSize, 0);
195 ASSERT_GT(AAudioStream_setBufferSizeInFrames(mStream, actualBufferSize), 0);
196
197 *success = true;
198 }
199
close()200 void StreamBuilderHelper::close() {
201 if (mBuilder != nullptr) {
202 ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(mBuilder));
203 }
204 if (mStream != nullptr) {
205 ASSERT_EQ(AAUDIO_OK, AAudioStream_close(mStream));
206 }
207 }
208
streamCommand(StreamCommand cmd,aaudio_stream_state_t fromState,aaudio_stream_state_t toState)209 void StreamBuilderHelper::streamCommand(
210 StreamCommand cmd, aaudio_stream_state_t fromState, aaudio_stream_state_t toState) {
211 ASSERT_EQ(AAUDIO_OK, cmd(mStream));
212 aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
213 ASSERT_EQ(AAUDIO_OK,
214 AAudioStream_waitForStateChange(mStream, fromState, &state, DEFAULT_STATE_TIMEOUT));
215 ASSERT_EQ(toState, state);
216 }
217
InputStreamBuilderHelper(aaudio_sharing_mode_t requestedSharingMode,aaudio_performance_mode_t requestedPerfMode,aaudio_format_t requestedFormat,int32_t requestedSampleRate)218 InputStreamBuilderHelper::InputStreamBuilderHelper(
219 aaudio_sharing_mode_t requestedSharingMode,
220 aaudio_performance_mode_t requestedPerfMode,
221 aaudio_format_t requestedFormat,
222 int32_t requestedSampleRate)
223 : StreamBuilderHelper{AAUDIO_DIRECTION_INPUT,
224 requestedSampleRate, 1, requestedFormat, requestedSharingMode, requestedPerfMode} {}
225
226
OutputStreamBuilderHelper(aaudio_sharing_mode_t requestedSharingMode,aaudio_performance_mode_t requestedPerfMode,aaudio_format_t requestedFormat,int32_t requestedSampleRate)227 OutputStreamBuilderHelper::OutputStreamBuilderHelper(
228 aaudio_sharing_mode_t requestedSharingMode,
229 aaudio_performance_mode_t requestedPerfMode,
230 aaudio_format_t requestedFormat,
231 int32_t requestedSampleRate)
232 : StreamBuilderHelper{AAUDIO_DIRECTION_OUTPUT,
233 requestedSampleRate, 2, requestedFormat, requestedSharingMode, requestedPerfMode} {}
234
initBuilder()235 void OutputStreamBuilderHelper::initBuilder() {
236 StreamBuilderHelper::initBuilder();
237 AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, kBufferCapacityFrames);
238 }
239
AAudioExtensions()240 AAudioExtensions::AAudioExtensions()
241 : mMMapSupported(isPolicyEnabled(getMMapPolicyProperty())),
242 mMMapExclusiveSupported(isPolicyEnabled(
243 getIntegerProperty("aaudio.mmap_exclusive_policy", AAUDIO_UNSPECIFIED))) {}
244
getIntegerProperty(const char * name,int defaultValue)245 int AAudioExtensions::getIntegerProperty(const char *name, int defaultValue) {
246 int result = defaultValue;
247 char valueText[PROP_VALUE_MAX] = {0};
248 if (__system_property_get(name, valueText) != 0) {
249 result = atoi(valueText);
250 }
251 return result;
252 }
253
254 static std::atomic_int sAudioServerCrashCount = 0;
255 static int sLastAudioServerCrashCount = 0;
256
onBinderDied(void *)257 void onBinderDied(void* /*cookie*/) {
258 sAudioServerCrashCount += 1;
259 AudioServerCrashMonitor::getInstance().onAudioServerCrash();
260 }
261
AudioServerCrashMonitor()262 AudioServerCrashMonitor::AudioServerCrashMonitor()
263 : mDeathRecipient{ScopedAIBinder_DeathRecipient(
264 AIBinder_DeathRecipient_new(onBinderDied))} {
265 linkToDeath();
266 }
267
~AudioServerCrashMonitor()268 AudioServerCrashMonitor::~AudioServerCrashMonitor() {
269 if (mDeathRecipientLinked) {
270 AIBinder_unlinkToDeath(mAudioFlinger.get(), mDeathRecipient.get(), nullptr /* cookie */);
271 }
272 }
273
linkToDeath()274 void AudioServerCrashMonitor::linkToDeath() {
275 if (getAudioFlinger().get() == nullptr) {
276 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to get audio flinger");
277 } else {
278 auto ret = AIBinder_linkToDeath(mAudioFlinger.get(), mDeathRecipient.get(),
279 nullptr /* cookie */);
280 if (ret != STATUS_OK) {
281 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to link to death, err=%d", ret);
282 } else {
283 mDeathRecipientLinked = true;
284 }
285 }
286 }
287
onAudioServerCrash()288 void AudioServerCrashMonitor::onAudioServerCrash() {
289 mDeathRecipientLinked = false;
290 mAudioFlinger.set(nullptr);
291 }
292
293 namespace {
294
getJNIEnv()295 JNIEnv* getJNIEnv() {
296 JavaVM* vm = GetJavaVM();
297 EXPECT_NE(nullptr, vm);
298 JNIEnv* env = nullptr;
299 jint attach = vm->AttachCurrentThread(&env, nullptr);
300 EXPECT_EQ(JNI_OK, attach);
301 EXPECT_NE(nullptr, env);
302 return env;
303 }
304
305 #define CALL_JAVA_STATIC_METHOD(_jtype, _jname) \
306 _jtype callJavaStatic##_jname##Function( \
307 JNIEnv* env, const char* className, \
308 const char* funcName, const char* signature, ...) { \
309 _jtype result; \
310 if (env == nullptr) { \
311 env = getJNIEnv(); \
312 } \
313 jclass cl = env->FindClass(className); \
314 EXPECT_NE(nullptr, cl); \
315 jmethodID mid = env->GetStaticMethodID(cl, funcName, signature); \
316 EXPECT_NE(nullptr, mid); \
317 va_list args; \
318 va_start(args, signature); \
319 result = env->CallStatic##_jname##Method(cl, mid, args); \
320 va_end(args); \
321 return result; \
322 } \
323
CALL_JAVA_STATIC_METHOD(jobject,Object)324 CALL_JAVA_STATIC_METHOD(jobject, Object)
325 CALL_JAVA_STATIC_METHOD(jboolean, Boolean)
326
327 void callJavaStaticVoidFunction(
328 JNIEnv* env, const char* className,
329 const char* funcName, const char* signature, ...) {
330 if (env == nullptr) {
331 env = getJNIEnv();
332 }
333 jclass cl = env->FindClass(className);
334 EXPECT_NE(nullptr, cl);
335 jmethodID mid = env->GetStaticMethodID(cl, funcName, signature);
336 EXPECT_NE(nullptr, mid);
337 va_list args;
338 va_start(args, signature);
339 env->CallStaticVoidMethod(cl, mid, args);
340 va_end(args);
341 }
342
343 } // namespace
344
getAudioFlinger()345 SpAIBinder AudioServerCrashMonitor::getAudioFlinger() {
346 if (mAudioFlinger.get() != nullptr) {
347 return mAudioFlinger;
348 }
349
350 JNIEnv *env = getJNIEnv();
351 jobject object = callJavaStaticObjectFunction(
352 env, "android/nativemedia/aaudio/AAudioTests",
353 "getAudioFlinger", "()Landroid/os/IBinder;");
354 EXPECT_NE(nullptr, object);
355
356 mAudioFlinger = SpAIBinder(AIBinder_fromJavaBinder(env, object));
357 return mAudioFlinger;
358 }
359
SetUp()360 void AAudioCtsBase::SetUp() {
361 checkIfAudioServerCrash();
362 }
363
TearDown()364 void AAudioCtsBase::TearDown() {
365 checkIfAudioServerCrash();
366 }
367
checkIfAudioServerCrash()368 void AAudioCtsBase::checkIfAudioServerCrash() {
369 EXPECT_EQ(sLastAudioServerCrashCount, sAudioServerCrashCount);
370 sLastAudioServerCrashCount = sAudioServerCrashCount;
371 EXPECT_TRUE(AudioServerCrashMonitor::getInstance().isDeathRecipientLinked());
372 if (!AudioServerCrashMonitor::getInstance().isDeathRecipientLinked()) {
373 AudioServerCrashMonitor::getInstance().linkToDeath();
374 }
375 }
376
isIEC61937Supported()377 bool isIEC61937Supported() {
378 return (bool) callJavaStaticBooleanFunction(
379 nullptr, "android/nativemedia/aaudio/AAudioTests", "isIEC61937Supported", "()Z");
380 }
381
isEchoReferenceSupported()382 bool isEchoReferenceSupported() {
383 return (bool) callJavaStaticBooleanFunction(
384 nullptr, "android/nativemedia/aaudio/AAudioTests", "isEchoReferenceSupported", "()Z");
385 }
386
enableAudioOutputPermission()387 void enableAudioOutputPermission() {
388 callJavaStaticVoidFunction(
389 nullptr, "android/nativemedia/aaudio/AAudioTests", "enableAudioOutputPermission",
390 "()V");
391 }
392
enableAudioHotwordPermission()393 void enableAudioHotwordPermission() {
394 callJavaStaticVoidFunction(
395 nullptr, "android/nativemedia/aaudio/AAudioTests", "enableAudioHotwordPermission",
396 "()V");
397 }
398
disablePermissions()399 void disablePermissions() {
400 callJavaStaticVoidFunction(
401 nullptr, "android/nativemedia/aaudio/AAudioTests", "disablePermissions", "()V");
402 }
403
isCompressedFormat(aaudio_format_t format)404 bool isCompressedFormat(aaudio_format_t format) {
405 switch (format) {
406 case AAUDIO_FORMAT_PCM_I16:
407 case AAUDIO_FORMAT_PCM_FLOAT:
408 case AAUDIO_FORMAT_PCM_I24_PACKED:
409 case AAUDIO_FORMAT_PCM_I32:
410 return false;
411 default:
412 return true;
413 }
414 }
415