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