title: How to reliably fetch and detect changes to the active audio route on Android?
body: <p>I have a feature where I need to <strong>fetch current</strong> AND <strong>detect switch</strong> of the active audio output device of the system. Something like:</p>
  
  <ul>
  <li>run app -> fetch current device (which might be embedded speakers)</li>
  <li>connect bluetooth speaker -> detect switch to bluetooth speaker</li>
  <li>connect audio jack -> detect switch from bluetooth speaker to audio jack</li>
  </ul>
  
  <p>It seems to me there should be one API for this but it seems there are several. The one that makes the most sense seems to be <code>MediaRouter</code> but there are two large issues with it that prevent me from using it as described above:</p>
  
  <ul>
  <li>There are at least 2 scenarios in which it fetches the wrong device as the active route and on other scenarios it straight up doesn't detect route switches at all. For details on this please check out <a href="https://issuetracker.google.com/issues/153552144" rel="noreferrer">the ticket I opened on the google issue tracker</a></li>
  <li>I need certain information on the current active output device, most importantly a universal identifier. For bluetooth devices, the <code>Complete Local Name</code> can be used for this, which is available from <code>BluetoothDevice</code> types but this is not obtainable from the <code>MediaRouter</code>. Instead, <code>MediaRouter</code> makes a <code>productName</code> available, which sometimes can be an <strong>alias</strong> of the bluetooth devices, and not the static name.</li>
  </ul>
  
  <p>I have since abandoned this API and moved on to find other solutions, let me know if anyone thinks there's a way around the problems above, most importantly the first one. I will note that I have used both the <code>android.media.MediaRouter</code> and the <code>androidx</code> one, they both have these issues.</p>
  
  <p>I am now leveraging a combination of other APIs. It works but it is a far from acceptable solution:</p>
  
  <ul>
  <li><p>Using <code>AudioManager.registerAudioDeviceCallback(object : AudioDeviceCallback)</code> for non-bluetooth devices detection (<code>AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)</code>). I'm getting callbacks when any of these devices get added or removed and then I can check if a <strong>wired</strong> type device is present then that's the active one 100% of the time, if it's not, then the embedded-type is active. It's kind of hacky but it works.</p></li>
  <li><p>More interestingly, for detection of <strong>Bluetooth</strong> output devices I use a private API, that <strong>Android itself uses in the Settings app to display the current audio output device</strong>. </p>
  
  <ul>
  <li><code>BluetoothA2dp::class.java.getDeclaredMethod("getActiveDevice")</code>  to get the current active output device. This is done by getting the profile proxy service <code>bluetoothAdapter.getProfileProxy</code></li>
  <li>register a receiver for this action <code>BluetoothA2dp::class.java.getField("ACTION_ACTIVE_DEVICE_CHANGED").get(null)</code> in order to detect switches of the bluetooth active audio device.</li>
  </ul></li>
  </ul>
  
  <p>This solution works but also has a few limitations:</p>
  
  <ul>
  <li>they are obvious private APIs, subjected to sudden change and potential breaks</li>
  <li>the <strong>active device</strong> APIs mentioned above are not available &lt; Android 9. I have yet to investigate what Android is using pre-Android9 in the Settings app</li>
  <li>On Samsung devices the <strong>active device</strong> API works incorrectly.</li>
  </ul>
  
  <p>So, the million dollar question. How do I fetch and detect the current audio route on Android, <strong>reliably</strong>?</p>