Merge "Restore Parameters after audio server restart" into qt-qpr1-dev
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 0ac85f4..06914e1 100644
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -684,7 +684,7 @@
}
static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,
- jboolean isSingleUserMode) {
+ jboolean isNiapMode) {
ALOGV("%s", __func__);
android_bluetooth_UidTraffic.clazz =
@@ -700,7 +700,7 @@
int ret = sBluetoothInterface->init(&sBluetoothCallbacks,
isGuest == JNI_TRUE ? 1 : 0,
- isSingleUserMode == JNI_TRUE ? 1 : 0);
+ isNiapMode == JNI_TRUE ? 1 : 0);
if (ret != BT_STATUS_SUCCESS) {
ALOGE("Error while setting the callbacks: %d\n", ret);
sBluetoothInterface = NULL;
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 2c6ec73..e24b808 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Accediu al gestor de baixades."</string>
<string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"Permet que l\'aplicació accedeixi al gestor d\'ús compartit de Bluetooth i que l\'utilitzi per transferir fitxers."</string>
- <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Accés al dispositiu Bluetooth en llista blanca."</string>
+ <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Accés de dispositius Bluetooth en llista blanca."</string>
<string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Permet que l\'aplicació col·loqui temporalment en una llista blanca un dispositiu Bluetooth, cosa que permet que el dispositiu enviï fitxers a aquest dispositiu sense la confirmació de l\'usuari."</string>
<string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
<string name="unknown_device" msgid="9221903979877041009">"Dispositiu desconegut"</string>
@@ -30,7 +30,7 @@
<string name="bt_enable_line2" msgid="4341936569415937994">"Vols activar el Bluetooth ara?"</string>
<string name="bt_enable_cancel" msgid="1988832367505151727">"Cancel·la"</string>
<string name="bt_enable_ok" msgid="3432462749994538265">"Activa"</string>
- <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transferència del fitxer"</string>
+ <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transferència de fitxers"</string>
<string name="incoming_file_confirm_content" msgid="2752605552743148036">"Acceptes el fitxer entrant?"</string>
<string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Rebutja"</string>
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepta"</string>
diff --git a/res/values-hi/test_strings.xml b/res/values-hi/test_strings.xml
index f8379b8..52e0e1a 100644
--- a/res/values-hi/test_strings.xml
+++ b/res/values-hi/test_strings.xml
@@ -3,7 +3,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="6006644116867509664">"ब्लूटूथ"</string>
<string name="insert_record" msgid="1450997173838378132">"रिकॉर्ड सम्मिलित करें"</string>
- <string name="update_record" msgid="2480425402384910635">"रिकॉर्ड की दुबारा पूछें"</string>
+ <string name="update_record" msgid="2480425402384910635">"रिकॉर्ड की पुष्टि करें"</string>
<string name="ack_record" msgid="6716152390978472184">"रिकॉर्ड अभिस्वीकृत करें"</string>
<string name="deleteAll_record" msgid="4383349788485210582">"सभी रिकॉर्ड मिटाएं"</string>
<string name="ok_button" msgid="6519033415223065454">"ठीक है"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7fe6a4f..415bd10 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -69,7 +69,7 @@
<string name="upload_succ_ok" msgid="7705428476405478828">"אישור"</string>
<string name="upload_fail_line1" msgid="7899394672421491701">"הקובץ לא נשלח אל <xliff:g id="RECIPIENT">%1$s</xliff:g>."</string>
<string name="upload_fail_line1_2" msgid="2108129204050841798">"קובץ: <xliff:g id="FILE">%1$s</xliff:g>"</string>
- <string name="upload_fail_ok" msgid="5807702461606714296">"נסה שוב"</string>
+ <string name="upload_fail_ok" msgid="5807702461606714296">"כדאי לנסות שוב"</string>
<string name="upload_fail_cancel" msgid="9118496285835687125">"סגור"</string>
<string name="bt_error_btn_ok" msgid="5965151173011534240">"אישור"</string>
<string name="unknown_file" msgid="6092727753965095366">"קובץ לא ידוע"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index a7c0f1e..4db5d0a 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -127,7 +127,7 @@
<string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"रद्द करा"</string>
<string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"तुम्ही ब्लूटूथद्वारे शेअर करू इच्छित असलेली खाती निवडा. कनेक्ट करताना अद्याप तुम्ही खात्यांमधील कोणताही अॅक्सेस स्वीकारण्याची आवश्यकता आहे."</string>
<string name="bluetooth_map_settings_count" msgid="4557473074937024833">"स्लॉट शिल्लक:"</string>
- <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"अॅप्लिकेशन आयकन"</string>
+ <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ॲप्लिकेशन आयकन"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ब्लूटूथ मेसेज सामायिकरण सेटिंग्ज"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"खाते निवडू शकत नाही. 0 स्लॉट शिल्लक"</string>
<string name="bluetooth_connected" msgid="6718623220072656906">"ब्लूटूथ ऑडिओ कनेक्ट केला"</string>
diff --git a/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java b/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
index 918c2ce..8127e76 100644
--- a/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
+++ b/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
@@ -57,7 +57,7 @@
void setCodecConfigPreference(BluetoothDevice device,
BluetoothCodecStatus codecStatus,
- BluetoothCodecConfig codecConfig) {
+ BluetoothCodecConfig newCodecConfig) {
Objects.requireNonNull(codecStatus);
// Check whether the codecConfig is selectable for this Bluetooth device.
@@ -66,34 +66,36 @@
codec.isMandatoryCodec())) {
// Do not set codec preference to native if the selectableCodecs not contain mandatory
// codec. The reason could be remote codec negotiation is not completed yet.
- Log.w(TAG, "Cannot find mandatory codec in selectableCodecs.");
+ Log.w(TAG, "setCodecConfigPreference: must have mandatory codec before changing.");
return;
}
- if (!isCodecConfigSelectable(codecConfig, selectableCodecs)) {
- Log.w(TAG, "Codec is not selectable: " + codecConfig);
+ if (!codecStatus.isCodecConfigSelectable(newCodecConfig)) {
+ Log.w(TAG, "setCodecConfigPreference: invalid codec "
+ + Objects.toString(newCodecConfig));
return;
}
// Check whether the codecConfig would change current codec config.
- int prioritizedCodecType = getPrioitizedCodecType(codecConfig, selectableCodecs);
+ int prioritizedCodecType = getPrioitizedCodecType(newCodecConfig, selectableCodecs);
BluetoothCodecConfig currentCodecConfig = codecStatus.getCodecConfig();
if (prioritizedCodecType == currentCodecConfig.getCodecType()
- && (currentCodecConfig.getCodecType() != codecConfig.getCodecType()
- || currentCodecConfig.sameAudioFeedingParameters(codecConfig))) {
+ && (prioritizedCodecType != newCodecConfig.getCodecType()
+ || (currentCodecConfig.similarCodecFeedingParameters(newCodecConfig)
+ && currentCodecConfig.sameCodecSpecificParameters(newCodecConfig)))) {
// Same codec with same parameters, no need to send this request to native.
- Log.i(TAG, "setCodecConfigPreference: codec not changed.");
+ Log.w(TAG, "setCodecConfigPreference: codec not changed.");
return;
}
BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1];
- codecConfigArray[0] = codecConfig;
+ codecConfigArray[0] = newCodecConfig;
mA2dpNativeInterface.setCodecConfigPreference(device, codecConfigArray);
}
void enableOptionalCodecs(BluetoothDevice device, BluetoothCodecConfig currentCodecConfig) {
if (currentCodecConfig != null && !currentCodecConfig.isMandatoryCodec()) {
- Log.i(TAG, "enableOptionalCodecs: already using optional codec: "
- + currentCodecConfig.getCodecType());
+ Log.i(TAG, "enableOptionalCodecs: already using optional codec "
+ + currentCodecConfig.getCodecName());
return;
}
@@ -115,7 +117,7 @@
void disableOptionalCodecs(BluetoothDevice device, BluetoothCodecConfig currentCodecConfig) {
if (currentCodecConfig != null && currentCodecConfig.isMandatoryCodec()) {
- Log.i(TAG, "disableOptionalCodecs: already using mandatory codec");
+ Log.i(TAG, "disableOptionalCodecs: already using mandatory codec.");
return;
}
@@ -150,20 +152,6 @@
return prioritizedCodecConfig.getCodecType();
}
- // Check whether the codecConfig is selectable
- private static boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig,
- BluetoothCodecConfig[] selectableCodecs) {
- for (BluetoothCodecConfig config : selectableCodecs) {
- if (codecConfig.getCodecType() == config.getCodecType()
- && (codecConfig.getSampleRate() & config.getSampleRate()) != 0
- && (codecConfig.getBitsPerSample() & config.getBitsPerSample()) != 0
- && (codecConfig.getChannelMode() & config.getChannelMode()) != 0) {
- return true;
- }
- }
- return false;
- }
-
// Assign the A2DP Source codec config priorities
private BluetoothCodecConfig[] assignCodecConfigPriorities() {
Resources resources = mContext.getResources();
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index dbec19b..cebf767 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -712,17 +712,16 @@
device = mActiveDevice;
}
if (device == null) {
- Log.e(TAG, "Cannot set codec config preference: no active A2DP device");
+ Log.e(TAG, "setCodecConfigPreference: Invalid device");
return;
}
- if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
- Log.e(TAG, "Cannot set codec config preference: not supported");
+ if (codecConfig == null) {
+ Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
return;
}
-
BluetoothCodecStatus codecStatus = getCodecStatus(device);
if (codecStatus == null) {
- Log.e(TAG, "Codec status is null on " + device);
+ Log.e(TAG, "setCodecConfigPreference: Codec status is null");
return;
}
mA2dpCodecConfig.setCodecConfigPreference(device, codecStatus, codecConfig);
@@ -744,16 +743,16 @@
device = mActiveDevice;
}
if (device == null) {
- Log.e(TAG, "Cannot enable optional codecs: no active A2DP device");
+ Log.e(TAG, "enableOptionalCodecs: Invalid device");
return;
}
if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
- Log.e(TAG, "Cannot enable optional codecs: not supported");
+ Log.e(TAG, "enableOptionalCodecs: No optional codecs");
return;
}
BluetoothCodecStatus codecStatus = getCodecStatus(device);
if (codecStatus == null) {
- Log.e(TAG, "Cannot enable optional codecs: codec status is null");
+ Log.e(TAG, "enableOptionalCodecs: Codec status is null");
return;
}
mA2dpCodecConfig.enableOptionalCodecs(device, codecStatus.getCodecConfig());
@@ -775,16 +774,16 @@
device = mActiveDevice;
}
if (device == null) {
- Log.e(TAG, "Cannot disable optional codecs: no active A2DP device");
+ Log.e(TAG, "disableOptionalCodecs: Invalid device");
return;
}
if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
- Log.e(TAG, "Cannot disable optional codecs: not supported");
+ Log.e(TAG, "disableOptionalCodecs: No optional codecs");
return;
}
BluetoothCodecStatus codecStatus = getCodecStatus(device);
if (codecStatus == null) {
- Log.e(TAG, "Cannot disable optional codecs: codec status is null");
+ Log.e(TAG, "disableOptionalCodecs: Codec status is null");
return;
}
mA2dpCodecConfig.disableOptionalCodecs(device, codecStatus.getCodecConfig());
diff --git a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index e0df8b2..cc8f88d 100644
--- a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -596,7 +596,7 @@
boolean isConnected() {
synchronized (this) {
- return (getCurrentState() == mConnected);
+ return (getConnectionState() == BluetoothProfile.STATE_CONNECTED);
}
}
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
index 19ed87f..77ead16 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
@@ -15,6 +15,8 @@
*/
package com.android.bluetooth.a2dpsink;
+import static android.bluetooth.BluetoothProfile.PRIORITY_OFF;
+
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAudioConfig;
import android.bluetooth.BluetoothDevice;
@@ -168,6 +170,15 @@
switch (event.mType) {
case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
switch (event.mState) {
+ case StackEvent.CONNECTION_STATE_CONNECTING:
+ if (mService.getPriority(mDevice) == PRIORITY_OFF) {
+ Log.w(TAG, "Ignore incoming connection, profile is"
+ + " turned off for " + mDevice);
+ mService.disconnectA2dpNative(mDeviceAddress);
+ } else {
+ transitionTo(mConnecting);
+ }
+ break;
case StackEvent.CONNECTION_STATE_CONNECTED:
transitionTo(mConnected);
break;
diff --git a/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java b/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java
index b5886c2..3ce2c5d 100644
--- a/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java
+++ b/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java
@@ -23,6 +23,9 @@
import android.os.Message;
import android.util.Log;
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -47,6 +50,7 @@
private static final int MSG_CONNECT_CB = 1;
private static final int MSG_TIMEOUT = 2;
+ private static BrowsablePlayerConnector sInjectConnector;
private Handler mHandler;
private Context mContext;
private PlayerListCallback mCallback;
@@ -58,11 +62,19 @@
void run(List<BrowsedPlayerWrapper> result);
}
+ private static void setInstanceForTesting(BrowsablePlayerConnector connector) {
+ Utils.enforceInstrumentationTestMode();
+ sInjectConnector = connector;
+ }
+
static BrowsablePlayerConnector connectToPlayers(
Context context,
Looper looper,
List<ResolveInfo> players,
PlayerListCallback cb) {
+ if (sInjectConnector != null) {
+ return sInjectConnector;
+ }
if (cb == null) {
Log.wtfStack(TAG, "Null callback passed");
return null;
diff --git a/src/com/android/bluetooth/avrcp/GPMWrapper.java b/src/com/android/bluetooth/avrcp/GPMWrapper.java
index 87e5ec0..ea9875d 100644
--- a/src/com/android/bluetooth/avrcp/GPMWrapper.java
+++ b/src/com/android/bluetooth/avrcp/GPMWrapper.java
@@ -17,6 +17,7 @@
package com.android.bluetooth.avrcp;
import android.media.session.MediaSession;
+import android.os.Looper;
import android.util.Log;
/**
@@ -28,6 +29,10 @@
private static final String TAG = "AvrcpGPMWrapper";
private static final boolean DEBUG = true;
+ GPMWrapper(MediaController controller, Looper looper) {
+ super(controller, looper);
+ }
+
@Override
boolean isMetadataSynced() {
if (getQueue() == null) {
diff --git a/src/com/android/bluetooth/avrcp/MediaPlayerList.java b/src/com/android/bluetooth/avrcp/MediaPlayerList.java
index 29f4cf9..5756121 100644
--- a/src/com/android/bluetooth/avrcp/MediaPlayerList.java
+++ b/src/com/android/bluetooth/avrcp/MediaPlayerList.java
@@ -24,6 +24,9 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioPlaybackConfiguration;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
@@ -33,6 +36,7 @@
import android.view.KeyEvent;
import com.android.bluetooth.Utils;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
@@ -79,6 +83,8 @@
private Looper mLooper; // Thread all media player callbacks and timeouts happen on
private PackageManager mPackageManager;
private MediaSessionManager mMediaSessionManager;
+ private MediaData mCurrMediaData = null;
+ private final AudioManager mAudioManager;
private Map<Integer, MediaPlayerWrapper> mMediaPlayers =
Collections.synchronizedMap(new HashMap<Integer, MediaPlayerWrapper>());
@@ -88,6 +94,9 @@
Collections.synchronizedMap(new HashMap<Integer, BrowsedPlayerWrapper>());
private int mActivePlayerId = NO_ACTIVE_PLAYER;
+ @VisibleForTesting
+ private boolean mAudioPlaybackIsActive = false;
+
private AvrcpTargetService.ListCallback mCallback;
private BrowsablePlayerConnector mBrowsablePlayerConnector;
@@ -122,6 +131,9 @@
pkgFilter.addDataScheme(PACKAGE_SCHEME);
context.registerReceiver(mPackageChangedBroadcastReceiver, pkgFilter);
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mAudioManager.registerAudioPlaybackCallback(mAudioPlaybackCallback, new Handler(mLooper));
+
mMediaSessionManager =
(MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
mMediaSessionManager.addOnActiveSessionsChangedListener(
@@ -190,6 +202,8 @@
mMediaSessionManager.setCallback(null, null);
mMediaSessionManager = null;
+ mAudioManager.unregisterAudioPlaybackCallback(mAudioPlaybackCallback);
+
mMediaPlayerIds.clear();
for (MediaPlayerWrapper player : mMediaPlayers.values()) {
@@ -275,7 +289,16 @@
final MediaPlayerWrapper player = getActivePlayer();
if (player == null) return null;
- return player.getPlaybackState();
+ PlaybackState state = player.getPlaybackState();
+ if (mAudioPlaybackIsActive
+ && (state == null || state.getState() != PlaybackState.STATE_PLAYING)) {
+ return new PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PLAYING,
+ state == null ? 0 : state.getPosition(),
+ 1.0f)
+ .build();
+ }
+ return state;
}
@NonNull
@@ -406,12 +429,8 @@
}
}
- // Adds the controller to the MediaPlayerList or updates the controller if we already had
- // a controller for a package. Returns the new ID of the controller where its added or its
- // previous value if it already existed. Returns -1 if the controller passed in is invalid
- int addMediaPlayer(android.media.session.MediaController controller) {
- if (controller == null) return -1;
-
+ @VisibleForTesting
+ int addMediaPlayer(MediaController controller) {
// Each new player has an ID of 1 plus the highest ID. The ID 0 is reserved to signify that
// there is no active player. If we already have a browsable player for the package, reuse
// that key.
@@ -427,7 +446,7 @@
if (mMediaPlayers.containsKey(playerId)) {
d("Already have a controller for the player: " + packageName + ", updating instead");
MediaPlayerWrapper player = mMediaPlayers.get(playerId);
- player.updateMediaController(MediaControllerFactory.wrap(controller));
+ player.updateMediaController(controller);
// If the media controller we updated was the active player check if the media updated
if (playerId == mActivePlayerId) {
@@ -437,8 +456,8 @@
return playerId;
}
- MediaPlayerWrapper newPlayer = MediaPlayerWrapper.wrap(
- MediaControllerFactory.wrap(controller),
+ MediaPlayerWrapper newPlayer = MediaPlayerWrapperFactory.wrap(
+ controller,
mLooper);
Log.i(TAG, "Adding wrapped media player: " + packageName + " at key: "
@@ -448,6 +467,18 @@
return playerId;
}
+ // Adds the controller to the MediaPlayerList or updates the controller if we already had
+ // a controller for a package. Returns the new ID of the controller where its added or its
+ // previous value if it already existed. Returns -1 if the controller passed in is invalid
+ int addMediaPlayer(android.media.session.MediaController controller) {
+ if (controller == null) {
+ e("Trying to add a null MediaController");
+ return -1;
+ }
+
+ return addMediaPlayer(MediaControllerFactory.wrap(controller));
+ }
+
void removeMediaPlayer(int playerId) {
if (!mMediaPlayers.containsKey(playerId)) {
e("Trying to remove nonexistent media player: " + playerId);
@@ -495,7 +526,12 @@
sendFolderUpdate(true, true, false);
}
- sendMediaUpdate(getActivePlayer().getCurrentMediaData());
+ MediaData data = getActivePlayer().getCurrentMediaData();
+ if (mAudioPlaybackIsActive) {
+ data.state = mCurrMediaData.state;
+ Log.d(TAG, "setActivePlayer mAudioPlaybackIsActive=true, state=" + data.state);
+ }
+ sendMediaUpdate(data);
}
// TODO (apanicke): Add logging for media key events in dumpsys
@@ -528,6 +564,8 @@
data.queue.add(data.metadata);
}
+ Log.d(TAG, "sendMediaUpdate state=" + data.state);
+ mCurrMediaData = data;
mCallback.run(data);
}
@@ -591,6 +629,78 @@
}
};
+ void updateMediaForAudioPlayback() {
+ MediaData currMediaData = null;
+ PlaybackState currState = null;
+ if (getActivePlayer() == null) {
+ Log.d(TAG, "updateMediaForAudioPlayback: no active player");
+ PlaybackState.Builder builder = new PlaybackState.Builder()
+ .setState(PlaybackState.STATE_STOPPED, 0L, 0L);
+ List<Metadata> queue = new ArrayList<Metadata>();
+ queue.add(Util.empty_data());
+ currMediaData = new MediaData(
+ Util.empty_data(),
+ builder.build(),
+ queue
+ );
+ } else {
+ currMediaData = getActivePlayer().getCurrentMediaData();
+ currState = currMediaData.state;
+ }
+
+ if (currState != null
+ && currState.getState() == PlaybackState.STATE_PLAYING) {
+ Log.i(TAG, "updateMediaForAudioPlayback: Active player is playing, drop it");
+ return;
+ }
+
+ if (mAudioPlaybackIsActive) {
+ PlaybackState.Builder builder = new PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PLAYING,
+ currState == null ? 0 : currState.getPosition(),
+ 1.0f);
+ currMediaData.state = builder.build();
+ }
+ Log.i(TAG, "updateMediaForAudioPlayback: update state=" + currMediaData.state);
+ sendMediaUpdate(currMediaData);
+ }
+
+ @VisibleForTesting
+ void injectAudioPlaybacActive(boolean isActive) {
+ mAudioPlaybackIsActive = isActive;
+ updateMediaForAudioPlayback();
+ }
+
+ private final AudioManager.AudioPlaybackCallback mAudioPlaybackCallback =
+ new AudioManager.AudioPlaybackCallback() {
+ @Override
+ public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
+ if (configs == null) {
+ return;
+ }
+ boolean isActive = false;
+ Log.v(TAG, "onPlaybackConfigChanged(): Configs list size=" + configs.size());
+ for (AudioPlaybackConfiguration config : configs) {
+ if (config.isActive() && (config.getAudioAttributes().getUsage()
+ == AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
+ && (config.getAudioAttributes().getContentType()
+ == AudioAttributes.CONTENT_TYPE_SPEECH)) {
+ if (DEBUG) {
+ Log.d(TAG, "onPlaybackConfigChanged(): config="
+ + AudioPlaybackConfiguration.toLogFriendlyString(config));
+ }
+ isActive = true;
+ }
+ }
+ if (isActive != mAudioPlaybackIsActive) {
+ Log.d(TAG, "onPlaybackConfigChanged isActive=" + isActive
+ + ", mAudioPlaybackIsActive=" + mAudioPlaybackIsActive);
+ mAudioPlaybackIsActive = isActive;
+ updateMediaForAudioPlayback();
+ }
+ }
+ };
+
private final MediaPlayerWrapper.Callback mMediaPlayerCallback =
new MediaPlayerWrapper.Callback() {
@Override
@@ -605,6 +715,10 @@
return;
}
+ if (mAudioPlaybackIsActive && (data.state.getState() != PlaybackState.STATE_PLAYING)) {
+ Log.d(TAG, "Some audio playbacks are still active, drop it");
+ return;
+ }
sendMediaUpdate(data);
}
};
diff --git a/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java b/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java
index e4176b3..9eba036 100644
--- a/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java
+++ b/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java
@@ -54,10 +54,6 @@
private final Object mCallbackLock = new Object();
private Callback mRegisteredCallback = null;
- protected MediaPlayerWrapper() {
- mCurrentData = new MediaData(null, null, null);
- }
-
public interface Callback {
void mediaUpdatedCallback(MediaData data);
}
@@ -80,30 +76,15 @@
return true;
}
- // TODO (apanicke): Implement a factory to make testing and creating interop wrappers easier
- static MediaPlayerWrapper wrap(MediaController controller, Looper looper) {
- if (controller == null || looper == null) {
- e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller
- + " | Looper: " + looper);
- return null;
- }
+ MediaPlayerWrapper(MediaController controller, Looper looper) {
+ mMediaController = controller;
+ mPackageName = controller.getPackageName();
+ mLooper = looper;
- MediaPlayerWrapper newWrapper;
- if (controller.getPackageName().equals("com.google.android.music")) {
- Log.v(TAG, "Creating compatibility wrapper for Google Play Music");
- newWrapper = new GPMWrapper();
- } else {
- newWrapper = new MediaPlayerWrapper();
- }
-
- newWrapper.mMediaController = controller;
- newWrapper.mPackageName = controller.getPackageName();
- newWrapper.mLooper = looper;
-
- newWrapper.mCurrentData.queue = Util.toMetadataList(newWrapper.getQueue());
- newWrapper.mCurrentData.metadata = Util.toMetadata(newWrapper.getMetadata());
- newWrapper.mCurrentData.state = newWrapper.getPlaybackState();
- return newWrapper;
+ mCurrentData = new MediaData(null, null, null);
+ mCurrentData.queue = Util.toMetadataList(getQueue());
+ mCurrentData.metadata = Util.toMetadata(getMetadata());
+ mCurrentData.state = getPlaybackState();
}
void cleanup() {
diff --git a/src/com/android/bluetooth/avrcp/mockable/MediaPlayerWrapperFactory.java b/src/com/android/bluetooth/avrcp/mockable/MediaPlayerWrapperFactory.java
new file mode 100644
index 0000000..d3ef751
--- /dev/null
+++ b/src/com/android/bluetooth/avrcp/mockable/MediaPlayerWrapperFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.os.Looper;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Provide a method to inject custom MediaControllerWrapper objects for testing. By using the
+ * factory methods instead of calling the wrap method of MediaControllerWrapper directly, we can
+ * inject a custom MediaControllerWrapper that can be used with JUnit and Mockito to set
+ * expectations and validate behaviour in tests.
+ */
+public final class MediaPlayerWrapperFactory {
+ private static MediaPlayerWrapper sInjectedWrapper;
+
+ static MediaPlayerWrapper wrap(MediaController controller, Looper looper) {
+ if (sInjectedWrapper != null) return sInjectedWrapper;
+ if (controller == null || looper == null) {
+ return null;
+ }
+
+ MediaPlayerWrapper newWrapper;
+ if (controller.getPackageName().equals("com.google.android.music")) {
+ newWrapper = new GPMWrapper(controller, looper);
+ } else {
+ newWrapper = new MediaPlayerWrapper(controller, looper);
+ }
+ return newWrapper;
+ }
+
+ @VisibleForTesting
+ static void inject(MediaPlayerWrapper wrapper) {
+ sInjectedWrapper = wrapper;
+ }
+}
+
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 53cf723..009e42c 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -393,7 +393,7 @@
mAdapterProperties = new AdapterProperties(this);
mAdapterStateMachine = AdapterState.make(this);
mJniCallbacks = new JniCallbacks(this, mAdapterProperties);
- initNative(isGuest(), isSingleUserMode());
+ initNative(isGuest(), isNiapMode());
mNativeAvailable = true;
mCallbacks = new RemoteCallbackList<IBluetoothCallback>();
mAppOps = getSystemService(AppOpsManager.class);
@@ -2314,6 +2314,8 @@
}
boolean setPhonebookAccessPermission(BluetoothDevice device, int value) {
+ enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+ "Need BLUETOOTH PRIVILEGED permission");
SharedPreferences pref = getSharedPreferences(PHONEBOOK_ACCESS_PERMISSION_PREFERENCE_FILE,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
@@ -2869,8 +2871,8 @@
return UserManager.get(this).isGuestUser();
}
- private boolean isSingleUserMode() {
- return UserManager.get(this).hasUserRestriction(UserManager.DISALLOW_ADD_USER);
+ private boolean isNiapMode() {
+ return Settings.Global.getInt(getContentResolver(), "niap_mode", 0) == 1;
}
/**
@@ -2889,7 +2891,7 @@
static native void classInitNative();
- native boolean initNative(boolean startRestricted, boolean isSingleUserMode);
+ native boolean initNative(boolean startRestricted, boolean isNiapMode);
native void cleanupNative();
diff --git a/src/com/android/bluetooth/gatt/AppScanStats.java b/src/com/android/bluetooth/gatt/AppScanStats.java
index ec8b1f8..a895ece 100644
--- a/src/com/android/bluetooth/gatt/AppScanStats.java
+++ b/src/com/android/bluetooth/gatt/AppScanStats.java
@@ -15,6 +15,7 @@
*/
package com.android.bluetooth.gatt;
+import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanSettings;
import android.os.Binder;
import android.os.RemoteException;
@@ -29,10 +30,12 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/**
* ScanStats class helps keep track of information about scans
@@ -44,12 +47,18 @@
static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss");
+ static final int OPPORTUNISTIC_WEIGHT = 0;
+ static final int LOW_POWER_WEIGHT = 10;
+ static final int BALANCED_WEIGHT = 25;
+ static final int LOW_LATENCY_WEIGHT = 100;
+
/* ContextMap here is needed to grab Apps and Connections */ ContextMap mContextMap;
/* GattService is needed to add scan event protos to be dumped later */ GattService
mGattService;
- /* Battery stats is used to keep track of scans and result stats */ IBatteryStats mBatteryStats;
+ /* Battery stats is used to keep track of scans and result stats */ IBatteryStats
+ mBatteryStats;
class LastScan {
public long duration;
@@ -57,25 +66,36 @@
public long suspendStartTime;
public boolean isSuspended;
public long timestamp;
- public boolean opportunistic;
- public boolean timeout;
- public boolean background;
- public boolean filtered;
+ public boolean isOpportunisticScan;
+ public boolean isTimeout;
+ public boolean isBackgroundScan;
+ public boolean isFilterScan;
+ public boolean isCallbackScan;
+ public boolean isBatchScan;
public int results;
public int scannerId;
+ public int scanMode;
+ public int scanCallbackType;
+ public String filterString;
- LastScan(long timestamp, long duration, boolean opportunistic, boolean background,
- boolean filtered, int scannerId) {
- this.duration = duration;
+ LastScan(long timestamp, boolean isFilterScan, boolean isCallbackScan, int scannerId,
+ int scanMode, int scanCallbackType) {
+ this.duration = 0;
this.timestamp = timestamp;
- this.opportunistic = opportunistic;
- this.background = background;
- this.filtered = filtered;
+ this.isOpportunisticScan = false;
+ this.isTimeout = false;
+ this.isBackgroundScan = false;
+ this.isFilterScan = isFilterScan;
+ this.isCallbackScan = isCallbackScan;
+ this.isBatchScan = false;
+ this.scanMode = scanMode;
+ this.scanCallbackType = scanCallbackType;
this.results = 0;
this.scannerId = scannerId;
this.suspendDuration = 0;
this.suspendStartTime = 0;
this.isSuspended = false;
+ this.filterString = "";
}
}
@@ -95,11 +115,18 @@
private int mScansStarted = 0;
private int mScansStopped = 0;
public boolean isRegistered = false;
- private long mMinScanTime = Long.MAX_VALUE;
- private long mMaxScanTime = 0;
private long mScanStartTime = 0;
- private long mTotalScanTime = 0;
+ private long mTotalActiveTime = 0;
private long mTotalSuspendTime = 0;
+ private long mTotalScanTime = 0;
+ private long mOppScanTime = 0;
+ private long mLowPowerScanTime = 0;
+ private long mBalancedScanTime = 0;
+ private long mLowLantencyScanTime = 0;
+ private int mOppScan = 0;
+ private int mLowPowerScan = 0;
+ private int mBalancedScan = 0;
+ private int mLowLantencyScan = 0;
private List<LastScan> mLastScans = new ArrayList<LastScan>(NUM_SCAN_DURATIONS_KEPT);
private HashMap<Integer, LastScan> mOngoingScans = new HashMap<Integer, LastScan>();
public long startTime = 0;
@@ -147,7 +174,8 @@
return mOngoingScans.get(scannerId);
}
- synchronized void recordScanStart(ScanSettings settings, boolean filtered, int scannerId) {
+ synchronized void recordScanStart(ScanSettings settings, List<ScanFilter> filters,
+ boolean isFilterScan, boolean isCallbackScan, int scannerId) {
LastScan existingScan = getScanFromScannerId(scannerId);
if (existingScan != null) {
return;
@@ -155,11 +183,36 @@
this.mScansStarted++;
startTime = SystemClock.elapsedRealtime();
- LastScan scan = new LastScan(startTime, 0, false, false, filtered, scannerId);
+ LastScan scan = new LastScan(startTime, isFilterScan, isCallbackScan, scannerId,
+ settings.getScanMode(), settings.getCallbackType());
if (settings != null) {
- scan.opportunistic = settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
- scan.background =
- (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0;
+ scan.isOpportunisticScan = scan.scanMode == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
+ scan.isBackgroundScan =
+ (scan.scanCallbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0;
+ scan.isBatchScan =
+ settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
+ && settings.getReportDelayMillis() != 0;
+ switch (scan.scanMode) {
+ case ScanSettings.SCAN_MODE_OPPORTUNISTIC:
+ mOppScan++;
+ break;
+ case ScanSettings.SCAN_MODE_LOW_POWER:
+ mLowPowerScan++;
+ break;
+ case ScanSettings.SCAN_MODE_BALANCED:
+ mBalancedScan++;
+ break;
+ case ScanSettings.SCAN_MODE_LOW_LATENCY:
+ mLowLantencyScan++;
+ break;
+ }
+ }
+
+ if (isFilterScan) {
+ for (ScanFilter filter : filters) {
+ scan.filterString +=
+ "\n └ " + filterToStringWithoutNullParam(filter);
+ }
}
BluetoothMetricsProto.ScanEvent scanEvent = BluetoothMetricsProto.ScanEvent.newBuilder()
@@ -174,14 +227,15 @@
mScanStartTime = startTime;
}
try {
- boolean isUnoptimized = !(scan.filtered || scan.background || scan.opportunistic);
+ boolean isUnoptimized =
+ !(scan.isFilterScan || scan.isBackgroundScan || scan.isOpportunisticScan);
mBatteryStats.noteBleScanStarted(mWorkSource, isUnoptimized);
} catch (RemoteException e) {
/* ignore */
}
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, mWorkSource,
StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON,
- scan.filtered, scan.background, scan.opportunistic);
+ scan.isFilterScan, scan.isBackgroundScan, scan.isOpportunisticScan);
mOngoingScans.put(scannerId, scan);
}
@@ -216,16 +270,28 @@
.build();
mGattService.addScanEvent(scanEvent);
- if (!isScanning()) {
- long totalDuration = stopTime - mScanStartTime;
- mTotalScanTime += totalDuration;
- mMinScanTime = Math.min(totalDuration, mMinScanTime);
- mMaxScanTime = Math.max(totalDuration, mMaxScanTime);
+ mTotalScanTime += scanDuration;
+ long activeDuration = scanDuration - scan.suspendDuration;
+ mTotalActiveTime += activeDuration;
+ switch (scan.scanMode) {
+ case ScanSettings.SCAN_MODE_OPPORTUNISTIC:
+ mOppScanTime += activeDuration;
+ break;
+ case ScanSettings.SCAN_MODE_LOW_POWER:
+ mLowPowerScanTime += activeDuration;
+ break;
+ case ScanSettings.SCAN_MODE_BALANCED:
+ mBalancedScanTime += activeDuration;
+ break;
+ case ScanSettings.SCAN_MODE_LOW_LATENCY:
+ mLowLantencyScanTime += activeDuration;
+ break;
}
try {
// Inform battery stats of any results it might be missing on scan stop
- boolean isUnoptimized = !(scan.filtered || scan.background || scan.opportunistic);
+ boolean isUnoptimized =
+ !(scan.isFilterScan || scan.isBackgroundScan || scan.isOpportunisticScan);
mBatteryStats.noteBleScanResults(mWorkSource, scan.results % 100);
mBatteryStats.noteBleScanStopped(mWorkSource, isUnoptimized);
} catch (RemoteException e) {
@@ -234,7 +300,7 @@
StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, mWorkSource, scan.results % 100);
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, mWorkSource,
StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF,
- scan.filtered, scan.background, scan.opportunistic);
+ scan.isFilterScan, scan.isBackgroundScan, scan.isOpportunisticScan);
}
synchronized void recordScanSuspend(int scannerId) {
@@ -266,7 +332,7 @@
LastScan scan = getScanFromScannerId(scannerId);
if (scan != null) {
- scan.timeout = true;
+ scan.isTimeout = true;
}
}
@@ -304,146 +370,266 @@
return initiator;
}
+ private static String filterToStringWithoutNullParam(ScanFilter filter) {
+ String filterString = "BluetoothLeScanFilter [";
+ if (filter.getDeviceName() != null) {
+ filterString += " DeviceName=" + filter.getDeviceName();
+ }
+ if (filter.getDeviceAddress() != null) {
+ filterString += " DeviceAddress=" + filter.getDeviceAddress();
+ }
+ if (filter.getServiceUuid() != null) {
+ filterString += " ServiceUuid=" + filter.getServiceUuid();
+ }
+ if (filter.getServiceUuidMask() != null) {
+ filterString += " ServiceUuidMask=" + filter.getServiceUuidMask();
+ }
+ if (filter.getServiceSolicitationUuid() != null) {
+ filterString += " ServiceSolicitationUuid=" + filter.getServiceSolicitationUuid();
+ }
+ if (filter.getServiceSolicitationUuidMask() != null) {
+ filterString +=
+ " ServiceSolicitationUuidMask=" + filter.getServiceSolicitationUuidMask();
+ }
+ if (filter.getServiceDataUuid() != null) {
+ filterString += " ServiceDataUuid=" + Objects.toString(filter.getServiceDataUuid());
+ }
+ if (filter.getServiceData() != null) {
+ filterString += " ServiceData=" + Arrays.toString(filter.getServiceData());
+ }
+ if (filter.getServiceDataMask() != null) {
+ filterString += " ServiceDataMask=" + Arrays.toString(filter.getServiceDataMask());
+ }
+ if (filter.getManufacturerId() >= 0) {
+ filterString += " ManufacturerId=" + filter.getManufacturerId();
+ }
+ if (filter.getManufacturerData() != null) {
+ filterString += " ManufacturerData=" + Arrays.toString(filter.getManufacturerData());
+ }
+ if (filter.getManufacturerDataMask() != null) {
+ filterString +=
+ " ManufacturerDataMask=" + Arrays.toString(filter.getManufacturerDataMask());
+ }
+ filterString += " ]";
+ return filterString;
+ }
+
+
+ private static String scanModeToString(int scanMode) {
+ switch (scanMode) {
+ case ScanSettings.SCAN_MODE_OPPORTUNISTIC:
+ return "OPPORTUNISTIC";
+ case ScanSettings.SCAN_MODE_LOW_LATENCY:
+ return "LOW_LATENCY";
+ case ScanSettings.SCAN_MODE_BALANCED:
+ return "BALANCED";
+ case ScanSettings.SCAN_MODE_LOW_POWER:
+ return "LOW_POWER";
+ default:
+ return "UNKNOWN(" + scanMode + ")";
+ }
+ }
+
+ private static String callbackTypeToString(int callbackType) {
+ switch (callbackType) {
+ case ScanSettings.CALLBACK_TYPE_ALL_MATCHES:
+ return "ALL_MATCHES";
+ case ScanSettings.CALLBACK_TYPE_FIRST_MATCH:
+ return "FIRST_MATCH";
+ case ScanSettings.CALLBACK_TYPE_MATCH_LOST:
+ return "LOST";
+ default:
+ return callbackType == (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
+ | ScanSettings.CALLBACK_TYPE_MATCH_LOST) ? "[FIRST_MATCH | LOST]" : "UNKNOWN: "
+ + callbackType;
+ }
+ }
+
synchronized void dumpToString(StringBuilder sb) {
+ long currentTime = System.currentTimeMillis();
long currTime = SystemClock.elapsedRealtime();
- long maxScan = mMaxScanTime;
- long minScan = mMinScanTime;
+ long Score = 0;
long scanDuration = 0;
+ long suspendDuration = 0;
+ long activeDuration = 0;
+ long totalActiveTime = mTotalActiveTime;
+ long totalSuspendTime = mTotalSuspendTime;
+ long totalScanTime = mTotalScanTime;
+ long oppScanTime = mOppScanTime;
+ long lowPowerScanTime = mLowPowerScanTime;
+ long balancedScanTime = mBalancedScanTime;
+ long lowLatencyScanTime = mLowLantencyScanTime;
+ int oppScan = mOppScan;
+ int lowPowerScan = mLowPowerScan;
+ int balancedScan = mBalancedScan;
+ int lowLatencyScan = mLowLantencyScan;
- if (isScanning()) {
- scanDuration = currTime - mScanStartTime;
- }
- minScan = Math.min(scanDuration, minScan);
- maxScan = Math.max(scanDuration, maxScan);
+ if (!mOngoingScans.isEmpty()) {
+ for (Integer key : mOngoingScans.keySet()) {
+ LastScan scan = mOngoingScans.get(key);
+ scanDuration = currTime - scan.timestamp;
- if (minScan == Long.MAX_VALUE) {
- minScan = 0;
- }
+ if (scan.isSuspended) {
+ suspendDuration = currTime - scan.suspendStartTime;
+ totalSuspendTime += suspendDuration;
+ }
- /*TODO: Average scan time can be skewed for
- * multiple scan clients. It will show less than
- * actual value.
- * */
- long avgScan = 0;
- long totalScanTime = mTotalScanTime + scanDuration;
- if (mScansStarted > 0) {
- avgScan = totalScanTime / mScansStarted;
+ totalScanTime += scanDuration;
+ totalSuspendTime += suspendDuration;
+ activeDuration = scanDuration - scan.suspendDuration - suspendDuration;
+ totalActiveTime += activeDuration;
+ switch (scan.scanMode) {
+ case ScanSettings.SCAN_MODE_OPPORTUNISTIC:
+ oppScanTime += activeDuration;
+ break;
+ case ScanSettings.SCAN_MODE_LOW_POWER:
+ lowPowerScanTime += activeDuration;
+ break;
+ case ScanSettings.SCAN_MODE_BALANCED:
+ balancedScanTime += activeDuration;
+ break;
+ case ScanSettings.SCAN_MODE_LOW_LATENCY:
+ lowLatencyScanTime += activeDuration;
+ break;
+ }
+ }
}
+ Score = (oppScanTime * OPPORTUNISTIC_WEIGHT + lowPowerScanTime * LOW_POWER_WEIGHT
+ + balancedScanTime * BALANCED_WEIGHT + lowLatencyScanTime * LOW_LATENCY_WEIGHT) / 100;
sb.append(" " + appName);
if (isRegistered) {
sb.append(" (Registered)");
}
- if (!mLastScans.isEmpty()) {
- LastScan lastScan = mLastScans.get(mLastScans.size() - 1);
- if (lastScan.opportunistic) {
- sb.append(" (Opportunistic)");
- }
- if (lastScan.background) {
- sb.append(" (Background)");
- }
- if (lastScan.timeout) {
- sb.append(" (Forced-Opportunistic)");
- }
- if (lastScan.filtered) {
- sb.append(" (Filtered)");
- }
- }
- sb.append("\n");
+ sb.append("\n LE scans (started/stopped) : "
+ + mScansStarted + " / " + mScansStopped);
+ sb.append("\n Scan time in ms (active/suspend/total) : "
+ + totalActiveTime + " / " + totalSuspendTime + " / " + totalScanTime);
+ sb.append("\n Scan time with mode in ms (Opp/LowPower/Balanced/LowLatency): "
+ + oppScanTime + " / " + lowPowerScanTime + " / " + balancedScanTime + " / "
+ + lowLatencyScanTime);
+ sb.append("\n Scan mode counter (Opp/LowPower/Balanced/LowLatency) : " + oppScan
+ + " / " + lowPowerScan + " / " + balancedScan + " / " + lowLatencyScan);
+ sb.append("\n Score : " + Score);
+ sb.append("\n Total number of results : " + results);
- sb.append(" LE scans (started/stopped) : " + mScansStarted + " / " + mScansStopped
- + "\n");
- sb.append(" Scan time in ms (min/max/avg/total): " + minScan + " / " + maxScan + " / "
- + avgScan + " / " + totalScanTime + "\n");
- if (mTotalSuspendTime != 0) {
- sb.append(" Total time suspended : " + mTotalSuspendTime + "ms\n");
- }
- sb.append(" Total number of results : " + results + "\n");
-
- long currentTime = System.currentTimeMillis();
- long elapsedRt = SystemClock.elapsedRealtime();
if (!mLastScans.isEmpty()) {
- sb.append(" Last " + mLastScans.size() + " scans :\n");
+ sb.append("\n Last " + mLastScans.size()
+ + " scans :");
for (int i = 0; i < mLastScans.size(); i++) {
LastScan scan = mLastScans.get(i);
- Date timestamp = new Date(currentTime - elapsedRt + scan.timestamp);
- sb.append(" " + DATE_FORMAT.format(timestamp) + " - ");
+ Date timestamp = new Date(currentTime - currTime + scan.timestamp);
+ sb.append("\n " + DATE_FORMAT.format(timestamp) + " - ");
sb.append(scan.duration + "ms ");
- if (scan.opportunistic) {
+ if (scan.isOpportunisticScan) {
sb.append("Opp ");
}
- if (scan.background) {
+ if (scan.isBackgroundScan) {
sb.append("Back ");
}
- if (scan.timeout) {
+ if (scan.isTimeout) {
sb.append("Forced ");
}
- if (scan.filtered) {
+ if (scan.isFilterScan) {
sb.append("Filter ");
}
sb.append(scan.results + " results");
- sb.append(" (" + scan.scannerId + ")");
- sb.append("\n");
+ sb.append(" (" + scan.scannerId + ") ");
+ if (scan.isCallbackScan) {
+ sb.append("CB ");
+ } else {
+ sb.append("PI ");
+ }
+ if (scan.isBatchScan) {
+ sb.append("Batch Scan");
+ } else {
+ sb.append("Regular Scan");
+ }
if (scan.suspendDuration != 0) {
- sb.append(" └" + " Suspended Time: " + scan.suspendDuration + "ms\n");
+ activeDuration = scan.duration - scan.suspendDuration;
+ sb.append("\n └ " + "Suspended Time: " + scan.suspendDuration
+ + "ms, Active Time: " + activeDuration);
+ }
+ sb.append("\n └ " + "Scan Config: [ ScanMode="
+ + scanModeToString(scan.scanMode) + ", callbackType="
+ + callbackTypeToString(scan.scanCallbackType) + " ]");
+ if (scan.isFilterScan) {
+ sb.append(scan.filterString);
}
}
}
if (!mOngoingScans.isEmpty()) {
- sb.append(" Ongoing scans :\n");
+ sb.append("\n Ongoing scans :");
for (Integer key : mOngoingScans.keySet()) {
LastScan scan = mOngoingScans.get(key);
- Date timestamp = new Date(currentTime - elapsedRt + scan.timestamp);
- sb.append(" " + DATE_FORMAT.format(timestamp) + " - ");
- sb.append((elapsedRt - scan.timestamp) + "ms ");
- if (scan.opportunistic) {
+ Date timestamp = new Date(currentTime - currTime + scan.timestamp);
+ sb.append("\n " + DATE_FORMAT.format(timestamp) + " - ");
+ sb.append((currTime - scan.timestamp) + "ms ");
+ if (scan.isOpportunisticScan) {
sb.append("Opp ");
}
- if (scan.background) {
+ if (scan.isBackgroundScan) {
sb.append("Back ");
}
- if (scan.timeout) {
+ if (scan.isTimeout) {
sb.append("Forced ");
}
- if (scan.filtered) {
+ if (scan.isFilterScan) {
sb.append("Filter ");
}
if (scan.isSuspended) {
sb.append("Suspended ");
}
sb.append(scan.results + " results");
- sb.append(" (" + scan.scannerId + ")");
- sb.append("\n");
+ sb.append(" (" + scan.scannerId + ") ");
+ if (scan.isCallbackScan) {
+ sb.append("CB ");
+ } else {
+ sb.append("PI ");
+ }
+ if (scan.isBatchScan) {
+ sb.append("Batch Scan");
+ } else {
+ sb.append("Regular Scan");
+ }
if (scan.suspendStartTime != 0) {
- long duration = scan.suspendDuration + (scan.isSuspended ? (elapsedRt
+ long duration = scan.suspendDuration + (scan.isSuspended ? (currTime
- scan.suspendStartTime) : 0);
- sb.append(" └" + " Suspended Time: " + duration + "ms\n");
+ activeDuration = scan.duration - scan.suspendDuration;
+ sb.append("\n └ " + "Suspended Time:" + scan.suspendDuration
+ + "ms, Active Time:" + activeDuration);
+ }
+ sb.append("\n └ " + "Scan Config: [ ScanMode="
+ + scanModeToString(scan.scanMode) + ", callbackType="
+ + callbackTypeToString(scan.scanCallbackType) + " ]");
+ if (scan.isFilterScan) {
+ sb.append(scan.filterString);
}
}
}
ContextMap.App appEntry = mContextMap.getByName(appName);
if (appEntry != null && isRegistered) {
- sb.append(" Application ID : " + appEntry.id + "\n");
- sb.append(" UUID : " + appEntry.uuid + "\n");
+ sb.append("\n Application ID : " + appEntry.id);
+ sb.append("\n UUID : " + appEntry.uuid);
List<ContextMap.Connection> connections = mContextMap.getConnectionByApp(appEntry.id);
- sb.append(" Connections: " + connections.size() + "\n");
+ sb.append("\n Connections: " + connections.size());
Iterator<ContextMap.Connection> ii = connections.iterator();
while (ii.hasNext()) {
ContextMap.Connection connection = ii.next();
- long connectionTime = elapsedRt - connection.startTime;
- Date timestamp = new Date(currentTime - elapsedRt + connection.startTime);
- sb.append(" " + DATE_FORMAT.format(timestamp) + " - ");
+ long connectionTime = currTime - connection.startTime;
+ Date timestamp = new Date(currentTime - currTime + connection.startTime);
+ sb.append("\n " + DATE_FORMAT.format(timestamp) + " - ");
sb.append((connectionTime) + "ms ");
- sb.append(": " + connection.address + " (" + connection.connId + ")\n");
+ sb.append(": " + connection.address + " (" + connection.connId + ")");
}
}
- sb.append("\n");
+ sb.append("\n\n");
}
}
diff --git a/src/com/android/bluetooth/gatt/GattService.java b/src/com/android/bluetooth/gatt/GattService.java
index 8a3a1aa..5f2b42e 100644
--- a/src/com/android/bluetooth/gatt/GattService.java
+++ b/src/com/android/bluetooth/gatt/GattService.java
@@ -1961,10 +1961,15 @@
Utils.checkCallerHasNetworkSetupWizardPermission(this);
AppScanStats app = mScannerMap.getAppScanStatsById(scannerId);
+ ScannerMap.App cbApp = mScannerMap.getById(scannerId);
if (app != null) {
scanClient.stats = app;
boolean isFilteredScan = (filters != null) && !filters.isEmpty();
- app.recordScanStart(settings, isFilteredScan, scannerId);
+ boolean isCallbackScan = false;
+ if (cbApp != null) {
+ isCallbackScan = cbApp.callback != null;
+ }
+ app.recordScanStart(settings, filters, isFilteredScan, isCallbackScan, scannerId);
}
mScanManager.startScan(scanClient);
@@ -1989,6 +1994,13 @@
piInfo.settings = settings;
piInfo.filters = filters;
piInfo.callingPackage = callingPackage;
+
+ // Don't start scan if the Pi scan already in mScannerMap.
+ if (mScannerMap.getByContextInfo(piInfo) != null) {
+ Log.d(TAG, "Don't startScan(PI) since the same Pi scan already in mScannerMap.");
+ return;
+ }
+
ScannerMap.App app = mScannerMap.add(uuid, null, null, piInfo, this);
app.mUserHandle = UserHandle.of(UserHandle.getCallingUserId());
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
@@ -2026,7 +2038,8 @@
if (scanStats != null) {
scanClient.stats = scanStats;
boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty();
- scanStats.recordScanStart(piInfo.settings, isFilteredScan, scannerId);
+ scanStats.recordScanStart(
+ piInfo.settings, piInfo.filters, isFilteredScan, false, scannerId);
}
mScanManager.startScan(scanClient);
@@ -3162,6 +3175,22 @@
return uuids;
}
+ void dumpRegisterId(StringBuilder sb) {
+ sb.append(" Scanner:\n");
+ for (Integer appId : mScannerMap.getAllAppsIds()) {
+ println(sb, " app_if: " + appId + ", appName: " + mScannerMap.getById(appId).name);
+ }
+ sb.append(" Client:\n");
+ for (Integer appId : mClientMap.getAllAppsIds()) {
+ println(sb, " app_if: " + appId + ", appName: " + mClientMap.getById(appId).name);
+ }
+ sb.append(" Server:\n");
+ for (Integer appId : mServerMap.getAllAppsIds()) {
+ println(sb, " app_if: " + appId + ", appName: " + mServerMap.getById(appId).name);
+ }
+ sb.append("\n\n");
+ }
+
@Override
public void dump(StringBuilder sb) {
super.dump(sb);
@@ -3172,7 +3201,10 @@
println(sb, "mMaxScanFilters: " + mMaxScanFilters);
- sb.append("\nGATT Scanner Map\n");
+ sb.append("\nRegistered App\n");
+ dumpRegisterId(sb);
+
+ sb.append("GATT Scanner Map\n");
mScannerMap.dump(sb);
sb.append("GATT Client Map\n");
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index 06c4822..e92688f 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -841,8 +841,6 @@
break;
}
case CALL_STATE_CHANGED: {
- if (mDeviceSilenced) break;
-
HeadsetCallState callState = (HeadsetCallState) message.obj;
if (!mNativeInterface.phoneStateChange(mDevice, callState)) {
stateLogW("processCallState: failed to update call state " + callState);
diff --git a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
index 6d14e59..914b5b1 100644
--- a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
+++ b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
@@ -337,7 +337,10 @@
if (DBG) {
Log.d(TAG, "Success = " + Boolean.toString(connectionSuccessful));
}
- } catch (IOException e) {
+ } catch (IOException | NullPointerException e) {
+ // Will get NPE if a null mSocket is passed to BluetoothObexTransport.
+ // mSocket can be set to null if an abort() --> closeSocket() was called between
+ // the calls to connectSocket() and connectObexSession().
Log.w(TAG, "CONNECT Failure " + e.toString());
closeSocket();
}
diff --git a/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java b/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
index c121cf7..a97a5b0 100644
--- a/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
+++ b/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
@@ -23,11 +23,14 @@
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.content.res.Resources;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.bluetooth.R;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -40,75 +43,126 @@
@RunWith(AndroidJUnit4.class)
public class A2dpCodecConfigTest {
private Context mTargetContext;
- private A2dpCodecConfig mA2dpCodecConfig;
- private BluetoothAdapter mAdapter;
- private BluetoothCodecConfig mCodecConfigSbc;
- private BluetoothCodecConfig mCodecConfigAac;
- private BluetoothCodecConfig mCodecConfigAptx;
- private BluetoothCodecConfig mCodecConfigAptxHd;
- private BluetoothCodecConfig mCodecConfigLdac;
private BluetoothDevice mTestDevice;
+ private A2dpCodecConfig mA2dpCodecConfig;
+ @Mock private Context mMockContext;
+ @Mock private Resources mMockResources;
@Mock private A2dpNativeInterface mA2dpNativeInterface;
- static final int SBC_PRIORITY_DEFAULT = 1001;
- static final int AAC_PRIORITY_DEFAULT = 2001;
- static final int APTX_PRIORITY_DEFAULT = 3001;
- static final int APTX_HD_PRIORITY_DEFAULT = 4001;
- static final int LDAC_PRIORITY_DEFAULT = 5001;
- static final int PRIORITY_HIGH = 1000000;
+ private static final int[] sOptionalCodecTypes = new int[] {
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC
+ };
+ // Not use the default value to make sure it reads from config
+ private static final int SBC_PRIORITY_DEFAULT = 1001;
+ private static final int AAC_PRIORITY_DEFAULT = 3001;
+ private static final int APTX_PRIORITY_DEFAULT = 5001;
+ private static final int APTX_HD_PRIORITY_DEFAULT = 7001;
+ private static final int LDAC_PRIORITY_DEFAULT = 9001;
+ private static final int PRIORITY_HIGH = 1000000;
+
+ private static final BluetoothCodecConfig[] sCodecCapabilities = new BluetoothCodecConfig[] {
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ SBC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_MONO
+ | BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ AAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ APTX_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100
+ | BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ APTX_HD_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100
+ | BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ LDAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100
+ | BluetoothCodecConfig.SAMPLE_RATE_48000
+ | BluetoothCodecConfig.SAMPLE_RATE_88200
+ | BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16
+ | BluetoothCodecConfig.BITS_PER_SAMPLE_24
+ | BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0) // Codec-specific fields
+ };
+
+ private static final BluetoothCodecConfig[] sDefaultCodecConfigs = new BluetoothCodecConfig[] {
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ SBC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ AAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ APTX_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ APTX_HD_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ LDAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0) // Codec-specific fields
+ };
@Before
public void setUp() throws Exception {
- mTargetContext = InstrumentationRegistry.getTargetContext();
// Set up mocks and test assets
MockitoAnnotations.initMocks(this);
+ mTargetContext = InstrumentationRegistry.getTargetContext();
- mA2dpCodecConfig = new A2dpCodecConfig(mTargetContext, mA2dpNativeInterface);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_sbc))
+ .thenReturn(SBC_PRIORITY_DEFAULT);
+ when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_aac))
+ .thenReturn(AAC_PRIORITY_DEFAULT);
+ when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_aptx))
+ .thenReturn(APTX_PRIORITY_DEFAULT);
+ when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_aptx_hd))
+ .thenReturn(APTX_HD_PRIORITY_DEFAULT);
+ when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_ldac))
+ .thenReturn(LDAC_PRIORITY_DEFAULT);
+
+ mA2dpCodecConfig = new A2dpCodecConfig(mMockContext, mA2dpNativeInterface);
mTestDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
doReturn(true).when(mA2dpNativeInterface).setCodecConfigPreference(
any(BluetoothDevice.class),
any(BluetoothCodecConfig[].class));
-
- // Create sample codec configs
- mCodecConfigSbc = new BluetoothCodecConfig(
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
- SBC_PRIORITY_DEFAULT,
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.BITS_PER_SAMPLE_16,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
- mCodecConfigAac = new BluetoothCodecConfig(
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
- AAC_PRIORITY_DEFAULT,
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.BITS_PER_SAMPLE_16,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
- mCodecConfigAptx = new BluetoothCodecConfig(
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
- APTX_PRIORITY_DEFAULT,
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.BITS_PER_SAMPLE_16,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
- mCodecConfigAptxHd = new BluetoothCodecConfig(
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
- APTX_HD_PRIORITY_DEFAULT,
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.BITS_PER_SAMPLE_16,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
- mCodecConfigLdac = new BluetoothCodecConfig(
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
- LDAC_PRIORITY_DEFAULT,
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.BITS_PER_SAMPLE_16,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
-
}
@After
@@ -139,128 +193,383 @@
}
}
+ /**
+ * Test that we can fallback to default codec by lower the codec priority we changed before.
+ */
@Test
public void testSetCodecPreference_priorityHighToDefault() {
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, SBC_PRIORITY_DEFAULT,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, AAC_PRIORITY_DEFAULT,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, APTX_PRIORITY_DEFAULT,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, APTX_HD_PRIORITY_DEFAULT,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
false);
}
+ /**
+ * Test that we can change the default codec to another by raising the codec priority.
+ * LDAC is the default highest codec, so no need to test others.
+ */
@Test
- public void testSetCodecPreference_priorityDefaultToHigh() {
- testSetCodecPreference_codecPriorityChangeCase(
+ public void testSetCodecPreference_priorityDefaultToRaiseHigh() {
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
false);
}
@Test
- public void testSetCodecPreference_priorityHighToHigh() {
- testSetCodecPreference_codecPriorityChangeCase(
+ public void testSetCodecPreference_prioritySbcHighToOthersHigh() {
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
false);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
true);
- testSetCodecPreference_codecPriorityChangeCase(
+ testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
true);
}
@Test
- public void testSetCodecPreference_parametersChange() {
- int unSupportedParameter = 200;
+ public void testSetCodecPreference_priorityAacHighToOthersHigh() {
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+ true);
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+ false);
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+ true);
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+ true);
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+ true);
+ }
- testSetCodecPreference_parametersChangeCase(
+ @Test
+ public void testSetCodecPreference_parametersChangedInSameCodec() {
+ // The SBC default / preferred config is Stereo
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
BluetoothCodecConfig.SAMPLE_RATE_44100,
BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
false);
- testSetCodecPreference_parametersChangeCase(
+ // SBC Mono is mandatory
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
true);
- testSetCodecPreference_parametersChangeCase(
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- unSupportedParameter,
+
+ // The LDAC default / preferred config within mDefaultCodecConfigs
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
false);
- testSetCodecPreference_parametersChangeCase(
- BluetoothCodecConfig.SAMPLE_RATE_48000,
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ true);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
true);
- testSetCodecPreference_parametersChangeCase(
- BluetoothCodecConfig.SAMPLE_RATE_48000,
- BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
true);
- testSetCodecPreference_parametersChangeCase(
- BluetoothCodecConfig.SAMPLE_RATE_48000,
- unSupportedParameter,
+
+ // None for system default (Developer options)
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_NONE,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ false);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ false);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_NONE,
false);
- testSetCodecPreference_parametersChangeCase(
- unSupportedParameter,
+ int unsupportedParameter = 0xc0;
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ unsupportedParameter,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ false);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ unsupportedParameter,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ false);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ unsupportedParameter,
+ false);
+
+ int multipleSupportedParameters = 0x03;
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ multipleSupportedParameters,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ false);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ multipleSupportedParameters,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ false);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ multipleSupportedParameters,
+ false);
+ }
+
+ @Test
+ public void testSetCodecPreference_parametersChangedToAnotherCodec() {
+ // different sample rate (44.1 kHz -> 96 kHz)
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ true);
+ // different bits per channel (16 bits -> 32 bits)
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ true);
+ // change all PCM parameters
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ true);
+
+ // None for system default (Developer options)
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_NONE,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ true);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ true);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_NONE,
+ true);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SAMPLE_RATE_NONE,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+ BluetoothCodecConfig.CHANNEL_MODE_NONE,
+ true);
+
+ int unsupportedParameter = 0xc0;
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ unsupportedParameter,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
false);
- testSetCodecPreference_parametersChangeCase(
- unSupportedParameter,
- BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ unsupportedParameter,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
false);
- testSetCodecPreference_parametersChangeCase(
- unSupportedParameter,
- unSupportedParameter,
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ unsupportedParameter,
+ false);
+
+ int multipleSupportedParameters = 0x03;
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ multipleSupportedParameters,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ false);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ multipleSupportedParameters,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ false);
+ testCodecParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ multipleSupportedParameters,
+ false);
+ }
+
+ @Test
+ public void testSetCodecPreference_ldacCodecSpecificFieldChanged() {
+ int ldacAudioQualityHigh = 1000;
+ int ldacAudioQualityABR = 1003;
+ int sbcCodecSpecificParameter = 0;
+ testCodecSpecificParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ ldacAudioQualityABR,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ ldacAudioQualityABR,
+ false);
+ testCodecSpecificParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ ldacAudioQualityHigh,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ ldacAudioQualityABR,
+ true);
+ testCodecSpecificParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ ldacAudioQualityABR,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ sbcCodecSpecificParameter,
+ true);
+
+ // Only LDAC will check the codec specific1 field, but not SBC
+ testCodecSpecificParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ sbcCodecSpecificParameter,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ ldacAudioQualityABR,
+ true);
+ testCodecSpecificParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ ldacAudioQualityABR,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ ldacAudioQualityABR,
+ true);
+ testCodecSpecificParametersChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ ldacAudioQualityHigh,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ sbcCodecSpecificParameter,
false);
}
@Test
public void testDisableOptionalCodecs() {
- int verifyCount = 0;
- BluetoothCodecConfig[] codecConfigArray =
+ BluetoothCodecConfig[] codecConfigsArray =
new BluetoothCodecConfig[BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX];
- codecConfigArray[0] = new BluetoothCodecConfig(
+ codecConfigsArray[0] = new BluetoothCodecConfig(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
BluetoothCodecConfig.SAMPLE_RATE_NONE,
@@ -268,32 +577,32 @@
BluetoothCodecConfig.CHANNEL_MODE_NONE,
0, 0, 0, 0); // Codec-specific fields
- // Test don't invoke to native when current codec is SBC
- mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigSbc);
- verify(mA2dpNativeInterface, times(verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
+ // shouldn't invoke to native when current codec is SBC
+ mA2dpCodecConfig.disableOptionalCodecs(
+ mTestDevice,
+ getDefaultCodecConfigByType(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT));
+ verify(mA2dpNativeInterface, times(0)).setCodecConfigPreference(mTestDevice,
+ codecConfigsArray);
- // Test invoke to native when current codec is not SBC
- mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigAac);
- verify(mA2dpNativeInterface, times(++verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
- mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigAptx);
- verify(mA2dpNativeInterface, times(++verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
- mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigAptxHd);
- verify(mA2dpNativeInterface, times(++verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
- mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigLdac);
- verify(mA2dpNativeInterface, times(++verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
+ // should invoke to native when current codec is an optional codec
+ int invokedCounter = 0;
+ for (int codecType : sOptionalCodecTypes) {
+ mA2dpCodecConfig.disableOptionalCodecs(
+ mTestDevice,
+ getDefaultCodecConfigByType(codecType,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT));
+ verify(mA2dpNativeInterface, times(++invokedCounter))
+ .setCodecConfigPreference(mTestDevice, codecConfigsArray);
+ }
}
@Test
public void testEnableOptionalCodecs() {
- int verifyCount = 0;
- BluetoothCodecConfig[] codecConfigArray =
+ BluetoothCodecConfig[] codecConfigsArray =
new BluetoothCodecConfig[BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX];
- codecConfigArray[0] = new BluetoothCodecConfig(
+ codecConfigsArray[0] = new BluetoothCodecConfig(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
SBC_PRIORITY_DEFAULT,
BluetoothCodecConfig.SAMPLE_RATE_NONE,
@@ -301,115 +610,292 @@
BluetoothCodecConfig.CHANNEL_MODE_NONE,
0, 0, 0, 0); // Codec-specific fields
- // Test invoke to native when current codec is SBC
- mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigSbc);
- verify(mA2dpNativeInterface, times(++verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
+ // should invoke to native when current codec is SBC
+ mA2dpCodecConfig.enableOptionalCodecs(
+ mTestDevice,
+ getDefaultCodecConfigByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT));
+ verify(mA2dpNativeInterface, times(1))
+ .setCodecConfigPreference(mTestDevice, codecConfigsArray);
- // Test don't invoke to native when current codec is not SBC
- mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigAac);
- verify(mA2dpNativeInterface, times(verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
- mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigAptx);
- verify(mA2dpNativeInterface, times(verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
- mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigAptxHd);
- verify(mA2dpNativeInterface, times(verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
- mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigLdac);
- verify(mA2dpNativeInterface, times(verifyCount))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
+ // shouldn't invoke to native when current codec is already an optional
+ for (int codecType : sOptionalCodecTypes) {
+ mA2dpCodecConfig.enableOptionalCodecs(
+ mTestDevice,
+ getDefaultCodecConfigByType(codecType,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT));
+ verify(mA2dpNativeInterface, times(1))
+ .setCodecConfigPreference(mTestDevice, codecConfigsArray);
+ }
}
- private void testSetCodecPreference_parametersChangeCase(int sampleRate, int bitPerSample,
- boolean invokeNative) {
- BluetoothCodecConfig[] invalidSelectableCodecs = new BluetoothCodecConfig[1];
- invalidSelectableCodecs[0] = new BluetoothCodecConfig(
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
- LDAC_PRIORITY_DEFAULT,
- (BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000),
- (BluetoothCodecConfig.BITS_PER_SAMPLE_16 | BluetoothCodecConfig.BITS_PER_SAMPLE_24),
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
+ private BluetoothCodecConfig getDefaultCodecConfigByType(int codecType, int codecPriority) {
+ for (BluetoothCodecConfig codecConfig : sDefaultCodecConfigs) {
+ if (codecConfig.getCodecType() != codecType) {
+ continue;
+ }
+ return new BluetoothCodecConfig(
+ codecConfig.getCodecType(),
+ (codecPriority != BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT
+ ? codecPriority : codecConfig.getCodecPriority()),
+ codecConfig.getSampleRate(), codecConfig.getBitsPerSample(),
+ codecConfig.getChannelMode(), codecConfig.getCodecSpecific1(),
+ codecConfig.getCodecSpecific2(), codecConfig.getCodecSpecific3(),
+ codecConfig.getCodecSpecific4());
+ }
+ Assert.fail("getDefaultCodecConfigByType: No such codecType=" + codecType
+ + " in sDefaultCodecConfigs");
+ return null;
+ }
+
+ private BluetoothCodecConfig getCodecCapabilitiesByType(int codecType) {
+ for (BluetoothCodecConfig codecCapabilities : sCodecCapabilities) {
+ if (codecCapabilities.getCodecType() != codecType) {
+ continue;
+ }
+ return new BluetoothCodecConfig(
+ codecCapabilities.getCodecType(), codecCapabilities.getCodecPriority(),
+ codecCapabilities.getSampleRate(), codecCapabilities.getBitsPerSample(),
+ codecCapabilities.getChannelMode(), codecCapabilities.getCodecSpecific1(),
+ codecCapabilities.getCodecSpecific2(), codecCapabilities.getCodecSpecific3(),
+ codecCapabilities.getCodecSpecific4());
+ }
+ Assert.fail("getCodecCapabilitiesByType: No such codecType=" + codecType
+ + " in sCodecCapabilities");
+ return null;
+ }
+
+ private void testCodecParametersChangeHelper(int newCodecType, int oldCodecType,
+ int sampleRate, int bitsPerSample, int channelMode, boolean invokeNative) {
+ BluetoothCodecConfig oldCodecConfig =
+ getDefaultCodecConfigByType(oldCodecType, PRIORITY_HIGH);
+ BluetoothCodecConfig[] newCodecConfigsArray = new BluetoothCodecConfig[] {
+ new BluetoothCodecConfig(newCodecType,
+ PRIORITY_HIGH,
+ sampleRate, bitsPerSample, channelMode,
+ 0, 0, 0, 0) // Codec-specific fields
+ };
+
+ // Test cases: 1. no mandatory; 2. mandatory + old + new; 3. all codecs
+ BluetoothCodecConfig[] minimumCodecsArray;
+ if (!oldCodecConfig.isMandatoryCodec() && !newCodecConfigsArray[0].isMandatoryCodec()) {
+ BluetoothCodecConfig[] optionalCodecsArray;
+ if (oldCodecType != newCodecType) {
+ optionalCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(oldCodecType),
+ getCodecCapabilitiesByType(newCodecType)
+ };
+ minimumCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+ getCodecCapabilitiesByType(oldCodecType),
+ getCodecCapabilitiesByType(newCodecType)
+ };
+ } else {
+ optionalCodecsArray = new BluetoothCodecConfig[]
+ {getCodecCapabilitiesByType(oldCodecType)};
+ minimumCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+ getCodecCapabilitiesByType(oldCodecType)
+ };
+ }
+ BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+ sCodecCapabilities,
+ optionalCodecsArray);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
+ // no mandatory codec in selectable, and should not apply
+ verify(mA2dpNativeInterface, times(0)).setCodecConfigPreference(mTestDevice,
+ newCodecConfigsArray);
- BluetoothCodecConfig[] selectableCodecs = new BluetoothCodecConfig[2];
- selectableCodecs[0] = mCodecConfigSbc;
- selectableCodecs[1] = new BluetoothCodecConfig(
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
- LDAC_PRIORITY_DEFAULT,
- (BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000),
- (BluetoothCodecConfig.BITS_PER_SAMPLE_16 | BluetoothCodecConfig.BITS_PER_SAMPLE_24),
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
+ } else {
+ if (oldCodecType != newCodecType) {
+ minimumCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(oldCodecType),
+ getCodecCapabilitiesByType(newCodecType)
+ };
+ } else {
+ minimumCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(oldCodecType),
+ };
+ }
+ }
- BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1];
- codecConfigArray[0] = new BluetoothCodecConfig(
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
- LDAC_PRIORITY_DEFAULT,
- sampleRate,
- bitPerSample,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
-
- BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(mCodecConfigLdac,
- invalidSelectableCodecs,
- invalidSelectableCodecs);
- mA2dpCodecConfig.setCodecConfigPreference(mTestDevice, codecStatus, codecConfigArray[0]);
-
- codecStatus = new BluetoothCodecStatus(mCodecConfigLdac,
- selectableCodecs,
- selectableCodecs);
-
- mA2dpCodecConfig.setCodecConfigPreference(mTestDevice, codecStatus, codecConfigArray[0]);
+ // 2. mandatory + old + new codecs only
+ BluetoothCodecStatus codecStatus =
+ new BluetoothCodecStatus(oldCodecConfig, sCodecCapabilities, minimumCodecsArray);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
verify(mA2dpNativeInterface, times(invokeNative ? 1 : 0))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
- }
-
- private void testSetCodecPreference_codecPriorityChangeCase(int newCodecType,
- int newCodecPriority, int oldCodecType, int oldCodecPriority, boolean invokeNative) {
- BluetoothCodecConfig[] selectableCodecs = new BluetoothCodecConfig[5];
- selectableCodecs[0] = mCodecConfigSbc;
- selectableCodecs[1] = mCodecConfigAac;
- selectableCodecs[2] = mCodecConfigAptx;
- selectableCodecs[3] = mCodecConfigAptxHd;
- selectableCodecs[4] = mCodecConfigLdac;
-
- BluetoothCodecConfig[] invalidSelectableCodecs = new BluetoothCodecConfig[4];
- invalidSelectableCodecs[0] = mCodecConfigAac;
- invalidSelectableCodecs[1] = mCodecConfigAptx;
- invalidSelectableCodecs[2] = mCodecConfigAptxHd;
- invalidSelectableCodecs[3] = mCodecConfigLdac;
-
- BluetoothCodecConfig oldCodecConfig = new BluetoothCodecConfig(
- oldCodecType,
- oldCodecPriority,
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.BITS_PER_SAMPLE_16,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
-
- BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1];
- codecConfigArray[0] = new BluetoothCodecConfig(
- newCodecType,
- newCodecPriority,
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.BITS_PER_SAMPLE_16,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- 0, 0, 0, 0); // Codec-specific fields
-
- BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
- invalidSelectableCodecs,
- invalidSelectableCodecs);
- mA2dpCodecConfig.setCodecConfigPreference(mTestDevice, codecStatus, codecConfigArray[0]);
-
+ // 3. all codecs were selectable
codecStatus = new BluetoothCodecStatus(oldCodecConfig,
- selectableCodecs,
- selectableCodecs);
+ sCodecCapabilities,
+ sCodecCapabilities);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
+ verify(mA2dpNativeInterface, times(invokeNative ? 2 : 0))
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+ }
- mA2dpCodecConfig.setCodecConfigPreference(mTestDevice, codecStatus, codecConfigArray[0]);
+ private void testCodecSpecificParametersChangeHelper(int newCodecType, int newCodecSpecific,
+ int oldCodecType, int oldCodecSpecific, boolean invokeNative) {
+ BluetoothCodecConfig codecDefaultTemp =
+ getDefaultCodecConfigByType(oldCodecType, PRIORITY_HIGH);
+ BluetoothCodecConfig oldCodecConfig =
+ new BluetoothCodecConfig(codecDefaultTemp.getCodecType(),
+ codecDefaultTemp.getCodecPriority(),
+ codecDefaultTemp.getSampleRate(),
+ codecDefaultTemp.getBitsPerSample(),
+ codecDefaultTemp.getChannelMode(),
+ oldCodecSpecific, 0, 0, 0); // Codec-specific fields
+ codecDefaultTemp = getDefaultCodecConfigByType(newCodecType, PRIORITY_HIGH);
+ BluetoothCodecConfig[] newCodecConfigsArray = new BluetoothCodecConfig[] {
+ new BluetoothCodecConfig(codecDefaultTemp.getCodecType(),
+ codecDefaultTemp.getCodecPriority(),
+ codecDefaultTemp.getSampleRate(),
+ codecDefaultTemp.getBitsPerSample(),
+ codecDefaultTemp.getChannelMode(),
+ newCodecSpecific, 0, 0, 0) // Codec-specific fields
+ };
+ BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+ sCodecCapabilities,
+ sCodecCapabilities);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
verify(mA2dpNativeInterface, times(invokeNative ? 1 : 0))
- .setCodecConfigPreference(mTestDevice, codecConfigArray);
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+ }
+
+ private void testCodecPriorityChangeHelper(int newCodecType, int newCodecPriority,
+ int oldCodecType, int oldCodecPriority, boolean shouldApplyWhenAllSelectable) {
+
+ BluetoothCodecConfig[] newCodecConfigsArray =
+ new BluetoothCodecConfig[] {
+ getDefaultCodecConfigByType(newCodecType, newCodecPriority)
+ };
+ BluetoothCodecConfig oldCodecConfig = getDefaultCodecConfigByType(oldCodecType,
+ oldCodecPriority);
+
+ // Test cases: 1. no mandatory; 2. no new codec; 3. mandatory + old + new; 4. all codecs
+ BluetoothCodecConfig[] minimumCodecsArray;
+ boolean isMinimumCodecsArraySelectable;
+ if (!oldCodecConfig.isMandatoryCodec()) {
+ if (oldCodecType == newCodecType || newCodecConfigsArray[0].isMandatoryCodec()) {
+ // selectable: {-mandatory, +oldCodec = newCodec}, or
+ // selectable: {-mandatory = newCodec, +oldCodec}. Not applied
+ BluetoothCodecConfig[] poorCodecsArray = new BluetoothCodecConfig[]
+ {getCodecCapabilitiesByType(oldCodecType)};
+ BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+ sCodecCapabilities,
+ poorCodecsArray);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
+ verify(mA2dpNativeInterface, times(0))
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+ // selectable: {+mandatory, +oldCodec = newCodec}, or
+ // selectable: {+mandatory = newCodec, +oldCodec}.
+ minimumCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+ getCodecCapabilitiesByType(oldCodecType)
+ };
+ } else {
+ // selectable: {-mandatory, +oldCodec, +newCodec}. Not applied
+ BluetoothCodecConfig[] poorCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(oldCodecType),
+ getCodecCapabilitiesByType(newCodecType)
+ };
+ BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+ sCodecCapabilities,
+ poorCodecsArray);
+ mA2dpCodecConfig.setCodecConfigPreference(
+ mTestDevice, codecStatus, newCodecConfigsArray[0]);
+ verify(mA2dpNativeInterface, times(0))
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+ // selectable: {+mandatory, +oldCodec, -newCodec}. Not applied
+ poorCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+ getCodecCapabilitiesByType(oldCodecType)
+ };
+ codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+ sCodecCapabilities,
+ poorCodecsArray);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
+ verify(mA2dpNativeInterface, times(0))
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+ // selectable: {+mandatory, +oldCodec, +newCodec}.
+ minimumCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+ getCodecCapabilitiesByType(oldCodecType),
+ getCodecCapabilitiesByType(newCodecType)
+ };
+ }
+ // oldCodec priority should be reset to default, so compare with the default
+ if (newCodecConfigsArray[0].getCodecPriority()
+ > getDefaultCodecConfigByType(
+ oldCodecType,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT).getCodecPriority()
+ && oldCodecType != newCodecType) {
+ isMinimumCodecsArraySelectable = true;
+ } else {
+ // the old codec was still the highest priority after reset to default
+ isMinimumCodecsArraySelectable = false;
+ }
+ } else if (oldCodecType != newCodecType) {
+ // selectable: {+mandatory = oldCodec, -newCodec}. Not applied
+ BluetoothCodecConfig[] poorCodecsArray = new BluetoothCodecConfig[]
+ {getCodecCapabilitiesByType(oldCodecType)};
+ BluetoothCodecStatus codecStatus =
+ new BluetoothCodecStatus(oldCodecConfig, sCodecCapabilities, poorCodecsArray);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
+ verify(mA2dpNativeInterface, times(0))
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+ // selectable: {+mandatory = oldCodec, +newCodec}.
+ minimumCodecsArray = new BluetoothCodecConfig[] {
+ getCodecCapabilitiesByType(oldCodecType),
+ getCodecCapabilitiesByType(newCodecType)
+ };
+ isMinimumCodecsArraySelectable = true;
+ } else {
+ // selectable: {mandatory = oldCodec = newCodec}.
+ minimumCodecsArray = new BluetoothCodecConfig[]
+ {getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC)};
+ isMinimumCodecsArraySelectable = false;
+ }
+
+ // 3. mandatory + old + new codecs only
+ int invokedCounter = (isMinimumCodecsArraySelectable ? 1 : 0);
+ BluetoothCodecStatus codecStatus =
+ new BluetoothCodecStatus(oldCodecConfig, sCodecCapabilities, minimumCodecsArray);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
+ verify(mA2dpNativeInterface, times(invokedCounter))
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+ // 4. all codecs were selectable
+ invokedCounter += (shouldApplyWhenAllSelectable ? 1 : 0);
+ codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+ sCodecCapabilities,
+ sCodecCapabilities);
+ mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+ codecStatus,
+ newCodecConfigsArray[0]);
+ verify(mA2dpNativeInterface, times(invokedCounter))
+ .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
}
}
diff --git a/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
index 82fa163..2d58a60 100644
--- a/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
@@ -101,7 +101,7 @@
}
Assert.assertNotNull(Looper.myLooper());
AdapterService adapterService = new AdapterService();
- adapterService.initNative(false /* is_restricted */, false /* is_single_user_mode */);
+ adapterService.initNative(false /* is_restricted */, false /* is_niap_mode */);
adapterService.cleanupNative();
HashMap<String, HashMap<String, String>> adapterConfig = TestUtils.readAdapterConfig();
Assert.assertNotNull(adapterConfig);
diff --git a/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java b/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
index 6567398..3fea6f5 100644
--- a/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
@@ -96,7 +96,7 @@
mProfiles = Config.getSupportedProfiles();
- mMockAdapterService.initNative(false /* is_restricted */, false /* is_single_user_mode */);
+ mMockAdapterService.initNative(false /* is_restricted */, false /* is_niap_mode */);
TestUtils.setAdapterService(mMockAdapterService);
diff --git a/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerListTest.java b/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerListTest.java
new file mode 100644
index 0000000..39c8b4c
--- /dev/null
+++ b/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerListTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.AudioManager;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.Looper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MediaPlayerListTest {
+ private MediaPlayerList mMediaPlayerList;
+
+ private @Captor ArgumentCaptor<AudioManager.AudioPlaybackCallback> mAudioCb;
+ private @Captor ArgumentCaptor<MediaPlayerWrapper.Callback> mPlayerWrapperCb;
+ private @Captor ArgumentCaptor<MediaData> mMediaUpdateData;
+ private @Mock Context mMockContext;
+ private @Mock AvrcpTargetService.ListCallback mAvrcpCallback;
+ private @Mock MediaController mMockController;
+ private @Mock MediaPlayerWrapper mMockPlayerWrapper;
+
+ private final String mFlagDexmarker = System.getProperty("dexmaker.share_classloader", "false");
+ private MediaPlayerWrapper.Callback mActivePlayerCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ if (!mFlagDexmarker.equals("true")) {
+ System.setProperty("dexmaker.share_classloader", "true");
+ }
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ Assert.assertNotNull(Looper.myLooper());
+
+ MockitoAnnotations.initMocks(this);
+
+ AudioManager mockAudioManager = mock(AudioManager.class);
+ when(mMockContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mockAudioManager);
+
+ MediaSessionManager mMediaSessionManager =
+ (MediaSessionManager) InstrumentationRegistry.getTargetContext()
+ .getSystemService(Context.MEDIA_SESSION_SERVICE);
+ PackageManager mockPackageManager = mock(PackageManager.class);
+ when(mMockContext.getSystemService(Context.MEDIA_SESSION_SERVICE))
+ .thenReturn(mMediaSessionManager);
+
+ mMediaPlayerList =
+ new MediaPlayerList(Looper.myLooper(), InstrumentationRegistry.getTargetContext());
+
+ when(mMockContext.registerReceiver(any(), any())).thenReturn(null);
+ when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
+ when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
+ List<ResolveInfo> playerList = new ArrayList<ResolveInfo>();
+ when(mockPackageManager.queryIntentServices(any(), anyInt())).thenReturn(null);
+
+ Method method = BrowsablePlayerConnector.class.getDeclaredMethod("setInstanceForTesting",
+ BrowsablePlayerConnector.class);
+ BrowsablePlayerConnector mockConnector = mock(BrowsablePlayerConnector.class);
+ method.setAccessible(true);
+ method.invoke(null, mockConnector);
+ mMediaPlayerList.init(mAvrcpCallback);
+
+ MediaControllerFactory.inject(mMockController);
+ MediaPlayerWrapperFactory.inject(mMockPlayerWrapper);
+
+ doReturn("testPlayer").when(mMockController).getPackageName();
+ when(mMockPlayerWrapper.isMetadataSynced()).thenReturn(false);
+ mMediaPlayerList.setActivePlayer(mMediaPlayerList.addMediaPlayer(mMockController));
+
+ verify(mMockPlayerWrapper).registerCallback(mPlayerWrapperCb.capture());
+ mActivePlayerCallback = mPlayerWrapperCb.getValue();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Method method = BrowsablePlayerConnector.class.getDeclaredMethod("setInstanceForTesting",
+ BrowsablePlayerConnector.class);
+ method.setAccessible(true);
+ method.invoke(null, (BrowsablePlayerConnector) null);
+
+ MediaControllerFactory.inject(null);
+ MediaPlayerWrapperFactory.inject(null);
+ mMediaPlayerList.cleanup();
+ if (!mFlagDexmarker.equals("true")) {
+ System.setProperty("dexmaker.share_classloader", mFlagDexmarker);
+ }
+ }
+
+ private MediaData prepareMediaData(int playbackState) {
+ PlaybackState.Builder builder = new PlaybackState.Builder();
+ builder.setState(playbackState, 0, 1);
+ ArrayList<Metadata> list = new ArrayList<Metadata>();
+ list.add(Util.empty_data());
+ MediaData newData = new MediaData(
+ Util.empty_data(),
+ builder.build(),
+ list);
+
+ return newData;
+ }
+
+ @Test
+ public void testUpdateMeidaDataForAudioPlaybackWhenActivePlayNotPlaying() {
+ // Verify update media data with playing state
+ doReturn(prepareMediaData(PlaybackState.STATE_PAUSED))
+ .when(mMockPlayerWrapper).getCurrentMediaData();
+ mMediaPlayerList.injectAudioPlaybacActive(true);
+ verify(mAvrcpCallback).run(mMediaUpdateData.capture());
+ MediaData data = mMediaUpdateData.getValue();
+ Assert.assertEquals(data.state.getState(), PlaybackState.STATE_PLAYING);
+
+ // verify update media data with current media player media data
+ MediaData currentMediaData = prepareMediaData(PlaybackState.STATE_PAUSED);
+ doReturn(currentMediaData).when(mMockPlayerWrapper).getCurrentMediaData();
+ mMediaPlayerList.injectAudioPlaybacActive(false);
+ verify(mAvrcpCallback, times(2)).run(mMediaUpdateData.capture());
+ data = mMediaUpdateData.getValue();
+ Assert.assertEquals(data.metadata, currentMediaData.metadata);
+ Assert.assertEquals(data.state.toString(), currentMediaData.state.toString());
+ Assert.assertEquals(data.queue, currentMediaData.queue);
+ }
+
+ @Test
+ public void testUpdateMediaDataForActivePlayerWhenAudioPlaybackIsNotActive() {
+ MediaData currMediaData = prepareMediaData(PlaybackState.STATE_PLAYING);
+ mActivePlayerCallback.mediaUpdatedCallback(currMediaData);
+ verify(mAvrcpCallback).run(currMediaData);
+
+ currMediaData = prepareMediaData(PlaybackState.STATE_PAUSED);
+ mActivePlayerCallback.mediaUpdatedCallback(currMediaData);
+ verify(mAvrcpCallback).run(currMediaData);
+ }
+
+ @Test
+ public void testNotUpdateMediaDataForAudioPlaybackWhenActivePlayerIsPlaying() {
+ // Verify not update media data for Audio Playback when active player is playing
+ doReturn(prepareMediaData(PlaybackState.STATE_PLAYING))
+ .when(mMockPlayerWrapper).getCurrentMediaData();
+ mMediaPlayerList.injectAudioPlaybacActive(true);
+ mMediaPlayerList.injectAudioPlaybacActive(false);
+ verify(mAvrcpCallback, never()).run(any());
+ }
+
+ @Test
+ public void testNotUpdateMediaDataForActivePlayerWhenAudioPlaybackIsActive() {
+ doReturn(prepareMediaData(PlaybackState.STATE_PLAYING))
+ .when(mMockPlayerWrapper).getCurrentMediaData();
+ mMediaPlayerList.injectAudioPlaybacActive(true);
+ verify(mAvrcpCallback, never()).run(any());
+
+ // Verify not update active player media data when audio playback is active
+ mActivePlayerCallback.mediaUpdatedCallback(prepareMediaData(PlaybackState.STATE_PAUSED));
+ verify(mAvrcpCallback, never()).run(any());
+ }
+
+}
diff --git a/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java b/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java
index ee41320..1e41033 100644
--- a/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java
+++ b/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java
@@ -138,10 +138,10 @@
*/
@Test
public void testNullControllerLooper() {
- MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper());
+ MediaPlayerWrapper wrapper = MediaPlayerWrapperFactory.wrap(null, mThread.getLooper());
Assert.assertNull(wrapper);
- wrapper = MediaPlayerWrapper.wrap(mMockController, null);
+ wrapper = MediaPlayerWrapperFactory.wrap(mMockController, null);
Assert.assertNull(wrapper);
}
@@ -151,7 +151,8 @@
*/
@Test
public void testIsReady() {
- MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapper wrapper =
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
Assert.assertTrue(wrapper.isPlaybackStateReady());
Assert.assertTrue(wrapper.isMetadataReady());
@@ -179,7 +180,8 @@
@Test
public void testControllerUpdate() {
// Create the wrapper object and register the looper with the timeout handler
- MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapper wrapper =
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
Assert.assertTrue(wrapper.isPlaybackStateReady());
Assert.assertTrue(wrapper.isMetadataReady());
wrapper.registerCallback(mTestCbs);
@@ -213,7 +215,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Return null when getting the queue
@@ -275,7 +277,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Return null when getting the queue
@@ -319,7 +321,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Return null when getting the queue
@@ -348,7 +350,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Return null when getting the queue
@@ -375,7 +377,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Call getCurrentQueue() multiple times.
@@ -398,7 +400,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Return null when getting the queue
@@ -455,7 +457,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Cleanup the wrapper
@@ -475,7 +477,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Grab the callbacks the wrapper registered with the controller
@@ -505,7 +507,7 @@
// Create the wrapper object and register the looper with the timeout handler
TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Grab the callbacks the wrapper registered with the controller
@@ -565,7 +567,7 @@
InstrumentationRegistry.getInstrumentation()
.acquireLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Grab the callbacks the wrapper registered with the controller
@@ -615,7 +617,7 @@
InstrumentationRegistry.getInstrumentation()
.acquireLooperManager(mThread.getLooper());
MediaPlayerWrapper wrapper =
- MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+ MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
wrapper.registerCallback(mTestCbs);
// Grab the callbacks the wrapper registered with the controller