Merge "AudioService: Observe changes to output devices per-stream."
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 28941b9..cb70e8b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -134,6 +134,22 @@
     public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
 
     /**
+     * @hide Broadcast intent when the devices for a particular stream type changes.
+     * Includes the stream, the new devices and previous devices.
+     * Notes:
+     *  - for internal platform use only, do not make public,
+     *  - never used for "remote" volume changes
+     *
+     * @see #EXTRA_VOLUME_STREAM_TYPE
+     * @see #EXTRA_VOLUME_STREAM_DEVICES
+     * @see #EXTRA_PREV_VOLUME_STREAM_DEVICES
+     * @see #getDevicesForStream
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String STREAM_DEVICES_CHANGED_ACTION =
+        "android.media.STREAM_DEVICES_CHANGED_ACTION";
+
+    /**
      * @hide Broadcast intent when a stream mute state changes.
      * Includes the stream that changed and the new mute state
      *
@@ -196,6 +212,18 @@
         "android.media.EXTRA_PREV_VOLUME_STREAM_VALUE";
 
     /**
+     * @hide The devices associated with the stream for the stream devices changed intent.
+     */
+    public static final String EXTRA_VOLUME_STREAM_DEVICES =
+        "android.media.EXTRA_VOLUME_STREAM_DEVICES";
+
+    /**
+     * @hide The previous devices associated with the stream for the stream devices changed intent.
+     */
+    public static final String EXTRA_PREV_VOLUME_STREAM_DEVICES =
+        "android.media.EXTRA_PREV_VOLUME_STREAM_DEVICES";
+
+    /**
      * @hide The new master volume mute state for the master mute changed intent.
      * Value is boolean
      */
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 983d83a..694e851 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -221,3 +221,4 @@
 # AudioService.java
 # ---------------------------
 40000 volume_changed (stream|1), (prev_level|1), (level|1), (max_level|1), (caller|3)
+40001 stream_devices_changed (stream|1), (prev_devices|1), (devices|1)
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9d311f9..1eddc8e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3299,7 +3299,7 @@
     }
 
     private int getDeviceForStream(int stream) {
-        int device = AudioSystem.getDevicesForStream(stream);
+        int device = getDevicesForStream(stream);
         if ((device & (device - 1)) != 0) {
             // Multiple device selection is either:
             //  - speaker + one other device: give priority to speaker in this case.
@@ -3322,6 +3322,27 @@
         return device;
     }
 
+    private int getDevicesForStream(int stream) {
+        return getDevicesForStream(stream, true /*checkOthers*/);
+    }
+
+    private int getDevicesForStream(int stream, boolean checkOthers) {
+        ensureValidStreamType(stream);
+        synchronized (VolumeStreamState.class) {
+            return mStreamStates[stream].observeDevicesForStream_syncVSS(checkOthers);
+        }
+    }
+
+    private void observeDevicesForStreams(int skipStream) {
+        synchronized (VolumeStreamState.class) {
+            for (int stream = 0; stream < mStreamStates.length; stream++) {
+                if (stream != skipStream) {
+                    mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/);
+                }
+            }
+        }
+    }
+
     /*
      * A class just for packaging up a set of connection parameters.
      */
@@ -3400,9 +3421,11 @@
 
         private boolean mIsMuted;
         private String mVolumeIndexSettingName;
+        private int mObservedDevices;
 
         private final SparseIntArray mIndexMap = new SparseIntArray(8);
         private final Intent mVolumeChanged;
+        private final Intent mStreamDevicesChanged;
 
         private VolumeStreamState(String settingName, int streamType) {
 
@@ -3416,6 +3439,29 @@
             readSettings();
             mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
             mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
+            mStreamDevicesChanged = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
+            mStreamDevicesChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
+        }
+
+        public int observeDevicesForStream_syncVSS(boolean checkOthers) {
+            final int devices = AudioSystem.getDevicesForStream(mStreamType);
+            if (devices == mObservedDevices) {
+                return devices;
+            }
+            final int prevDevices = mObservedDevices;
+            mObservedDevices = devices;
+            if (checkOthers) {
+                // one stream's devices have changed, check the others
+                observeDevicesForStreams(mStreamType);
+            }
+            // log base stream changes to the event log
+            if (mStreamVolumeAlias[mStreamType] == mStreamType) {
+                EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices);
+            }
+            sendBroadcastToAll(mStreamDevicesChanged
+                    .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, prevDevices)
+                    .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, devices));
+            return devices;
         }
 
         public String getSettingNameForDevice(int device) {
@@ -3710,7 +3756,7 @@
             }
             pw.println();
             pw.print("   Devices: ");
-            final int devices = AudioSystem.getDevicesForStream(mStreamType);
+            final int devices = getDevicesForStream(mStreamType);
             int device, i = 0, n = 0;
             // iterate all devices from 1 to DEVICE_OUT_DEFAULT exclusive
             // (the default device is not returned by getDevicesForStream)
@@ -4244,6 +4290,7 @@
                         }
                     }
                     mRoutesObservers.finishBroadcast();
+                    observeDevicesForStreams(-1);
                     break;
                 }
 
@@ -5342,7 +5389,7 @@
                                 on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED :
                                      AudioSystem.FORCE_NONE);
                     }
-                    device = AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
+                    device = getDevicesForStream(AudioSystem.STREAM_MUSIC);
                 }
             }
         }