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