diff options
author | Robert Wu <85952307+robertwu1@users.noreply.github.com> | 2023-03-24 17:09:00 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-24 10:09:00 -0700 |
commit | 79f9d8fc52aa59091d8ecd7e2c3146a524261fb8 (patch) | |
tree | 18ecf1850e64845bc9c9906e46a4045d096289cb | |
parent | e54826cfdb2c9480ca0b7bb38f4558f1e99ad152 (diff) | |
download | oboe-79f9d8fc52aa59091d8ecd7e2c3146a524261fb8.tar.gz |
Add privacy sensitive mode for API 30+ (#1751)
-rw-r--r-- | include/oboe/AudioStreamBase.h | 17 | ||||
-rw-r--r-- | include/oboe/AudioStreamBuilder.h | 25 | ||||
-rw-r--r-- | include/oboe/Definitions.h | 26 | ||||
-rw-r--r-- | src/aaudio/AAudioLoader.cpp | 18 | ||||
-rw-r--r-- | src/aaudio/AAudioLoader.h | 14 | ||||
-rw-r--r-- | src/aaudio/AudioStreamAAudio.cpp | 14 | ||||
-rw-r--r-- | src/opensles/AudioStreamOpenSLES.cpp | 9 | ||||
-rw-r--r-- | tests/testStreamOpen.cpp | 78 |
8 files changed, 197 insertions, 4 deletions
diff --git a/include/oboe/AudioStreamBase.h b/include/oboe/AudioStreamBase.h index c0a582e9..b956f351 100644 --- a/include/oboe/AudioStreamBase.h +++ b/include/oboe/AudioStreamBase.h @@ -158,6 +158,20 @@ public: SessionId getSessionId() const { return mSessionId; } /** + * Return whether this input stream is marked as privacy sensitive. + * + * See AudioStreamBuilder_setPrivacySensitiveMode(). + * + * Added in API level 30 to AAudio. + * + * @param stream reference provided by AAudioStreamBuilder_openStream() + * @return PrivacySensitiveMode::Enabled if privacy sensitive, + * PrivacySensitiveMode::Disabled if not privacy sensitive, and + * PrivacySensitiveMode::Unspecified if API is not supported. + */ + PrivacySensitiveMode getPrivacySensitiveMode() const { return mPrivacySensitiveMode; } + + /** * @return true if Oboe can convert channel counts to achieve optimal results. */ bool isChannelConversionAllowed() const { @@ -244,6 +258,9 @@ protected: /** Stream session ID allocation strategy. Only active on Android 28+ */ SessionId mSessionId = SessionId::None; + /** Privacy Sensitive Mode. Only active on Android 30+ */ + PrivacySensitiveMode mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified; + /** Control the name of the package creating the stream. Only active on Android 31+ */ std::string mPackageName; /** Control the attribution tag of the context creating the stream. Only active on Android 31+ */ diff --git a/include/oboe/AudioStreamBuilder.h b/include/oboe/AudioStreamBuilder.h index 5c7ac4b6..04e50292 100644 --- a/include/oboe/AudioStreamBuilder.h +++ b/include/oboe/AudioStreamBuilder.h @@ -348,6 +348,31 @@ public: return this; } + + /** Indicates whether this input stream must be marked as privacy sensitive or not. + * + * When PrivacySensitiveMode::Enabled, this input stream is privacy sensitive and any + * concurrent capture is not permitted. + * + * This is off (PrivacySensitiveMode::Disabled) by default except when the input preset is + * InputPreset::VoiceRecognition or InputPreset::Camcorder + * + * Always takes precedence over default from input preset when set explicitly. + * + * Only relevant if the stream direction is Direction::Input and AAudio is used. + * + * Added in API level 30 to AAudio. + * + * @param builder reference provided by AAudio_createStreamBuilder() + * @param privacySensitive PrivacySensitiveMode::Enabled if capture from this stream must be + * marked as privacy sensitive, PrivacySensitiveMode::Disabled if stream should be marked as + * not sensitive. + */ + AudioStreamBuilder *setPrivacySensitiveMode(PrivacySensitiveMode privacySensitiveMode) { + mPrivacySensitiveMode = privacySensitiveMode; + return this; + } + /** * Specifies an object to handle data related callbacks from the underlying API. * diff --git a/include/oboe/Definitions.h b/include/oboe/Definitions.h index 763d1d2a..3cc4c03e 100644 --- a/include/oboe/Definitions.h +++ b/include/oboe/Definitions.h @@ -651,6 +651,32 @@ namespace oboe { }; /** + * The PrivacySensitiveMode attribute determines whether an input stream can be shared + * with another privileged app, for example the Assistant. + * + * This allows to override the default behavior tied to the audio source (e.g + * InputPreset::VoiceCommunication is private by default but InputPreset::Unprocessed is not). + */ + enum class PrivacySensitiveMode : int32_t { + + /** + * When not explicitly requested, set privacy sensitive mode according to input preset: + * communication and camcorder captures are considered privacy sensitive by default. + */ + Unspecified = kUnspecified, + + /** + * Privacy sensitive mode disabled. + */ + Disabled = 1, + + /** + * Privacy sensitive mode enabled. + */ + Enabled = 2, + }; + + /** * On API 16 to 26 OpenSL ES will be used. When using OpenSL ES the optimal values for sampleRate and * framesPerBurst are not known by the native code. * On API 17+ these values should be obtained from the AudioManager using this code: diff --git a/src/aaudio/AAudioLoader.cpp b/src/aaudio/AAudioLoader.cpp index e873e5dd..6d9d6445 100644 --- a/src/aaudio/AAudioLoader.cpp +++ b/src/aaudio/AAudioLoader.cpp @@ -83,6 +83,10 @@ int AAudioLoader::open() { builder_setSessionId = load_V_PBI("AAudioStreamBuilder_setSessionId"); } + if (getSdkVersion() >= __ANDROID_API_R__){ + builder_setPrivacySensitive = load_V_PBO("AAudioStreamBuilder_setPrivacySensitive"); + } + if (getSdkVersion() >= __ANDROID_API_S__){ builder_setPackageName = load_V_PBCPH("AAudioStreamBuilder_setPackageName"); builder_setAttributionTag = load_V_PBCPH("AAudioStreamBuilder_setAttributionTag"); @@ -147,6 +151,10 @@ int AAudioLoader::open() { stream_getSessionId = load_I_PS("AAudioStream_getSessionId"); } + if (getSdkVersion() >= __ANDROID_API_R__){ + stream_isPrivacySensitive = load_O_PS("AAudioStream_isPrivacySensitive"); + } + if (getSdkVersion() >= __ANDROID_API_S_V2__) { stream_getChannelMask = load_U_PS("AAudioStream_getChannelMask"); } @@ -227,10 +235,10 @@ AAudioLoader::signature_F_PS AAudioLoader::load_F_PS(const char *functionName) { return reinterpret_cast<signature_F_PS>(proc); } -AAudioLoader::signature_B_PS AAudioLoader::load_B_PS(const char *functionName) { +AAudioLoader::signature_O_PS AAudioLoader::load_O_PS(const char *functionName) { void *proc = dlsym(mLibHandle, functionName); AAudioLoader_check(proc, functionName); - return reinterpret_cast<signature_B_PS>(proc); + return reinterpret_cast<signature_O_PS>(proc); } AAudioLoader::signature_I_PB AAudioLoader::load_I_PB(const char *functionName) { @@ -281,6 +289,12 @@ AAudioLoader::signature_U_PS AAudioLoader::load_U_PS(const char *functionName) { return reinterpret_cast<signature_U_PS>(proc); } +AAudioLoader::signature_V_PBO AAudioLoader::load_V_PBO(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast<signature_V_PBO>(proc); +} + // Ensure that all AAudio primitive data types are int32_t #define ASSERT_INT32(type) static_assert(std::is_same<int32_t, type>::value, \ #type" must be int32_t") diff --git a/src/aaudio/AAudioLoader.h b/src/aaudio/AAudioLoader.h index cff0bea9..4114af9e 100644 --- a/src/aaudio/AAudioLoader.h +++ b/src/aaudio/AAudioLoader.h @@ -108,6 +108,8 @@ class AAudioLoader { // C = Const prefix // H = cHar // U = uint32_t + // O = bOol + typedef int32_t (*signature_I_PPB)(AAudioStreamBuilder **builder); typedef const char * (*signature_CPH_I)(int32_t); @@ -124,6 +126,9 @@ class AAudioLoader { typedef void (*signature_V_PBCPH)(AAudioStreamBuilder *, const char *); + // AAudioStreamBuilder_setPrivacySensitive + typedef void (*signature_V_PBO)(AAudioStreamBuilder *, bool); + typedef int32_t (*signature_I_PS)(AAudioStream *); // AAudioStream_getSampleRate() typedef int64_t (*signature_L_PS)(AAudioStream *); // AAudioStream_getFramesRead() // AAudioStream_setBufferSizeInFrames() @@ -149,7 +154,7 @@ class AAudioLoader { typedef int32_t (*signature_I_PSKPLPL)(AAudioStream *, clockid_t, int64_t *, int64_t *); - typedef bool (*signature_B_PS)(AAudioStream *); + typedef bool (*signature_O_PS)(AAudioStream *); typedef uint32_t (*signature_U_PS)(AAudioStream *); @@ -189,6 +194,8 @@ class AAudioLoader { signature_V_PBI builder_setInputPreset = nullptr; signature_V_PBI builder_setSessionId = nullptr; + signature_V_PBO builder_setPrivacySensitive = nullptr; + signature_V_PBCPH builder_setPackageName = nullptr; signature_V_PBCPH builder_setAttributionTag = nullptr; @@ -237,6 +244,8 @@ class AAudioLoader { signature_I_PS stream_getInputPreset = nullptr; signature_I_PS stream_getSessionId = nullptr; + signature_O_PS stream_isPrivacySensitive = nullptr; + signature_U_PS stream_getChannelMask = nullptr; signature_I_PS stream_getHardwareChannelCount = nullptr; @@ -259,7 +268,7 @@ class AAudioLoader { signature_I_PS load_I_PS(const char *name); signature_L_PS load_L_PS(const char *name); signature_F_PS load_F_PS(const char *name); - signature_B_PS load_B_PS(const char *name); + signature_O_PS load_O_PS(const char *name); signature_I_PSI load_I_PSI(const char *name); signature_I_PSPVIL load_I_PSPVIL(const char *name); signature_I_PSCPVIL load_I_PSCPVIL(const char *name); @@ -267,6 +276,7 @@ class AAudioLoader { signature_I_PSKPLPL load_I_PSKPLPL(const char *name); signature_V_PBU load_V_PBU(const char *name); signature_U_PS load_U_PS(const char *name); + signature_V_PBO load_V_PBO(const char *name); void *mLibHandle = nullptr; }; diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp index 46c76668..121bd525 100644 --- a/src/aaudio/AudioStreamAAudio.cpp +++ b/src/aaudio/AudioStreamAAudio.cpp @@ -263,6 +263,12 @@ Result AudioStreamAAudio::open() { mAttributionTag.c_str()); } + if (mLibLoader->builder_setPrivacySensitive != nullptr && mDirection == oboe::Direction::Input + && mPrivacySensitiveMode != PrivacySensitiveMode::Unspecified) { + mLibLoader->builder_setPrivacySensitive(aaudioBuilder, + mPrivacySensitiveMode == PrivacySensitiveMode::Enabled); + } + if (isDataCallbackSpecified()) { mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this); mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerDataCallback()); @@ -320,6 +326,14 @@ Result AudioStreamAAudio::open() { mSessionId = SessionId::None; } + if (mLibLoader->stream_isPrivacySensitive != nullptr && mDirection == oboe::Direction::Input) { + bool isPrivacySensitive = mLibLoader->stream_isPrivacySensitive(mAAudioStream); + mPrivacySensitiveMode = isPrivacySensitive ? PrivacySensitiveMode::Enabled : + PrivacySensitiveMode::Disabled; + } else { + mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified; + } + if (mLibLoader->stream_getChannelMask != nullptr) { mChannelMask = static_cast<ChannelMask>(mLibLoader->stream_getChannelMask(mAAudioStream)); } diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp index 62692a42..8f730b01 100644 --- a/src/opensles/AudioStreamOpenSLES.cpp +++ b/src/opensles/AudioStreamOpenSLES.cpp @@ -108,6 +108,9 @@ Result AudioStreamOpenSLES::open() { SLresult AudioStreamOpenSLES::finishCommonOpen(SLAndroidConfigurationItf configItf) { + // Setting privacy sensitive mode is not supported for OpenSL ES. + mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified; + SLresult result = registerBufferQueueCallback(); if (SL_RESULT_SUCCESS != result) { return result; @@ -285,6 +288,12 @@ void AudioStreamOpenSLES::logUnsupportedAttributes() { LOGW("SessionId [AudioStreamBuilder::setSessionId()] " "is not supported on OpenSLES streams."); } + + // Privacy Sensitive Mode + if (mPrivacySensitiveMode != PrivacySensitiveMode::Unspecified) { + LOGW("PrivacySensitiveMode [AudioStreamBuilder::setPrivacySensitiveMode()] " + "is not supported on OpenSLES streams."); + } } SLresult AudioStreamOpenSLES::configurePerformanceMode(SLAndroidConfigurationItf configItf) { diff --git a/tests/testStreamOpen.cpp b/tests/testStreamOpen.cpp index 9d01c072..8eb97b94 100644 --- a/tests/testStreamOpen.cpp +++ b/tests/testStreamOpen.cpp @@ -460,3 +460,81 @@ TEST_F(StreamOpenOutput, OboeExtensions){ ASSERT_TRUE(OboeExtensions::isMMapEnabled()); } } + +TEST_F(StreamOpenInput, AAudioInputSetPrivacySensitiveModeUnspecifiedUnprocessed){ + if (getSdkVersion() >= __ANDROID_API_R__){ + mBuilder.setDirection(Direction::Input); + mBuilder.setAudioApi(AudioApi::AAudio); + mBuilder.setInputPreset(InputPreset::Unprocessed); + ASSERT_TRUE(openStream()); + ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Disabled); + ASSERT_TRUE(closeStream()); + } +} + +TEST_F(StreamOpenInput, AAudioInputSetPrivacySensitiveModeUnspecifiedVoiceCommunication){ + if (getSdkVersion() >= __ANDROID_API_R__){ + mBuilder.setDirection(Direction::Input); + mBuilder.setAudioApi(AudioApi::AAudio); + mBuilder.setInputPreset(InputPreset::VoiceCommunication); + ASSERT_TRUE(openStream()); + ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Enabled); + ASSERT_TRUE(closeStream()); + } +} + +TEST_F(StreamOpenInput, AAudioInputSetPrivacySensitiveModeVoiceDisabled){ + if (getSdkVersion() >= __ANDROID_API_R__){ + mBuilder.setDirection(Direction::Input); + mBuilder.setAudioApi(AudioApi::AAudio); + mBuilder.setInputPreset(InputPreset::VoiceCommunication); + mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Disabled); + ASSERT_TRUE(openStream()); + ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Disabled); + ASSERT_TRUE(closeStream()); + } +} + +TEST_F(StreamOpenInput, AAudioInputSetPrivacySensitiveModeUnprocessedEnabled){ + if (getSdkVersion() >= __ANDROID_API_R__){ + mBuilder.setDirection(Direction::Input); + mBuilder.setAudioApi(AudioApi::AAudio); + mBuilder.setInputPreset(InputPreset::Unprocessed); + mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Enabled); + ASSERT_TRUE(openStream()); + ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Enabled); + ASSERT_TRUE(closeStream()); + } +} + +TEST_F(StreamOpenOutput, AAudioOutputSetPrivacySensitiveModeGetsUnspecified){ + if (getSdkVersion() >= __ANDROID_API_R__){ + mBuilder.setDirection(Direction::Output); + mBuilder.setAudioApi(AudioApi::AAudio); + mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Enabled); + ASSERT_TRUE(openStream()); + ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Unspecified); + ASSERT_TRUE(closeStream()); + } +} + +TEST_F(StreamOpenInput, OpenSLESInputSetPrivacySensitiveModeDoesNotCrash){ + mBuilder.setDirection(Direction::Input); + mBuilder.setAudioApi(AudioApi::OpenSLES); + mBuilder.setInputPreset(InputPreset::Unprocessed); + mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Enabled); + ASSERT_TRUE(openStream()); + ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Unspecified); + ASSERT_TRUE(closeStream()); +} + +TEST_F(StreamOpenInput, OldAndroidVersionInputSetPrivacySensitiveModeDoesNotCrash){ + if (getSdkVersion() < __ANDROID_API_R__) { + mBuilder.setDirection(Direction::Input); + mBuilder.setInputPreset(InputPreset::Unprocessed); + mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Enabled); + ASSERT_TRUE(openStream()); + ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Unspecified); + ASSERT_TRUE(closeStream()); + } +} |