Loading core/java/android/server/BluetoothA2dpService.java +49 −10 Original line number Diff line number Diff line Loading @@ -33,7 +33,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.Handler; import android.os.Message; import android.os.ParcelUuid; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.provider.Settings; import android.util.Log; Loading Loading @@ -65,6 +69,10 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private final BluetoothAdapter mAdapter; private int mTargetA2dpState; private BluetoothDevice mPlayingA2dpDevice; private IntentBroadcastHandler mIntentBroadcastHandler; private final WakeLock mWakeLock; private static final int MSG_CONNECTION_STATE_CHANGED = 0; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -131,6 +139,11 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { public BluetoothA2dpService(Context context, BluetoothService bluetoothService) { mContext = context; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService"); mIntentBroadcastHandler = new IntentBroadcastHandler(); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mBluetoothService = bluetoothService; Loading Loading @@ -514,17 +527,15 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { adjustOtherSinkPriorities(device); } Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, state); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BLUETOOTH_PERM); int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, state); if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state); mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state, prevState); mWakeLock.acquire(); mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( MSG_CONNECTION_STATE_CHANGED, prevState, state, device), delay); } } Loading Loading @@ -586,6 +597,34 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } } /** Handles A2DP connection state change intent broadcasts. */ private class IntentBroadcastHandler extends Handler { private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, state); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BLUETOOTH_PERM); if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state); mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state, prevState); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_CONNECTION_STATE_CHANGED: onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); mWakeLock.release(); break; } } } @Override protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); Loading media/java/android/media/AudioManager.java +37 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.media; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading Loading @@ -2376,6 +2377,42 @@ public class AudioManager { } } /** * Indicate wired accessory connection state change. * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx) * @param state new connection state: 1 connected, 0 disconnected * @param name device name * {@hide} */ public void setWiredDeviceConnectionState(int device, int state, String name) { IAudioService service = getService(); try { service.setWiredDeviceConnectionState(device, state, name); } catch (RemoteException e) { Log.e(TAG, "Dead object in setWiredDeviceConnectionState "+e); } } /** * Indicate A2DP sink connection state change. * @param device Bluetooth device connected/disconnected * @param state new connection state (BluetoothProfile.STATE_xxx) * @return a delay in ms that the caller should wait before broadcasting * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent. * {@hide} */ public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state) { IAudioService service = getService(); int delay = 0; try { delay = service.setBluetoothA2dpDeviceConnectionState(device, state); } catch (RemoteException e) { Log.e(TAG, "Dead object in setBluetoothA2dpDeviceConnectionState "+e); } finally { return delay; } } /** {@hide} */ public IRingtonePlayer getRingtonePlayer() { try { Loading media/java/android/media/AudioService.java +125 −50 Original line number Diff line number Diff line Loading @@ -135,6 +135,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private static final int MSG_RCDISPLAY_UPDATE = 13; private static final int MSG_SET_ALL_VOLUMES = 14; private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15; private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 16; private static final int MSG_SET_A2DP_CONNECTION_STATE = 17; // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be Loading Loading @@ -442,15 +444,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // Register for device connection intent broadcasts. IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); intentFilter.addAction(Intent.ACTION_DOCK_EVENT); intentFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); intentFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG); intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG); intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); Loading Loading @@ -1961,7 +1957,19 @@ public class AudioService extends IAudioService.Stub implements OnFinished { deviceList = a2dp.getConnectedDevices(); if (deviceList.size() > 0) { btDevice = deviceList.get(0); handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice)); synchronized (mConnectedDevices) { int state = a2dp.getConnectionState(btDevice); int delay = checkSendBecomingNoisyIntent( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0); sendMsg(mAudioHandler, MSG_SET_A2DP_CONNECTION_STATE, SENDMSG_QUEUE, state, 0, btDevice, delay); } } break; Loading Loading @@ -2262,6 +2270,36 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return device; } public void setWiredDeviceConnectionState(int device, int state, String name) { synchronized (mConnectedDevices) { int delay = checkSendBecomingNoisyIntent(device, state); sendMsg(mAudioHandler, MSG_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, device, state, name, delay); } } public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state) { int delay; synchronized (mConnectedDevices) { delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0); sendMsg(mAudioHandler, MSG_SET_A2DP_CONNECTION_STATE, SENDMSG_QUEUE, state, 0, device, delay); } return delay; } /////////////////////////////////////////////////////////////////////////// // Inner classes /////////////////////////////////////////////////////////////////////////// Loading Loading @@ -2959,6 +2997,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { case MSG_BT_HEADSET_CNCT_FAILED: resetBluetoothSco(); break; case MSG_SET_WIRED_DEVICE_CONNECTION_STATE: onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj); break; case MSG_SET_A2DP_CONNECTION_STATE: onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1); break; } } } Loading Loading @@ -3020,7 +3066,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // must be called synchronized on mConnectedDevices private void makeA2dpDeviceUnavailableNow(String address) { sendBecomingNoisyIntent(); AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address); Loading Loading @@ -3050,7 +3095,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); } private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state) private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state) { if (btDevice == null) { return; Loading Loading @@ -3116,6 +3161,76 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return false; } // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only // sent if none of these devices is connected. int mBecomingNoisyIntentDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE | AudioSystem.DEVICE_OUT_ALL_A2DP; // must be called before removing the device from mConnectedDevices private int checkSendBecomingNoisyIntent(int device, int state) { int delay = 0; if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) { int devices = 0; for (int dev : mConnectedDevices.keySet()) { if ((dev & mBecomingNoisyIntentDevices) != 0) { devices |= dev; } } if (devices == device) { delay = 1000; sendBecomingNoisyIntent(); } } if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) || mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) { delay = 1000; } return delay; } private void sendDeviceConnectionIntent(int device, int state, String name) { Intent intent = new Intent(); intent.putExtra("state", state); intent.putExtra("name", name); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) { intent.setAction(Intent.ACTION_HEADSET_PLUG); intent.putExtra("microphone", 1); } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) { intent.setAction(Intent.ACTION_HEADSET_PLUG); intent.putExtra("microphone", 0); } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) { intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) { intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) { intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG); } ActivityManagerNative.broadcastStickyIntent(intent, null); } private void onSetWiredDeviceConnectionState(int device, int state, String name) { synchronized (mConnectedDevices) { if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) || (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) { setBluetoothA2dpOnInt(true); } handleDeviceConnection((state == 1), device, ""); if ((state != 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) || (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) { setBluetoothA2dpOnInt(false); } sendDeviceConnectionIntent(device, state, name); } } /* cache of the address of the last dock the device was connected to */ private String mDockAddress; Loading Loading @@ -3151,12 +3266,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { config = AudioSystem.FORCE_NONE; } AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); handleA2dpConnectionStateChange(btDevice, state); } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); Loading Loading @@ -3197,43 +3306,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } } } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { state = intent.getIntExtra("state", 0); int microphone = intent.getIntExtra("microphone", 0); if (microphone != 0) { device = AudioSystem.DEVICE_OUT_WIRED_HEADSET; } else { device = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE; } // enable A2DP before notifying headset disconnection to avoid glitches if (state == 0) { setBluetoothA2dpOnInt(true); } handleDeviceConnection((state == 1), device, ""); // disable A2DP after notifying headset connection to avoid glitches if (state != 0) { setBluetoothA2dpOnInt(false); } } else if (action.equals(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG)) { state = intent.getIntExtra("state", 0); Log.v(TAG, "Broadcast Receiver: Got ACTION_ANALOG_AUDIO_DOCK_PLUG, state = "+state); handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, ""); } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) { state = intent.getIntExtra("state", 0); Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state); handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_AUX_DIGITAL, ""); } else if (action.equals(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG)) { state = intent.getIntExtra("state", 0); Log.v(TAG, "Broadcast Receiver Got ACTION_DIGITAL_AUDIO_DOCK_PLUG, state = " + state); handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, ""); } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) || action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) { state = intent.getIntExtra("state", 0); if (state == 0) { sendBecomingNoisyIntent(); } int alsaCard = intent.getIntExtra("card", -1); int alsaDevice = intent.getIntExtra("device", -1); String params = (alsaCard == -1 && alsaDevice == -1 ? "" Loading media/java/android/media/IAudioService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.media; import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.media.IAudioFocusDispatcher; import android.media.IRemoteControlClient; Loading Loading @@ -133,4 +134,7 @@ interface IAudioService { void setRingtonePlayer(IRingtonePlayer player); IRingtonePlayer getRingtonePlayer(); int getMasterStreamType(); void setWiredDeviceConnectionState(int device, int state, String name); int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state); } services/java/com/android/server/WiredAccessoryObserver.java +36 −74 Original line number Diff line number Diff line Loading @@ -139,11 +139,14 @@ class WiredAccessoryObserver extends UEventObserver { private final Context mContext; private final WakeLock mWakeLock; // held while there is a pending route change private final AudioManager mAudioManager; public WiredAccessoryObserver(Context context) { mContext = context; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver"); mWakeLock.setReferenceCounted(false); mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); context.registerReceiver(new BootCompletedReceiver(), new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); Loading Loading @@ -250,106 +253,65 @@ class WiredAccessoryObserver extends UEventObserver { mPrevHeadsetState = mHeadsetState; mHeadsetState = headsetState; if (headsetState == 0) { if (mContext.getResources().getBoolean( com.android.internal.R.bool.config_sendAudioBecomingNoisy)) { Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); mContext.sendBroadcast(intent); } // It can take hundreds of ms flush the audio pipeline after // apps pause audio playback, but audio route changes are // immediate, so delay the route change by 1000ms. // This could be improved once the audio sub-system provides an // interface to clear the audio pipeline. delay = 1000; } else { // Insert the same delay for headset connection so that the connection event is not // broadcast before the disconnection event in case of fast removal/insertion if (mHandler.hasMessages(0)) { delay = 1000; } } mWakeLock.acquire(); mHandler.sendMessageDelayed(mHandler.obtainMessage(0, mHandler.sendMessage(mHandler.obtainMessage(0, mHeadsetState, mPrevHeadsetState, mHeadsetName), delay); mHeadsetName)); } private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) { private synchronized final void setDevicesState(int headsetState, int prevHeadsetState, String headsetName) { int allHeadsets = SUPPORTED_HEADSETS; for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { if ((curHeadset & allHeadsets) != 0) { sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName); setDeviceState(curHeadset, headsetState, prevHeadsetState, headsetName); allHeadsets &= ~curHeadset; } } } private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) { private final void setDeviceState(int headset, int headsetState, int prevHeadsetState, String headsetName) { if ((headsetState & headset) != (prevHeadsetState & headset)) { int device; int state; int state = 0; if ((headsetState & headset) != 0) { state = 1; } else { state = 0; } if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL) || (headset == BIT_HDMI_AUDIO)) { Intent intent; // Pack up the values and broadcast them to everyone if (headset == BIT_USB_HEADSET_ANLG) { intent = new Intent(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra("state", state); intent.putExtra("name", headsetName); ActivityManagerNative.broadcastStickyIntent(intent, null); if (headset == BIT_HEADSET) { device = AudioManager.DEVICE_OUT_WIRED_HEADSET; } else if (headset == BIT_HEADSET_NO_MIC){ device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; } else if (headset == BIT_USB_HEADSET_ANLG) { device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; } else if (headset == BIT_USB_HEADSET_DGTL) { intent = new Intent(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra("state", state); intent.putExtra("name", headsetName); ActivityManagerNative.broadcastStickyIntent(intent, null); device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET; } else if (headset == BIT_HDMI_AUDIO) { intent = new Intent(Intent.ACTION_HDMI_AUDIO_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra("state", state); intent.putExtra("name", headsetName); ActivityManagerNative.broadcastStickyIntent(intent, null); } if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName); // TODO: Should we require a permission? } if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) { // Pack up the values and broadcast them to everyone Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); //int state = 0; int microphone = 0; if ((headset & HEADSETS_WITH_MIC) != 0) { microphone = 1; device = AudioManager.DEVICE_OUT_AUX_DIGITAL; } else { Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); return; } intent.putExtra("state", state); intent.putExtra("name", headsetName); intent.putExtra("microphone", microphone); if (LOG) Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected")); if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); // TODO: Should we require a permission? ActivityManagerNative.broadcastStickyIntent(intent, null); } mAudioManager.setWiredDeviceConnectionState(device, state, headsetName); } } private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { sendIntents(msg.arg1, msg.arg2, (String)msg.obj); setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); mWakeLock.release(); } }; Loading Loading
core/java/android/server/BluetoothA2dpService.java +49 −10 Original line number Diff line number Diff line Loading @@ -33,7 +33,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.Handler; import android.os.Message; import android.os.ParcelUuid; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.provider.Settings; import android.util.Log; Loading Loading @@ -65,6 +69,10 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private final BluetoothAdapter mAdapter; private int mTargetA2dpState; private BluetoothDevice mPlayingA2dpDevice; private IntentBroadcastHandler mIntentBroadcastHandler; private final WakeLock mWakeLock; private static final int MSG_CONNECTION_STATE_CHANGED = 0; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -131,6 +139,11 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { public BluetoothA2dpService(Context context, BluetoothService bluetoothService) { mContext = context; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService"); mIntentBroadcastHandler = new IntentBroadcastHandler(); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mBluetoothService = bluetoothService; Loading Loading @@ -514,17 +527,15 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { adjustOtherSinkPriorities(device); } Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, state); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BLUETOOTH_PERM); int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, state); if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state); mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state, prevState); mWakeLock.acquire(); mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( MSG_CONNECTION_STATE_CHANGED, prevState, state, device), delay); } } Loading Loading @@ -586,6 +597,34 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } } /** Handles A2DP connection state change intent broadcasts. */ private class IntentBroadcastHandler extends Handler { private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, state); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BLUETOOTH_PERM); if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state); mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state, prevState); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_CONNECTION_STATE_CHANGED: onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); mWakeLock.release(); break; } } } @Override protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); Loading
media/java/android/media/AudioManager.java +37 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.media; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading Loading @@ -2376,6 +2377,42 @@ public class AudioManager { } } /** * Indicate wired accessory connection state change. * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx) * @param state new connection state: 1 connected, 0 disconnected * @param name device name * {@hide} */ public void setWiredDeviceConnectionState(int device, int state, String name) { IAudioService service = getService(); try { service.setWiredDeviceConnectionState(device, state, name); } catch (RemoteException e) { Log.e(TAG, "Dead object in setWiredDeviceConnectionState "+e); } } /** * Indicate A2DP sink connection state change. * @param device Bluetooth device connected/disconnected * @param state new connection state (BluetoothProfile.STATE_xxx) * @return a delay in ms that the caller should wait before broadcasting * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent. * {@hide} */ public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state) { IAudioService service = getService(); int delay = 0; try { delay = service.setBluetoothA2dpDeviceConnectionState(device, state); } catch (RemoteException e) { Log.e(TAG, "Dead object in setBluetoothA2dpDeviceConnectionState "+e); } finally { return delay; } } /** {@hide} */ public IRingtonePlayer getRingtonePlayer() { try { Loading
media/java/android/media/AudioService.java +125 −50 Original line number Diff line number Diff line Loading @@ -135,6 +135,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private static final int MSG_RCDISPLAY_UPDATE = 13; private static final int MSG_SET_ALL_VOLUMES = 14; private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15; private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 16; private static final int MSG_SET_A2DP_CONNECTION_STATE = 17; // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be Loading Loading @@ -442,15 +444,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // Register for device connection intent broadcasts. IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); intentFilter.addAction(Intent.ACTION_DOCK_EVENT); intentFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); intentFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG); intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG); intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); Loading Loading @@ -1961,7 +1957,19 @@ public class AudioService extends IAudioService.Stub implements OnFinished { deviceList = a2dp.getConnectedDevices(); if (deviceList.size() > 0) { btDevice = deviceList.get(0); handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice)); synchronized (mConnectedDevices) { int state = a2dp.getConnectionState(btDevice); int delay = checkSendBecomingNoisyIntent( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0); sendMsg(mAudioHandler, MSG_SET_A2DP_CONNECTION_STATE, SENDMSG_QUEUE, state, 0, btDevice, delay); } } break; Loading Loading @@ -2262,6 +2270,36 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return device; } public void setWiredDeviceConnectionState(int device, int state, String name) { synchronized (mConnectedDevices) { int delay = checkSendBecomingNoisyIntent(device, state); sendMsg(mAudioHandler, MSG_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, device, state, name, delay); } } public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state) { int delay; synchronized (mConnectedDevices) { delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0); sendMsg(mAudioHandler, MSG_SET_A2DP_CONNECTION_STATE, SENDMSG_QUEUE, state, 0, device, delay); } return delay; } /////////////////////////////////////////////////////////////////////////// // Inner classes /////////////////////////////////////////////////////////////////////////// Loading Loading @@ -2959,6 +2997,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { case MSG_BT_HEADSET_CNCT_FAILED: resetBluetoothSco(); break; case MSG_SET_WIRED_DEVICE_CONNECTION_STATE: onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj); break; case MSG_SET_A2DP_CONNECTION_STATE: onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1); break; } } } Loading Loading @@ -3020,7 +3066,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // must be called synchronized on mConnectedDevices private void makeA2dpDeviceUnavailableNow(String address) { sendBecomingNoisyIntent(); AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address); Loading Loading @@ -3050,7 +3095,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); } private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state) private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state) { if (btDevice == null) { return; Loading Loading @@ -3116,6 +3161,76 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return false; } // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only // sent if none of these devices is connected. int mBecomingNoisyIntentDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE | AudioSystem.DEVICE_OUT_ALL_A2DP; // must be called before removing the device from mConnectedDevices private int checkSendBecomingNoisyIntent(int device, int state) { int delay = 0; if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) { int devices = 0; for (int dev : mConnectedDevices.keySet()) { if ((dev & mBecomingNoisyIntentDevices) != 0) { devices |= dev; } } if (devices == device) { delay = 1000; sendBecomingNoisyIntent(); } } if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) || mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) { delay = 1000; } return delay; } private void sendDeviceConnectionIntent(int device, int state, String name) { Intent intent = new Intent(); intent.putExtra("state", state); intent.putExtra("name", name); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) { intent.setAction(Intent.ACTION_HEADSET_PLUG); intent.putExtra("microphone", 1); } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) { intent.setAction(Intent.ACTION_HEADSET_PLUG); intent.putExtra("microphone", 0); } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) { intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) { intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) { intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG); } ActivityManagerNative.broadcastStickyIntent(intent, null); } private void onSetWiredDeviceConnectionState(int device, int state, String name) { synchronized (mConnectedDevices) { if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) || (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) { setBluetoothA2dpOnInt(true); } handleDeviceConnection((state == 1), device, ""); if ((state != 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) || (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) { setBluetoothA2dpOnInt(false); } sendDeviceConnectionIntent(device, state, name); } } /* cache of the address of the last dock the device was connected to */ private String mDockAddress; Loading Loading @@ -3151,12 +3266,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { config = AudioSystem.FORCE_NONE; } AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); handleA2dpConnectionStateChange(btDevice, state); } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); Loading Loading @@ -3197,43 +3306,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } } } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { state = intent.getIntExtra("state", 0); int microphone = intent.getIntExtra("microphone", 0); if (microphone != 0) { device = AudioSystem.DEVICE_OUT_WIRED_HEADSET; } else { device = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE; } // enable A2DP before notifying headset disconnection to avoid glitches if (state == 0) { setBluetoothA2dpOnInt(true); } handleDeviceConnection((state == 1), device, ""); // disable A2DP after notifying headset connection to avoid glitches if (state != 0) { setBluetoothA2dpOnInt(false); } } else if (action.equals(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG)) { state = intent.getIntExtra("state", 0); Log.v(TAG, "Broadcast Receiver: Got ACTION_ANALOG_AUDIO_DOCK_PLUG, state = "+state); handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, ""); } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) { state = intent.getIntExtra("state", 0); Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state); handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_AUX_DIGITAL, ""); } else if (action.equals(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG)) { state = intent.getIntExtra("state", 0); Log.v(TAG, "Broadcast Receiver Got ACTION_DIGITAL_AUDIO_DOCK_PLUG, state = " + state); handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, ""); } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) || action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) { state = intent.getIntExtra("state", 0); if (state == 0) { sendBecomingNoisyIntent(); } int alsaCard = intent.getIntExtra("card", -1); int alsaDevice = intent.getIntExtra("device", -1); String params = (alsaCard == -1 && alsaDevice == -1 ? "" Loading
media/java/android/media/IAudioService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.media; import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.media.IAudioFocusDispatcher; import android.media.IRemoteControlClient; Loading Loading @@ -133,4 +134,7 @@ interface IAudioService { void setRingtonePlayer(IRingtonePlayer player); IRingtonePlayer getRingtonePlayer(); int getMasterStreamType(); void setWiredDeviceConnectionState(int device, int state, String name); int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state); }
services/java/com/android/server/WiredAccessoryObserver.java +36 −74 Original line number Diff line number Diff line Loading @@ -139,11 +139,14 @@ class WiredAccessoryObserver extends UEventObserver { private final Context mContext; private final WakeLock mWakeLock; // held while there is a pending route change private final AudioManager mAudioManager; public WiredAccessoryObserver(Context context) { mContext = context; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver"); mWakeLock.setReferenceCounted(false); mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); context.registerReceiver(new BootCompletedReceiver(), new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); Loading Loading @@ -250,106 +253,65 @@ class WiredAccessoryObserver extends UEventObserver { mPrevHeadsetState = mHeadsetState; mHeadsetState = headsetState; if (headsetState == 0) { if (mContext.getResources().getBoolean( com.android.internal.R.bool.config_sendAudioBecomingNoisy)) { Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); mContext.sendBroadcast(intent); } // It can take hundreds of ms flush the audio pipeline after // apps pause audio playback, but audio route changes are // immediate, so delay the route change by 1000ms. // This could be improved once the audio sub-system provides an // interface to clear the audio pipeline. delay = 1000; } else { // Insert the same delay for headset connection so that the connection event is not // broadcast before the disconnection event in case of fast removal/insertion if (mHandler.hasMessages(0)) { delay = 1000; } } mWakeLock.acquire(); mHandler.sendMessageDelayed(mHandler.obtainMessage(0, mHandler.sendMessage(mHandler.obtainMessage(0, mHeadsetState, mPrevHeadsetState, mHeadsetName), delay); mHeadsetName)); } private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) { private synchronized final void setDevicesState(int headsetState, int prevHeadsetState, String headsetName) { int allHeadsets = SUPPORTED_HEADSETS; for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { if ((curHeadset & allHeadsets) != 0) { sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName); setDeviceState(curHeadset, headsetState, prevHeadsetState, headsetName); allHeadsets &= ~curHeadset; } } } private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) { private final void setDeviceState(int headset, int headsetState, int prevHeadsetState, String headsetName) { if ((headsetState & headset) != (prevHeadsetState & headset)) { int device; int state; int state = 0; if ((headsetState & headset) != 0) { state = 1; } else { state = 0; } if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL) || (headset == BIT_HDMI_AUDIO)) { Intent intent; // Pack up the values and broadcast them to everyone if (headset == BIT_USB_HEADSET_ANLG) { intent = new Intent(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra("state", state); intent.putExtra("name", headsetName); ActivityManagerNative.broadcastStickyIntent(intent, null); if (headset == BIT_HEADSET) { device = AudioManager.DEVICE_OUT_WIRED_HEADSET; } else if (headset == BIT_HEADSET_NO_MIC){ device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; } else if (headset == BIT_USB_HEADSET_ANLG) { device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; } else if (headset == BIT_USB_HEADSET_DGTL) { intent = new Intent(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra("state", state); intent.putExtra("name", headsetName); ActivityManagerNative.broadcastStickyIntent(intent, null); device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET; } else if (headset == BIT_HDMI_AUDIO) { intent = new Intent(Intent.ACTION_HDMI_AUDIO_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra("state", state); intent.putExtra("name", headsetName); ActivityManagerNative.broadcastStickyIntent(intent, null); } if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName); // TODO: Should we require a permission? } if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) { // Pack up the values and broadcast them to everyone Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); //int state = 0; int microphone = 0; if ((headset & HEADSETS_WITH_MIC) != 0) { microphone = 1; device = AudioManager.DEVICE_OUT_AUX_DIGITAL; } else { Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); return; } intent.putExtra("state", state); intent.putExtra("name", headsetName); intent.putExtra("microphone", microphone); if (LOG) Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected")); if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); // TODO: Should we require a permission? ActivityManagerNative.broadcastStickyIntent(intent, null); } mAudioManager.setWiredDeviceConnectionState(device, state, headsetName); } } private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { sendIntents(msg.arg1, msg.arg2, (String)msg.obj); setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); mWakeLock.release(); } }; Loading