Skip to content

Commit b302400

Browse files
authored
oboe: remove OpenSL ES direct links (#2014)
Use dlsym() to find the address of symbols like SL_IID_PLAY. This allows Oboe to be run on a device that does not have "libOpenSLES.so". This fixes b/337360630 Fixed a deadlock that could occur if OpenSL ES was used when the library was not present. Failing to link the OpenSL ES engine would call close(), which tried to lock mLock again. Use dlerror() to print info about link problems. Add a state variable for better management of the dynamic linking.
1 parent 2d6295c commit b302400

9 files changed

Lines changed: 150 additions & 56 deletions

src/opensles/AudioInputStreamOpenSLES.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616

1717
#include <cassert>
1818

19-
#include <SLES/OpenSLES.h>
20-
#include <SLES/OpenSLES_Android.h>
21-
2219
#include "common/OboeDebug.h"
2320
#include "oboe/AudioStreamBuilder.h"
2421
#include "AudioInputStreamOpenSLES.h"
@@ -151,8 +148,8 @@ Result AudioInputStreamOpenSLES::open() {
151148

152149
// Configure the stream.
153150
result = (*mObjectInterface)->GetInterface(mObjectInterface,
154-
SL_IID_ANDROIDCONFIGURATION,
155-
&configItf);
151+
EngineOpenSLES::getInstance().getIidAndroidConfiguration(),
152+
&configItf);
156153

157154
if (SL_RESULT_SUCCESS != result) {
158155
LOGW("%s() GetInterface(SL_IID_ANDROIDCONFIGURATION) failed with %s",
@@ -190,7 +187,9 @@ Result AudioInputStreamOpenSLES::open() {
190187
goto error;
191188
}
192189

193-
result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_RECORD, &mRecordInterface);
190+
result = (*mObjectInterface)->GetInterface(mObjectInterface,
191+
EngineOpenSLES::getInstance().getIidRecord(),
192+
&mRecordInterface);
194193
if (SL_RESULT_SUCCESS != result) {
195194
LOGE("GetInterface RECORD result:%s", getSLErrStr(result));
196195
goto error;

src/opensles/AudioInputStreamOpenSLES.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818
#define AUDIO_INPUT_STREAM_OPENSL_ES_H_
1919

2020

21-
#include <SLES/OpenSLES.h>
22-
#include <SLES/OpenSLES_Android.h>
23-
2421
#include "oboe/Oboe.h"
22+
#include "EngineOpenSLES.h"
2523
#include "AudioStreamOpenSLES.h"
2624

2725
namespace oboe {

src/opensles/AudioOutputStreamOpenSLES.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
#include <cassert>
1818

19-
#include <SLES/OpenSLES.h>
20-
#include <SLES/OpenSLES_Android.h>
2119
#include <common/AudioClock.h>
2220

2321
#include "common/OboeDebug.h"
@@ -180,8 +178,8 @@ Result AudioOutputStreamOpenSLES::open() {
180178

181179
// Configure the stream.
182180
result = (*mObjectInterface)->GetInterface(mObjectInterface,
183-
SL_IID_ANDROIDCONFIGURATION,
184-
(void *)&configItf);
181+
EngineOpenSLES::getInstance().getIidAndroidConfiguration(),
182+
(void *)&configItf);
185183
if (SL_RESULT_SUCCESS != result) {
186184
LOGW("%s() GetInterface(SL_IID_ANDROIDCONFIGURATION) failed with %s",
187185
__func__, getSLErrStr(result));
@@ -207,7 +205,9 @@ Result AudioOutputStreamOpenSLES::open() {
207205
goto error;
208206
}
209207

210-
result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_PLAY, &mPlayInterface);
208+
result = (*mObjectInterface)->GetInterface(mObjectInterface,
209+
EngineOpenSLES::getInstance().getIidPlay(),
210+
&mPlayInterface);
211211
if (SL_RESULT_SUCCESS != result) {
212212
LOGE("GetInterface PLAY result:%s", getSLErrStr(result));
213213
goto error;

src/opensles/AudioOutputStreamOpenSLES.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818
#define AUDIO_OUTPUT_STREAM_OPENSL_ES_H_
1919

2020

21-
#include <SLES/OpenSLES.h>
22-
#include <SLES/OpenSLES_Android.h>
23-
2421
#include "oboe/Oboe.h"
22+
#include "EngineOpenSLES.h"
2523
#include "AudioStreamOpenSLES.h"
2624

2725
namespace oboe {

src/opensles/AudioStreamOpenSLES.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
#include <cassert>
1717
#include <android/log.h>
1818

19-
#include <SLES/OpenSLES.h>
20-
#include <SLES/OpenSLES_Android.h>
2119
#include <oboe/AudioStream.h>
2220
#include <common/AudioClock.h>
2321

2422
#include "common/OboeDebug.h"
2523
#include "oboe/AudioStreamBuilder.h"
24+
#include "EngineOpenSLES.h"
2625
#include "AudioStreamOpenSLES.h"
2726
#include "OpenSLESUtilities.h"
2827

@@ -442,8 +441,9 @@ static void bqCallbackGlue(SLAndroidSimpleBufferQueueItf bq, void *context) {
442441

443442
SLresult AudioStreamOpenSLES::registerBufferQueueCallback() {
444443
// The BufferQueue
445-
SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
446-
&mSimpleBufferQueueInterface);
444+
SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface,
445+
EngineOpenSLES::getInstance().getIidAndroidSimpleBufferQueue(),
446+
&mSimpleBufferQueueInterface);
447447
if (SL_RESULT_SUCCESS != result) {
448448
LOGE("get buffer queue interface:%p result:%s",
449449
mSimpleBufferQueueInterface,

src/opensles/AudioStreamOpenSLES.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919

2020
#include <memory>
2121

22-
#include <SLES/OpenSLES.h>
23-
#include <SLES/OpenSLES_Android.h>
24-
2522
#include "oboe/Oboe.h"
2623
#include "common/MonotonicCounter.h"
2724
#include "opensles/AudioStreamBuffered.h"

src/opensles/EngineOpenSLES.cpp

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
#include <dlfcn.h>
18+
1819
#include "common/OboeDebug.h"
1920
#include "EngineOpenSLES.h"
2021
#include "OpenSLESUtilities.h"
@@ -24,40 +25,92 @@ using namespace oboe;
2425
// OpenSL ES is deprecated in SDK 30.
2526
// So we use custom dynamic linking to access the library.
2627
#define LIB_OPENSLES_NAME "libOpenSLES.so"
27-
typedef SLresult (*prototype_slCreateEngine)(
28-
SLObjectItf *pEngine,
29-
SLuint32 numOptions,
30-
const SLEngineOption *pEngineOptions,
31-
SLuint32 numInterfaces,
32-
const SLInterfaceID *pInterfaceIds,
33-
const SLboolean *pInterfaceRequired
34-
);
35-
static prototype_slCreateEngine gFunction_slCreateEngine = nullptr;
36-
static void *gLibOpenSlesLibraryHandle = nullptr;
28+
29+
EngineOpenSLES &EngineOpenSLES::getInstance() {
30+
static EngineOpenSLES sInstance;
31+
return sInstance;
32+
}
33+
34+
// Satisfy extern in OpenSLES.h
35+
// These are required because of b/337360630, which was causing
36+
// Oboe to have link failures if libOpenSLES.so was not available.
37+
SL_API const SLInterfaceID SL_IID_ENGINE = nullptr;
38+
SL_API const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE = nullptr;
39+
SL_API const SLInterfaceID SL_IID_ANDROIDCONFIGURATION = nullptr;
40+
SL_API const SLInterfaceID SL_IID_RECORD = nullptr;
41+
SL_API const SLInterfaceID SL_IID_BUFFERQUEUE = nullptr;
42+
SL_API const SLInterfaceID SL_IID_VOLUME = nullptr;
43+
SL_API const SLInterfaceID SL_IID_PLAY = nullptr;
44+
45+
static const char *getSafeDlerror() {
46+
static const char *defaultMessage = "not found?";
47+
char *errorMessage = dlerror();
48+
return (errorMessage == nullptr) ? defaultMessage : errorMessage;
49+
}
3750

3851
// Load the OpenSL ES library and the one primary entry point.
3952
// @return true if linked OK
40-
static bool linkOpenSLES() {
41-
if (gLibOpenSlesLibraryHandle == nullptr && gFunction_slCreateEngine == nullptr) {
53+
bool EngineOpenSLES::linkOpenSLES() {
54+
if (mDynamicLinkState == kLinkStateBad) {
55+
LOGE("%s(), OpenSL ES not available, based on previous link failure.", __func__);
56+
} else if (mDynamicLinkState == kLinkStateUninitialized) {
57+
// Set to BAD now in case we return because of an error.
58+
// This is safe form race conditions because this function is always called
59+
// under mLock amd the state is only accessed from this function.
60+
mDynamicLinkState = kLinkStateBad;
4261
// Use RTLD_NOW to avoid the unpredictable behavior that RTLD_LAZY can cause.
4362
// Also resolving all the links now will prevent a run-time penalty later.
44-
gLibOpenSlesLibraryHandle = dlopen(LIB_OPENSLES_NAME, RTLD_NOW);
45-
if (gLibOpenSlesLibraryHandle == nullptr) {
46-
LOGE("linkOpenSLES() could not find " LIB_OPENSLES_NAME);
63+
mLibOpenSlesLibraryHandle = dlopen(LIB_OPENSLES_NAME, RTLD_NOW);
64+
if (mLibOpenSlesLibraryHandle == nullptr) {
65+
LOGE("%s() could not dlopen(%s), %s", __func__, LIB_OPENSLES_NAME, getSafeDlerror());
66+
return false;
4767
} else {
48-
gFunction_slCreateEngine = (prototype_slCreateEngine) dlsym(
49-
gLibOpenSlesLibraryHandle,
68+
mFunction_slCreateEngine = (prototype_slCreateEngine) dlsym(
69+
mLibOpenSlesLibraryHandle,
5070
"slCreateEngine");
51-
LOGD("linkOpenSLES(): dlsym(%s) returned %p", "slCreateEngine",
52-
gFunction_slCreateEngine);
71+
LOGD("%s(): dlsym(%s) returned %p", __func__,
72+
"slCreateEngine", mFunction_slCreateEngine);
73+
if (mFunction_slCreateEngine == nullptr) {
74+
LOGE("%s(): dlsym(slCreateEngine) returned null, %s", __func__, getSafeDlerror());
75+
return false;
76+
}
77+
78+
// Load IID interfaces.
79+
LOCAL_SL_IID_ENGINE = getIidPointer("SL_IID_ENGINE");
80+
if (LOCAL_SL_IID_ENGINE == nullptr) return false;
81+
LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE = getIidPointer(
82+
"SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
83+
if (LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE == nullptr) return false;
84+
LOCAL_SL_IID_ANDROIDCONFIGURATION = getIidPointer(
85+
"SL_IID_ANDROIDCONFIGURATION");
86+
if (LOCAL_SL_IID_ANDROIDCONFIGURATION == nullptr) return false;
87+
LOCAL_SL_IID_RECORD = getIidPointer("SL_IID_RECORD");
88+
if (LOCAL_SL_IID_RECORD == nullptr) return false;
89+
LOCAL_SL_IID_BUFFERQUEUE = getIidPointer("SL_IID_BUFFERQUEUE");
90+
if (LOCAL_SL_IID_BUFFERQUEUE == nullptr) return false;
91+
LOCAL_SL_IID_VOLUME = getIidPointer("SL_IID_VOLUME");
92+
if (LOCAL_SL_IID_VOLUME == nullptr) return false;
93+
LOCAL_SL_IID_PLAY = getIidPointer("SL_IID_PLAY");
94+
if (LOCAL_SL_IID_PLAY == nullptr) return false;
95+
96+
mDynamicLinkState = kLinkStateGood;
5397
}
5498
}
55-
return gFunction_slCreateEngine != nullptr;
99+
return (mDynamicLinkState == kLinkStateGood);
56100
}
57101

58-
EngineOpenSLES &EngineOpenSLES::getInstance() {
59-
static EngineOpenSLES sInstance;
60-
return sInstance;
102+
// A symbol like SL_IID_PLAY is a pointer to a structure.
103+
// The dlsym() function returns the address of the pointer, not the structure.
104+
// To get the address of the structure we have to dereference the pointer.
105+
SLInterfaceID EngineOpenSLES::getIidPointer(const char *symbolName) {
106+
SLInterfaceID *iid_address = (SLInterfaceID *) dlsym(
107+
mLibOpenSlesLibraryHandle,
108+
symbolName);
109+
if (iid_address == nullptr) {
110+
LOGE("%s(): dlsym(%s) returned null, %s", __func__, symbolName, getSafeDlerror());
111+
return (SLInterfaceID) nullptr;
112+
}
113+
return *iid_address; // Get address of the structure.
61114
}
62115

63116
SLresult EngineOpenSLES::open() {
@@ -72,7 +125,7 @@ SLresult EngineOpenSLES::open() {
72125
};
73126

74127
// create engine
75-
result = (*gFunction_slCreateEngine)(&mEngineObject, 0, NULL, 0, NULL, NULL);
128+
result = (*mFunction_slCreateEngine)(&mEngineObject, 0, NULL, 0, NULL, NULL);
76129
if (SL_RESULT_SUCCESS != result) {
77130
LOGE("EngineOpenSLES - slCreateEngine() result:%s", getSLErrStr(result));
78131
goto error;
@@ -86,7 +139,9 @@ SLresult EngineOpenSLES::open() {
86139
}
87140

88141
// get the engine interface, which is needed in order to create other objects
89-
result = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface);
142+
result = (*mEngineObject)->GetInterface(mEngineObject,
143+
EngineOpenSLES::getInstance().getIidEngine(),
144+
&mEngineInterface);
90145
if (SL_RESULT_SUCCESS != result) {
91146
LOGE("EngineOpenSLES - GetInterface() engine result:%s", getSLErrStr(result));
92147
goto error;
@@ -96,12 +151,17 @@ SLresult EngineOpenSLES::open() {
96151
return result;
97152

98153
error:
99-
close();
154+
close_l();
100155
return result;
101156
}
102157

103158
void EngineOpenSLES::close() {
104159
std::lock_guard<std::mutex> lock(mLock);
160+
close_l();
161+
}
162+
163+
// This must be called under mLock
164+
void EngineOpenSLES::close_l() {
105165
if (--mOpenCount == 0) {
106166
if (mEngineObject != nullptr) {
107167
(*mEngineObject)->Destroy(mEngineObject);
@@ -119,8 +179,8 @@ SLresult EngineOpenSLES::createAudioPlayer(SLObjectItf *objectItf,
119179
SLDataSource *audioSource,
120180
SLDataSink *audioSink) {
121181

122-
const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
123-
const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
182+
SLInterfaceID ids[] = {LOCAL_SL_IID_BUFFERQUEUE, LOCAL_SL_IID_ANDROIDCONFIGURATION};
183+
SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
124184

125185
return (*mEngineInterface)->CreateAudioPlayer(mEngineInterface, objectItf, audioSource,
126186
audioSink,
@@ -131,8 +191,9 @@ SLresult EngineOpenSLES::createAudioRecorder(SLObjectItf *objectItf,
131191
SLDataSource *audioSource,
132192
SLDataSink *audioSink) {
133193

134-
const SLInterfaceID ids[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
135-
const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
194+
SLInterfaceID ids[] = {LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
195+
LOCAL_SL_IID_ANDROIDCONFIGURATION };
196+
SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
136197

137198
return (*mEngineInterface)->CreateAudioRecorder(mEngineInterface, objectItf, audioSource,
138199
audioSink,

src/opensles/EngineOpenSLES.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,24 @@
2525

2626
namespace oboe {
2727

28+
typedef SLresult (*prototype_slCreateEngine)(
29+
SLObjectItf *pEngine,
30+
SLuint32 numOptions,
31+
const SLEngineOption *pEngineOptions,
32+
SLuint32 numInterfaces,
33+
const SLInterfaceID *pInterfaceIds,
34+
const SLboolean *pInterfaceRequired
35+
);
36+
2837
/**
2938
* INTERNAL USE ONLY
3039
*/
3140
class EngineOpenSLES {
3241
public:
3342
static EngineOpenSLES &getInstance();
3443

44+
bool linkOpenSLES();
45+
3546
SLresult open();
3647

3748
void close();
@@ -45,18 +56,49 @@ class EngineOpenSLES {
4556
SLDataSource *audioSource,
4657
SLDataSink *audioSink);
4758

59+
SLInterfaceID getIidEngine() { return LOCAL_SL_IID_ENGINE; }
60+
SLInterfaceID getIidAndroidSimpleBufferQueue() { return LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE; }
61+
SLInterfaceID getIidAndroidConfiguration() { return LOCAL_SL_IID_ANDROIDCONFIGURATION; }
62+
SLInterfaceID getIidRecord() { return LOCAL_SL_IID_RECORD; }
63+
SLInterfaceID getIidBufferQueue() { return LOCAL_SL_IID_BUFFERQUEUE; }
64+
SLInterfaceID getIidVolume() { return LOCAL_SL_IID_VOLUME; }
65+
SLInterfaceID getIidPlay() { return LOCAL_SL_IID_PLAY; }
66+
4867
private:
4968
// Make this a safe Singleton
5069
EngineOpenSLES()= default;
5170
~EngineOpenSLES()= default;
5271
EngineOpenSLES(const EngineOpenSLES&)= delete;
5372
EngineOpenSLES& operator=(const EngineOpenSLES&)= delete;
5473

74+
SLInterfaceID getIidPointer(const char *symbolName);
75+
76+
/**
77+
* Close the OpenSL ES engine.
78+
* This must be called under mLock
79+
*/
80+
void close_l();
81+
5582
std::mutex mLock;
5683
int32_t mOpenCount = 0;
5784

85+
static constexpr int32_t kLinkStateUninitialized = 0;
86+
static constexpr int32_t kLinkStateGood = 1;
87+
static constexpr int32_t kLinkStateBad = 2;
88+
int32_t mDynamicLinkState = kLinkStateUninitialized;
5889
SLObjectItf mEngineObject = nullptr;
5990
SLEngineItf mEngineInterface = nullptr;
91+
92+
// These symbols are loaded using dlsym().
93+
prototype_slCreateEngine mFunction_slCreateEngine = nullptr;
94+
void *mLibOpenSlesLibraryHandle = nullptr;
95+
SLInterfaceID LOCAL_SL_IID_ENGINE = nullptr;
96+
SLInterfaceID LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE = nullptr;
97+
SLInterfaceID LOCAL_SL_IID_ANDROIDCONFIGURATION = nullptr;
98+
SLInterfaceID LOCAL_SL_IID_RECORD = nullptr;
99+
SLInterfaceID LOCAL_SL_IID_BUFFERQUEUE = nullptr;
100+
SLInterfaceID LOCAL_SL_IID_VOLUME = nullptr;
101+
SLInterfaceID LOCAL_SL_IID_PLAY = nullptr;
60102
};
61103

62104
} // namespace oboe

src/opensles/OutputMixerOpenSLES.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
#include <atomic>
2121
#include <mutex>
2222

23-
#include <SLES/OpenSLES.h>
24-
#include <SLES/OpenSLES_Android.h>
23+
#include "EngineOpenSLES.h"
2524

2625
namespace oboe {
2726

0 commit comments

Comments
 (0)