Skip to content

setPreferredMixerAttributes on USB DAC can cause whole system to hang on MTK device with MTK Hi-Fi #2198

Open
@nift4

Description

@nift4

Android version(s): Android 14 (not QPR) stock ROM
Android device(s): Any MTK device with MTK Hi-Fi enabled (I used Unihertz Jelly Max)
Oboe version: Any (reproduces on 1.9.2)
App name used for testing: OboeTester with patch on top of e3bde1b

diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java
index f286ffbb..ff62151e 100644
--- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java
+++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java
@@ -27,6 +27,7 @@ import android.content.pm.ServiceInfo;
 import android.media.AudioAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
+import android.media.AudioMixerAttributes;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -46,6 +47,8 @@ import androidx.appcompat.app.AppCompatActivity;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -631,6 +634,39 @@ abstract class TestAudioActivity extends AppCompatActivity {
     public void openAudio() throws IOException {
         closeAudio();
 
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+            AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+            List<AudioDeviceInfo> info = Arrays.asList(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS));
+            for (AudioDeviceInfo device : info) {
+                List<AudioMixerAttributes> attr = audioManager.getSupportedMixerAttributes(device);
+                if (attr.isEmpty()) continue;
+                attr.sort((a, b) -> {
+                    if (a.getMixerBehavior() != b.getMixerBehavior()) {
+                        return a.getMixerBehavior() == AudioMixerAttributes.MIXER_BEHAVIOR_BIT_PERFECT ? 1 : -1;
+                    }
+                    if (a.getFormat().getSampleRate() != b.getFormat().getSampleRate()) {
+                        return a.getFormat().getSampleRate() > b.getFormat().getSampleRate() ? 1 : -1;
+                    }
+                    if (a.getFormat().getChannelCount() != b.getFormat().getChannelCount()) {
+                        return a.getFormat().getChannelCount() > b.getFormat().getChannelCount() ? 1 : -1;
+                    }
+                    if (a.getFormat().getEncoding() != b.getFormat().getEncoding()) { // :)
+                        return a.getFormat().getEncoding() > b.getFormat().getEncoding() ? 1 : -1;
+                    }
+                    return 0;
+                });
+                audioManager.setPreferredMixerAttributes(new AudioAttributes.Builder()
+                        .setUsage(AudioAttributes.USAGE_MEDIA)
+                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+                        .build(), device, attr.get(0));
+            }
+               try {
+                       Thread.sleep(500);
+               } catch (InterruptedException e) {
+                       throw new RuntimeException(e);
+               }
+        }
+
         updateNativeAudioParameters();
 
         if (!isTestConfiguredUsingBundle()) {
@@ -811,6 +847,19 @@ abstract class TestAudioActivity extends AppCompatActivity {
             updateEnabledWidgets();
             onStopAllContexts();
         }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+            AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+            List<AudioDeviceInfo> info = Arrays.asList(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS));
+            for (AudioDeviceInfo device : info) {
+                List<AudioMixerAttributes> attr = audioManager.getSupportedMixerAttributes(device);
+                if (attr.isEmpty()) continue;
+                audioManager.clearPreferredMixerAttributes(new AudioAttributes.Builder()
+                        .setUsage(AudioAttributes.USAGE_MEDIA)
+                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+                        .build(), device);
+            }
+        }
     }
 
     public void runTest() {

Short description
When stress testing open/close with preferred mixer attributes, device will progressively become more and more unresponsive until eventually hanging for minutes in buggy state (live wallpaper became black, can't open settings app - hanging forever, etc) before recovering. Presumably root cause is audioserver 100% CPU:

Image

Please see attached video for how to reproduce

file.mp4

Bug report ZIP
bugreport-Jelly_Max_EEA-UP1A.231005.007-2025-04-08-19-44-54.zip

Steps to reproduce
Please see the attached video, it was recorded on freshly factory reset test device, no device settings modified.

Expected behavior
No ANR, wallpaper not turning black, settings app keeps working, audioserver not 100% cpu, etc

Actual behavior
see above :)

Device
ro.product.brand = Unihertz
ro.product.manufacturer = Unihertz
ro.product.model = Jelly Max
ro.product.device = Jelly_Max
ro.product.cpu.abi = arm64-v8a
ro.build.description = sys_mssi_64_64only_ww_armv82_g78v78c2k_dfl_eea-user 14 UP1A.231005.007 V01.00.00 release-keys
ro.hardware = mt6878
ro.hardware.chipname =
ro.arch =
| grep aaudio = [aaudio.mmap_exclusive_policy]: [2]
[aaudio.mmap_policy]: [2]

Any additional context
not reproducible on Pixel

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions