Skip to content

Commit b1fbaac

Browse files
author
Eric Laurent
committed
Send device connection intents from AudioService
AudioService is currently notified of wired headset and A2DP sink connection states via broadcast intents from WiredAccessoryObserver and BluetoothA2dpService. This is a problem as there is no guaranty that AudioService can take actions upon the change before other apps are notified. For instance, the Play On feature requires the UI to be refreshed when a device is inserted/removed and we must guaranty that the UI component can read new A2DP enable state from AudioManager after it receives a device connection state change intent. - Added hidden methods to AudioManager so that WiredAccessoryObserver and BluetoothA2dpService can notify AudioService of device connection directly. - The wired accessories connection intents are now sent by AudioService. - The A2DP state change intent is delayed by BluetoothA2DPService when ACTION_AUDIO_BECOMING_NOISY is sent by AudioService - ACTION_AUDIO_BECOMING_NOISY intent is not sent when disconnecting A2DP while a wired headset is present and vice versa. Bug 6485897. Change-Id: Ie160b3ee5f451132065530772b868593c90afd94
1 parent 48c22c8 commit b1fbaac

File tree

5 files changed

+251
-134
lines changed

5 files changed

+251
-134
lines changed

core/java/android/server/BluetoothA2dpService.java

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@
3333
import android.content.Intent;
3434
import android.content.IntentFilter;
3535
import android.media.AudioManager;
36+
import android.os.Handler;
37+
import android.os.Message;
3638
import android.os.ParcelUuid;
39+
import android.os.PowerManager;
40+
import android.os.PowerManager.WakeLock;
3741
import android.provider.Settings;
3842
import android.util.Log;
3943

@@ -65,6 +69,10 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
6569
private final BluetoothAdapter mAdapter;
6670
private int mTargetA2dpState;
6771
private BluetoothDevice mPlayingA2dpDevice;
72+
private IntentBroadcastHandler mIntentBroadcastHandler;
73+
private final WakeLock mWakeLock;
74+
75+
private static final int MSG_CONNECTION_STATE_CHANGED = 0;
6876

6977
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
7078
@Override
@@ -131,6 +139,11 @@ private boolean isPhoneDocked(BluetoothDevice device) {
131139
public BluetoothA2dpService(Context context, BluetoothService bluetoothService) {
132140
mContext = context;
133141

142+
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
143+
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService");
144+
145+
mIntentBroadcastHandler = new IntentBroadcastHandler();
146+
134147
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
135148

136149
mBluetoothService = bluetoothService;
@@ -514,17 +527,15 @@ private void handleSinkStateChange(BluetoothDevice device, int prevState, int st
514527
adjustOtherSinkPriorities(device);
515528
}
516529

517-
Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
518-
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
519-
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
520-
intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
521-
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
522-
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
530+
int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, state);
523531

524-
if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
525-
526-
mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state,
527-
prevState);
532+
mWakeLock.acquire();
533+
mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage(
534+
MSG_CONNECTION_STATE_CHANGED,
535+
prevState,
536+
state,
537+
device),
538+
delay);
528539
}
529540
}
530541

@@ -586,6 +597,34 @@ private void onConnectSinkResult(String deviceObjectPath, boolean result) {
586597
}
587598
}
588599

600+
/** Handles A2DP connection state change intent broadcasts. */
601+
private class IntentBroadcastHandler extends Handler {
602+
603+
private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) {
604+
Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
605+
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
606+
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
607+
intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
608+
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
609+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
610+
611+
if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
612+
613+
mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state,
614+
prevState);
615+
}
616+
617+
@Override
618+
public void handleMessage(Message msg) {
619+
switch (msg.what) {
620+
case MSG_CONNECTION_STATE_CHANGED:
621+
onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2);
622+
mWakeLock.release();
623+
break;
624+
}
625+
}
626+
}
627+
589628
@Override
590629
protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
591630
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);

media/java/android/media/AudioManager.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.annotation.SdkConstant;
2020
import android.annotation.SdkConstant.SdkConstantType;
2121
import android.app.PendingIntent;
22+
import android.bluetooth.BluetoothDevice;
2223
import android.content.ComponentName;
2324
import android.content.Context;
2425
import android.content.Intent;
@@ -2376,6 +2377,42 @@ public int getDevicesForStream(int streamType) {
23762377
}
23772378
}
23782379

2380+
/**
2381+
* Indicate wired accessory connection state change.
2382+
* @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
2383+
* @param state new connection state: 1 connected, 0 disconnected
2384+
* @param name device name
2385+
* {@hide}
2386+
*/
2387+
public void setWiredDeviceConnectionState(int device, int state, String name) {
2388+
IAudioService service = getService();
2389+
try {
2390+
service.setWiredDeviceConnectionState(device, state, name);
2391+
} catch (RemoteException e) {
2392+
Log.e(TAG, "Dead object in setWiredDeviceConnectionState "+e);
2393+
}
2394+
}
2395+
2396+
/**
2397+
* Indicate A2DP sink connection state change.
2398+
* @param device Bluetooth device connected/disconnected
2399+
* @param state new connection state (BluetoothProfile.STATE_xxx)
2400+
* @return a delay in ms that the caller should wait before broadcasting
2401+
* BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
2402+
* {@hide}
2403+
*/
2404+
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state) {
2405+
IAudioService service = getService();
2406+
int delay = 0;
2407+
try {
2408+
delay = service.setBluetoothA2dpDeviceConnectionState(device, state);
2409+
} catch (RemoteException e) {
2410+
Log.e(TAG, "Dead object in setBluetoothA2dpDeviceConnectionState "+e);
2411+
} finally {
2412+
return delay;
2413+
}
2414+
}
2415+
23792416
/** {@hide} */
23802417
public IRingtonePlayer getRingtonePlayer() {
23812418
try {

media/java/android/media/AudioService.java

Lines changed: 125 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
135135
private static final int MSG_RCDISPLAY_UPDATE = 13;
136136
private static final int MSG_SET_ALL_VOLUMES = 14;
137137
private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
138+
private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 16;
139+
private static final int MSG_SET_A2DP_CONNECTION_STATE = 17;
138140

139141

140142
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -442,15 +444,9 @@ public AudioService(Context context) {
442444

443445
// Register for device connection intent broadcasts.
444446
IntentFilter intentFilter =
445-
new IntentFilter(Intent.ACTION_HEADSET_PLUG);
446-
447-
intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
448-
intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
447+
new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
449448
intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
450449
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
451-
intentFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
452-
intentFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
453-
intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
454450
intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
455451
intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
456452
intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
@@ -1961,7 +1957,19 @@ public void onServiceConnected(int profile, BluetoothProfile proxy) {
19611957
deviceList = a2dp.getConnectedDevices();
19621958
if (deviceList.size() > 0) {
19631959
btDevice = deviceList.get(0);
1964-
handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice));
1960+
synchronized (mConnectedDevices) {
1961+
int state = a2dp.getConnectionState(btDevice);
1962+
int delay = checkSendBecomingNoisyIntent(
1963+
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1964+
(state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
1965+
sendMsg(mAudioHandler,
1966+
MSG_SET_A2DP_CONNECTION_STATE,
1967+
SENDMSG_QUEUE,
1968+
state,
1969+
0,
1970+
btDevice,
1971+
delay);
1972+
}
19651973
}
19661974
break;
19671975

@@ -2262,6 +2270,36 @@ private int getDeviceForStream(int stream) {
22622270
return device;
22632271
}
22642272

2273+
public void setWiredDeviceConnectionState(int device, int state, String name) {
2274+
synchronized (mConnectedDevices) {
2275+
int delay = checkSendBecomingNoisyIntent(device, state);
2276+
sendMsg(mAudioHandler,
2277+
MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
2278+
SENDMSG_QUEUE,
2279+
device,
2280+
state,
2281+
name,
2282+
delay);
2283+
}
2284+
}
2285+
2286+
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
2287+
{
2288+
int delay;
2289+
synchronized (mConnectedDevices) {
2290+
delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2291+
(state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
2292+
sendMsg(mAudioHandler,
2293+
MSG_SET_A2DP_CONNECTION_STATE,
2294+
SENDMSG_QUEUE,
2295+
state,
2296+
0,
2297+
device,
2298+
delay);
2299+
}
2300+
return delay;
2301+
}
2302+
22652303
///////////////////////////////////////////////////////////////////////////
22662304
// Inner classes
22672305
///////////////////////////////////////////////////////////////////////////
@@ -2959,6 +2997,14 @@ public void handleMessage(Message msg) {
29592997
case MSG_BT_HEADSET_CNCT_FAILED:
29602998
resetBluetoothSco();
29612999
break;
3000+
3001+
case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
3002+
onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
3003+
break;
3004+
3005+
case MSG_SET_A2DP_CONNECTION_STATE:
3006+
onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
3007+
break;
29623008
}
29633009
}
29643010
}
@@ -3020,7 +3066,6 @@ private void sendBecomingNoisyIntent() {
30203066

30213067
// must be called synchronized on mConnectedDevices
30223068
private void makeA2dpDeviceUnavailableNow(String address) {
3023-
sendBecomingNoisyIntent();
30243069
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
30253070
AudioSystem.DEVICE_STATE_UNAVAILABLE,
30263071
address);
@@ -3050,7 +3095,7 @@ private boolean hasScheduledA2dpDockTimeout() {
30503095
return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
30513096
}
30523097

3053-
private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state)
3098+
private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
30543099
{
30553100
if (btDevice == null) {
30563101
return;
@@ -3116,6 +3161,76 @@ private boolean handleDeviceConnection(boolean connected, int device, String par
31163161
return false;
31173162
}
31183163

3164+
// Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only
3165+
// sent if none of these devices is connected.
3166+
int mBecomingNoisyIntentDevices =
3167+
AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
3168+
AudioSystem.DEVICE_OUT_ALL_A2DP;
3169+
3170+
// must be called before removing the device from mConnectedDevices
3171+
private int checkSendBecomingNoisyIntent(int device, int state) {
3172+
int delay = 0;
3173+
if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
3174+
int devices = 0;
3175+
for (int dev : mConnectedDevices.keySet()) {
3176+
if ((dev & mBecomingNoisyIntentDevices) != 0) {
3177+
devices |= dev;
3178+
}
3179+
}
3180+
if (devices == device) {
3181+
delay = 1000;
3182+
sendBecomingNoisyIntent();
3183+
}
3184+
}
3185+
3186+
if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) ||
3187+
mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
3188+
delay = 1000;
3189+
}
3190+
return delay;
3191+
}
3192+
3193+
private void sendDeviceConnectionIntent(int device, int state, String name)
3194+
{
3195+
Intent intent = new Intent();
3196+
3197+
intent.putExtra("state", state);
3198+
intent.putExtra("name", name);
3199+
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
3200+
3201+
if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
3202+
intent.setAction(Intent.ACTION_HEADSET_PLUG);
3203+
intent.putExtra("microphone", 1);
3204+
} else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
3205+
intent.setAction(Intent.ACTION_HEADSET_PLUG);
3206+
intent.putExtra("microphone", 0);
3207+
} else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
3208+
intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
3209+
} else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
3210+
intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
3211+
} else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
3212+
intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
3213+
}
3214+
3215+
ActivityManagerNative.broadcastStickyIntent(intent, null);
3216+
}
3217+
3218+
private void onSetWiredDeviceConnectionState(int device, int state, String name)
3219+
{
3220+
synchronized (mConnectedDevices) {
3221+
if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3222+
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
3223+
setBluetoothA2dpOnInt(true);
3224+
}
3225+
handleDeviceConnection((state == 1), device, "");
3226+
if ((state != 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3227+
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
3228+
setBluetoothA2dpOnInt(false);
3229+
}
3230+
sendDeviceConnectionIntent(device, state, name);
3231+
}
3232+
}
3233+
31193234
/* cache of the address of the last dock the device was connected to */
31203235
private String mDockAddress;
31213236

@@ -3151,12 +3266,6 @@ public void onReceive(Context context, Intent intent) {
31513266
config = AudioSystem.FORCE_NONE;
31523267
}
31533268
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
3154-
} else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
3155-
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
3156-
BluetoothProfile.STATE_DISCONNECTED);
3157-
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3158-
3159-
handleA2dpConnectionStateChange(btDevice, state);
31603269
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
31613270
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
31623271
BluetoothProfile.STATE_DISCONNECTED);
@@ -3197,43 +3306,9 @@ public void onReceive(Context context, Intent intent) {
31973306
}
31983307
}
31993308
}
3200-
} else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
3201-
state = intent.getIntExtra("state", 0);
3202-
int microphone = intent.getIntExtra("microphone", 0);
3203-
3204-
if (microphone != 0) {
3205-
device = AudioSystem.DEVICE_OUT_WIRED_HEADSET;
3206-
} else {
3207-
device = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
3208-
}
3209-
// enable A2DP before notifying headset disconnection to avoid glitches
3210-
if (state == 0) {
3211-
setBluetoothA2dpOnInt(true);
3212-
}
3213-
handleDeviceConnection((state == 1), device, "");
3214-
// disable A2DP after notifying headset connection to avoid glitches
3215-
if (state != 0) {
3216-
setBluetoothA2dpOnInt(false);
3217-
}
3218-
} else if (action.equals(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG)) {
3219-
state = intent.getIntExtra("state", 0);
3220-
Log.v(TAG, "Broadcast Receiver: Got ACTION_ANALOG_AUDIO_DOCK_PLUG, state = "+state);
3221-
handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, "");
3222-
} else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
3223-
state = intent.getIntExtra("state", 0);
3224-
Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
3225-
handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_AUX_DIGITAL, "");
3226-
} else if (action.equals(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG)) {
3227-
state = intent.getIntExtra("state", 0);
3228-
Log.v(TAG,
3229-
"Broadcast Receiver Got ACTION_DIGITAL_AUDIO_DOCK_PLUG, state = " + state);
3230-
handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, "");
32313309
} else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
32323310
action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
32333311
state = intent.getIntExtra("state", 0);
3234-
if (state == 0) {
3235-
sendBecomingNoisyIntent();
3236-
}
32373312
int alsaCard = intent.getIntExtra("card", -1);
32383313
int alsaDevice = intent.getIntExtra("device", -1);
32393314
String params = (alsaCard == -1 && alsaDevice == -1 ? ""

0 commit comments

Comments
 (0)