Merge "A2dp: update correct state to HeadsetA2dpSync while enter Disconnected" into q-keystone-qcom-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bc10b4b..e435470 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index fd23784..b232568 100755
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -72,6 +72,7 @@
private static A2dpService sA2dpService;
private static A2dpSinkService sA2dpSinkService;
private static boolean mA2dpSrcSnkConcurrency;
+ private static boolean a2dpMulticast = false;
private AdapterService mAdapterService;
private HandlerThread mStateMachinesThread;
@@ -285,6 +286,10 @@
if (DBG) {
Log.d(TAG, "A2DP concurrency mode set to " + mA2dpSrcSnkConcurrency);
}
+ a2dpMulticast = SystemProperties.getBoolean("persist.vendor.service.bt.a2dp_multicast_enable", false);
+ if (DBG) {
+ Log.d(TAG, "A2DP Multicast flag set to " + a2dpMulticast);
+ }
return true;
}
@@ -978,8 +983,11 @@
Log.e(TAG,"adapterService is null");
}
}
- if (mAvrcp_ext != null && !tws_switch) {
- mAvrcp_ext.setAbsVolumeFlag(device);
+ // Don't update the absVolume flags when disconnect one device in multicast mode
+ if (!a2dpMulticast || previousActiveDevice == null) {
+ if (mAvrcp_ext != null && !tws_switch) {
+ mAvrcp_ext.setAbsVolumeFlag(device);
+ }
}
tws_switch = false;
return true;
@@ -1072,6 +1080,18 @@
}
}
+ public void storeDeviceAudioVolume(BluetoothDevice device) {
+ if (device != null)
+ {
+ if (AvrcpTargetService.get() != null) {
+ AvrcpTargetService.get().storeVolumeForDevice(device);
+ } else if (mAvrcp_ext != null) {
+ //store volume in multi-a2dp for the device doesn't set as active
+ mAvrcp_ext.storeVolumeForDevice(device);
+ }
+ }
+ }
+
public void resetAvrcpBlacklist(BluetoothDevice device) {
synchronized(mBtAvrcpLock) {
if (mAvrcp_ext != null) {
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 3b9056e..d3219e2 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -25,6 +25,7 @@
import android.media.MediaDescription;
import android.media.browse.MediaBrowser.MediaItem;
import android.media.session.PlaybackState;
+import android.os.Message;
import android.os.Bundle;
import android.util.Log;
@@ -96,11 +97,36 @@
"android.bluetooth.avrcp-controller.profile.extra.PLAYBACK";
+ /**
+ * Intent used to broadcast the change of folder list.
+ *
+ * <p>This intent will have the one extra:
+ * <ul>
+ * <li> {@link #EXTRA_FOLDER_LIST} - array of {@link MediaBrowser#MediaItem}
+ * containing the folder listing of currently selected folder.
+ * </ul>
+ */
+ public static final String ACTION_FOLDER_LIST =
+ "android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST";
+
+ public static final String EXTRA_FOLDER_LIST =
+ "android.bluetooth.avrcp-controller.profile.extra.FOLDER_LIST";
+
+ public static final String EXTRA_FOLDER_ID = "com.android.bluetooth.avrcp.EXTRA_FOLDER_ID";
+ public static final String EXTRA_FOLDER_BT_ID =
+ "com.android.bluetooth.avrcp-controller.EXTRA_FOLDER_BT_ID";
+
+ public static final String EXTRA_METADATA =
+ "android.bluetooth.avrcp-controller.profile.extra.METADATA";
+
static BrowseTree sBrowseTree;
private static AvrcpControllerService sService;
private final BluetoothAdapter mAdapter;
+ private int mFeatures;
+ private int mCaPsm;
+
protected Map<BluetoothDevice, AvrcpControllerStateMachine> mDeviceStateMap =
new ConcurrentHashMap<>(1);
@@ -312,11 +338,15 @@
// Called by JNI to notify Avrcp of features supported by the Remote device.
private void getRcFeatures(byte[] address, int features, int caPsm) {
- /*Log.i(TAG, " getRcFeatures caPsm :" + caPsm);
+ Log.i(TAG, " getRcFeatures caPsm :" + caPsm);
+ mFeatures = features;
+ mCaPsm = caPsm;
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
- Message msg = mAvrcpCtSm.obtainMessage(
- AvrcpControllerStateMachine.MESSAGE_PROCESS_RC_FEATURES, features, caPsm, device);
- mAvrcpCtSm.sendMessage(msg); */
+ AvrcpControllerStateMachine stateMachine = getStateMachine(device);
+ if (stateMachine != null) {
+ stateMachine.sendMessage(
+ AvrcpControllerStateMachine.MESSAGE_PROCESS_RC_FEATURES, features, caPsm, device);
+ }
}
// Called by JNI
@@ -361,15 +391,29 @@
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
AvrcpControllerStateMachine stateMachine = getStateMachine(device);
if (stateMachine != null) {
- stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED,
- TrackInfo.getMetadata(attributes, attribVals));
+ List<Integer> attrList = new ArrayList<>();
+ for (int attr : attributes) {
+ attrList.add(attr);
+ }
+ List<String> attrValList = Arrays.asList(attribVals);
+ TrackInfo trackInfo = new TrackInfo(attrList, attrValList);
+ stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED, trackInfo);
}
}
private void onElementAttributeUpdate(byte[] address, byte numAttributes, int[] attributes,
String[] attribVals) {
-
+ if (DBG) {
+ Log.d(TAG, "onElementAttributeUpdate");
+ }
+ BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+ AvrcpControllerStateMachine stateMachine = getStateMachine(device);
+ if (stateMachine == null)
+ return;
+ CoverArtUtils coverArtUtils = new CoverArtUtils();
+ coverArtUtils.onElementAttributeUpdate(address, numAttributes, attributes, attribVals,
+ device, stateMachine);
}
// Called by JNI periodically based upon timer to update play position
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index ec85592..03d8f48 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -80,6 +80,7 @@
static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214;
static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215;
static final int MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED = 216;
+ static final int MESSAGE_PROCESS_RC_FEATURES = 217;
//300->399 Events for Browsing
static final int MESSAGE_GET_FOLDER_ITEMS = 300;
@@ -112,11 +113,15 @@
protected final Disconnecting mDisconnecting;
private A2dpSinkService mA2dpSinkService;
+ private static CoverArtUtils mCoveArtUtils;
+ private AvrcpControllerBipStateMachine mBipStateMachine;
+
protected int mMostRecentState = BluetoothProfile.STATE_DISCONNECTED;
boolean mRemoteControlConnected = false;
boolean mBrowsingConnected = false;
final BrowseTree mBrowseTree;
+ private boolean smActive = false;
private AvrcpPlayer mAddressedPlayer = new AvrcpPlayer();
private RemoteDevice mRemoteDevice;
private int mPreviousPercentageVol = -1;
@@ -151,6 +156,7 @@
addState(mConnected);
addState(mDisconnecting);
+ smActive = true;
mGetFolderList = new GetFolderList();
addState(mGetFolderList, mConnected);
@@ -161,6 +167,9 @@
IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
mService.registerReceiver(mBroadcastReceiver, filter);
+ mCoveArtUtils = new CoverArtUtils();
+ mBipStateMachine = AvrcpControllerBipStateMachine.make(this, getHandler(), service);
+
setInitialState(mDisconnected);
}
@@ -228,7 +237,7 @@
}
synchronized void onBrowsingConnected() {
- if (mBrowsingConnected) return;
+ if (mBrowsingConnected || (!smActive)) return;
mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode);
BluetoothMediaBrowserService.notifyChanged(mService
.sBrowseTree.mRootNode);
@@ -237,7 +246,7 @@
}
synchronized void onBrowsingDisconnected() {
- if (!mBrowsingConnected) return;
+ if (!mBrowsingConnected || (!smActive)) return;
mAddressedPlayer.setPlayStatus(PlaybackState.STATE_ERROR);
mAddressedPlayer.updateCurrentTrack(null);
if (mBrowseTree != null && mBrowseTree.mNowPlayingNode != null) {
@@ -354,8 +363,11 @@
return true;
case MESSAGE_PROCESS_TRACK_CHANGED:
- mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj);
- BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj);
+ TrackInfo trackInfo = (TrackInfo)msg.obj;
+ mAddressedPlayer.updateCurrentTrack((MediaMetadata) trackInfo.getMediaMetaData());
+ BluetoothMediaBrowserService.trackChanged((MediaMetadata) trackInfo.getMediaMetaData());
+ mAddressedPlayer.updateCurrentTrackInfo(trackInfo);
+ mCoveArtUtils.msgTrackChanged(mService, mBipStateMachine,mAddressedPlayer,mRemoteDevice);
return true;
case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
@@ -458,6 +470,20 @@
mVolumeChangedNotificationsToIgnore = 0;
return true;
+ case MESSAGE_PROCESS_RC_FEATURES:
+ mRemoteDevice.setRemoteFeatures(msg.arg1);
+ if (msg.arg2 > 0) {
+ mCoveArtUtils.msgProcessRcFeatures(mBipStateMachine, mRemoteDevice,msg.arg2);
+ }
+ return true;
+
+ case CoverArtUtils.MESSAGE_BIP_CONNECTED:
+ case CoverArtUtils.MESSAGE_BIP_DISCONNECTED:
+ case CoverArtUtils.MESSAGE_BIP_IMAGE_FETCHED:
+ case CoverArtUtils.MESSAGE_BIP_THUMB_NAIL_FETCHED:
+ mCoveArtUtils.processBipAction(mService, mAddressedPlayer,
+ mRemoteDevice, msg.what, msg);
+ return true;
default:
return super.processMessage(msg);
}
@@ -799,6 +825,9 @@
// If the receiver was never registered unregister will throw an
// IllegalArgumentException.
}
+ synchronized(AvrcpControllerStateMachine.this) {
+ smActive = false;
+ }
// we should disacrd, all currently queuedup messages.
quitNow();
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
index ab0e987..7bd7bcb 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
@@ -53,6 +53,8 @@
private MediaMetadata mCurrentTrack;
private PlaybackState mPlaybackState;
+ private TrackInfo mCurrentTrackInfo = new TrackInfo();
+
AvrcpPlayer() {
mId = INVALID_ID;
//Set Default Actions in case Player data isn't available.
@@ -175,4 +177,12 @@
}
if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions);
}
+
+ public synchronized void updateCurrentTrackInfo(TrackInfo update) {
+ mCurrentTrackInfo = update;
+ }
+
+ public synchronized TrackInfo getCurrentTrackInfo() {
+ return mCurrentTrackInfo;
+ }
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/TrackInfo.java b/src/com/android/bluetooth/avrcpcontroller/TrackInfo.java
index 0240610..961e92e 100644
--- a/src/com/android/bluetooth/avrcpcontroller/TrackInfo.java
+++ b/src/com/android/bluetooth/avrcpcontroller/TrackInfo.java
@@ -17,9 +17,29 @@
package com.android.bluetooth.avrcpcontroller;
import android.media.MediaMetadata;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.MediaMetadata;
+import android.net.Uri;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
final class TrackInfo {
+
+ private static final String TAG = "AvrcpTrackInfo";
+ private static final boolean VDBG = AvrcpControllerService.VDBG;
+ /*
+ * Default values for each of the items from JNI
+ */
+ private static final int TRACK_NUM_INVALID = -1;
+ private static final int TOTAL_TRACKS_INVALID = -1;
+ private static final int TOTAL_TRACK_TIME_INVALID = -1;
+ private static final String UNPOPULATED_ATTRIBUTE = "";
+
/*
*Element Id Values for GetMetaData from JNI
*/
@@ -30,6 +50,108 @@
private static final int MEDIA_ATTRIBUTE_TOTAL_TRACK_NUMBER = 0x05;
private static final int MEDIA_ATTRIBUTE_GENRE = 0x06;
private static final int MEDIA_ATTRIBUTE_PLAYING_TIME = 0x07;
+ private static final int MEDIA_ATTRIBUTE_COVER_ART_HANDLE = 0x08;
+
+ private final String mArtistName;
+ private final String mTrackTitle;
+ private final String mAlbumTitle;
+ private final String mGenre;
+ private final long mTrackNum; // number of audio file on original recording.
+ private final long mTotalTracks; // total number of tracks on original recording
+ private final long mTrackLen; // full length of AudioFile.
+ private String mCoverArtHandle;
+ private String mImageLocation;
+ private String mThumbNailLocation;
+
+
+ TrackInfo() {
+ this(new ArrayList<Integer>(), new ArrayList<String>());
+ }
+
+ TrackInfo(List<Integer> attrIds, List<String> attrMap) {
+ Map<Integer, String> attributeMap = new HashMap<>();
+ for (int i = 0; i < attrIds.size(); i++) {
+ attributeMap.put(attrIds.get(i), attrMap.get(i));
+ }
+
+ String attribute;
+ mTrackTitle = attributeMap.getOrDefault(MEDIA_ATTRIBUTE_TITLE, UNPOPULATED_ATTRIBUTE);
+
+ mArtistName = attributeMap.getOrDefault(MEDIA_ATTRIBUTE_ARTIST_NAME, UNPOPULATED_ATTRIBUTE);
+
+ mAlbumTitle = attributeMap.getOrDefault(MEDIA_ATTRIBUTE_ALBUM_NAME, UNPOPULATED_ATTRIBUTE);
+
+ attribute = attributeMap.get(MEDIA_ATTRIBUTE_TRACK_NUMBER);
+ mTrackNum = (attribute != null && !attribute.isEmpty()) ? Long.valueOf(attribute)
+ : TRACK_NUM_INVALID;
+
+ attribute = attributeMap.get(MEDIA_ATTRIBUTE_TOTAL_TRACK_NUMBER);
+ mTotalTracks = (attribute != null && !attribute.isEmpty()) ? Long.valueOf(attribute)
+ : TOTAL_TRACKS_INVALID;
+
+ mGenre = attributeMap.getOrDefault(MEDIA_ATTRIBUTE_GENRE, UNPOPULATED_ATTRIBUTE);
+
+ attribute = attributeMap.get(MEDIA_ATTRIBUTE_PLAYING_TIME);
+ mTrackLen = (attribute != null && !attribute.isEmpty()) ? Long.valueOf(attribute)
+ : TOTAL_TRACK_TIME_INVALID;
+ mCoverArtHandle = attributeMap.getOrDefault(MEDIA_ATTRIBUTE_COVER_ART_HANDLE,
+ UNPOPULATED_ATTRIBUTE);
+ mImageLocation = UNPOPULATED_ATTRIBUTE;
+ mThumbNailLocation = UNPOPULATED_ATTRIBUTE;
+ }
+
+ boolean updateImageLocation(String mCAHandle, String mLocation) {
+ if (VDBG) Log.d(TAG, " updateImageLocation hndl " + mCAHandle + " location " + mLocation);
+ if (!mCAHandle.equals(mCoverArtHandle) || (mLocation == null)) {
+ return false;
+ }
+ mImageLocation = mLocation;
+ return true;
+ }
+
+ boolean updateThumbNailLocation(String mCAHandle, String mLocation) {
+ if (VDBG) Log.d(TAG, " mCAHandle " + mCAHandle + " location " + mLocation);
+ if (!mCAHandle.equals(mCoverArtHandle) || (mLocation == null)) {
+ return false;
+ }
+ mThumbNailLocation = mLocation;
+ return true;
+ }
+
+ public String toString() {
+ return "Metadata [artist=" + mArtistName + " trackTitle= " + mTrackTitle + " albumTitle= "
+ + mAlbumTitle + " genre= " + mGenre + " trackNum= " + Long.toString(mTrackNum)
+ + " track_len : " + Long.toString(mTrackLen) + " TotalTracks " + Long.toString(
+ mTotalTracks) + " mCoverArtHandle=" + mCoverArtHandle +
+ " mImageLocation :"+mImageLocation+"]";
+ }
+
+
+ public MediaMetadata getMediaMetaData() {
+ if (VDBG) {
+ Log.d(TAG, " TrackInfo " + toString());
+ }
+ MediaMetadata.Builder mMetaDataBuilder = new MediaMetadata.Builder();
+ mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, mArtistName);
+ mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, mTrackTitle);
+ mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_ALBUM, mAlbumTitle);
+ mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_GENRE, mGenre);
+ mMetaDataBuilder.putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, mTrackNum);
+ mMetaDataBuilder.putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, mTotalTracks);
+ mMetaDataBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION, mTrackLen);
+ if (mImageLocation != UNPOPULATED_ATTRIBUTE) {
+ Uri imageUri = Uri.parse(mImageLocation);
+ if (VDBG) Log.d(TAG," updating image uri = " + imageUri.toString());
+ mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
+ imageUri.toString());
+ }
+ if (mThumbNailLocation != UNPOPULATED_ATTRIBUTE) {
+ Bitmap mThumbNailBitmap = BitmapFactory.decodeFile(mThumbNailLocation);
+ mMetaDataBuilder.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, mThumbNailBitmap);
+ }
+ return mMetaDataBuilder.build();
+ }
+
static MediaMetadata getMetadata(int[] attrIds, String[] attrMap) {
MediaMetadata.Builder metaDataBuilder = new MediaMetadata.Builder();
@@ -74,7 +196,45 @@
break;
}
}
-
+
return metaDataBuilder.build();
}
+
+ public String displayMetaData() {
+ MediaMetadata metaData = getMediaMetaData();
+ StringBuffer sb = new StringBuffer();
+ // getDescription only contains artist, title and album
+ sb.append(metaData.getDescription().toString() + " ");
+ if (metaData.containsKey(MediaMetadata.METADATA_KEY_GENRE)) {
+ sb.append(metaData.getString(MediaMetadata.METADATA_KEY_GENRE) + " ");
+ }
+ if (metaData.containsKey(MediaMetadata.METADATA_KEY_MEDIA_ID)) {
+ sb.append(metaData.getString(MediaMetadata.METADATA_KEY_MEDIA_ID) + " ");
+ }
+ if (metaData.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) {
+ sb.append(
+ Long.toString(metaData.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) + " ");
+ }
+ if (metaData.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) {
+ sb.append(Long.toString(metaData.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)) + " ");
+ }
+ if (metaData.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) {
+ sb.append(Long.toString(metaData.getLong(MediaMetadata.METADATA_KEY_DURATION)) + " ");
+ }
+ if (metaData.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) {
+ sb.append(Long.toString(metaData.getLong(MediaMetadata.METADATA_KEY_DURATION)) + " ");
+ }
+ return sb.toString();
+ }
+
+ String getCoverArtHandle() {
+ return mCoverArtHandle;
+ }
+
+ void clearCoverArtData() {
+ mCoverArtHandle = UNPOPULATED_ATTRIBUTE;
+ mImageLocation = UNPOPULATED_ATTRIBUTE;
+ mThumbNailLocation = UNPOPULATED_ATTRIBUTE;
+ }
+
}
diff --git a/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
old mode 100644
new mode 100755
index 1a356a8..17b9100
--- a/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -33,6 +33,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemProperties;
import android.util.Log;
import com.android.bluetooth.a2dp.A2dpService;
@@ -124,6 +125,8 @@
private BluetoothDevice mA2dpActiveDevice = null;
private BluetoothDevice mHfpActiveDevice = null;
private BluetoothDevice mHearingAidActiveDevice = null;
+ private boolean mTwsPlusSwitch = false;
+ private static boolean a2dpMulticast = false;
// Broadcast receiver for all changes
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -214,8 +217,19 @@
mA2dpConnectedDevices.add(device);
if (mHearingAidActiveDevice == null) {
// New connected device: select it as active
- setA2dpActiveDevice(device);
- break;
+ if (!a2dpMulticast) {
+ setA2dpActiveDevice(device);
+ }
+ else {
+ if (mA2dpActiveDevice == null) {
+ setA2dpActiveDevice(device);
+ }
+ else {
+ // store the volume for the new added device
+ final A2dpService a2dpService = mFactory.getA2dpService();
+ a2dpService.storeDeviceAudioVolume(device);
+ }
+ }
}
break;
}
@@ -231,7 +245,7 @@
if (Objects.equals(mA2dpActiveDevice, device)) {
final A2dpService mA2dpService = mFactory.getA2dpService();
BluetoothDevice mDevice = null;
- if (mAdapterService.isTwsPlusDevice(device) &&
+ if (mAdapterService.isTwsPlusDevice(device) && !mTwsPlusSwitch &&
!mA2dpConnectedDevices.isEmpty()) {
for (BluetoothDevice connected_device: mA2dpConnectedDevices) {
if (mAdapterService.isTwsPlusDevice(connected_device) &&
@@ -241,9 +255,22 @@
break;
}
}
+ } else if (device.isTwsPlusDevice() && mTwsPlusSwitch) {
+ Log.d(TAG, "Resetting mTwsPlusSwitch");
+ mTwsPlusSwitch = false;
+ }
+ else if (a2dpMulticast && !mA2dpConnectedDevices.isEmpty()) {
+ for (BluetoothDevice connected_device: mA2dpConnectedDevices) {
+ if (mA2dpService.getConnectionState(connected_device) ==
+ BluetoothProfile.STATE_CONNECTED) {
+ Log.d(TAG, "a2dp Multicast calling set a2dp Active dev: " + connected_device);
+ mDevice = connected_device;
+ break;
+ }
+ }
}
if (!setA2dpActiveDevice(mDevice) && (mDevice != null) &&
- mAdapterService.isTwsPlusDevice(mDevice)) {
+ (mAdapterService.isTwsPlusDevice(mDevice) || a2dpMulticast)) {
Log.w(TAG, "Switch A2dp active device to peer earbud failed");
setA2dpActiveDevice(null);
}
@@ -289,7 +316,7 @@
break; // The device is already connected
}
mHfpConnectedDevices.add(device);
- if (mHearingAidActiveDevice == null) {
+ if ((!a2dpMulticast || mHfpActiveDevice == null) && mHearingAidActiveDevice == null) {
// New connected device: select it as active
setHfpActiveDevice(device);
break;
@@ -329,7 +356,23 @@
"as there is no Connected TWS+ peer");
setHfpActiveDevice(null);
}
- } else {
+ } else if (a2dpMulticast && !mHfpConnectedDevices.isEmpty()) {
+ if (hfpService == null) {
+ Log.e(TAG, "no headsetService, FATAL");
+ return;
+ }
+ for (BluetoothDevice connected_device: mHfpConnectedDevices) {
+ if (hfpService.getConnectionState(connected_device) ==
+ BluetoothProfile.STATE_CONNECTED) {
+ Log.d(TAG, "a2dp Multicast calling set HFP Active dev: " + connected_device);
+ if (!setHfpActiveDevice(connected_device)) {
+ setHfpActiveDevice(null);
+ }
+ break;
+ }
+ }
+ }
+ else {
setHfpActiveDevice(null);
}
}
@@ -439,6 +482,7 @@
mAdapterService.registerReceiver(mReceiver, filter);
mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler);
+ a2dpMulticast = SystemProperties.getBoolean("persist.vendor.service.bt.a2dp_multicast_enable", false);
}
void cleanup() {
@@ -454,7 +498,12 @@
}
resetState();
}
-
+ public void notify_active_device_unbonding(BluetoothDevice device) {
+ if (device.isTwsPlusDevice() && Objects.equals(mA2dpActiveDevice, device)) {
+ Log.d(TAG,"TWS+ active device is getting unpaired, avoid switch to pair");
+ mTwsPlusSwitch = true;
+ }
+ }
/**
* Get the {@link Looper} for the handler thread. This is used in testing and helper
* objects
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index d9e0eb4..d7c6469 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -2472,7 +2472,9 @@
return false;
}
deviceProp.setBondingInitiatedLocally(false);
-
+ if (device.isTwsPlusDevice()) {
+ mActiveDeviceManager.notify_active_device_unbonding(device);
+ }
Message msg = mBondStateMachine.obtainMessage(BondStateMachine.REMOVE_BOND);
msg.obj = device;
mBondStateMachine.sendMessage(msg);
@@ -3598,15 +3600,7 @@
debugLog(action);
if (action == null) return;
if (isEnabled() && (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION))) {
- WifiManager wifiMgr = (WifiManager) getSystemService(Context.WIFI_SERVICE);
- if ((wifiMgr != null) && (wifiMgr.isWifiEnabled())) {
- WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
- if ((wifiInfo != null) && (wifiInfo.getNetworkId() != -1)) {
- mVendor.setWifiState(true);
- } else {
- mVendor.setWifiState(false);
- }
- }
+ fetchWifiState();
} else if (isEnabled() &&
(action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) ||
(action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)))){
diff --git a/src/com/android/bluetooth/hfp/HeadsetA2dpSync.java b/src/com/android/bluetooth/hfp/HeadsetA2dpSync.java
index 4c28e8b..7fb2e2d 100644
--- a/src/com/android/bluetooth/hfp/HeadsetA2dpSync.java
+++ b/src/com/android/bluetooth/hfp/HeadsetA2dpSync.java
@@ -38,6 +38,8 @@
import android.util.Log;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
+import com.android.bluetooth.hearingaid.HearingAidService;
+import java.util.List;
/**
* Defines methods used for synchronization between HFP and A2DP
@@ -113,6 +115,18 @@
*/
public boolean suspendA2DP(int reason, BluetoothDevice device){
int a2dpState = isA2dpPlaying();
+
+ List<BluetoothDevice> HAActiveDevices = null;
+ HearingAidService mHaService = HearingAidService.getHearingAidService();
+ if (mHaService != null) {
+ HAActiveDevices = mHaService.getActiveDevices();
+ }
+ if (HAActiveDevices != null && (HAActiveDevices.get(0) != null
+ || HAActiveDevices.get(1) != null)) {
+ Log.d(TAG,"Ignore suspendA2DP if active device is HearingAid");
+ return false;
+ }
+
Log.d(TAG," suspendA2DP currPlayingState = "+ a2dpState + " for reason " + reason
+ "mA2dpSuspendTriggered = " + mA2dpSuspendTriggered + " for device " + device);
if (mA2dpSuspendTriggered != A2DP_SUSPENDED_NOT_TRIGGERED) {