xref: /aosp_15_r20/external/oboe/src/common/QuirksManager.cpp (revision 05767d913155b055644481607e6fa1e35e2fe72c)
1*05767d91SRobert Wu /*
2*05767d91SRobert Wu  * Copyright 2019 The Android Open Source Project
3*05767d91SRobert Wu  *
4*05767d91SRobert Wu  * Licensed under the Apache License, Version 2.0 (the "License");
5*05767d91SRobert Wu  * you may not use this file except in compliance with the License.
6*05767d91SRobert Wu  * You may obtain a copy of the License at
7*05767d91SRobert Wu  *
8*05767d91SRobert Wu  *      http://www.apache.org/licenses/LICENSE-2.0
9*05767d91SRobert Wu  *
10*05767d91SRobert Wu  * Unless required by applicable law or agreed to in writing, software
11*05767d91SRobert Wu  * distributed under the License is distributed on an "AS IS" BASIS,
12*05767d91SRobert Wu  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*05767d91SRobert Wu  * See the License for the specific language governing permissions and
14*05767d91SRobert Wu  * limitations under the License.
15*05767d91SRobert Wu  */
16*05767d91SRobert Wu 
17*05767d91SRobert Wu #include <oboe/AudioStreamBuilder.h>
18*05767d91SRobert Wu #include <oboe/Oboe.h>
19*05767d91SRobert Wu 
20*05767d91SRobert Wu #include "OboeDebug.h"
21*05767d91SRobert Wu #include "QuirksManager.h"
22*05767d91SRobert Wu 
23*05767d91SRobert Wu using namespace oboe;
24*05767d91SRobert Wu 
clipBufferSize(AudioStream & stream,int32_t requestedSize)25*05767d91SRobert Wu int32_t QuirksManager::DeviceQuirks::clipBufferSize(AudioStream &stream,
26*05767d91SRobert Wu             int32_t requestedSize) {
27*05767d91SRobert Wu     if (!OboeGlobals::areWorkaroundsEnabled()) {
28*05767d91SRobert Wu         return requestedSize;
29*05767d91SRobert Wu     }
30*05767d91SRobert Wu     int bottomMargin = kDefaultBottomMarginInBursts;
31*05767d91SRobert Wu     int topMargin = kDefaultTopMarginInBursts;
32*05767d91SRobert Wu     if (isMMapUsed(stream)) {
33*05767d91SRobert Wu         if (stream.getSharingMode() == SharingMode::Exclusive) {
34*05767d91SRobert Wu             bottomMargin = getExclusiveBottomMarginInBursts();
35*05767d91SRobert Wu             topMargin = getExclusiveTopMarginInBursts();
36*05767d91SRobert Wu         }
37*05767d91SRobert Wu     } else {
38*05767d91SRobert Wu         bottomMargin = kLegacyBottomMarginInBursts;
39*05767d91SRobert Wu     }
40*05767d91SRobert Wu 
41*05767d91SRobert Wu     int32_t burst = stream.getFramesPerBurst();
42*05767d91SRobert Wu     int32_t minSize = bottomMargin * burst;
43*05767d91SRobert Wu     int32_t adjustedSize = requestedSize;
44*05767d91SRobert Wu     if (adjustedSize < minSize ) {
45*05767d91SRobert Wu         adjustedSize = minSize;
46*05767d91SRobert Wu     } else {
47*05767d91SRobert Wu         int32_t maxSize = stream.getBufferCapacityInFrames() - (topMargin * burst);
48*05767d91SRobert Wu         if (adjustedSize > maxSize ) {
49*05767d91SRobert Wu             adjustedSize = maxSize;
50*05767d91SRobert Wu         }
51*05767d91SRobert Wu     }
52*05767d91SRobert Wu     return adjustedSize;
53*05767d91SRobert Wu }
54*05767d91SRobert Wu 
isAAudioMMapPossible(const AudioStreamBuilder & builder) const55*05767d91SRobert Wu bool QuirksManager::DeviceQuirks::isAAudioMMapPossible(const AudioStreamBuilder &builder) const {
56*05767d91SRobert Wu     bool isSampleRateCompatible =
57*05767d91SRobert Wu             builder.getSampleRate() == oboe::Unspecified
58*05767d91SRobert Wu             || builder.getSampleRate() == kCommonNativeRate
59*05767d91SRobert Wu             || builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None;
60*05767d91SRobert Wu     return builder.getPerformanceMode() == PerformanceMode::LowLatency
61*05767d91SRobert Wu             && isSampleRateCompatible
62*05767d91SRobert Wu             && builder.getChannelCount() <= kChannelCountStereo;
63*05767d91SRobert Wu }
64*05767d91SRobert Wu 
shouldConvertFloatToI16ForOutputStreams()65*05767d91SRobert Wu bool QuirksManager::DeviceQuirks::shouldConvertFloatToI16ForOutputStreams() {
66*05767d91SRobert Wu     std::string productManufacturer = getPropertyString("ro.product.manufacturer");
67*05767d91SRobert Wu     if (getSdkVersion() < __ANDROID_API_L__) {
68*05767d91SRobert Wu         return true;
69*05767d91SRobert Wu     } else if ((productManufacturer == "vivo") && (getSdkVersion() < __ANDROID_API_M__)) {
70*05767d91SRobert Wu         return true;
71*05767d91SRobert Wu     }
72*05767d91SRobert Wu     return false;
73*05767d91SRobert Wu }
74*05767d91SRobert Wu 
75*05767d91SRobert Wu /**
76*05767d91SRobert Wu  * This is for Samsung Exynos quirks. Samsung Mobile uses Qualcomm chips so
77*05767d91SRobert Wu  * the QualcommDeviceQuirks would apply.
78*05767d91SRobert Wu  */
79*05767d91SRobert Wu class SamsungExynosDeviceQuirks : public  QuirksManager::DeviceQuirks {
80*05767d91SRobert Wu public:
SamsungExynosDeviceQuirks()81*05767d91SRobert Wu     SamsungExynosDeviceQuirks() {
82*05767d91SRobert Wu         std::string chipname = getPropertyString("ro.hardware.chipname");
83*05767d91SRobert Wu         isExynos9810 = (chipname == "exynos9810");
84*05767d91SRobert Wu         isExynos990 = (chipname == "exynos990");
85*05767d91SRobert Wu         isExynos850 = (chipname == "exynos850");
86*05767d91SRobert Wu 
87*05767d91SRobert Wu         mBuildChangelist = getPropertyInteger("ro.build.changelist", 0);
88*05767d91SRobert Wu     }
89*05767d91SRobert Wu 
90*05767d91SRobert Wu     virtual ~SamsungExynosDeviceQuirks() = default;
91*05767d91SRobert Wu 
getExclusiveBottomMarginInBursts() const92*05767d91SRobert Wu     int32_t getExclusiveBottomMarginInBursts() const override {
93*05767d91SRobert Wu         return kBottomMargin;
94*05767d91SRobert Wu     }
95*05767d91SRobert Wu 
getExclusiveTopMarginInBursts() const96*05767d91SRobert Wu     int32_t getExclusiveTopMarginInBursts() const override {
97*05767d91SRobert Wu         return kTopMargin;
98*05767d91SRobert Wu     }
99*05767d91SRobert Wu 
100*05767d91SRobert Wu     // See Oboe issues #824 and #1247 for more information.
isMonoMMapActuallyStereo() const101*05767d91SRobert Wu     bool isMonoMMapActuallyStereo() const override {
102*05767d91SRobert Wu         return isExynos9810 || isExynos850; // TODO We can make this version specific if it gets fixed.
103*05767d91SRobert Wu     }
104*05767d91SRobert Wu 
isAAudioMMapPossible(const AudioStreamBuilder & builder) const105*05767d91SRobert Wu     bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const override {
106*05767d91SRobert Wu         return DeviceQuirks::isAAudioMMapPossible(builder)
107*05767d91SRobert Wu                 // Samsung says they use Legacy for Camcorder
108*05767d91SRobert Wu                 && builder.getInputPreset() != oboe::InputPreset::Camcorder;
109*05767d91SRobert Wu     }
110*05767d91SRobert Wu 
isMMapSafe(const AudioStreamBuilder & builder)111*05767d91SRobert Wu     bool isMMapSafe(const AudioStreamBuilder &builder) override {
112*05767d91SRobert Wu         const bool isInput = builder.getDirection() == Direction::Input;
113*05767d91SRobert Wu         // This detects b/159066712 , S20 LSI has corrupt low latency audio recording
114*05767d91SRobert Wu         // and turns off MMAP.
115*05767d91SRobert Wu         // See also https://github.com/google/oboe/issues/892
116*05767d91SRobert Wu         bool isRecordingCorrupted = isInput
117*05767d91SRobert Wu             && isExynos990
118*05767d91SRobert Wu             && mBuildChangelist < 19350896;
119*05767d91SRobert Wu 
120*05767d91SRobert Wu         // Certain S9+ builds record silence when using MMAP and not using the VoiceCommunication
121*05767d91SRobert Wu         // preset.
122*05767d91SRobert Wu         // See https://github.com/google/oboe/issues/1110
123*05767d91SRobert Wu         bool wouldRecordSilence = isInput
124*05767d91SRobert Wu             && isExynos9810
125*05767d91SRobert Wu             && mBuildChangelist <= 18847185
126*05767d91SRobert Wu             && (builder.getInputPreset() != InputPreset::VoiceCommunication);
127*05767d91SRobert Wu 
128*05767d91SRobert Wu         if (wouldRecordSilence){
129*05767d91SRobert Wu             LOGI("QuirksManager::%s() Requested stream configuration would result in silence on "
130*05767d91SRobert Wu                  "this device. Switching off MMAP.", __func__);
131*05767d91SRobert Wu         }
132*05767d91SRobert Wu 
133*05767d91SRobert Wu         return !isRecordingCorrupted && !wouldRecordSilence;
134*05767d91SRobert Wu     }
135*05767d91SRobert Wu 
136*05767d91SRobert Wu private:
137*05767d91SRobert Wu     // Stay farther away from DSP position on Exynos devices.
138*05767d91SRobert Wu     static constexpr int32_t kBottomMargin = 2;
139*05767d91SRobert Wu     static constexpr int32_t kTopMargin = 1;
140*05767d91SRobert Wu     bool isExynos9810 = false;
141*05767d91SRobert Wu     bool isExynos990 = false;
142*05767d91SRobert Wu     bool isExynos850 = false;
143*05767d91SRobert Wu     int mBuildChangelist = 0;
144*05767d91SRobert Wu };
145*05767d91SRobert Wu 
146*05767d91SRobert Wu class QualcommDeviceQuirks : public  QuirksManager::DeviceQuirks {
147*05767d91SRobert Wu public:
QualcommDeviceQuirks()148*05767d91SRobert Wu     QualcommDeviceQuirks() {
149*05767d91SRobert Wu         std::string modelName = getPropertyString("ro.soc.model");
150*05767d91SRobert Wu         isSM8150 = (modelName == "SDM8150");
151*05767d91SRobert Wu     }
152*05767d91SRobert Wu 
153*05767d91SRobert Wu     virtual ~QualcommDeviceQuirks() = default;
154*05767d91SRobert Wu 
getExclusiveBottomMarginInBursts() const155*05767d91SRobert Wu     int32_t getExclusiveBottomMarginInBursts() const override {
156*05767d91SRobert Wu         return kBottomMargin;
157*05767d91SRobert Wu     }
158*05767d91SRobert Wu 
isMMapSafe(const AudioStreamBuilder & builder)159*05767d91SRobert Wu     bool isMMapSafe(const AudioStreamBuilder &builder) override {
160*05767d91SRobert Wu         // See https://github.com/google/oboe/issues/1121#issuecomment-897957749
161*05767d91SRobert Wu         bool isMMapBroken = false;
162*05767d91SRobert Wu         if (isSM8150 && (getSdkVersion() <= __ANDROID_API_P__)) {
163*05767d91SRobert Wu             LOGI("QuirksManager::%s() MMAP not actually supported on this chip."
164*05767d91SRobert Wu                  " Switching off MMAP.", __func__);
165*05767d91SRobert Wu             isMMapBroken = true;
166*05767d91SRobert Wu         }
167*05767d91SRobert Wu 
168*05767d91SRobert Wu         return !isMMapBroken;
169*05767d91SRobert Wu     }
170*05767d91SRobert Wu 
171*05767d91SRobert Wu private:
172*05767d91SRobert Wu     bool isSM8150 = false;
173*05767d91SRobert Wu     static constexpr int32_t kBottomMargin = 1;
174*05767d91SRobert Wu };
175*05767d91SRobert Wu 
QuirksManager()176*05767d91SRobert Wu QuirksManager::QuirksManager() {
177*05767d91SRobert Wu     std::string productManufacturer = getPropertyString("ro.product.manufacturer");
178*05767d91SRobert Wu     if (productManufacturer == "samsung") {
179*05767d91SRobert Wu         std::string arch = getPropertyString("ro.arch");
180*05767d91SRobert Wu         bool isExynos = (arch.rfind("exynos", 0) == 0); // starts with?
181*05767d91SRobert Wu         if (isExynos) {
182*05767d91SRobert Wu             mDeviceQuirks = std::make_unique<SamsungExynosDeviceQuirks>();
183*05767d91SRobert Wu         }
184*05767d91SRobert Wu     }
185*05767d91SRobert Wu     if (!mDeviceQuirks) {
186*05767d91SRobert Wu         std::string socManufacturer = getPropertyString("ro.soc.manufacturer");
187*05767d91SRobert Wu         if (socManufacturer == "Qualcomm") {
188*05767d91SRobert Wu             // This may include Samsung Mobile devices.
189*05767d91SRobert Wu             mDeviceQuirks = std::make_unique<QualcommDeviceQuirks>();
190*05767d91SRobert Wu         } else {
191*05767d91SRobert Wu             mDeviceQuirks = std::make_unique<DeviceQuirks>();
192*05767d91SRobert Wu         }
193*05767d91SRobert Wu     }
194*05767d91SRobert Wu }
195*05767d91SRobert Wu 
isConversionNeeded(const AudioStreamBuilder & builder,AudioStreamBuilder & childBuilder)196*05767d91SRobert Wu bool QuirksManager::isConversionNeeded(
197*05767d91SRobert Wu         const AudioStreamBuilder &builder,
198*05767d91SRobert Wu         AudioStreamBuilder &childBuilder) {
199*05767d91SRobert Wu     bool conversionNeeded = false;
200*05767d91SRobert Wu     const bool isLowLatency = builder.getPerformanceMode() == PerformanceMode::LowLatency;
201*05767d91SRobert Wu     const bool isInput = builder.getDirection() == Direction::Input;
202*05767d91SRobert Wu     const bool isFloat = builder.getFormat() == AudioFormat::Float;
203*05767d91SRobert Wu     const bool isIEC61937 = builder.getFormat() == AudioFormat::IEC61937;
204*05767d91SRobert Wu 
205*05767d91SRobert Wu     // There should be no conversion for IEC61937. Sample rates and channel counts must be set explicitly.
206*05767d91SRobert Wu     if (isIEC61937) {
207*05767d91SRobert Wu         LOGI("QuirksManager::%s() conversion not needed for IEC61937", __func__);
208*05767d91SRobert Wu         return false;
209*05767d91SRobert Wu     }
210*05767d91SRobert Wu 
211*05767d91SRobert Wu     // There are multiple bugs involving using callback with a specified callback size.
212*05767d91SRobert Wu     // Issue #778: O to Q had a problem with Legacy INPUT streams for FLOAT streams
213*05767d91SRobert Wu     // and a specified callback size. It would assert because of a bad buffer size.
214*05767d91SRobert Wu     //
215*05767d91SRobert Wu     // Issue #973: O to R had a problem with Legacy output streams using callback and a specified callback size.
216*05767d91SRobert Wu     // An AudioTrack stream could still be running when the AAudio FixedBlockReader was closed.
217*05767d91SRobert Wu     // Internally b/161914201#comment25
218*05767d91SRobert Wu     //
219*05767d91SRobert Wu     // Issue #983: O to R would glitch if the framesPerCallback was too small.
220*05767d91SRobert Wu     //
221*05767d91SRobert Wu     // Most of these problems were related to Legacy stream. MMAP was OK. But we don't
222*05767d91SRobert Wu     // know if we will get an MMAP stream. So, to be safe, just do the conversion in Oboe.
223*05767d91SRobert Wu     if (OboeGlobals::areWorkaroundsEnabled()
224*05767d91SRobert Wu             && builder.willUseAAudio()
225*05767d91SRobert Wu             && builder.isDataCallbackSpecified()
226*05767d91SRobert Wu             && builder.getFramesPerDataCallback() != 0
227*05767d91SRobert Wu             && getSdkVersion() <= __ANDROID_API_R__) {
228*05767d91SRobert Wu         LOGI("QuirksManager::%s() avoid setFramesPerCallback(n>0)", __func__);
229*05767d91SRobert Wu         childBuilder.setFramesPerCallback(oboe::Unspecified);
230*05767d91SRobert Wu         conversionNeeded = true;
231*05767d91SRobert Wu     }
232*05767d91SRobert Wu 
233*05767d91SRobert Wu     // If a SAMPLE RATE is specified for low latency, let the native code choose an optimal rate.
234*05767d91SRobert Wu     // This isn't really a workaround. It is an Oboe feature that is convenient to place here.
235*05767d91SRobert Wu     // TODO There may be a problem if the devices supports low latency
236*05767d91SRobert Wu     //      at a higher rate than the default.
237*05767d91SRobert Wu     if (builder.getSampleRate() != oboe::Unspecified
238*05767d91SRobert Wu             && builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None
239*05767d91SRobert Wu             && isLowLatency
240*05767d91SRobert Wu             ) {
241*05767d91SRobert Wu         childBuilder.setSampleRate(oboe::Unspecified); // native API decides the best sample rate
242*05767d91SRobert Wu         conversionNeeded = true;
243*05767d91SRobert Wu     }
244*05767d91SRobert Wu 
245*05767d91SRobert Wu     // Data Format
246*05767d91SRobert Wu     // OpenSL ES and AAudio before P do not support FAST path for FLOAT capture.
247*05767d91SRobert Wu     if (OboeGlobals::areWorkaroundsEnabled()
248*05767d91SRobert Wu             && isFloat
249*05767d91SRobert Wu             && isInput
250*05767d91SRobert Wu             && builder.isFormatConversionAllowed()
251*05767d91SRobert Wu             && isLowLatency
252*05767d91SRobert Wu             && (!builder.willUseAAudio() || (getSdkVersion() < __ANDROID_API_P__))
253*05767d91SRobert Wu             ) {
254*05767d91SRobert Wu         childBuilder.setFormat(AudioFormat::I16); // needed for FAST track
255*05767d91SRobert Wu         conversionNeeded = true;
256*05767d91SRobert Wu         LOGI("QuirksManager::%s() forcing internal format to I16 for low latency", __func__);
257*05767d91SRobert Wu     }
258*05767d91SRobert Wu 
259*05767d91SRobert Wu     // Add quirk for float output when needed.
260*05767d91SRobert Wu     if (OboeGlobals::areWorkaroundsEnabled()
261*05767d91SRobert Wu             && isFloat
262*05767d91SRobert Wu             && !isInput
263*05767d91SRobert Wu             && builder.isFormatConversionAllowed()
264*05767d91SRobert Wu             && mDeviceQuirks->shouldConvertFloatToI16ForOutputStreams()
265*05767d91SRobert Wu             ) {
266*05767d91SRobert Wu         childBuilder.setFormat(AudioFormat::I16);
267*05767d91SRobert Wu         conversionNeeded = true;
268*05767d91SRobert Wu         LOGI("QuirksManager::%s() float was requested but not supported on pre-L devices "
269*05767d91SRobert Wu              "and some devices like Vivo devices may have issues on L devices, "
270*05767d91SRobert Wu              "creating an underlying I16 stream and using format conversion to provide a float "
271*05767d91SRobert Wu              "stream", __func__);
272*05767d91SRobert Wu     }
273*05767d91SRobert Wu 
274*05767d91SRobert Wu     // Channel Count conversions
275*05767d91SRobert Wu     if (OboeGlobals::areWorkaroundsEnabled()
276*05767d91SRobert Wu             && builder.isChannelConversionAllowed()
277*05767d91SRobert Wu             && builder.getChannelCount() == kChannelCountStereo
278*05767d91SRobert Wu             && isInput
279*05767d91SRobert Wu             && isLowLatency
280*05767d91SRobert Wu             && (!builder.willUseAAudio() && (getSdkVersion() == __ANDROID_API_O__))
281*05767d91SRobert Wu             ) {
282*05767d91SRobert Wu         // Workaround for heap size regression in O.
283*05767d91SRobert Wu         // b/66967812 AudioRecord does not allow FAST track for stereo capture in O
284*05767d91SRobert Wu         childBuilder.setChannelCount(kChannelCountMono);
285*05767d91SRobert Wu         conversionNeeded = true;
286*05767d91SRobert Wu         LOGI("QuirksManager::%s() using mono internally for low latency on O", __func__);
287*05767d91SRobert Wu     } else if (OboeGlobals::areWorkaroundsEnabled()
288*05767d91SRobert Wu                && builder.getChannelCount() == kChannelCountMono
289*05767d91SRobert Wu                && isInput
290*05767d91SRobert Wu                && mDeviceQuirks->isMonoMMapActuallyStereo()
291*05767d91SRobert Wu                && builder.willUseAAudio()
292*05767d91SRobert Wu                // Note: we might use this workaround on a device that supports
293*05767d91SRobert Wu                // MMAP but will use Legacy for this stream.  But this will only happen
294*05767d91SRobert Wu                // on devices that have the broken mono.
295*05767d91SRobert Wu                && mDeviceQuirks->isAAudioMMapPossible(builder)
296*05767d91SRobert Wu                ) {
297*05767d91SRobert Wu         // Workaround for mono actually running in stereo mode.
298*05767d91SRobert Wu         childBuilder.setChannelCount(kChannelCountStereo); // Use stereo and extract first channel.
299*05767d91SRobert Wu         conversionNeeded = true;
300*05767d91SRobert Wu         LOGI("QuirksManager::%s() using stereo internally to avoid broken mono", __func__);
301*05767d91SRobert Wu     }
302*05767d91SRobert Wu     // Note that MMAP does not support mono in 8.1. But that would only matter on Pixel 1
303*05767d91SRobert Wu     // phones and they have almost all been updated to 9.0.
304*05767d91SRobert Wu 
305*05767d91SRobert Wu     return conversionNeeded;
306*05767d91SRobert Wu }
307*05767d91SRobert Wu 
isMMapSafe(AudioStreamBuilder & builder)308*05767d91SRobert Wu bool QuirksManager::isMMapSafe(AudioStreamBuilder &builder) {
309*05767d91SRobert Wu     if (!OboeGlobals::areWorkaroundsEnabled()) return true;
310*05767d91SRobert Wu     return mDeviceQuirks->isMMapSafe(builder);
311*05767d91SRobert Wu }
312