Let WiredHeadsetManager recognize USB headsets.

WiredHeadsetManager will now determine whether a headset is plugged in
by querying the list of audio devices and matching it against a list of
device types recognized as wired headsets, rather than calling
AudioManager#isWiredHeadsetOn which ignores USB headsets.

Detecting when the list of connected audio devices is now done through
an AudioManager callback instead of listening to an Intent.

Bug: 25110008
Change-Id: I27ac4b835be8a76529c3564df3f8d3d00540cc84
diff --git a/src/com/android/server/telecom/WiredHeadsetManager.java b/src/com/android/server/telecom/WiredHeadsetManager.java
index 253dfca..b73c013 100644
--- a/src/com/android/server/telecom/WiredHeadsetManager.java
+++ b/src/com/android/server/telecom/WiredHeadsetManager.java
@@ -16,10 +16,9 @@
 
 package com.android.server.telecom;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -32,30 +31,47 @@
 /** Listens for and caches headset state. */
 @VisibleForTesting
 public class WiredHeadsetManager {
+    private static final int VALID_WIRED_DEVICES = AudioManager.DEVICE_OUT_WIRED_HEADSET |
+            AudioManager.DEVICE_OUT_WIRED_HEADPHONE |
+            AudioManager.DEVICE_OUT_USB_DEVICE;
+
     @VisibleForTesting
     public interface Listener {
         void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn);
     }
 
     /** Receiver for wired headset plugged and unplugged events. */
-    private class WiredHeadsetBroadcastReceiver extends BroadcastReceiver {
+    private class WiredHeadsetCallback extends AudioDeviceCallback {
         @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.startSession("WHBR.oR");
+        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+            Log.startSession("WHC.oADA");
             try {
-                if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
-                    boolean isPluggedIn = mAudioManager.isWiredHeadsetOn();
-                    Log.v(WiredHeadsetManager.this, "ACTION_HEADSET_PLUG event, plugged in: %b",
-                            isPluggedIn);
-                    onHeadsetPluggedInChanged(isPluggedIn);
-                }
+                updateHeadsetStatus();
             } finally {
                 Log.endSession();
             }
         }
+
+        @Override
+        public void onAudioDevicesRemoved(AudioDeviceInfo[] devices) {
+            Log.startSession("WHC.oADR");
+            try {
+                updateHeadsetStatus();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        private void updateHeadsetStatus() {
+            int devices = mAudioManager.getDevicesForStream(AudioManager.STREAM_VOICE_CALL);
+
+            boolean isPluggedIn = (devices & VALID_WIRED_DEVICES) != 0;
+            Log.i(WiredHeadsetManager.this, "ACTION_HEADSET_PLUG event, plugged in: %b, " +
+                    "devices: %s", isPluggedIn, Integer.toHexString(devices));
+            onHeadsetPluggedInChanged(isPluggedIn);
+        }
     }
 
-    private final WiredHeadsetBroadcastReceiver mReceiver;
     private final AudioManager mAudioManager;
     private boolean mIsPluggedIn;
     /**
@@ -64,17 +80,13 @@
      * access the map so make only a single shard
      */
     private final Set<Listener> mListeners = Collections.newSetFromMap(
-            new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
+            new ConcurrentHashMap<>(8, 0.9f, 1));
 
     public WiredHeadsetManager(Context context) {
-        mReceiver = new WiredHeadsetBroadcastReceiver();
-
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         mIsPluggedIn = mAudioManager.isWiredHeadsetOn();
 
-        // Register for misc other intent broadcasts.
-        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
-        context.registerReceiver(mReceiver, intentFilter);
+        mAudioManager.registerAudioDeviceCallback(new WiredHeadsetCallback(), null);
     }
 
     @VisibleForTesting