• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)218 InputStreamBuilderHelper::InputStreamBuilderHelper(
219         aaudio_sharing_mode_t requestedSharingMode,
220         aaudio_performance_mode_t requestedPerfMode,
221         aaudio_format_t requestedFormat)
222         : StreamBuilderHelper{AAUDIO_DIRECTION_INPUT,
223             48000, 1, requestedFormat, requestedSharingMode, requestedPerfMode} {}
224 
225 
OutputStreamBuilderHelper(aaudio_sharing_mode_t requestedSharingMode,aaudio_performance_mode_t requestedPerfMode,aaudio_format_t requestedFormat)226 OutputStreamBuilderHelper::OutputStreamBuilderHelper(
227         aaudio_sharing_mode_t requestedSharingMode,
228         aaudio_performance_mode_t requestedPerfMode,
229         aaudio_format_t requestedFormat)
230         : StreamBuilderHelper{AAUDIO_DIRECTION_OUTPUT,
231             48000, 2, requestedFormat, requestedSharingMode, requestedPerfMode} {}
232 
initBuilder()233 void OutputStreamBuilderHelper::initBuilder() {
234     StreamBuilderHelper::initBuilder();
235     AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, kBufferCapacityFrames);
236 }
237 
AAudioExtensions()238 AAudioExtensions::AAudioExtensions()
239     : mMMapSupported(isPolicyEnabled(getMMapPolicyProperty()))
240     , mMMapExclusiveSupported(isPolicyEnabled(getIntegerProperty(
241             "aaudio.mmap_exclusive_policy", AAUDIO_POLICY_UNSPECIFIED))) {
242     loadLibrary();
243 }
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 // This should only be called once from the constructor.
loadLibrary()255 bool AAudioExtensions::loadLibrary() {
256     mLibHandle = dlopen(LIB_AAUDIO_NAME, 0);
257     if (mLibHandle == nullptr) {
258         //LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__);
259         return false;
260     }
261 
262     mAAudioStream_isMMap = (bool (*)(AAudioStream *stream))
263             dlsym(mLibHandle, FUNCTION_IS_MMAP);
264     if (mAAudioStream_isMMap == nullptr) {
265         //LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__);
266         return false;
267     }
268 
269     mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy))
270             dlsym(mLibHandle, FUNCTION_SET_MMAP_POLICY);
271     if (mAAudio_setMMapPolicy == nullptr) {
272         //LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__);
273         return false;
274     }
275 
276     mAAudio_getMMapPolicy = (aaudio_policy_t (*)())
277             dlsym(mLibHandle, FUNCTION_GET_MMAP_POLICY);
278     if (mAAudio_getMMapPolicy == nullptr) {
279         //LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__);
280         return false;
281     }
282 
283     mFunctionsLoaded = true;
284     return mFunctionsLoaded;
285 }
286 
287 static std::atomic_int sAudioServerCrashCount = 0;
288 static int sLastAudioServerCrashCount = 0;
289 
onBinderDied(void *)290 void onBinderDied(void* /*cookie*/) {
291     sAudioServerCrashCount += 1;
292     AudioServerCrashMonitor::getInstance().onAudioServerCrash();
293 }
294 
AudioServerCrashMonitor()295 AudioServerCrashMonitor::AudioServerCrashMonitor()
296         : mDeathRecipient{ScopedAIBinder_DeathRecipient(
297                 AIBinder_DeathRecipient_new(onBinderDied))} {
298     linkToDeath();
299 }
300 
~AudioServerCrashMonitor()301 AudioServerCrashMonitor::~AudioServerCrashMonitor() {
302     if (mDeathRecipientLinked) {
303         AIBinder_unlinkToDeath(mAudioFlinger.get(), mDeathRecipient.get(), nullptr /* cookie */);
304     }
305 }
306 
linkToDeath()307 void AudioServerCrashMonitor::linkToDeath() {
308     if (getAudioFlinger().get() == nullptr) {
309         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to get audio flinger");
310     } else {
311         auto ret = AIBinder_linkToDeath(mAudioFlinger.get(), mDeathRecipient.get(),
312                                         nullptr /* cookie */);
313         if (ret != STATUS_OK) {
314             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to link to death, err=%d", ret);
315         } else {
316             mDeathRecipientLinked = true;
317         }
318     }
319 }
320 
onAudioServerCrash()321 void AudioServerCrashMonitor::onAudioServerCrash() {
322     mDeathRecipientLinked = false;
323     mAudioFlinger.set(nullptr);
324 }
325 
326 namespace {
327 
getJNIEnv()328 JNIEnv* getJNIEnv() {
329     JavaVM* vm = GetJavaVM();
330     EXPECT_NE(nullptr, vm);
331     JNIEnv* env = nullptr;
332     jint attach = vm->AttachCurrentThread(&env, nullptr);
333     EXPECT_EQ(JNI_OK, attach);
334     EXPECT_NE(nullptr, env);
335     return env;
336 }
337 
338 #define CALL_JAVA_STATIC_METHOD(_jtype, _jname)                                        \
339     _jtype callJavaStatic##_jname##Function(                                           \
340             JNIEnv* env, const char* className,                                        \
341             const char* funcName, const char* signature, ...) {                        \
342         _jtype result;                                                                 \
343         if (env == nullptr) {                                                          \
344             env = getJNIEnv();                                                         \
345         }                                                                              \
346         jclass cl = env->FindClass(className);                                         \
347         EXPECT_NE(nullptr, cl);                                                        \
348         jmethodID mid = env->GetStaticMethodID(cl, funcName, signature);               \
349         EXPECT_NE(nullptr, mid);                                                       \
350         va_list args;                                                                  \
351         va_start(args, signature);                                                     \
352         result = env->CallStatic##_jname##Method(cl, mid, args);                       \
353         va_end(args);                                                                  \
354         return result;                                                                 \
355     }                                                                                  \
356 
357 CALL_JAVA_STATIC_METHOD(jobject, Object)
358 CALL_JAVA_STATIC_METHOD(jboolean, Boolean)
359 
360 } // namespace
361 
getAudioFlinger()362 SpAIBinder AudioServerCrashMonitor::getAudioFlinger() {
363     if (mAudioFlinger.get() != nullptr) {
364         return mAudioFlinger;
365     }
366 
367     JNIEnv *env = getJNIEnv();
368     jobject object = callJavaStaticObjectFunction(
369             env, "android/nativemedia/aaudio/AAudioTests",
370             "getAudioFlinger", "()Landroid/os/IBinder;");
371     EXPECT_NE(nullptr, object);
372 
373     mAudioFlinger = SpAIBinder(AIBinder_fromJavaBinder(env, object));
374     return mAudioFlinger;
375 }
376 
SetUp()377 void AAudioCtsBase::SetUp() {
378     checkIfAudioServerCrash();
379 }
380 
TearDown()381 void AAudioCtsBase::TearDown() {
382     checkIfAudioServerCrash();
383 }
384 
checkIfAudioServerCrash()385 void AAudioCtsBase::checkIfAudioServerCrash() {
386     EXPECT_EQ(sLastAudioServerCrashCount, sAudioServerCrashCount);
387     sLastAudioServerCrashCount = sAudioServerCrashCount;
388     EXPECT_TRUE(AudioServerCrashMonitor::getInstance().isDeathRecipientLinked());
389     if (!AudioServerCrashMonitor::getInstance().isDeathRecipientLinked()) {
390         AudioServerCrashMonitor::getInstance().linkToDeath();
391     }
392 }
393 
isIEC61937Supported()394 bool isIEC61937Supported() {
395     return (bool) callJavaStaticBooleanFunction(
396             nullptr, "android/nativemedia/aaudio/AAudioTests", "isIEC61937Supported", "()Z");
397 }
398 
399