From bbf6ff33740df7e742b5597ea806ec6d57c26465 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 1 Oct 2019 17:09:13 +0200 Subject: Restore Parameters after audio server restart Bug: 141724113 Test: kill audioserver during phone call Change-Id: I9b452a69ac711499b8a465b81225a3080a24e726 (cherry picked from commit 2974124d6f0325ec5c397fba070101a6b732aa2c) --- .../android/bluetooth/hfp/HeadsetStateMachine.java | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'src') diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java index c5485f992..06c48223d 100644 --- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java @@ -1198,6 +1198,21 @@ public class HeadsetStateMachine extends StateMachine { } } + class MyAudioServerStateCallback extends AudioManager.AudioServerStateCallback { + @Override + public void onAudioServerDown() { + logi("onAudioServerDown"); + } + + @Override + public void onAudioServerUp() { + logi("onAudioServerUp restoring audio parameters"); + setAudioParameters(); + } + } + + MyAudioServerStateCallback mAudioServerStateCallback = new MyAudioServerStateCallback(); + class AudioOn extends ConnectedBase { @Override int getAudioStateInt() { @@ -1216,9 +1231,20 @@ public class HeadsetStateMachine extends StateMachine { mHeadsetService.setActiveDevice(mDevice); } setAudioParameters(); + + mSystemInterface.getAudioManager().setAudioServerStateCallback( + mHeadsetService.getMainExecutor(), mAudioServerStateCallback); + broadcastStateTransitions(); } + @Override + public void exit() { + super.exit(); + + mSystemInterface.getAudioManager().clearAudioServerStateCallback(); + } + @Override public boolean processMessage(Message message) { switch (message.what) { -- cgit v1.2.3 From 5626dea3f8440ceb1d8ba75d03a57c892a6f4160 Mon Sep 17 00:00:00 2001 From: Ted Wang Date: Mon, 2 Mar 2020 21:14:21 +0800 Subject: Block guest user from Bluetooth tethering connect/disconnect Guset user does not have permission to change WiFi network, block guest user to connect WiFi through Bluetooth thethering to secure it. Bug: 126206353 Test: atest BluetoothInstrumentationTests Merged-In: Id863513f2e6b4bfa7ab56446e2a77389348e8934 Change-Id: Id863513f2e6b4bfa7ab56446e2a77389348e8934 (cherry picked from commit 312202c1d75013d55cc3fad6a333445a91dfd4ce) --- src/com/android/bluetooth/pan/PanService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src') diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java index aa5a1fd31..92eab7787 100644 --- a/src/com/android/bluetooth/pan/PanService.java +++ b/src/com/android/bluetooth/pan/PanService.java @@ -40,6 +40,7 @@ import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.ProfileService; +import com.android.internal.annotations.VisibleForTesting; import java.net.InetAddress; import java.util.ArrayList; @@ -67,6 +68,9 @@ public class PanService extends ProfileService { private String mNapIfaceAddr; private boolean mNativeAvailable; + @VisibleForTesting + UserManager mUserManager; + private static final int MESSAGE_CONNECT = 1; private static final int MESSAGE_DISCONNECT = 2; private static final int MESSAGE_CONNECT_STATE_CHANGED = 11; @@ -116,6 +120,8 @@ public class PanService extends ProfileService { initializeNative(); mNativeAvailable = true; + mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); + mNetworkFactory = new BluetoothTetheringNetworkFactory(getBaseContext(), getMainLooper(), this); setPanService(this); @@ -137,6 +143,9 @@ public class PanService extends ProfileService { cleanupNative(); mNativeAvailable = false; } + + mUserManager = null; + if (mPanDevices != null) { int[] desiredStates = {BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTING}; @@ -319,6 +328,10 @@ public class PanService extends ProfileService { public boolean connect(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (mUserManager.isGuestUser()) { + Log.w(TAG, "Guest user does not have the permission to change the WiFi network"); + return false; + } if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) { Log.e(TAG, "Pan Device not disconnected: " + device); return false; -- cgit v1.2.3 From 363c6e02b9dd76f242564de31433180302bb27de Mon Sep 17 00:00:00 2001 From: weichinweng Date: Thu, 5 Mar 2020 09:59:06 +0800 Subject: Fix bluetooth can't turn off during network reset (1/3) Remove disable Bluetooth action from AdapterService and move to BluetoothManagerService. Bug: 110181479 Test: manual Change-Id: Iaaa82675f072406970afc97ebe754b29f28d9490 Merged-In: Iaaa82675f072406970afc97ebe754b29f28d9490 --- src/com/android/bluetooth/btservice/AdapterService.java | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java index 009e42cef..a65bcfdf9 100644 --- a/src/com/android/bluetooth/btservice/AdapterService.java +++ b/src/com/android/bluetooth/btservice/AdapterService.java @@ -1559,7 +1559,6 @@ public class AdapterService extends Service { if (service == null) { return false; } - service.disable(); return service.factoryReset(); } -- cgit v1.2.3 From 9ccb9ff3fcf0df5c2bfe9f4ce3d1e09edec49648 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 12 Mar 2020 08:56:10 -0700 Subject: DO NOT MERGE: Revert "AVRCP Controller transient loss while idle" This revert is reapplied after a few CLs to match change order to AOSP. Bug: 136092891 This reverts commit 1812aafde9b3fd63e51f3579552f63278006c185. --- .../android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java | 5 +---- .../avrcpcontroller/BluetoothMediaBrowserService.java | 14 -------------- 2 files changed, 1 insertion(+), 18 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java index 5e3b3567c..c24eca9e8 100644 --- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java +++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java @@ -24,8 +24,6 @@ import android.media.AudioFocusRequest; import android.media.AudioManager; import android.media.AudioManager.OnAudioFocusChangeListener; import android.media.MediaPlayer; -import android.media.session.PlaybackState; - import android.os.Handler; import android.os.Message; import android.util.Log; @@ -227,8 +225,7 @@ public class A2dpSinkStreamHandler extends Handler { break; case DELAYED_PAUSE: - if (BluetoothMediaBrowserService.getPlaybackState() - == PlaybackState.STATE_PLAYING && !inCallFromStreamingDevice()) { + if (mStreamAvailable && !inCallFromStreamingDevice()) { sendAvrcpPause(); mSentPause = true; mStreamAvailable = false; diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java index 304d5a2c9..3504cd49c 100644 --- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java @@ -176,20 +176,6 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { } } - /** - * Get playback state - */ - public static synchronized int getPlaybackState() { - if (sBluetoothMediaBrowserService != null) { - PlaybackState currentPlaybackState = - sBluetoothMediaBrowserService.mSession.getController().getPlaybackState(); - if (currentPlaybackState != null) { - return currentPlaybackState.getState(); - } - } - return PlaybackState.STATE_ERROR; - } - /** * Get object for controlling playback */ -- cgit v1.2.3 From a8bcf640648a8f6395812a1f36e6064e52305035 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 12 Mar 2020 08:28:40 -0700 Subject: DO NOT MERGE: Revert "AVRCP Controller AbsoluteVolumeNotification" This revert is reapplied after a few CLs to match change order to AOSP. This reverts commit fc9aee6d0bb93f8947f8c50e66ff7ba0e6f7a662. Bug: 129478624 Change-Id: I4bfdaa1717df89492bd74a1c59ad151d2161d0ab --- .../AvrcpControllerStateMachine.java | 45 ++++++++-------------- 1 file changed, 17 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index 66571c4e7..1d1a6356d 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -89,14 +89,7 @@ class AvrcpControllerStateMachine extends StateMachine { */ private static final int ABS_VOL_BASE = 127; - /* - * Notification types for Avrcp protocol JNI. - */ - private static final byte NOTIFICATION_RSP_TYPE_INTERIM = 0x00; - private static final byte NOTIFICATION_RSP_TYPE_CHANGED = 0x01; - private final AudioManager mAudioManager; - private final boolean mIsVolumeFixed; protected final BluetoothDevice mDevice; protected final byte[] mDeviceAddress; @@ -115,7 +108,6 @@ class AvrcpControllerStateMachine extends StateMachine { private int mAddressedPlayerId = -1; private SparseArray mAvailablePlayerList = new SparseArray(); private int mVolumeChangedNotificationsToIgnore = 0; - private int mVolumeNotificationLabel = -1; GetFolderList mGetFolderList = null; @@ -145,7 +137,6 @@ class AvrcpControllerStateMachine extends StateMachine { mGetFolderList = new GetFolderList(); addState(mGetFolderList, mConnected); mAudioManager = (AudioManager) service.getSystemService(Context.AUDIO_SERVICE); - mIsVolumeFixed = mAudioManager.isVolumeFixed(); setInitialState(mDisconnected); } @@ -318,13 +309,6 @@ class AvrcpControllerStateMachine extends StateMachine { setAbsVolume(msg.arg1, msg.arg2); return true; - case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: - mVolumeNotificationLabel = msg.arg1; - mService.sendRegisterAbsVolRspNative(mDeviceAddress, - NOTIFICATION_RSP_TYPE_INTERIM, - getAbsVolumeResponse(), mVolumeNotificationLabel); - return true; - case MESSAGE_GET_FOLDER_ITEMS: transitionTo(mGetFolderList); return true; @@ -564,9 +548,24 @@ class AvrcpControllerStateMachine extends StateMachine { } break; - default: + case CONNECT: + case DISCONNECT: + case MSG_AVRCP_PASSTHRU: + case MESSAGE_PROCESS_SET_ABS_VOL_CMD: + case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: + case MESSAGE_PROCESS_TRACK_CHANGED: + case MESSAGE_PROCESS_PLAY_POS_CHANGED: + case MESSAGE_PROCESS_PLAY_STATUS_CHANGED: + case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION: + case MESSAGE_PLAY_ITEM: + case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED: // All of these messages should be handled by parent state immediately. return false; + + default: + logD(STATE_TAG + " deferring message " + msg.what + + " to connected!"); + deferMessage(msg); } return true; } @@ -695,17 +694,7 @@ class AvrcpControllerStateMachine extends StateMachine { mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newIndex, AudioManager.FLAG_SHOW_UI); } - mService.sendAbsVolRspNative(mDeviceAddress, getAbsVolumeResponse(), label); - } - - private int getAbsVolumeResponse() { - if (mIsVolumeFixed) { - return ABS_VOL_BASE; - } - int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); - int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); - int newIndex = (currIndex * ABS_VOL_BASE) / maxVolume; - return newIndex; + mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); } MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() { -- cgit v1.2.3 From deb3aaa91a50390c23bab57bf3f7bfdfe996c513 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Fri, 17 May 2019 14:47:51 -0700 Subject: Broadcast MAP Client messages oldest first Bug: b/130260536 Test: Tested with kitchen sink on automotive hardware Change-Id: I6a373d7890e7680bb99d5e3c5a50c18648d8c003 (cherry picked from commit bfa93ce0bbb80d9e1de882769db4538627199b79) Merged-In: I6a373d7890e7680bb99d5e3c5a50c18648d8c003 Change-Id: Iaa4344b403a2ca539e818f09edf4e4b9f7a5c366 --- src/com/android/bluetooth/mapclient/MceStateMachine.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/mapclient/MceStateMachine.java b/src/com/android/bluetooth/mapclient/MceStateMachine.java index 0a428b418..867ab185f 100644 --- a/src/com/android/bluetooth/mapclient/MceStateMachine.java +++ b/src/com/android/bluetooth/mapclient/MceStateMachine.java @@ -42,7 +42,6 @@ package com.android.bluetooth.mapclient; import android.app.Activity; import android.app.PendingIntent; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothMapClient; import android.bluetooth.BluetoothProfile; @@ -69,7 +68,6 @@ import com.android.vcard.VCardProperty; import java.util.ArrayList; import java.util.Calendar; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -625,9 +623,12 @@ final class MceStateMachine extends StateMachine { } ArrayList messageListing = request.getList(); if (messageListing != null) { - for (com.android.bluetooth.mapclient.Message msg : messageListing) { + // Message listings by spec arrive ordered newest first but we wish to broadcast as + // oldest first. Iterate in reverse order so we initiate requests oldest first. + for (int i = messageListing.size() - 1; i >= 0; i--) { + com.android.bluetooth.mapclient.Message msg = messageListing.get(i); if (DBG) { - Log.d(TAG, "getting message "); + Log.d(TAG, "getting message for handle " + msg.getHandle()); } // A message listing coming from the server should always have up to date data mMessages.put(msg.getHandle(), new MessageMetadata(msg.getHandle(), -- cgit v1.2.3 From 3dad083344c6c2b570e10a552e2617fd80b960a6 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 11 Jul 2019 08:01:18 -0700 Subject: AVRCP Controller Shuffle/Repeat support Add Custom actions to the Playback status for Shuffle and Repeat capabilities when available from the remote device. Bug: 72495707 Test: AvrcpControllerStateMachineTest#testShuffle AvrcpControllerStateMachineTest#testRepeat Change Shuffle and Repeat mode on connected phone via Headunit Change-Id: Ia0a62d10032b6e918a38e66bd486a29199b772e2 (cherry picked from commit 1c862460a544108b8b12fb24827f9a717961d410) Merged-In: Ia0a62d10032b6e918a38e66bd486a29199b772e2 Change-Id: I8669dbbe9552e5a53cb8c864c3e00c1c4df569ec --- .../avrcpcontroller/AvrcpControllerService.java | 33 ++-- .../AvrcpControllerStateMachine.java | 74 ++++++-- .../bluetooth/avrcpcontroller/AvrcpPlayer.java | 102 +++++++---- .../BluetoothMediaBrowserService.java | 72 +++++--- .../avrcpcontroller/PlayerApplicationSettings.java | 201 ++++----------------- 5 files changed, 237 insertions(+), 245 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java index 64e63df00..693b4a291 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java @@ -412,9 +412,10 @@ public class AvrcpControllerService extends ProfileService { if (stateMachine != null) { PlayerApplicationSettings supportedSettings = PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp); + stateMachine.sendMessage( + AvrcpControllerStateMachine.MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS, + supportedSettings); } - /* Do nothing */ - } private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, @@ -426,10 +427,12 @@ public class AvrcpControllerService extends ProfileService { AvrcpControllerStateMachine stateMachine = getStateMachine(device); if (stateMachine != null) { - PlayerApplicationSettings desiredSettings = + PlayerApplicationSettings currentSettings = PlayerApplicationSettings.makeSettings(playerAttribRsp); + stateMachine.sendMessage( + AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS, + currentSettings); } - /* Do nothing */ } // Browsing related JNI callbacks. @@ -711,7 +714,7 @@ public class AvrcpControllerService extends ProfileService { /** * Send button press commands to addressed device * - * @param keyCode key code as defined in AVRCP specification + * @param keyCode key code as defined in AVRCP specification * @param keyState 0 = key pressed, 1 = key released * @return command was sent */ @@ -720,7 +723,7 @@ public class AvrcpControllerService extends ProfileService { /** * Send group navigation commands * - * @param keyCode next/previous + * @param keyCode next/previous * @param keyState state * @return command was sent */ @@ -741,7 +744,7 @@ public class AvrcpControllerService extends ProfileService { * Send response to set absolute volume * * @param absVol new volume - * @param label label + * @param label label */ public native void sendAbsVolRspNative(byte[] address, int absVol, int label); @@ -749,8 +752,8 @@ public class AvrcpControllerService extends ProfileService { * Register for any volume level changes * * @param rspType type of response - * @param absVol current volume - * @param label label + * @param absVol current volume + * @param label label */ public native void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol, int label); @@ -764,7 +767,7 @@ public class AvrcpControllerService extends ProfileService { * Fetch the current now playing list * * @param start first index to retrieve - * @param end last index to retrieve + * @param end last index to retrieve */ public native void getNowPlayingListNative(byte[] address, int start, int end); @@ -772,7 +775,7 @@ public class AvrcpControllerService extends ProfileService { * Fetch the current folder's listing * * @param start first index to retrieve - * @param end last index to retrieve + * @param end last index to retrieve */ public native void getFolderListNative(byte[] address, int start, int end); @@ -780,7 +783,7 @@ public class AvrcpControllerService extends ProfileService { * Fetch the listing of players * * @param start first index to retrieve - * @param end last index to retrieve + * @param end last index to retrieve */ public native void getPlayerListNative(byte[] address, int start, int end); @@ -788,15 +791,15 @@ public class AvrcpControllerService extends ProfileService { * Change the current browsed folder * * @param direction up/down - * @param uid folder unique id + * @param uid folder unique id */ public native void changeFolderPathNative(byte[] address, byte direction, long uid); /** * Play item with provided uid * - * @param scope scope of item to played - * @param uid song unique id + * @param scope scope of item to played + * @param uid song unique id * @param uidCounter counter */ public native void playItemNative(byte[] address, byte scope, long uid, int uidCounter); diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index 1d1a6356d..7a6c54126 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -24,10 +24,10 @@ import android.content.Intent; import android.media.AudioManager; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; -import android.media.session.MediaSession; -import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Message; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import android.util.SparseArray; @@ -42,6 +42,7 @@ import com.android.internal.util.StateMachine; import java.util.ArrayList; import java.util.List; + /** * Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections * and interactions with a remote controlable device. @@ -76,11 +77,15 @@ class AvrcpControllerStateMachine extends StateMachine { static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214; static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215; static final int MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED = 216; + static final int MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS = 217; + static final int MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS = 218; //300->399 Events for Browsing static final int MESSAGE_GET_FOLDER_ITEMS = 300; static final int MESSAGE_PLAY_ITEM = 301; static final int MSG_AVRCP_PASSTHRU = 302; + static final int MSG_AVRCP_SET_SHUFFLE = 303; + static final int MSG_AVRCP_SET_REPEAT = 304; static final int MESSAGE_INTERNAL_ABS_VOL_TIMEOUT = 404; @@ -215,12 +220,12 @@ class AvrcpControllerStateMachine extends StateMachine { synchronized void onBrowsingDisconnected() { if (!mBrowsingConnected) return; - mAddressedPlayer.setPlayStatus(PlaybackState.STATE_ERROR); + mAddressedPlayer.setPlayStatus(PlaybackStateCompat.STATE_ERROR); mAddressedPlayer.updateCurrentTrack(null); mBrowseTree.mNowPlayingNode.setCached(false); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); - PlaybackState.Builder pbb = new PlaybackState.Builder(); - pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN, + PlaybackStateCompat.Builder pbb = new PlaybackStateCompat.Builder(); + pbb.setState(PlaybackStateCompat.STATE_ERROR, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected)); BluetoothMediaBrowserService.notifyChanged(pbb.build()); @@ -322,6 +327,14 @@ class AvrcpControllerStateMachine extends StateMachine { passThru(msg.arg1); return true; + case MSG_AVRCP_SET_REPEAT: + setRepeat(msg.arg1); + return true; + + case MSG_AVRCP_SET_SHUFFLE: + setShuffle(msg.arg1); + return true; + case MESSAGE_PROCESS_TRACK_CHANGED: mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj); BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj); @@ -331,7 +344,7 @@ class AvrcpControllerStateMachine extends StateMachine { mAddressedPlayer.setPlayStatus(msg.arg1); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); if (mAddressedPlayer.getPlaybackState().getState() - == PlaybackState.STATE_PLAYING + == PlaybackStateCompat.STATE_PLAYING && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE && !shouldRequestFocus()) { sendMessage(MSG_AVRCP_PASSTHRU, @@ -362,6 +375,18 @@ class AvrcpControllerStateMachine extends StateMachine { } return true; + case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS: + mAddressedPlayer.setSupportedPlayerApplicationSettings( + (PlayerApplicationSettings) msg.obj); + BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); + return true; + + case MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS: + mAddressedPlayer.setCurrentPlayerApplicationSettings( + (PlayerApplicationSettings) msg.obj); + BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); + return true; + case DISCONNECT: transitionTo(mDisconnecting); return true; @@ -419,6 +444,20 @@ class AvrcpControllerStateMachine extends StateMachine { return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND) || (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF); } + + private void setRepeat(int repeatMode) { + mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1, + new byte[]{PlayerApplicationSettings.REPEAT_STATUS}, new byte[]{ + PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal( + PlayerApplicationSettings.REPEAT_STATUS, repeatMode)}); + } + + private void setShuffle(int shuffleMode) { + mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1, + new byte[]{PlayerApplicationSettings.SHUFFLE_STATUS}, new byte[]{ + PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal( + PlayerApplicationSettings.SHUFFLE_STATUS, shuffleMode)}); + } } // Handle the get folder listing action @@ -538,7 +577,7 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_GET_FOLDER_ITEMS: if (!mBrowseNode.equals(msg.obj)) { if (shouldAbort(mBrowseNode.getScope(), - ((BrowseTree.BrowseNode) msg.obj).getScope())) { + ((BrowseTree.BrowseNode) msg.obj).getScope())) { mAbort = true; } deferMessage(msg); @@ -564,7 +603,7 @@ class AvrcpControllerStateMachine extends StateMachine { default: logD(STATE_TAG + " deferring message " + msg.what - + " to connected!"); + + " to connected!"); deferMessage(msg); } return true; @@ -575,8 +614,8 @@ class AvrcpControllerStateMachine extends StateMachine { * necessary. * * @return true: a new folder in the same scope - * a new player while fetching contents of a folder - * false: other cases, specifically Now Playing while fetching a folder + * a new player while fetching contents of a folder + * false: other cases, specifically Now Playing while fetching a folder */ private boolean shouldAbort(int currentScope, int fetchScope) { if ((currentScope == fetchScope) @@ -697,7 +736,7 @@ class AvrcpControllerStateMachine extends StateMachine { mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); } - MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() { + MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() { @Override public void onPlay() { logD("onPlay"); @@ -770,6 +809,19 @@ class AvrcpControllerStateMachine extends StateMachine { BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId); sendMessage(MESSAGE_PLAY_ITEM, node); } + + @Override + public void onSetRepeatMode(int repeatMode) { + logD("onSetRepeatMode"); + sendMessage(MSG_AVRCP_SET_REPEAT, repeatMode); + } + + @Override + public void onSetShuffleMode(int shuffleMode) { + logD("onSetShuffleMode"); + sendMessage(MSG_AVRCP_SET_SHUFFLE, shuffleMode); + + } }; protected void broadcastConnectionStateChanged(int currentState) { diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java index bed38d905..2ef3c1480 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java @@ -17,8 +17,9 @@ package com.android.bluetooth.avrcpcontroller; import android.media.MediaMetadata; -import android.media.session.PlaybackState; import android.os.SystemClock; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import java.util.Arrays; @@ -41,27 +42,31 @@ class AvrcpPlayer { public static final int FEATURE_PREVIOUS = 48; public static final int FEATURE_BROWSING = 59; - private int mPlayStatus = PlaybackState.STATE_NONE; - private long mPlayTime = PlaybackState.PLAYBACK_POSITION_UNKNOWN; + private int mPlayStatus = PlaybackStateCompat.STATE_NONE; + private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN; private long mPlayTimeUpdate = 0; private float mPlaySpeed = 1; private int mId; private String mName = ""; private int mPlayerType; - private byte[] mPlayerFeatures; + private byte[] mPlayerFeatures = new byte[16]; private long mAvailableActions; private MediaMetadata mCurrentTrack; - private PlaybackState mPlaybackState; + private PlaybackStateCompat mPlaybackStateCompat; + private PlayerApplicationSettings mSupportedPlayerApplicationSettings = + new PlayerApplicationSettings(); + private PlayerApplicationSettings mCurrentPlayerApplicationSettings; AvrcpPlayer() { mId = INVALID_ID; //Set Default Actions in case Player data isn't available. - mAvailableActions = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY - | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS - | PlaybackState.ACTION_STOP; - PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder() + mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY + | PlaybackStateCompat.ACTION_SKIP_TO_NEXT + | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + | PlaybackStateCompat.ACTION_STOP; + PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder() .setActions(mAvailableActions); - mPlaybackState = playbackStateBuilder.build(); + mPlaybackStateCompat = playbackStateBuilder.build(); } AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) { @@ -70,10 +75,10 @@ class AvrcpPlayer { mPlayStatus = playStatus; mPlayerType = playerType; mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length); - updateAvailableActions(); - PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder() + PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder() .setActions(mAvailableActions); - mPlaybackState = playbackStateBuilder.build(); + mPlaybackStateCompat = playbackStateBuilder.build(); + updateAvailableActions(); } public int getId() { @@ -87,7 +92,8 @@ class AvrcpPlayer { public void setPlayTime(int playTime) { mPlayTime = playTime; mPlayTimeUpdate = SystemClock.elapsedRealtime(); - mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime, + mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState( + mPlayStatus, mPlayTime, mPlaySpeed).build(); } @@ -97,30 +103,48 @@ class AvrcpPlayer { public void setPlayStatus(int playStatus) { mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime() - - mPlaybackState.getLastPositionUpdateTime()); + - mPlaybackStateCompat.getLastPositionUpdateTime()); mPlayStatus = playStatus; switch (mPlayStatus) { - case PlaybackState.STATE_STOPPED: + case PlaybackStateCompat.STATE_STOPPED: mPlaySpeed = 0; break; - case PlaybackState.STATE_PLAYING: + case PlaybackStateCompat.STATE_PLAYING: mPlaySpeed = 1; break; - case PlaybackState.STATE_PAUSED: + case PlaybackStateCompat.STATE_PAUSED: mPlaySpeed = 0; break; - case PlaybackState.STATE_FAST_FORWARDING: + case PlaybackStateCompat.STATE_FAST_FORWARDING: mPlaySpeed = 3; break; - case PlaybackState.STATE_REWINDING: + case PlaybackStateCompat.STATE_REWINDING: mPlaySpeed = -3; break; } - mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime, + mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState( + mPlayStatus, mPlayTime, mPlaySpeed).build(); } + public void setSupportedPlayerApplicationSettings( + PlayerApplicationSettings playerApplicationSettings) { + mSupportedPlayerApplicationSettings = playerApplicationSettings; + updateAvailableActions(); + } + + public void setCurrentPlayerApplicationSettings( + PlayerApplicationSettings playerApplicationSettings) { + Log.d(TAG, "Settings changed"); + mCurrentPlayerApplicationSettings = playerApplicationSettings; + MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); + session.setRepeatMode(mCurrentPlayerApplicationSettings.getSetting( + PlayerApplicationSettings.REPEAT_STATUS)); + session.setShuffleMode(mCurrentPlayerApplicationSettings.getSetting( + PlayerApplicationSettings.SHUFFLE_STATUS)); + } + public int getPlayStatus() { return mPlayStatus; } @@ -131,17 +155,22 @@ class AvrcpPlayer { return (mPlayerFeatures[byteNumber] & bitMask) == bitMask; } - public PlaybackState getPlaybackState() { + public boolean supportsSetting(int settingType, int settingValue) { + return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue); + } + + public PlaybackStateCompat getPlaybackState() { if (DBG) { Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime); } - return mPlaybackState; + return mPlaybackStateCompat; } public synchronized void updateCurrentTrack(MediaMetadata update) { if (update != null) { long trackNumber = update.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); - mPlaybackState = new PlaybackState.Builder(mPlaybackState).setActiveQueueItemId( + mPlaybackStateCompat = new PlaybackStateCompat.Builder( + mPlaybackStateCompat).setActiveQueueItemId( trackNumber - 1).build(); } mCurrentTrack = update; @@ -153,26 +182,37 @@ class AvrcpPlayer { private void updateAvailableActions() { if (supportsFeature(FEATURE_PLAY)) { - mAvailableActions = mAvailableActions | PlaybackState.ACTION_PLAY; + mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY; } if (supportsFeature(FEATURE_STOP)) { - mAvailableActions = mAvailableActions | PlaybackState.ACTION_STOP; + mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP; } if (supportsFeature(FEATURE_PAUSE)) { - mAvailableActions = mAvailableActions | PlaybackState.ACTION_PAUSE; + mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE; } if (supportsFeature(FEATURE_REWIND)) { - mAvailableActions = mAvailableActions | PlaybackState.ACTION_REWIND; + mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND; } if (supportsFeature(FEATURE_FAST_FORWARD)) { - mAvailableActions = mAvailableActions | PlaybackState.ACTION_FAST_FORWARD; + mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD; } if (supportsFeature(FEATURE_FORWARD)) { - mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_NEXT; + mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT; } if (supportsFeature(FEATURE_PREVIOUS)) { - mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_PREVIOUS; + mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; } + if (mSupportedPlayerApplicationSettings.supportsSetting( + PlayerApplicationSettings.REPEAT_STATUS)) { + mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE; + } + if (mSupportedPlayerApplicationSettings.supportsSetting( + PlayerApplicationSettings.SHUFFLE_STATUS)) { + mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE; + } + mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat) + .setActions(mAvailableActions).build(); + if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions); } } diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java index 3504cd49c..774f95349 100644 --- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java @@ -18,13 +18,17 @@ package com.android.bluetooth.avrcpcontroller; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; -import android.media.session.MediaController; -import android.media.session.MediaSession; -import android.media.session.PlaybackState; import android.os.Bundle; -import android.service.media.MediaBrowserService; +import android.support.v4.media.MediaBrowserCompat; +import android.support.v4.media.MediaDescriptionCompat; +import android.support.v4.media.MediaMetadataCompat; +import android.support.v4.media.session.MediaControllerCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; +import androidx.media.MediaBrowserServiceCompat; + import com.android.bluetooth.R; import java.util.ArrayList; @@ -37,43 +41,44 @@ import java.util.List; * The applications are expected to use MediaBrowser (see API) and all the music * browsing/playback/metadata can be controlled via MediaBrowser and MediaController. * - * The current behavior of MediaSession exposed by this service is as follows: - * 1. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is - * connected and first starts playing. Before it starts playing we do not active the session. + * The current behavior of MediaSessionCompat exposed by this service is as follows: + * 1. MediaSessionCompat is active (i.e. SystemUI and other overview UIs can see updates) when + * device is connected and first starts playing. Before it starts playing we do not activate the + * session. * 1.1 The session is active throughout the duration of connection. * 2. The session is de-activated when the device disconnects. It will be connected again when (1) * happens. */ -public class BluetoothMediaBrowserService extends MediaBrowserService { +public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private static final String TAG = "BluetoothMediaBrowserService"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static BluetoothMediaBrowserService sBluetoothMediaBrowserService; - private MediaSession mSession; + private MediaSessionCompat mSession; // Browsing related structures. - private List mMediaQueue = new ArrayList<>(); + private List mMediaQueue = new ArrayList<>(); /** - * Initialize this BluetoothMediaBrowserService, creating our MediaSession, MediaPlayer and - * MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. + * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer + * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. */ @Override public void onCreate() { if (DBG) Log.d(TAG, "onCreate"); super.onCreate(); - // Create and configure the MediaSession - mSession = new MediaSession(this, TAG); + // Create and configure the MediaSessionCompat + mSession = new MediaSessionCompat(this, TAG); setSessionToken(mSession.getSessionToken()); - mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS - | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); + mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS + | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name)); mSession.setQueue(mMediaQueue); - PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder(); - playbackStateBuilder.setState(PlaybackState.STATE_ERROR, - PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); + PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder(); + playbackStateBuilder.setState(PlaybackStateCompat.STATE_ERROR, + PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected)); mSession.setPlaybackState(playbackStateBuilder.build()); sBluetoothMediaBrowserService = this; @@ -91,9 +96,10 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { @Override public synchronized void onLoadChildren(final String parentMediaId, - final Result> result) { + final Result> result) { if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId); - List contents = getContents(parentMediaId); + List contents = + MediaBrowserCompat.MediaItem.fromMediaItemList(getContents(parentMediaId)); if (contents == null) { result.detach(); } else { @@ -112,7 +118,8 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { mMediaQueue.clear(); if (songList != null) { for (MediaItem song : songList) { - mMediaQueue.add(new MediaSession.QueueItem(song.getDescription(), + mMediaQueue.add(new MediaSessionCompat.QueueItem( + MediaDescriptionCompat.fromMediaDescription(song.getDescription()), mMediaQueue.size())); } } @@ -129,7 +136,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { } } - static synchronized void addressedPlayerChanged(MediaSession.Callback callback) { + static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setCallback(callback); } else { @@ -139,13 +146,14 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { static synchronized void trackChanged(MediaMetadata mediaMetadata) { if (sBluetoothMediaBrowserService != null) { - sBluetoothMediaBrowserService.mSession.setMetadata(mediaMetadata); + sBluetoothMediaBrowserService.mSession.setMetadata( + MediaMetadataCompat.fromMediaMetadata(mediaMetadata)); } else { Log.w(TAG, "trackChanged Unavailable"); } } - static synchronized void notifyChanged(PlaybackState playbackState) { + static synchronized void notifyChanged(PlaybackStateCompat playbackState) { Log.d(TAG, "notifyChanged PlaybackState" + playbackState); if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState); @@ -179,7 +187,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { /** * Get object for controlling playback */ - public static synchronized MediaController.TransportControls getTransportControls() { + public static synchronized MediaControllerCompat.TransportControls getTransportControls() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession.getController().getTransportControls(); } else { @@ -198,4 +206,16 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { Log.w(TAG, "setActive Unavailable"); } } + + /** + * Get Media session for updating state + */ + public static synchronized MediaSessionCompat getSession() { + if (sBluetoothMediaBrowserService != null) { + return sBluetoothMediaBrowserService.mSession; + } else { + Log.w(TAG, "getSession Unavailable"); + return null; + } + } } diff --git a/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java b/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java index c34a2d7d0..362548e5f 100644 --- a/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java +++ b/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java @@ -16,12 +16,11 @@ package com.android.bluetooth.avrcpcontroller; -import android.bluetooth.BluetoothAvrcpPlayerSettings; +import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; +import android.util.SparseArray; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; /* * Contains information Player Application Setting extended from BluetootAvrcpPlayerSettings @@ -32,10 +31,10 @@ class PlayerApplicationSettings { /* * Values for SetPlayerApplicationSettings from AVRCP Spec V1.6 Appendix F. */ - private static final byte JNI_ATTRIB_EQUALIZER_STATUS = 0x01; - private static final byte JNI_ATTRIB_REPEAT_STATUS = 0x02; - private static final byte JNI_ATTRIB_SHUFFLE_STATUS = 0x03; - private static final byte JNI_ATTRIB_SCAN_STATUS = 0x04; + static final byte EQUALIZER_STATUS = 0x01; + static final byte REPEAT_STATUS = 0x02; + static final byte SHUFFLE_STATUS = 0x03; + static final byte SCAN_STATUS = 0x04; private static final byte JNI_EQUALIZER_STATUS_OFF = 0x01; private static final byte JNI_EQUALIZER_STATUS_ON = 0x02; @@ -55,18 +54,17 @@ class PlayerApplicationSettings { private static final byte JNI_STATUS_INVALID = -1; - /* * Hash map of current settings. */ - private Map mSettings = new HashMap(); + private SparseArray mSettings = new SparseArray<>(); /* * Hash map of supported values, a setting should be supported by the remote in order to enable * in mSettings. */ - private Map> mSupportedValues = - new HashMap>(); + private SparseArray> mSupportedValues = + new SparseArray>(); /* Convert from JNI array to Java classes. */ static PlayerApplicationSettings makeSupportedSettings(byte[] btAvrcpAttributeList) { @@ -82,8 +80,7 @@ class PlayerApplicationSettings { supportedValues.add( mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++])); } - newObj.mSupportedValues.put(mapBTAttribIdToAvrcpPlayerSettings(attrId), - supportedValues); + newObj.mSupportedValues.put(attrId, supportedValues); } } catch (ArrayIndexOutOfBoundsException exception) { Log.e(TAG, "makeSupportedSettings attributeList index error."); @@ -91,25 +88,13 @@ class PlayerApplicationSettings { return newObj; } - public BluetoothAvrcpPlayerSettings getAvrcpSettings() { - int supportedSettings = 0; - for (Integer setting : mSettings.keySet()) { - supportedSettings |= setting; - } - BluetoothAvrcpPlayerSettings result = new BluetoothAvrcpPlayerSettings(supportedSettings); - for (Integer setting : mSettings.keySet()) { - result.addSettingValue(setting, mSettings.get(setting)); - } - return result; - } - static PlayerApplicationSettings makeSettings(byte[] btAvrcpAttributeList) { PlayerApplicationSettings newObj = new PlayerApplicationSettings(); try { for (int i = 0; i < btAvrcpAttributeList.length; ) { byte attrId = btAvrcpAttributeList[i++]; - newObj.mSettings.put(mapBTAttribIdToAvrcpPlayerSettings(attrId), + newObj.mSettings.put(attrId, mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++])); } } catch (ArrayIndexOutOfBoundsException exception) { @@ -123,177 +108,69 @@ class PlayerApplicationSettings { mSupportedValues = updates.mSupportedValues; } - public void setValues(BluetoothAvrcpPlayerSettings updates) { - int supportedSettings = updates.getSettings(); - for (int i = 1; i <= BluetoothAvrcpPlayerSettings.SETTING_SCAN; i++) { - if ((i & supportedSettings) > 0) { - mSettings.put(i, updates.getSettingValue(i)); - } - } + public boolean supportsSetting(int settingType, int settingValue) { + if (null == mSupportedValues.get(settingType)) return false; + return mSupportedValues.valueAt(settingType).contains(settingValue); } - /* - * Check through all settings to ensure that they are all available to be set and then check - * that the desired value is in fact supported by our remote player. - */ - public boolean supportsSettings(BluetoothAvrcpPlayerSettings settingsToCheck) { - int settingSubset = settingsToCheck.getSettings(); - int supportedSettings = 0; - for (Integer setting : mSupportedValues.keySet()) { - supportedSettings |= setting; - } - try { - if ((supportedSettings & settingSubset) == settingSubset) { - for (Integer settingId : mSettings.keySet()) { - // The setting is in both settings to check and supported settings but the - // value is not supported. - if ((settingId & settingSubset) == settingId && (!mSupportedValues.get( - settingId).contains(settingsToCheck.getSettingValue(settingId)))) { - return false; - } - } - return true; - } - } catch (NullPointerException e) { - Log.e(TAG, - "supportsSettings received a supported setting that has no supported values."); - } - return false; + public boolean supportsSetting(int settingType) { + return (null != mSupportedValues.get(settingType)); } - // Convert currently desired settings into an attribute array to pass to the native layer to - // enable them. - public ArrayList getNativeSettings() { - int i = 0; - ArrayList attribArray = new ArrayList(); - for (Integer settingId : mSettings.keySet()) { - switch (settingId) { - case BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER: - attribArray.add(JNI_ATTRIB_EQUALIZER_STATUS); - attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId, - mSettings.get(settingId))); - break; - case BluetoothAvrcpPlayerSettings.SETTING_REPEAT: - attribArray.add(JNI_ATTRIB_REPEAT_STATUS); - attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId, - mSettings.get(settingId))); - break; - case BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE: - attribArray.add(JNI_ATTRIB_SHUFFLE_STATUS); - attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId, - mSettings.get(settingId))); - break; - case BluetoothAvrcpPlayerSettings.SETTING_SCAN: - attribArray.add(JNI_ATTRIB_SCAN_STATUS); - attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId, - mSettings.get(settingId))); - break; - default: - Log.w(TAG, "Unknown setting found in getNativeSettings: " + settingId); - } - } - return attribArray; + public int getSetting(int settingType) { + if (null == mSettings.get(settingType)) return -1; + return mSettings.get(settingType); } // Convert a native Attribute Id/Value pair into the AVRCP equivalent value. private static int mapAttribIdValtoAvrcpPlayerSetting(byte attribId, byte attribVal) { - if (attribId == JNI_ATTRIB_EQUALIZER_STATUS) { - switch (attribVal) { - case JNI_EQUALIZER_STATUS_OFF: - return BluetoothAvrcpPlayerSettings.STATE_OFF; - case JNI_EQUALIZER_STATUS_ON: - return BluetoothAvrcpPlayerSettings.STATE_ON; - } - } else if (attribId == JNI_ATTRIB_REPEAT_STATUS) { + if (attribId == REPEAT_STATUS) { switch (attribVal) { case JNI_REPEAT_STATUS_ALL_TRACK_REPEAT: - return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK; + return PlaybackStateCompat.REPEAT_MODE_ALL; case JNI_REPEAT_STATUS_GROUP_REPEAT: - return BluetoothAvrcpPlayerSettings.STATE_GROUP; + return PlaybackStateCompat.REPEAT_MODE_GROUP; case JNI_REPEAT_STATUS_OFF: - return BluetoothAvrcpPlayerSettings.STATE_OFF; + return PlaybackStateCompat.REPEAT_MODE_NONE; case JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT: - return BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK; - } - } else if (attribId == JNI_ATTRIB_SCAN_STATUS) { - switch (attribVal) { - case JNI_SCAN_STATUS_ALL_TRACK_SCAN: - return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK; - case JNI_SCAN_STATUS_GROUP_SCAN: - return BluetoothAvrcpPlayerSettings.STATE_GROUP; - case JNI_SCAN_STATUS_OFF: - return BluetoothAvrcpPlayerSettings.STATE_OFF; + return PlaybackStateCompat.REPEAT_MODE_ONE; } - } else if (attribId == JNI_ATTRIB_SHUFFLE_STATUS) { + } else if (attribId == SHUFFLE_STATUS) { switch (attribVal) { case JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE: - return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK; + return PlaybackStateCompat.SHUFFLE_MODE_ALL; case JNI_SHUFFLE_STATUS_GROUP_SHUFFLE: - return BluetoothAvrcpPlayerSettings.STATE_GROUP; + return PlaybackStateCompat.SHUFFLE_MODE_GROUP; case JNI_SHUFFLE_STATUS_OFF: - return BluetoothAvrcpPlayerSettings.STATE_OFF; + return PlaybackStateCompat.SHUFFLE_MODE_NONE; } } - return BluetoothAvrcpPlayerSettings.STATE_INVALID; + return JNI_STATUS_INVALID; } // Convert an AVRCP Setting/Value pair into the native equivalent value; - private static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) { - if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) { - switch (mSettingVal) { - case BluetoothAvrcpPlayerSettings.STATE_OFF: - return JNI_EQUALIZER_STATUS_OFF; - case BluetoothAvrcpPlayerSettings.STATE_ON: - return JNI_EQUALIZER_STATUS_ON; - } - } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_REPEAT) { + static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) { + if (mSetting == REPEAT_STATUS) { switch (mSettingVal) { - case BluetoothAvrcpPlayerSettings.STATE_OFF: + case PlaybackStateCompat.REPEAT_MODE_NONE: return JNI_REPEAT_STATUS_OFF; - case BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK: + case PlaybackStateCompat.REPEAT_MODE_ONE: return JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT; - case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK: + case PlaybackStateCompat.REPEAT_MODE_ALL: return JNI_REPEAT_STATUS_ALL_TRACK_REPEAT; - case BluetoothAvrcpPlayerSettings.STATE_GROUP: + case PlaybackStateCompat.REPEAT_MODE_GROUP: return JNI_REPEAT_STATUS_GROUP_REPEAT; } - } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) { + } else if (mSetting == SHUFFLE_STATUS) { switch (mSettingVal) { - case BluetoothAvrcpPlayerSettings.STATE_OFF: + case PlaybackStateCompat.SHUFFLE_MODE_NONE: return JNI_SHUFFLE_STATUS_OFF; - case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK: + case PlaybackStateCompat.SHUFFLE_MODE_ALL: return JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE; - case BluetoothAvrcpPlayerSettings.STATE_GROUP: + case PlaybackStateCompat.SHUFFLE_MODE_GROUP: return JNI_SHUFFLE_STATUS_GROUP_SHUFFLE; } - } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SCAN) { - switch (mSettingVal) { - case BluetoothAvrcpPlayerSettings.STATE_OFF: - return JNI_SCAN_STATUS_OFF; - case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK: - return JNI_SCAN_STATUS_ALL_TRACK_SCAN; - case BluetoothAvrcpPlayerSettings.STATE_GROUP: - return JNI_SCAN_STATUS_GROUP_SCAN; - } } return JNI_STATUS_INVALID; } - - // convert a native Attribute Id into the AVRCP Setting equivalent value; - private static int mapBTAttribIdToAvrcpPlayerSettings(byte attribId) { - switch (attribId) { - case JNI_ATTRIB_EQUALIZER_STATUS: - return BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER; - case JNI_ATTRIB_REPEAT_STATUS: - return BluetoothAvrcpPlayerSettings.SETTING_REPEAT; - case JNI_ATTRIB_SHUFFLE_STATUS: - return BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE; - case JNI_ATTRIB_SCAN_STATUS: - return BluetoothAvrcpPlayerSettings.SETTING_SCAN; - default: - return BluetoothAvrcpPlayerSettings.STATE_INVALID; - } - } - } - -- cgit v1.2.3 From 4f32edc6e08126dff80f25661a1dbac745d2b940 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Fri, 26 Jul 2019 12:45:09 -0700 Subject: Use Calendar.Builder for MAP Client timestamp parsing MAP Client was reporting different timestamps for a given message-listing object each time getUnreadMessages() was called. The milliseconds value was always different. This was a result of the Calendar object pre-populating all fields with the current date-time. The reported date-time would always have the current time's milliseconds field. Datetime parsing has been updated to use a Calendar.Builder which is clearer and doesn't use the current time when initializing. The milliseconds field is explicitly set to zero. Tests were added for the ObexTime class to prevent this from happening in the future. Bug: b/135606822 Test: atest ObexTimeTest.java Change-Id: Ie872b4bb30f68e0c00b15767eb0413a6e67ba630 (cherry picked from commit 85fb0fe5d730d93850c7db0bc7eb2dc26d2ced50) Merged-In: Ie872b4bb30f68e0c00b15767eb0413a6e67ba630 Change-Id: Iab4e3cf5937958f500d58b9ba6fa64c439cd4dc9 --- .../android/bluetooth/mapclient/obex/ObexTime.java | 43 +++++++++++++++------- 1 file changed, 29 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/mapclient/obex/ObexTime.java b/src/com/android/bluetooth/mapclient/obex/ObexTime.java index 42a32c10f..cc58a5144 100644 --- a/src/com/android/bluetooth/mapclient/obex/ObexTime.java +++ b/src/com/android/bluetooth/mapclient/obex/ObexTime.java @@ -29,8 +29,17 @@ public final class ObexTime { public ObexTime(String time) { /* - * match OBEX time string: YYYYMMDDTHHMMSS with optional UTF offset - * +/-hhmm + * Match OBEX time string: YYYYMMDDTHHMMSS with optional UTF offset +/-hhmm + * + * Matched groups are numberes as follows: + * + * YYYY MM DD T HH MM SS + hh mm + * ^^^^ ^^ ^^ ^^ ^^ ^^ ^ ^^ ^^ + * 1 2 3 4 5 6 8 9 10 + * |---7---| + * + * All groups are guaranteed to be numeric so conversion will always succeed (except group 8 + * which is either + or -) */ Pattern p = Pattern.compile( "(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})(([+-])(\\d{2})(\\d{2})" + ")?"); @@ -39,20 +48,26 @@ public final class ObexTime { if (m.matches()) { /* - * matched groups are numberes as follows: YYYY MM DD T HH MM SS + - * hh mm ^^^^ ^^ ^^ ^^ ^^ ^^ ^ ^^ ^^ 1 2 3 4 5 6 8 9 10 all groups - * are guaranteed to be numeric so conversion will always succeed - * (except group 8 which is either + or -) + * MAP spec says to default to "Local Time basis" for a message listing timestamp. We'll + * use the system default timezone and assume it knows best what our local timezone is. + * The builder defaults to the default locale and timezone if none is provided. */ + Calendar.Builder builder = new Calendar.Builder(); - Calendar cal = Calendar.getInstance(); - cal.set(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)) - 1, - Integer.parseInt(m.group(3)), Integer.parseInt(m.group(4)), - Integer.parseInt(m.group(5)), Integer.parseInt(m.group(6))); + /* Note that Calendar months are zero-based */ + builder.setDate(Integer.parseInt(m.group(1)), /* year */ + Integer.parseInt(m.group(2)) - 1, /* month */ + Integer.parseInt(m.group(3))); /* day of month */ + + /* Note the MAP timestamp doesn't have milliseconds and we're explicitly setting to 0 */ + builder.setTimeOfDay(Integer.parseInt(m.group(4)), /* hours */ + Integer.parseInt(m.group(5)), /* minutes */ + Integer.parseInt(m.group(6)), /* seconds */ + 0); /* milliseconds */ /* - * if 7th group is matched then we have UTC offset information - * included + * If 7th group is matched then we're no longer using "Local Time basis" and instead + * have a UTC based timestamp and offset information included */ if (m.group(7) != null) { int ohh = Integer.parseInt(m.group(9)); @@ -68,10 +83,10 @@ public final class ObexTime { TimeZone tz = TimeZone.getTimeZone("UTC"); tz.setRawOffset(offset); - cal.setTimeZone(tz); + builder.setTimeZone(tz); } - mDate = cal.getTime(); + mDate = builder.build().getTime(); } } -- cgit v1.2.3 From 4d931c8105fd62a7e2290f2e0e37d601fd0942d2 Mon Sep 17 00:00:00 2001 From: Andrew Cheng Date: Tue, 23 Jul 2019 10:05:33 -0700 Subject: Splitting over-the-air downloading into batches Splitting the over-the-air downloading of contacts into batches to ameliorate PBAP crash when system goes to S2R while download is in progress. Test: Use ACTS/SL4A: act.py -c .json -tc BtCarPbapTest:test_contact_download Bug: b/117588939 Change-Id: I7d138a7d497c04c54710702320ba1fdea8f15ea7 (cherry picked from commit 1128539a1aced99db216bd86080dbab9c2b710ed) Merged-In: I7d138a7d497c04c54710702320ba1fdea8f15ea7 Change-Id: Idd1b1b5b22360ab36b05ddfbf62e5043e873098d --- .../BluetoothPbapRequestPullPhoneBook.java | 2 +- .../BluetoothPbapRequestPullPhoneBookSize.java | 66 +++++++++++++++++ .../pbapclient/PbapClientConnectionHandler.java | 83 ++++++++++++++++------ 3 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java (limited to 'src') diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java index 34ab7d52c..a5b3fbfea 100644 --- a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java +++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java @@ -73,7 +73,7 @@ final class BluetoothPbapRequestPullPhoneBook extends BluetoothPbapRequest { oap.add(OAP_TAGID_FORMAT, format); /* - * maxListCount is a special case which is handled in + * maxListCount == 0 is a special case which is handled in * BluetoothPbapRequestPullPhoneBookSize */ if (maxListCount > 0) { diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java new file mode 100644 index 000000000..f0706610f --- /dev/null +++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 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.pbapclient; + +import android.util.Log; + +import javax.obex.HeaderSet; + +final class BluetoothPbapRequestPullPhoneBookSize extends BluetoothPbapRequest { + + private static final boolean VDBG = Utils.VDBG; + + private static final String TAG = "BtPbapReqPullPhoneBookSize"; + + private static final String TYPE = "x-bt/phonebook"; + + private int mSize; + + BluetoothPbapRequestPullPhoneBookSize(String pbName, long filter) { + mHeaderSet.setHeader(HeaderSet.NAME, pbName); + + mHeaderSet.setHeader(HeaderSet.TYPE, TYPE); + + ObexAppParameters oap = new ObexAppParameters(); + // Set MaxListCount in the request to 0 to get PhonebookSize in the response. + // If a vCardSelector is present in the request, then the result shall + // contain the number of items that satisfy the selector’s criteria. + // See PBAP v1.2.3, Sec. 5.1.4.5. + oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 0); + if (filter != 0) { + oap.add(OAP_TAGID_FILTER, filter); + } + oap.addToHeaderSet(mHeaderSet); + } + + @Override + protected void readResponseHeaders(HeaderSet headerset) { + if (VDBG) { + Log.v(TAG, "readResponseHeaders"); + } + + ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset); + + if (oap.exists(OAP_TAGID_PHONEBOOK_SIZE)) { + mSize = oap.getShort(OAP_TAGID_PHONEBOOK_SIZE); + } + } + + public int getSize() { + return mSize; + } +} diff --git a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java index 914b5b163..43d384dfc 100644 --- a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java +++ b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java @@ -46,6 +46,15 @@ import javax.obex.ResponseCodes; * controlling state machine. */ class PbapClientConnectionHandler extends Handler { + // Tradeoff: larger BATCH_SIZE leads to faster download rates, while smaller + // BATCH_SIZE is less prone to IO Exceptions if there is a download in + // progress when Bluetooth stack is torn down. + private static final int DEFAULT_BATCH_SIZE = 250; + + // Upper limit on the indices of the vcf cards/entries, inclusive, + // i.e., valid indices are [0, 1, ... , UPPER_LIMIT] + private static final int UPPER_LIMIT = 65535; + static final String TAG = "PbapClientConnHandler"; static final boolean DBG = Utils.DBG; static final boolean VDBG = Utils.VDBG; @@ -239,29 +248,12 @@ class PbapClientConnectionHandler extends Handler { break; case MSG_DOWNLOAD: - try { - mAccountCreated = addAccount(mAccount); - if (!mAccountCreated) { - Log.e(TAG, "Account creation failed."); - return; - } - // Start at contact 1 to exclued Owner Card PBAP 1.1 sec 3.1.5.2 - BluetoothPbapRequestPullPhoneBook request = - new BluetoothPbapRequestPullPhoneBook(PB_PATH, mAccount, - PBAP_REQUESTED_FIELDS, VCARD_TYPE_30, 0, 1); - request.execute(mObexSession); - PhonebookPullRequest processor = - new PhonebookPullRequest(mPbapClientStateMachine.getContext(), - mAccount); - processor.setResults(request.getList()); - processor.onPullComplete(); - HashMap callCounter = new HashMap<>(); - downloadCallLog(MCH_PATH, callCounter); - downloadCallLog(ICH_PATH, callCounter); - downloadCallLog(OCH_PATH, callCounter); - } catch (IOException e) { - Log.w(TAG, "DOWNLOAD_CONTACTS Failure" + e.toString()); - } + downloadContacts(); + + HashMap callCounter = new HashMap<>(); + downloadCallLog(MCH_PATH, callCounter); + downloadCallLog(ICH_PATH, callCounter); + downloadCallLog(OCH_PATH, callCounter); break; default: @@ -369,6 +361,51 @@ class PbapClientConnectionHandler extends Handler { } } + void downloadContacts() { + try { + mAccountCreated = addAccount(mAccount); + if (!mAccountCreated) { + Log.e(TAG, "Account creation failed."); + return; + } + PhonebookPullRequest processor = + new PhonebookPullRequest(mPbapClientStateMachine.getContext(), + mAccount); + + // Download contacts in batches of size DEFAULT_BATCH_SIZE + BluetoothPbapRequestPullPhoneBookSize requestPbSize = + new BluetoothPbapRequestPullPhoneBookSize(PB_PATH, + PBAP_REQUESTED_FIELDS); + requestPbSize.execute(mObexSession); + + // "-1" because Owner Card is also included in PhoneBookSize + int numberOfContactsRemaining = requestPbSize.getSize() - 1; + + // Start at contact 1 to exclude Owner Card PBAP 1.1 sec 3.1.5.2 + int startOffset = 1; + while ((numberOfContactsRemaining > 0) && (startOffset <= UPPER_LIMIT)) { + int numberOfContactsToDownload = + Math.min(Math.min(DEFAULT_BATCH_SIZE, numberOfContactsRemaining), + UPPER_LIMIT - startOffset + 1); + BluetoothPbapRequestPullPhoneBook request = + new BluetoothPbapRequestPullPhoneBook(PB_PATH, mAccount, + PBAP_REQUESTED_FIELDS, VCARD_TYPE_30, + numberOfContactsToDownload, startOffset); + request.execute(mObexSession); + processor.setResults(request.getList()); + processor.onPullComplete(); + + startOffset += numberOfContactsToDownload; + numberOfContactsRemaining -= numberOfContactsToDownload; + } + if ((startOffset > UPPER_LIMIT) && (numberOfContactsRemaining > 0)) { + Log.w(TAG, "Download contacts incomplete, index exceeded upper limit."); + } + } catch (IOException e) { + Log.w(TAG, "Download contacts failure" + e.toString()); + } + } + void downloadCallLog(String path, HashMap callCounter) { try { BluetoothPbapRequestPullPhoneBook request = -- cgit v1.2.3 From 7c3a74093b155ffea85038c75621fb73a1a46b36 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Mon, 24 Jun 2019 17:08:50 -0700 Subject: AVRCP Controller AbsoluteVolumeNotification Update AbsoluteVolumeNotification request to properly respond to requests based upon the Fixed vs Dynamic volume levels. Bug: 129478624 Test: atest com.android.bluetooth.avrcpcontroller.AvrcpControllerStateMachineTest#testRegisterAbsVolumeNotification Change-Id: Ic9b745641a84712c005d40ecec50e55e9684d3ef (cherry picked from commit 8fd119d8a27d42e09a90280020925859fd0a1678) Merged-In: Ic9b745641a84712c005d40ecec50e55e9684d3ef Change-Id: I1212a70ab84e3aed926bcfcac04dc62efed4b489 --- .../AvrcpControllerStateMachine.java | 90 +++++++++++++++------- 1 file changed, 64 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index 7a6c54126..83ba8505d 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -94,7 +94,14 @@ class AvrcpControllerStateMachine extends StateMachine { */ private static final int ABS_VOL_BASE = 127; + /* + * Notification types for Avrcp protocol JNI. + */ + private static final byte NOTIFICATION_RSP_TYPE_INTERIM = 0x00; + private static final byte NOTIFICATION_RSP_TYPE_CHANGED = 0x01; + private final AudioManager mAudioManager; + private final boolean mIsVolumeFixed; protected final BluetoothDevice mDevice; protected final byte[] mDeviceAddress; @@ -113,6 +120,7 @@ class AvrcpControllerStateMachine extends StateMachine { private int mAddressedPlayerId = -1; private SparseArray mAvailablePlayerList = new SparseArray(); private int mVolumeChangedNotificationsToIgnore = 0; + private int mVolumeNotificationLabel = -1; GetFolderList mGetFolderList = null; @@ -142,6 +150,7 @@ class AvrcpControllerStateMachine extends StateMachine { mGetFolderList = new GetFolderList(); addState(mGetFolderList, mConnected); mAudioManager = (AudioManager) service.getSystemService(Context.AUDIO_SERVICE); + mIsVolumeFixed = mAudioManager.isVolumeFixed(); setInitialState(mDisconnected); } @@ -311,7 +320,14 @@ class AvrcpControllerStateMachine extends StateMachine { removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT, ABS_VOL_TIMEOUT_MILLIS); - setAbsVolume(msg.arg1, msg.arg2); + handleAbsVolumeRequest(msg.arg1, msg.arg2); + return true; + + case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: + mVolumeNotificationLabel = msg.arg1; + mService.sendRegisterAbsVolRspNative(mDeviceAddress, + NOTIFICATION_RSP_TYPE_INTERIM, + getAbsVolume(), mVolumeNotificationLabel); return true; case MESSAGE_GET_FOLDER_ITEMS: @@ -587,24 +603,9 @@ class AvrcpControllerStateMachine extends StateMachine { } break; - case CONNECT: - case DISCONNECT: - case MSG_AVRCP_PASSTHRU: - case MESSAGE_PROCESS_SET_ABS_VOL_CMD: - case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: - case MESSAGE_PROCESS_TRACK_CHANGED: - case MESSAGE_PROCESS_PLAY_POS_CHANGED: - case MESSAGE_PROCESS_PLAY_STATUS_CHANGED: - case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION: - case MESSAGE_PLAY_ITEM: - case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED: + default: // All of these messages should be handled by parent state immediately. return false; - - default: - logD(STATE_TAG + " deferring message " + msg.what - + " to connected!"); - deferMessage(msg); } return true; } @@ -717,23 +718,60 @@ class AvrcpControllerStateMachine extends StateMachine { } } + /** + * Handle a request to align our local volume with the volume of a remote device. If + * we're assuming the source volume is fixed then a response of ABS_VOL_MAX will always be + * sent and no volume adjustment action will be taken on the sink side. + * + * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX] + * @param label Volume notification label + */ + private void handleAbsVolumeRequest(int absVol, int label) { + logD("handleAbsVolumeRequest: absVol = " + absVol + ", label = " + label); + if (mIsVolumeFixed) { + logD("Source volume is assumed to be fixed, responding with max volume"); + absVol = ABS_VOL_BASE; + } else { + mVolumeChangedNotificationsToIgnore++; + removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); + sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT, + ABS_VOL_TIMEOUT_MILLIS); + setAbsVolume(absVol); + } + mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); + } + + /** + * Align our volume with a requested absolute volume level + * + * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX] + */ + private void setAbsVolume(int absVol) { + int maxLocalVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int curLocalVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + int reqLocalVolume = (maxLocalVolume * absVol) / ABS_VOL_BASE; + logD("setAbsVolme: absVol = " + absVol + ", reqLocal = " + reqLocalVolume + + ", curLocal = " + curLocalVolume + ", maxLocal = " + maxLocalVolume); - private void setAbsVolume(int absVol, int label) { - int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); - int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); - int newIndex = (maxVolume * absVol) / ABS_VOL_BASE; - logD(" setAbsVolume =" + absVol + " maxVol = " + maxVolume - + " cur = " + currIndex + " new = " + newIndex); /* * In some cases change in percentage is not sufficient enough to warrant * change in index values which are in range of 0-15. For such cases * no action is required */ - if (newIndex != currIndex) { - mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newIndex, + if (reqLocalVolume != curLocalVolume) { + mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, reqLocalVolume, AudioManager.FLAG_SHOW_UI); } - mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); + } + + private int getAbsVolume() { + if (mIsVolumeFixed) { + return ABS_VOL_BASE; + } + int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + int newIndex = (currIndex * ABS_VOL_BASE) / maxVolume; + return newIndex; } MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() { -- cgit v1.2.3 From 769be7335adfc4a2df42c78432f7efdd439db3b0 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Tue, 25 Jun 2019 09:54:46 -0700 Subject: AVRCP Controller request focus when idle When no music is playing and an incoming play request comes in from a paired device, request focus on it's behalf. Bug: 135112399 Test: atest com.android.bluetooth.avrcpcontroller.AvrcpControllerStateMachineTest Change-Id: Ia764ea54af95166be6b6c3d9b47f3e3c2d09a0d7 (cherry picked from commit e6ff50d6328dca536acd131d91c109072febd70f) Merged-In: Ia764ea54af95166be6b6c3d9b47f3e3c2d09a0d7 Change-Id: I16d132e6fc90c1bb0f21d05de3ddf877ff5ea5b4 --- src/com/android/bluetooth/a2dpsink/A2dpSinkService.java | 3 ++- .../bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java index e41def075..5271cc791 100644 --- a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java +++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java @@ -413,7 +413,8 @@ public class A2dpSinkService extends ProfileService { if (state == StackEvent.AUDIO_STATE_STARTED) { mA2dpSinkStreamHandler.obtainMessage( A2dpSinkStreamHandler.SRC_STR_START).sendToTarget(); - } else if (state == StackEvent.AUDIO_STATE_STOPPED) { + } else if (state == StackEvent.AUDIO_STATE_STOPPED + || state == StackEvent.AUDIO_STATE_REMOTE_SUSPEND) { mA2dpSinkStreamHandler.obtainMessage( A2dpSinkStreamHandler.SRC_STR_STOP).sendToTarget(); } diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index 83ba8505d..23f1ce75a 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -361,10 +361,13 @@ class AvrcpControllerStateMachine extends StateMachine { BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); if (mAddressedPlayer.getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING - && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE - && !shouldRequestFocus()) { + && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE) { + if (shouldRequestFocus()) { + mSessionCallbacks.onPrepare(); + } else { sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE); + } } return true; @@ -882,6 +885,7 @@ class AvrcpControllerStateMachine extends StateMachine { private boolean shouldRequestFocus() { return mService.getResources() - .getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus); + .getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus) + || !mAudioManager.isMusicActive(); } } -- cgit v1.2.3 From 70710dc735cba120c1c2be86afba4e9318cf7929 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 8 Aug 2019 16:16:49 -0700 Subject: AVRCP Controller transient loss while idle While gaining and losing focus due to transient focus requests ensure Bluetooth restores to the correct playback state. This corrects issues that could arise during non media sonification durring a transient focus loss. Bug: 136092891 Test: atest com.android.bluetooth.a2dpsink.A2dpSinkStreamHandlerTest Change-Id: I58997b3b8b96da309ff4c31a2b3039dccf9b3578 (cherry picked from commit 32d3d5acac7eabd2bd00be46ea9e399a8b8db10b) Merged-In: I58997b3b8b96da309ff4c31a2b3039dccf9b3578 Change-Id: Ibefa524f14e2c0792278ab76161f7a8bcbc4fb9b --- .../android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java | 4 +++- .../avrcpcontroller/BluetoothMediaBrowserService.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java index c24eca9e8..fa872845a 100644 --- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java +++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java @@ -26,6 +26,7 @@ import android.media.AudioManager.OnAudioFocusChangeListener; import android.media.MediaPlayer; import android.os.Handler; import android.os.Message; +import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import com.android.bluetooth.R; @@ -225,7 +226,8 @@ public class A2dpSinkStreamHandler extends Handler { break; case DELAYED_PAUSE: - if (mStreamAvailable && !inCallFromStreamingDevice()) { + if (BluetoothMediaBrowserService.getPlaybackState() + == PlaybackStateCompat.STATE_PLAYING && !inCallFromStreamingDevice()) { sendAvrcpPause(); mSentPause = true; mStreamAvailable = false; diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java index 774f95349..b8a33379e 100644 --- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java @@ -184,6 +184,20 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { } } + /** + * Get playback state + */ + public static synchronized int getPlaybackState() { + if (sBluetoothMediaBrowserService != null) { + PlaybackStateCompat currentPlaybackState = + sBluetoothMediaBrowserService.mSession.getController().getPlaybackState(); + if (currentPlaybackState != null) { + return currentPlaybackState.getState(); + } + } + return PlaybackStateCompat.STATE_ERROR; + } + /** * Get object for controlling playback */ -- cgit v1.2.3 From fdba1f6020b53a5aeaf67776c36caafb16ca0c32 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Fri, 9 Aug 2019 16:16:16 -0700 Subject: AVRCP Controller getCurrentCalls NPE Check for null call list if media device and phone call device are different. Bug: 138256493 Test: Connect 2 phones, one HFP one A2DP, receive incomming call. Change-Id: I5f5c85d65283add3790dfaa1f252c3a14127f822 (cherry picked from commit 26041cc83d7f02da41b88eb71eeb30b392d0cfc9) Merged-In: I5f5c85d65283add3790dfaa1f252c3a14127f822 Change-Id: I58538ae0faddec9a66a3a695f70a2c030681f603 --- src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java index fa872845a..ffd648af9 100644 --- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java +++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java @@ -17,6 +17,7 @@ package com.android.bluetooth.a2dpsink; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadsetClientCall; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioAttributes; @@ -361,7 +362,10 @@ public class A2dpSinkStreamHandler extends Handler { } HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService(); if (targetDevice != null && headsetClientService != null) { - return headsetClientService.getCurrentCalls(targetDevice).size() > 0; + List currentCalls = + headsetClientService.getCurrentCalls(targetDevice); + if (currentCalls == null) return false; + return currentCalls.size() > 0; } return false; } -- cgit v1.2.3 From 4ee2b85ad93152ee429b69262a25b4c640e58587 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Fri, 9 Aug 2019 17:33:36 -0700 Subject: AVRCP Controller onSkipToQueueItem invalid Prevent exceptions where skip to queue item results in an invalid song. Bug: 138803592 Test: atest com.android.bluetooth.avrcpcontroller.AvrcpControllerStateMachineTest#testSkipToQueueInvalid Change-Id: Icb9b0e85188335299fb62f0b30ada0a005ad20a3 (cherry picked from commit 4eec318acc6cd14e09d5f8ef3495aee8a7317b8c) Merged-In: Icb9b0e85188335299fb62f0b30ada0a005ad20a3 Change-Id: I4adeb57c190c8214c83a153161ac138a8cd6ad51 --- src/com/android/bluetooth/avrcpcontroller/BrowseTree.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java b/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java index accea2a70..923282d34 100644 --- a/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java +++ b/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java @@ -100,7 +100,7 @@ public class BrowseTree { } BrowseNode getTrackFromNowPlayingList(int trackNumber) { - return mNowPlayingNode.mChildren.get(trackNumber); + return mNowPlayingNode.getChild(trackNumber); } // Each node of the tree is represented by Folder ID, Folder Name and the children. @@ -218,6 +218,13 @@ public class BrowseTree { return mChildren; } + synchronized BrowseNode getChild(int index) { + if (index < 0 || index >= mChildren.size()) { + return null; + } + return mChildren.get(index); + } + synchronized BrowseNode getParent() { return mParent; } -- cgit v1.2.3 From 1b10f6a7b2a5b2b2d0bd1eae3cd94e4d16a6a80d Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 10 Oct 2019 13:52:46 -0700 Subject: AVRCP MediaBrowserService support ACTION_PREPARE Report support for ACTION_PREPARE when connected to a bluetooth device capable of streaming. Bug: 141469207 Test: dumpsys media_session reports ACTION_PREPARE in actions Change-Id: I43702ba629750c02411cd39a1211bb352cc7457b (cherry picked from commit fb2633f7877e04a82f409a39b89a1a90189c70c4) Merged-In: I43702ba629750c02411cd39a1211bb352cc7457b Change-Id: I09cb56e8f59dfa15faea6581f379c06665076a63 --- src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java index 2ef3c1480..4736acffa 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java @@ -50,7 +50,7 @@ class AvrcpPlayer { private String mName = ""; private int mPlayerType; private byte[] mPlayerFeatures = new byte[16]; - private long mAvailableActions; + private long mAvailableActions = PlaybackStateCompat.ACTION_PREPARE; private MediaMetadata mCurrentTrack; private PlaybackStateCompat mPlaybackStateCompat; private PlayerApplicationSettings mSupportedPlayerApplicationSettings = @@ -63,7 +63,7 @@ class AvrcpPlayer { mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS - | PlaybackStateCompat.ACTION_STOP; + | PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_PREPARE; PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder() .setActions(mAvailableActions); mPlaybackStateCompat = playbackStateBuilder.build(); -- cgit v1.2.3 From ec62a0319cdf3ac374527d182209ef6eb2c2550c Mon Sep 17 00:00:00 2001 From: Andrew Cheng Date: Wed, 7 Aug 2019 10:29:13 -0700 Subject: PBAP client, download favorite contacts Bug: 132636859 Test: ACTS/SL4A and PTS Change-Id: Ifb16fbad1b9fc2e75008678f4551f515c46161b3 (cherry picked from commit bef7cd95b9e718257bc0e290c815e8b004679846) Merged-In: Ifb16fbad1b9fc2e75008678f4551f515c46161b3 Change-Id: I7c00be60f19141c3120808eead51503f22e21891 --- .../pbapclient/PbapClientConnectionHandler.java | 69 +++++++++++++++++----- 1 file changed, 55 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java index 43d384dfc..53670ff3a 100644 --- a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java +++ b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java @@ -32,8 +32,10 @@ import android.util.Log; import com.android.bluetooth.BluetoothObexTransport; import com.android.bluetooth.R; +import com.android.vcard.VCardEntry; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import javax.obex.ClientSession; @@ -104,9 +106,19 @@ class PbapClientConnectionHandler extends Handler { private static final int L2CAP_INVALID_PSM = -1; public static final String PB_PATH = "telecom/pb.vcf"; + public static final String FAV_PATH = "telecom/fav.vcf"; public static final String MCH_PATH = "telecom/mch.vcf"; public static final String ICH_PATH = "telecom/ich.vcf"; public static final String OCH_PATH = "telecom/och.vcf"; + public static final String SIM_PB_PATH = "SIM1/telecom/pb.vcf"; + public static final String SIM_MCH_PATH = "SIM1/telecom/mch.vcf"; + public static final String SIM_ICH_PATH = "SIM1/telecom/ich.vcf"; + public static final String SIM_OCH_PATH = "SIM1/telecom/och.vcf"; + + // PBAP v1.2.3 Sec. 7.1.2 + private static final int SUPPORTED_REPOSITORIES_LOCALPHONEBOOK = 1 << 0; + private static final int SUPPORTED_REPOSITORIES_SIMCARD = 1 << 1; + private static final int SUPPORTED_REPOSITORIES_FAVORITES = 1 << 3; public static final int PBAP_V1_2 = 0x0102; public static final byte VCARD_TYPE_21 = 0; @@ -248,7 +260,20 @@ class PbapClientConnectionHandler extends Handler { break; case MSG_DOWNLOAD: - downloadContacts(); + mAccountCreated = addAccount(mAccount); + if (!mAccountCreated) { + Log.e(TAG, "Account creation failed."); + return; + } + if (isRepositorySupported(SUPPORTED_REPOSITORIES_FAVORITES)) { + downloadContacts(FAV_PATH); + } + if (isRepositorySupported(SUPPORTED_REPOSITORIES_LOCALPHONEBOOK)) { + downloadContacts(PB_PATH); + } + if (isRepositorySupported(SUPPORTED_REPOSITORIES_SIMCARD)) { + downloadContacts(SIM_PB_PATH); + } HashMap callCounter = new HashMap<>(); downloadCallLog(MCH_PATH, callCounter); @@ -361,38 +386,46 @@ class PbapClientConnectionHandler extends Handler { } } - void downloadContacts() { + void downloadContacts(String path) { try { - mAccountCreated = addAccount(mAccount); - if (!mAccountCreated) { - Log.e(TAG, "Account creation failed."); - return; - } PhonebookPullRequest processor = new PhonebookPullRequest(mPbapClientStateMachine.getContext(), mAccount); // Download contacts in batches of size DEFAULT_BATCH_SIZE BluetoothPbapRequestPullPhoneBookSize requestPbSize = - new BluetoothPbapRequestPullPhoneBookSize(PB_PATH, + new BluetoothPbapRequestPullPhoneBookSize(path, PBAP_REQUESTED_FIELDS); requestPbSize.execute(mObexSession); - // "-1" because Owner Card is also included in PhoneBookSize - int numberOfContactsRemaining = requestPbSize.getSize() - 1; + int numberOfContactsRemaining = requestPbSize.getSize(); + int startOffset = 0; + if (PB_PATH.equals(path)) { + // PBAP v1.2.3, Sec 3.1.5. The first contact in pb is owner card 0.vcf, which we + // do not want to download. The other phonebook objects (e.g., fav) don't have an + // owner card, so they don't need an offset. + startOffset = 1; + // "-1" because Owner Card 0.vcf is also included in /pb, but not in /fav. + numberOfContactsRemaining -= 1; + } - // Start at contact 1 to exclude Owner Card PBAP 1.1 sec 3.1.5.2 - int startOffset = 1; while ((numberOfContactsRemaining > 0) && (startOffset <= UPPER_LIMIT)) { int numberOfContactsToDownload = Math.min(Math.min(DEFAULT_BATCH_SIZE, numberOfContactsRemaining), UPPER_LIMIT - startOffset + 1); BluetoothPbapRequestPullPhoneBook request = - new BluetoothPbapRequestPullPhoneBook(PB_PATH, mAccount, + new BluetoothPbapRequestPullPhoneBook(path, mAccount, PBAP_REQUESTED_FIELDS, VCARD_TYPE_30, numberOfContactsToDownload, startOffset); request.execute(mObexSession); - processor.setResults(request.getList()); + ArrayList vcards = request.getList(); + if (path == FAV_PATH) { + // mark each vcard as a favorite + for (VCardEntry v : vcards) { + v.setStarred(true); + } + } + processor.setResults(vcards); processor.onPullComplete(); startOffset += numberOfContactsToDownload; @@ -456,4 +489,12 @@ class PbapClientConnectionHandler extends Handler { Log.d(TAG, "Call Logs could not be deleted, they may not exist yet."); } } + + private boolean isRepositorySupported(int mask) { + if (mPseRec == null) { + if (VDBG) Log.v(TAG, "No PBAP Server SDP Record"); + return false; + } + return (mask & mPseRec.getSupportedRepositories()) != 0; + } } -- cgit v1.2.3 From cc193bd4b47afb5d17fe6f3295dd88a8924b9b31 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Tue, 29 Oct 2019 17:47:24 -0700 Subject: MAP Client support incomming MMS Add support for incomming MMS messages by leveraging the already existing parser. Bug: 119750641 Test: atest MapClientStateMachineTest Change-Id: Iad3806e39fa11b520f7bcc3b33426f0a65779530 (cherry picked from commit f3965e7d943daa79e2868c4c27f0bda3c59ad051) Merged-In: Iad3806e39fa11b520f7bcc3b33426f0a65779530 Change-Id: I54d1399350a5e3d89f51625dde101a1cc9129c06 --- src/com/android/bluetooth/mapclient/MceStateMachine.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/mapclient/MceStateMachine.java b/src/com/android/bluetooth/mapclient/MceStateMachine.java index 867ab185f..cdfc98699 100644 --- a/src/com/android/bluetooth/mapclient/MceStateMachine.java +++ b/src/com/android/bluetooth/mapclient/MceStateMachine.java @@ -58,6 +58,7 @@ import android.util.Log; import com.android.bluetooth.BluetoothMetricsProto; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.ProfileService; +import com.android.bluetooth.map.BluetoothMapbMessageMime; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IState; import com.android.internal.util.State; @@ -345,7 +346,9 @@ final class MceStateMachine extends StateMachine { void setDefaultMessageType(SdpMasRecord sdpMasRecord) { int supportedMessageTypes = sdpMasRecord.getSupportedMessageTypes(); synchronized (mDefaultMessageType) { - if ((supportedMessageTypes & SdpMasRecord.MessageType.SMS_CDMA) > 0) { + if ((supportedMessageTypes & SdpMasRecord.MessageType.MMS) > 0) { + mDefaultMessageType = Bmessage.Type.MMS; + } else if ((supportedMessageTypes & SdpMasRecord.MessageType.SMS_CDMA) > 0) { mDefaultMessageType = Bmessage.Type.SMS_CDMA; } else if ((supportedMessageTypes & SdpMasRecord.MessageType.SMS_GSM) > 0) { mDefaultMessageType = Bmessage.Type.SMS_GSM; @@ -666,6 +669,7 @@ final class MceStateMachine extends StateMachine { switch (message.getType()) { case SMS_CDMA: case SMS_GSM: + case MMS: if (DBG) { Log.d(TAG, "Body: " + message.getBodyContent()); } @@ -706,6 +710,12 @@ final class MceStateMachine extends StateMachine { intent.putExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME, originator.getDisplayName()); } + if (message.getType() == Bmessage.Type.MMS) { + BluetoothMapbMessageMime mmsBmessage = new BluetoothMapbMessageMime(); + mmsBmessage.parseMsgPart(message.getBodyContent()); + intent.putExtra(android.content.Intent.EXTRA_TEXT, + mmsBmessage.getMessageAsText()); + } // Only send to the current default SMS app if one exists String defaultMessagingPackage = Telephony.Sms.getDefaultSmsPackage(mService); if (defaultMessagingPackage != null) { @@ -713,8 +723,6 @@ final class MceStateMachine extends StateMachine { } mService.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS); break; - - case MMS: case EMAIL: default: Log.e(TAG, "Received unhandled type" + message.getType().toString()); -- cgit v1.2.3 From 7ce948ec7cf933a9ac7f71be4fed820ffc3722fe Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Tue, 29 Oct 2019 19:49:04 -0700 Subject: MAP Client BMessage parser length Validate and catch errors in the BMessage pertaining to erroneous length. Bug: 123244713 Test: atest BmessageTest Change-Id: Ie9101e0be12d627a6fd3cec73eec9b977d8d40bb (cherry picked from commit a353a95c230b5546a17daea10390fbd4be0f9e63) Merged-In: Ie9101e0be12d627a6fd3cec73eec9b977d8d40bb Change-Id: Ie83f30e898d0f81eb81100fdb70859f92680d167 --- src/com/android/bluetooth/mapclient/obex/BmessageParser.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/com/android/bluetooth/mapclient/obex/BmessageParser.java b/src/com/android/bluetooth/mapclient/obex/BmessageParser.java index 2705e3429..5b844dce5 100644 --- a/src/com/android/bluetooth/mapclient/obex/BmessageParser.java +++ b/src/com/android/bluetooth/mapclient/obex/BmessageParser.java @@ -309,6 +309,12 @@ class BmessageParser { String remng = mParser.remaining(); byte[] data = remng.getBytes(); + if (offset < 0 || offset > data.length) { + /* Handle possible exception for incorrect LENGTH value + * from MSE while parsing end of props */ + throw new ParseException("Invalid LENGTH value", mParser.pos()); + } + /* restart parsing from after 'message' */ mParser = new BmsgTokenizer(new String(data, offset, data.length - offset), restartPos); -- cgit v1.2.3 From 362108ec36150b8556892d2786f5c16b97051fd4 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Tue, 29 Oct 2019 20:36:18 -0700 Subject: MAP Client Only connect MNS in connected state Only allow the MNS to connect when the MAS client is in the connected state, this prevents the possibility of the MNS client from connecting if the MAS fails to connect. Bug: 135088863 Test: disallow MAS connection and verify MNS notifications don't arrive. Change-Id: I75de88e74708283ebbaef70251f7575c0da0c4d4 (cherry picked from commit ac9715de616a2e8de421d72dbb2fd1ef6a55842c) Merged-In: I75de88e74708283ebbaef70251f7575c0da0c4d4 Change-Id: I3103c679d37a0664da4f6e777d9d5f42af257f51 --- src/com/android/bluetooth/mapclient/MnsService.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/com/android/bluetooth/mapclient/MnsService.java b/src/com/android/bluetooth/mapclient/MnsService.java index c1ab39e00..b3317df90 100644 --- a/src/com/android/bluetooth/mapclient/MnsService.java +++ b/src/com/android/bluetooth/mapclient/MnsService.java @@ -17,6 +17,7 @@ package com.android.bluetooth.mapclient; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.os.Handler; @@ -129,6 +130,11 @@ public class MnsService { Log.e(TAG, "Error: NO statemachine for device: " + device.getAddress() + " (name: " + device.getName()); return false; + } else if (stateMachine.getState() != BluetoothProfile.STATE_CONNECTED) { + Log.e(TAG, "Error: statemachine for device: " + device.getAddress() + + " (name: " + device.getName() + ") is not currently CONNECTED : " + + stateMachine.getCurrentState()); + return false; } MnsObexServer srv = new MnsObexServer(stateMachine, sServerSockets); BluetoothObexTransport transport = new BluetoothObexTransport(socket); -- cgit v1.2.3 From f53904d617cfba99662175a2d9307c30e435c5c8 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Tue, 29 Oct 2019 20:52:35 -0700 Subject: MAP Client close connection on MNS disconnect If the MNS connection is terminated from the client side disconnect the corresponding MAS client. Bug: 129908795 Test: disconnect the MAP connection from the MAS server (MNS client) Change-Id: I39b524b2222d47a553eeaa4b9781f3200443cf8e (cherry picked from commit ec407574bee6c1cff4228daed8b413a82ff509b3) Merged-In: I39b524b2222d47a553eeaa4b9781f3200443cf8e Change-Id: I71426bef61234d1f1c61dfe94f23c266ea8e8a34 --- src/com/android/bluetooth/mapclient/MnsObexServer.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/com/android/bluetooth/mapclient/MnsObexServer.java b/src/com/android/bluetooth/mapclient/MnsObexServer.java index 53cd79bb2..33ba1ea74 100644 --- a/src/com/android/bluetooth/mapclient/MnsObexServer.java +++ b/src/com/android/bluetooth/mapclient/MnsObexServer.java @@ -90,6 +90,10 @@ class MnsObexServer extends ServerRequestHandler { if (VDBG) { Log.v(TAG, "onDisconnect"); } + MceStateMachine currentStateMachine = mStateMachineReference.get(); + if (currentStateMachine != null) { + currentStateMachine.disconnect(); + } } @Override -- cgit v1.2.3 From edaa9c5dda402e5d80bfb99a7ed5c4ead012488f Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Tue, 29 Oct 2019 21:08:35 -0700 Subject: MAP Client disconnect state machine if MAS client disconnected If the MAS client disconnects (Obex error or remote device terminates connection) close the state machine safely. Bug: 138379476 Test: atest MapClientStateMachineTest Change-Id: I11362911fc96cb15416242456fa3cf026c085745 (cherry picked from commit 9b5cd43f3b297ddf9c0c2bd7136a7de7f45312a1) Merged-In: I11362911fc96cb15416242456fa3cf026c085745 Change-Id: Ie0ec535543ef477130878f7a722c2c89edf022a0 --- src/com/android/bluetooth/mapclient/MceStateMachine.java | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/com/android/bluetooth/mapclient/MceStateMachine.java b/src/com/android/bluetooth/mapclient/MceStateMachine.java index cdfc98699..9b86aaef1 100644 --- a/src/com/android/bluetooth/mapclient/MceStateMachine.java +++ b/src/com/android/bluetooth/mapclient/MceStateMachine.java @@ -474,6 +474,11 @@ final class MceStateMachine extends StateMachine { } break; + case MSG_MAS_DISCONNECTED: + deferMessage(message); + transitionTo(mDisconnecting); + break; + case MSG_OUTBOUND_MESSAGE: mMasClient.makeRequest( new RequestPushMessage(FOLDER_OUTBOX, (Bmessage) message.obj, null, -- cgit v1.2.3 From 65bd91a08f78494f11638085974891edc4b50dca Mon Sep 17 00:00:00 2001 From: Simon Dai Date: Mon, 24 Jun 2019 15:28:02 -0700 Subject: Add bluetooth prefs for Android Automotive Add extra to BluetoothMediaBrowserService to redirect to settings when Bluetooth is not connected Fixes: 134514401 Test: verify extra in the media session state at startup and after disconnection. Change-Id: Iffbd6704eb82e02233b738c7ee06f5c4d8e3c0b5 (cherry picked from commit 6b9199e0548439300d41d70c7915012e2434ab78) Merged-In: Iffbd6704eb82e02233b738c7ee06f5c4d8e3c0b5 Change-Id: Ie5364fa2c0339c81e4444548d5e132caad789994 --- src/com/android/bluetooth/BluetoothPrefs.java | 40 ++++++++++++++++++++++ .../AvrcpControllerStateMachine.java | 6 +--- .../BluetoothMediaBrowserService.java | 36 ++++++++++++++++--- 3 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 src/com/android/bluetooth/BluetoothPrefs.java (limited to 'src') diff --git a/src/com/android/bluetooth/BluetoothPrefs.java b/src/com/android/bluetooth/BluetoothPrefs.java new file mode 100644 index 000000000..2c7c87aaa --- /dev/null +++ b/src/com/android/bluetooth/BluetoothPrefs.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 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; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +/** + * Activity that routes to Bluetooth settings when launched + */ +public class BluetoothPrefs extends Activity { + + public static final String BLUETOOTH_SETTING_ACTION = "android.settings.BLUETOOTH_SETTINGS"; + public static final String BLUETOOTH_SETTING_CATEGORY = "android.intent.category.DEFAULT"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent launchIntent = new Intent(); + launchIntent.setAction(BLUETOOTH_SETTING_ACTION); + launchIntent.addCategory(BLUETOOTH_SETTING_CATEGORY); + startActivity(launchIntent); + finish(); + } +} diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index 23f1ce75a..ae94a4db5 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -233,11 +233,7 @@ class AvrcpControllerStateMachine extends StateMachine { mAddressedPlayer.updateCurrentTrack(null); mBrowseTree.mNowPlayingNode.setCached(false); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); - PlaybackStateCompat.Builder pbb = new PlaybackStateCompat.Builder(); - pbb.setState(PlaybackStateCompat.STATE_ERROR, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, - 1.0f).setActions(0); - pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected)); - BluetoothMediaBrowserService.notifyChanged(pbb.build()); + BluetoothMediaBrowserService.addressedPlayerChanged(null); mService.sBrowseTree.mRootNode.removeChild( mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java index b8a33379e..a0b1224ee 100644 --- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcpcontroller; +import android.app.PendingIntent; +import android.content.Intent; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; import android.os.Bundle; @@ -29,6 +31,7 @@ import android.util.Log; import androidx.media.MediaBrowserServiceCompat; +import com.android.bluetooth.BluetoothPrefs; import com.android.bluetooth.R; import java.util.ArrayList; @@ -60,6 +63,12 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { // Browsing related structures. private List mMediaQueue = new ArrayList<>(); + // Error messaging extras + public static final String ERROR_RESOLUTION_ACTION_INTENT = + "android.media.extras.ERROR_RESOLUTION_ACTION_INTENT"; + public static final String ERROR_RESOLUTION_ACTION_LABEL = + "android.media.extras.ERROR_RESOLUTION_ACTION_LABEL"; + /** * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. @@ -76,11 +85,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name)); mSession.setQueue(mMediaQueue); - PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder(); - playbackStateBuilder.setState(PlaybackStateCompat.STATE_ERROR, - PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); - playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected)); - mSession.setPlaybackState(playbackStateBuilder.build()); + setErrorPlaybackState(); sBluetoothMediaBrowserService = this; } @@ -94,6 +99,24 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { } } + private void setErrorPlaybackState() { + Bundle extras = new Bundle(); + extras.putString(ERROR_RESOLUTION_ACTION_LABEL, + getString(R.string.bluetooth_connect_action)); + Intent launchIntent = new Intent(); + launchIntent.setAction(BluetoothPrefs.BLUETOOTH_SETTING_ACTION); + launchIntent.addCategory(BluetoothPrefs.BLUETOOTH_SETTING_CATEGORY); + PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, + launchIntent, PendingIntent.FLAG_UPDATE_CURRENT); + extras.putParcelable(ERROR_RESOLUTION_ACTION_INTENT, pendingIntent); + PlaybackStateCompat errorState = new PlaybackStateCompat.Builder() + .setErrorMessage(getString(R.string.bluetooth_disconnected)) + .setExtras(extras) + .setState(PlaybackStateCompat.STATE_ERROR, 0, 0) + .build(); + mSession.setPlaybackState(errorState); + } + @Override public synchronized void onLoadChildren(final String parentMediaId, final Result> result) { @@ -138,6 +161,9 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) { if (sBluetoothMediaBrowserService != null) { + if (callback == null) { + sBluetoothMediaBrowserService.setErrorPlaybackState(); + } sBluetoothMediaBrowserService.mSession.setCallback(callback); } else { Log.w(TAG, "addressedPlayerChanged Unavailable"); -- cgit v1.2.3 From 902bddd11cf3181a0ef5ee56c923e91b7b537d6c Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Wed, 13 Nov 2019 09:59:15 -0800 Subject: AVRCP Controller State without Browsing Some devices do not support Browsing over AVRCP and the playback state of those devices was inconsistent on disconnection of the Control channel. This change improves the consistency of the data reported via the media_session. Bug: 144013853 Test: atest AvrcpControllerStateMachineTest Change-Id: I3ce26d76a81422d1e5af8b439d8bc1ab30d320dc (cherry picked from commit 527da93c0bfa15a95b1de9b93cb9d955885af849) Merged-In: I3ce26d76a81422d1e5af8b439d8bc1ab30d320dc Change-Id: I1eeb1d3bd1c4dcb3bd14ba9c2851a9ad4aee6b61 --- .../bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index ae94a4db5..e3abc4d8f 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -223,7 +223,6 @@ class AvrcpControllerStateMachine extends StateMachine { mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService .sBrowseTree.mRootNode); - BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); mBrowsingConnected = true; } @@ -233,12 +232,10 @@ class AvrcpControllerStateMachine extends StateMachine { mAddressedPlayer.updateCurrentTrack(null); mBrowseTree.mNowPlayingNode.setCached(false); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); - BluetoothMediaBrowserService.addressedPlayerChanged(null); mService.sBrowseTree.mRootNode.removeChild( mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService .sBrowseTree.mRootNode); - BluetoothMediaBrowserService.trackChanged(null); mBrowsingConnected = false; } @@ -299,6 +296,7 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void enter() { if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) { + BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED); BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); } else { @@ -712,6 +710,8 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void enter() { onBrowsingDisconnected(); + BluetoothMediaBrowserService.trackChanged(null); + BluetoothMediaBrowserService.addressedPlayerChanged(null); broadcastConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING); transitionTo(mDisconnected); } -- cgit v1.2.3 From 80c191cb3b35e635069b8b13ffc9e63bba79403d Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Wed, 20 Nov 2019 09:07:23 -0800 Subject: A2DP Sink: Focus gain while transient loss While Bluetooth is under a transient focus loss if the user presses the play button, Bluetooth should attempt to regain audio focus rather than just wait passive for the transient holder to release it. Bug: 143916073 Test: atest A2dpSinkStreamHandlerTest Change-Id: I7b958a9ff06c9f1fcb9383a3a40ca147f4ad9399 (cherry picked from commit 97e20beea72e7b717465cb280ef576f5f2e29711) Merged-In: I7b958a9ff06c9f1fcb9383a3a40ca147f4ad9399 Change-Id: I78dfe8917d02c4b9d71d82db4fae586f52612050 --- src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java index ffd648af9..87ccfc873 100644 --- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java +++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java @@ -183,8 +183,9 @@ public class A2dpSinkStreamHandler extends Handler { break; case AUDIO_FOCUS_CHANGE: + mAudioFocus = (int) message.obj; // message.obj is the newly granted audio focus. - switch ((int) message.obj) { + switch (mAudioFocus) { case AudioManager.AUDIOFOCUS_GAIN: removeMessages(DELAYED_PAUSE); // Begin playing audio, if we paused the remote, send a play now. @@ -245,7 +246,7 @@ public class A2dpSinkStreamHandler extends Handler { */ private void requestAudioFocusIfNone() { if (DBG) Log.d(TAG, "requestAudioFocusIfNone()"); - if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) { + if (mAudioFocus != AudioManager.AUDIOFOCUS_GAIN) { requestAudioFocus(); } // On the off change mMediaPlayer errors out and dies, we want to make sure we retry this. -- cgit v1.2.3 From 3797ae45f48e706acae23d213ad1b828db0bad15 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Wed, 20 Nov 2019 10:30:57 -0800 Subject: AVRCP Controller Disable Automatic Focus Request Remove automatic focus requests when isMusicActive returns false as there are too many edge cases in behavior to work well. Bug: 144507838 Test: Attempt to play media from connected device while no audio is active Change-Id: I547d195012c56dc73029ef59ed24bbfe7d536646 (cherry picked from commit 7bf3489331e9b645c3692f9f86beb108a1a0e874) Merged-In: I547d195012c56dc73029ef59ed24bbfe7d536646 Change-Id: I5870c92a24d1501bd10162d28a3dde5e83b26251 --- .../android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index e3abc4d8f..8704b078e 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -881,7 +881,6 @@ class AvrcpControllerStateMachine extends StateMachine { private boolean shouldRequestFocus() { return mService.getResources() - .getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus) - || !mAudioManager.isMusicActive(); + .getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus); } } -- cgit v1.2.3 From da1dc71cffb5fbb1e55186a20410e3d4f95246c3 Mon Sep 17 00:00:00 2001 From: Andrew Cheng Date: Thu, 15 Aug 2019 09:32:41 -0700 Subject: PBAP server, send favorite contacts Bug: 132636859 Test: ACTS/SL4A and PTS Change-Id: I57326dfafe898187477b524b0fe03b181292dda9 (cherry picked from commit 131e778204a9143ad27793a341f4e13925b7d44a) Merged-In: I57326dfafe898187477b524b0fe03b181292dda9 Change-Id: Icfa9a81d9aaf4ddad35e51b5410b8b367d820160 --- .../bluetooth/pbap/BluetoothPbapObexServer.java | 65 +++++++++++++++++----- .../bluetooth/pbap/BluetoothPbapService.java | 3 +- .../bluetooth/pbap/BluetoothPbapVcardManager.java | 37 +++++++++--- 3 files changed, 82 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java b/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java index 979becdca..34ee96270 100755 --- a/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java +++ b/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java @@ -97,6 +97,7 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { private static final String[] LEGAL_PATH = { "/telecom", "/telecom/pb", + "/telecom/fav", "/telecom/ich", "/telecom/och", "/telecom/mch", @@ -106,6 +107,7 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { @SuppressWarnings("unused") private static final String[] LEGAL_PATH_WITH_SIM = { "/telecom", "/telecom/pb", + "/telecom/fav", "/telecom/ich", "/telecom/och", "/telecom/mch", @@ -138,6 +140,9 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { // phone book private static final String PB = "pb"; + // favorites + private static final String FAV = "fav"; + private static final String TELECOM_PATH = "/telecom"; private static final String ICH_PATH = "/telecom/ich"; @@ -150,6 +155,8 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { private static final String PB_PATH = "/telecom/pb"; + private static final String FAV_PATH = "/telecom/fav"; + // type for list vcard objects private static final String TYPE_LISTING = "x-bt/vcard-listing"; @@ -212,6 +219,8 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { public static final int MISSED_CALL_HISTORY = 4; public static final int COMBINED_CALL_HISTORY = 5; + + public static final int FAVORITES = 6; } public BluetoothPbapObexServer(Handler callback, Context context, @@ -441,6 +450,8 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { if (mCurrentPath.equals(PB_PATH)) { appParamValue.needTag = ContentType.PHONEBOOK; + } else if (mCurrentPath.equals(FAV_PATH)) { + appParamValue.needTag = ContentType.FAVORITES; } else if (mCurrentPath.equals(ICH_PATH)) { appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY; } else if (mCurrentPath.equals(OCH_PATH)) { @@ -478,6 +489,11 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { if (D) { Log.v(TAG, "download phonebook request"); } + } else if (isNameMatchTarget(name, FAV)) { + appParamValue.needTag = ContentType.FAVORITES; + if (D) { + Log.v(TAG, "download favorites request"); + } } else if (isNameMatchTarget(name, ICH)) { appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY; appParamValue.callHistoryVersionCounter = @@ -751,7 +767,8 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { result.append(""); // Phonebook listing request - if (appParamValue.needTag == ContentType.PHONEBOOK) { + if ((appParamValue.needTag == ContentType.PHONEBOOK) + || (appParamValue.needTag == ContentType.FAVORITES)) { String type = ""; if (appParamValue.searchAttr.equals("0")) { type = "name"; @@ -948,7 +965,7 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { checkPbapFeatureSupport(mFolderVersionCounterbitMask); } boolean needSendPhonebookVersionCounters = false; - if (isNameMatchTarget(name, PB)) { + if (isNameMatchTarget(name, PB) || isNameMatchTarget(name, FAV)) { needSendPhonebookVersionCounters = checkPbapFeatureSupport(mFolderVersionCounterbitMask); } @@ -1192,11 +1209,12 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { if (appParamValue.needTag == 0) { Log.w(TAG, "wrong path!"); return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; - } else if (appParamValue.needTag == ContentType.PHONEBOOK) { + } else if ((appParamValue.needTag == ContentType.PHONEBOOK) + || (appParamValue.needTag == ContentType.FAVORITES)) { if (intIndex < 0 || intIndex >= size) { Log.w(TAG, "The requested vcard is not acceptable! name= " + name); return ResponseCodes.OBEX_HTTP_NOT_FOUND; - } else if (intIndex == 0) { + } else if ((intIndex == 0) && (appParamValue.needTag == ContentType.PHONEBOOK)) { // For PB_PATH, 0.vcf is the phone number of this phone. String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21, appParamValue.ignorefilter ? null : appParamValue.propertySelector); @@ -1252,30 +1270,49 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { int requestSize = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize; - int startPoint = appParamValue.listStartOffset; - if (startPoint < 0 || startPoint >= pbSize) { + /** + * startIndex (resp., lastIndex) corresponds to the index of the first (resp., last) + * vcard entry in the phonebook object. + * PBAP v1.2.3: only pb starts indexing at 0.vcf (owner card), the other phonebook + * objects (e.g., fav) start at 1.vcf. Additionally, the owner card is included in + * pb's pbSize. This means pbSize corresponds to the index of the last vcf in the fav + * phonebook object, but does not for the pb phonebook object. + */ + int startIndex = 1; + int lastIndex = pbSize; + if (appParamValue.needTag == BluetoothPbapObexServer.ContentType.PHONEBOOK) { + startIndex = 0; + lastIndex = pbSize - 1; + } + // [startPoint, endPoint] denote the range of vcf indices to send, inclusive. + int startPoint = startIndex + appParamValue.listStartOffset; + int endPoint = startPoint + requestSize - 1; + if (appParamValue.listStartOffset < 0 || startPoint > lastIndex) { Log.w(TAG, "listStartOffset is not correct! " + startPoint); return ResponseCodes.OBEX_HTTP_OK; } + if (endPoint > lastIndex) { + endPoint = lastIndex; + } // Limit the number of call log to CALLLOG_NUM_LIMIT - if (appParamValue.needTag != BluetoothPbapObexServer.ContentType.PHONEBOOK) { + if ((appParamValue.needTag != BluetoothPbapObexServer.ContentType.PHONEBOOK) + && (appParamValue.needTag != BluetoothPbapObexServer.ContentType.FAVORITES)) { if (requestSize > CALLLOG_NUM_LIMIT) { requestSize = CALLLOG_NUM_LIMIT; } } - int endPoint = startPoint + requestSize - 1; - if (endPoint > pbSize - 1) { - endPoint = pbSize - 1; - } if (D) { Log.d(TAG, "pullPhonebook(): requestSize=" + requestSize + " startPoint=" + startPoint + " endPoint=" + endPoint); } boolean vcard21 = appParamValue.vcard21; - if (appParamValue.needTag == BluetoothPbapObexServer.ContentType.PHONEBOOK) { + boolean favorites = + (appParamValue.needTag == BluetoothPbapObexServer.ContentType.FAVORITES); + if ((appParamValue.needTag == BluetoothPbapObexServer.ContentType.PHONEBOOK) + || favorites) { if (startPoint == 0) { String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21, appParamValue.ignorefilter ? null : appParamValue.propertySelector); @@ -1285,13 +1322,13 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { return mVcardManager.composeAndSendPhonebookVcards(op, 1, endPoint, vcard21, ownerVcard, needSendBody, pbSize, appParamValue.ignorefilter, appParamValue.propertySelector, appParamValue.vCardSelector, - appParamValue.vCardSelectorOperator, mVcardSelector); + appParamValue.vCardSelectorOperator, mVcardSelector, favorites); } } else { return mVcardManager.composeAndSendPhonebookVcards(op, startPoint, endPoint, vcard21, null, needSendBody, pbSize, appParamValue.ignorefilter, appParamValue.propertySelector, appParamValue.vCardSelector, - appParamValue.vCardSelectorOperator, mVcardSelector); + appParamValue.vCardSelectorOperator, mVcardSelector, favorites); } } else { return mVcardManager.composeAndSendSelectedCallLogVcards(appParamValue.needTag, op, diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/src/com/android/bluetooth/pbap/BluetoothPbapService.java index e7dba2a96..280186f8f 100644 --- a/src/com/android/bluetooth/pbap/BluetoothPbapService.java +++ b/src/com/android/bluetooth/pbap/BluetoothPbapService.java @@ -140,7 +140,8 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect private ObexServerSockets mServerSockets = null; private static final int SDP_PBAP_SERVER_VERSION = 0x0102; - private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0001; + // PBAP v1.2.3, Sec. 7.1.2: local phonebook and favorites + private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0009; private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F; /* PBAP will use Bluetooth notification ID from 1000000 (included) to 2000000 (excluded). diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java index 5ba2b4b8b..8801c16fb 100755 --- a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java +++ b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java @@ -153,7 +153,8 @@ public class BluetoothPbapVcardManager { int size; switch (type) { case BluetoothPbapObexServer.ContentType.PHONEBOOK: - size = getContactsSize(); + case BluetoothPbapObexServer.ContentType.FAVORITES: + size = getContactsSize(type); break; default: size = getCallHistorySize(type); @@ -165,16 +166,30 @@ public class BluetoothPbapVcardManager { return size; } - public final int getContactsSize() { + /** + * Returns the number of contacts (i.e., vcf) in a phonebook object. + * @param type specifies which phonebook object, e.g., pb, fav + * @return + */ + public final int getContactsSize(final int type) { final Uri myUri = DevicePolicyUtils.getEnterprisePhoneUri(mContext); Cursor contactCursor = null; + String selectionClause = null; + if (type == BluetoothPbapObexServer.ContentType.FAVORITES) { + selectionClause = Phone.STARRED + " = 1"; + } try { - contactCursor = mResolver.query(myUri, new String[]{Phone.CONTACT_ID}, null, null, - Phone.CONTACT_ID); + contactCursor = mResolver.query(myUri, + new String[]{Phone.CONTACT_ID}, selectionClause, + null, Phone.CONTACT_ID); if (contactCursor == null) { return 0; } - return getDistinctContactIdSize(contactCursor) + 1; // always has the 0.vcf + int contactsSize = getDistinctContactIdSize(contactCursor); + if (type == BluetoothPbapObexServer.ContentType.PHONEBOOK) { + contactsSize += 1; // pb has the 0.vcf owner's card + } + return contactsSize; } catch (CursorWindowAllocationException e) { Log.e(TAG, "CursorWindowAllocationException while getting Contacts size"); } finally { @@ -551,7 +566,7 @@ public class BluetoothPbapVcardManager { final int composeAndSendPhonebookVcards(Operation op, final int startPoint, final int endPoint, final boolean vcardType21, String ownerVCard, int needSendBody, int pbSize, boolean ignorefilter, byte[] filter, byte[] vcardselector, String vcardselectorop, - boolean vcardselect) { + boolean vcardselect, boolean favorites) { if (startPoint < 1 || startPoint > endPoint) { Log.e(TAG, "internal error: startPoint or endPoint is not correct."); return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; @@ -562,9 +577,15 @@ public class BluetoothPbapVcardManager { Cursor contactIdCursor = new MatrixCursor(new String[]{ Phone.CONTACT_ID }); + + String selectionClause = null; + if (favorites) { + selectionClause = Phone.STARRED + " = 1"; + } + try { - contactCursor = mResolver.query(myUri, PHONES_CONTACTS_PROJECTION, null, null, - Phone.CONTACT_ID); + contactCursor = mResolver.query(myUri, PHONES_CONTACTS_PROJECTION, selectionClause, + null, Phone.CONTACT_ID); if (contactCursor != null) { contactIdCursor = ContactCursorFilter.filterByRange(contactCursor, startPoint, endPoint); -- cgit v1.2.3 From 1efbe39d3e325566462f19c3fd80ead92c8e5581 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Wed, 11 Dec 2019 17:04:47 -0800 Subject: HFP Client call status update When a call is terminating only inform telecom about its termination when there is an actual state change from the attached phone. Bug: 138753272 Test: start and end call, observe logs from HfpClientDeviceBlock that a closing call still in its previous state doesn't get destroyed prematurely. Change-Id: Ia5d105daa239f64b73c058d064c2e5181d862e4e (cherry picked from commit c1f83b71f34d101c023db154607c8ea50218214f) Merged-In: Ia5d105daa239f64b73c058d064c2e5181d862e4e Change-Id: I5d9d59c64bc8a79b397d2d4ef6feef1320345f42 --- src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java b/src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java index 05af73e08..b567371f1 100644 --- a/src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java +++ b/src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java @@ -224,7 +224,7 @@ public class HfpClientDeviceBlock { if (DBG) { Log.d(mTAG, "prevConn " + prevConn.isClosing() + " new call " + newCall.getState()); } - if (prevConn.isClosing() + if (prevConn.isClosing() && prevConn.getCall().getState() != newCall.getState() && newCall.getState() != BluetoothHeadsetClientCall.CALL_STATE_TERMINATED) { return true; } -- cgit v1.2.3 From c1c7176aa4b6785b2bd071f5caed0862faa14510 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Thu, 19 Dec 2019 15:19:14 -0800 Subject: Allow subsequent requests for media keys to replay the silent audio sample Bug: b/146010863 Test: Build, flash, interopt test with hard key events using automotive hardware. Change-Id: I4943c9f79011a1ee315fd3178af9171444dffa0b (cherry picked from commit f43be3b331a4a5575f2bbd4d631531c1f17e17fe) Merged-In: I4943c9f79011a1ee315fd3178af9171444dffa0b Change-Id: I8ef5ff15c3570fc98b80a1bfe8f750d7eaa5d32f --- .../bluetooth/a2dpsink/A2dpSinkStreamHandler.java | 47 +++++++++++----------- 1 file changed, 24 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java index 87ccfc873..5aa3cbbd5 100644 --- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java +++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java @@ -249,9 +249,6 @@ public class A2dpSinkStreamHandler extends Handler { if (mAudioFocus != AudioManager.AUDIOFOCUS_GAIN) { requestAudioFocus(); } - // On the off change mMediaPlayer errors out and dies, we want to make sure we retry this. - // This function immediately exits if we have a MediaPlayer object. - requestMediaKeyFocus(); } private synchronized int requestAudioFocus() { @@ -278,8 +275,11 @@ public class A2dpSinkStreamHandler extends Handler { } /** - * Creates a MediaPlayer that plays a silent audio sample so that MediaSessionService will be - * aware of the fact that Bluetooth is playing audio. + * Plays a silent audio sample so that MediaSessionService will be aware of the fact that + * Bluetooth is playing audio. + * + * Creates a new MediaPlayer if one does not already exist. Repeat calls to this function are + * safe and will result in the silent audio sample again. * * This allows the MediaSession in AVRCP Controller to be routed media key events, if we've * chosen to use it. @@ -287,25 +287,25 @@ public class A2dpSinkStreamHandler extends Handler { private synchronized void requestMediaKeyFocus() { if (DBG) Log.d(TAG, "requestMediaKeyFocus()"); - if (mMediaPlayer != null) return; - - AudioAttributes attrs = new AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_MEDIA) - .build(); - - mMediaPlayer = MediaPlayer.create(mContext, R.raw.silent, attrs, - mAudioManager.generateAudioSessionId()); if (mMediaPlayer == null) { - Log.e(TAG, "Failed to initialize media player. You may not get media key events"); - return; - } + AudioAttributes attrs = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .build(); + + mMediaPlayer = MediaPlayer.create(mContext, R.raw.silent, attrs, + mAudioManager.generateAudioSessionId()); + if (mMediaPlayer == null) { + Log.e(TAG, "Failed to initialize media player. You may not get media key events"); + return; + } - mMediaPlayer.setLooping(false); - mMediaPlayer.setOnErrorListener((mp, what, extra) -> { - Log.e(TAG, "Silent media player error: " + what + ", " + extra); - releaseMediaKeyFocus(); - return false; - }); + mMediaPlayer.setLooping(false); + mMediaPlayer.setOnErrorListener((mp, what, extra) -> { + Log.e(TAG, "Silent media player error: " + what + ", " + extra); + releaseMediaKeyFocus(); + return false; + }); + } mMediaPlayer.start(); BluetoothMediaBrowserService.setActive(true); @@ -314,7 +314,6 @@ public class A2dpSinkStreamHandler extends Handler { private synchronized void abandonAudioFocus() { if (DBG) Log.d(TAG, "abandonAudioFocus()"); stopFluorideStreaming(); - releaseMediaKeyFocus(); mAudioManager.abandonAudioFocus(mAudioFocusListener); mAudioFocus = AudioManager.AUDIOFOCUS_NONE; } @@ -337,9 +336,11 @@ public class A2dpSinkStreamHandler extends Handler { private void startFluorideStreaming() { mA2dpSinkService.informAudioFocusStateNative(STATE_FOCUS_GRANTED); mA2dpSinkService.informAudioTrackGainNative(1.0f); + requestMediaKeyFocus(); } private void stopFluorideStreaming() { + releaseMediaKeyFocus(); mA2dpSinkService.informAudioFocusStateNative(STATE_FOCUS_LOST); } -- cgit v1.2.3 From 46d12da05b1ed7420ce83525d95b847d0f69e9f2 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 30 Jan 2020 19:45:14 -0800 Subject: AVRCP Controller Media Controller not ready Flip two lines of code to ensure that the media controller is set before updating the available actions. Prevents a race condition where a media player responds to a media state change before the controller is available. Bug: 147469352 Test: Inspection (And many on off cycles) Change-Id: I2aad90c1028ab0ca81da09233e7fb49bf18d89c9 (cherry picked from commit 64d01153446c3751085a848a63e155285bb52805) Merged-In: I2aad90c1028ab0ca81da09233e7fb49bf18d89c9 Change-Id: Ieac01f46658d8e426666b8b316caf69d31fc4c5a --- .../android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index 8704b078e..c319364c1 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -296,9 +296,9 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void enter() { if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) { + BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED); - BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); } else { logD("ReEnteringConnected"); } -- cgit v1.2.3 From 0c5249af8f81b0b9aac51cc233914b1649c5f000 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Thu, 6 Feb 2020 15:36:50 -0800 Subject: Return an empty list when the requested node is not in the tree This change makes it so a request for the data in a folder that doesn't exist always returns promptly. Prior to this, all requests would hang indefinitely instead. This is only a stand in for a future solution to send a null result through the media framework instead. Unfortunately, our structure needs some tweaking to make that work. This will be fine for the interim. Bug: 148604797 Test: Build, flash, browse deep into a tree, switch players to invalid that parents of the node you're on, try browsing upward. See that we return an empty list for all missing nodes. Change-Id: Ifd5ed106d3b46377f0d7cd2d1e565cde45ab06f9 Merged-In: Ifd5ed106d3b46377f0d7cd2d1e565cde45ab06f9 --- src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java index 693b4a291..56684cd7c 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java @@ -172,9 +172,11 @@ public class AvrcpControllerService extends ProfileService { } } + // If we don't find a node in the tree then do not have any way to browse for the contents. + // Return an empty list instead. if (requestedNode == null) { if (DBG) Log.d(TAG, "Didn't find a node"); - return null; + return new ArrayList(0); } else { if (!requestedNode.isCached()) { if (DBG) Log.d(TAG, "node is not cached"); -- cgit v1.2.3 From 8cc90212a21c562e0cd2e4c751465ebb1c942c95 Mon Sep 17 00:00:00 2001 From: Andrew Cheng Date: Wed, 20 Nov 2019 09:29:31 -0800 Subject: Set Browsing bit to 0 in PbapSupportedFeatures PbapClient currently doesn't support browsing. This CL sets the bit to zero in PbapSupportedFeatures to accurately reflect the functionalities. Bug: 139624050 Test: Check the snoop/HCI logs. Look under the OBEX protocol header of the connection request packet for PBAP. Under Application Parameters > PbapSupportedFeatures, verify the Browsing bit is now 0. Change-Id: I2fcd959abf6aba178cd5fffb2f6e69cf1863fd89 Merged-In: I2fcd959abf6aba178cd5fffb2f6e69cf1863fd89 (cherry picked from commit 522d6cb1d42b80b64a51645105ec5bb0b561a5e9) --- src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java index 914b5b163..e2e35be1a 100644 --- a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java +++ b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java @@ -88,7 +88,7 @@ class PbapClientConnectionHandler extends Handler { private static final long PBAP_FILTER_NICKNAME = 1 << 23; private static final int PBAP_SUPPORTED_FEATURE = - PBAP_FEATURE_DEFAULT_IMAGE_FORMAT | PBAP_FEATURE_BROWSING | PBAP_FEATURE_DOWNLOADING; + PBAP_FEATURE_DEFAULT_IMAGE_FORMAT | PBAP_FEATURE_DOWNLOADING; private static final long PBAP_REQUESTED_FIELDS = PBAP_FILTER_VERSION | PBAP_FILTER_FN | PBAP_FILTER_N | PBAP_FILTER_PHOTO | PBAP_FILTER_ADR | PBAP_FILTER_EMAIL | PBAP_FILTER_TEL | PBAP_FILTER_NICKNAME; -- cgit v1.2.3 From b0add93bd09f8ad8383174080908b0e41026fcd2 Mon Sep 17 00:00:00 2001 From: Andrew Cheng Date: Fri, 15 Nov 2019 10:22:13 -0800 Subject: Delete call logs when calls are made without PBAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PbapClientConnectionHandler has code to remove calllogs when PBAP disconnects. However, if PBAP was never connected/enabled in the first place, and calls are made over HFP, these calllogs will not be removed when the device disconnects. This CL ensures calllogs are still removed in this case. Bug: 143723278 Test: (1) Verifying call logs are removed: Connect phone to carkit without enabling PBAP (e.g., don't allow permissions during pairing). Place a phone call over Bluetooth. Examine the calllog database directly: "sqlite3 calllog.db .dump” to verify call entry is present. Disconnect Bluetooth from the phone. Examine database again to verify entry is deleted. Repeat by disconnecting from the carkit, as well as disconnecting only HFP. (2) Verifying only HFP call logs are removed: Connect phone to carkit, with PBAP enabled this time. Place a phone call over HFP. Disable HFP profile, leaving PBAP enabled. Verify the HFP-entry is removed from calllog.db, while the PBAP-entries are still present. Change-Id: I305b323194f7732967db9a086d2c8a689798e64d Merged-In: I305b323194f7732967db9a086d2c8a689798e64d (cherry picked from commit f74a94dff84681645d2e519122b626bd5c4fe7fa) --- .../bluetooth/pbapclient/PbapClientService.java | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'src') diff --git a/src/com/android/bluetooth/pbapclient/PbapClientService.java b/src/com/android/bluetooth/pbapclient/PbapClientService.java index f150cddec..02b1e7a51 100644 --- a/src/com/android/bluetooth/pbapclient/PbapClientService.java +++ b/src/com/android/bluetooth/pbapclient/PbapClientService.java @@ -19,9 +19,11 @@ package com.android.bluetooth.pbapclient; import android.accounts.Account; import android.accounts.AccountManager; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothPbapClient; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -31,6 +33,7 @@ import android.util.Log; import com.android.bluetooth.R; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; +import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService; import com.android.bluetooth.sdp.SdpManager; import java.util.ArrayList; @@ -71,6 +74,9 @@ public class PbapClientService extends ProfileService { filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); // delay initial download until after the user is unlocked to add an account. filter.addAction(Intent.ACTION_USER_UNLOCKED); + // To remove call logs when PBAP was never connected while calls were made, + // we also listen for HFP to become disconnected. + filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); try { registerReceiver(mPbapBroadcastReceiver, filter); } catch (Exception e) { @@ -128,6 +134,21 @@ public class PbapClientService extends ProfileService { } } + private void removeHfpCallLog(String accountName, Context context) { + if (DBG) Log.d(TAG, "Removing call logs from " + accountName); + // Delete call logs belonging to accountName==BD_ADDR that also match + // component name "hfpclient". + ComponentName componentName = new ComponentName(context, HfpClientConnectionService.class); + String selectionFilter = CallLog.Calls.PHONE_ACCOUNT_ID + "=? AND " + + CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME + "=?"; + String[] selectionArgs = new String[]{accountName, componentName.flattenToString()}; + try { + getContentResolver().delete(CallLog.Calls.CONTENT_URI, selectionFilter, selectionArgs); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Call Logs could not be deleted, they may not exist yet."); + } + } + private void registerSdpRecord() { SdpManager sdpManager = SdpManager.getDefaultManager(); if (sdpManager == null) { @@ -171,6 +192,21 @@ public class PbapClientService extends ProfileService { for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) { stateMachine.resumeDownload(); } + } else if (action.equals(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED)) { + // PbapClientConnectionHandler has code to remove calllogs when PBAP disconnects. + // However, if PBAP was never connected/enabled in the first place, and calls are + // made over HFP, these calllogs will not be removed when the device disconnects. + // This code ensures callogs are still removed in this case. + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); + + if (newState == BluetoothProfile.STATE_DISCONNECTED) { + if (DBG) { + Log.d(TAG, "Received intent to disconnect HFP with " + device); + } + // HFP client stores entries in calllog.db by BD_ADDR and component name + removeHfpCallLog(device.getAddress(), context); + } } } } -- cgit v1.2.3 From e60a883032572c6cebd1676aa9c45be5f72799ff Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 19 Mar 2020 19:18:18 +0000 Subject: Revert "PBAP server, send favorite contacts" This reverts commit da1dc71cffb5fbb1e55186a20410e3d4f95246c3. Reason for revert: Not intended for this build. Change-Id: I3d8f51080e377d5dc7b67395a4979b8e5937a181 --- .../bluetooth/pbap/BluetoothPbapObexServer.java | 65 +++++----------------- .../bluetooth/pbap/BluetoothPbapService.java | 3 +- .../bluetooth/pbap/BluetoothPbapVcardManager.java | 37 +++--------- 3 files changed, 23 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java b/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java index 34ee96270..979becdca 100755 --- a/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java +++ b/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java @@ -97,7 +97,6 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { private static final String[] LEGAL_PATH = { "/telecom", "/telecom/pb", - "/telecom/fav", "/telecom/ich", "/telecom/och", "/telecom/mch", @@ -107,7 +106,6 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { @SuppressWarnings("unused") private static final String[] LEGAL_PATH_WITH_SIM = { "/telecom", "/telecom/pb", - "/telecom/fav", "/telecom/ich", "/telecom/och", "/telecom/mch", @@ -140,9 +138,6 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { // phone book private static final String PB = "pb"; - // favorites - private static final String FAV = "fav"; - private static final String TELECOM_PATH = "/telecom"; private static final String ICH_PATH = "/telecom/ich"; @@ -155,8 +150,6 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { private static final String PB_PATH = "/telecom/pb"; - private static final String FAV_PATH = "/telecom/fav"; - // type for list vcard objects private static final String TYPE_LISTING = "x-bt/vcard-listing"; @@ -219,8 +212,6 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { public static final int MISSED_CALL_HISTORY = 4; public static final int COMBINED_CALL_HISTORY = 5; - - public static final int FAVORITES = 6; } public BluetoothPbapObexServer(Handler callback, Context context, @@ -450,8 +441,6 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { if (mCurrentPath.equals(PB_PATH)) { appParamValue.needTag = ContentType.PHONEBOOK; - } else if (mCurrentPath.equals(FAV_PATH)) { - appParamValue.needTag = ContentType.FAVORITES; } else if (mCurrentPath.equals(ICH_PATH)) { appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY; } else if (mCurrentPath.equals(OCH_PATH)) { @@ -489,11 +478,6 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { if (D) { Log.v(TAG, "download phonebook request"); } - } else if (isNameMatchTarget(name, FAV)) { - appParamValue.needTag = ContentType.FAVORITES; - if (D) { - Log.v(TAG, "download favorites request"); - } } else if (isNameMatchTarget(name, ICH)) { appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY; appParamValue.callHistoryVersionCounter = @@ -767,8 +751,7 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { result.append(""); // Phonebook listing request - if ((appParamValue.needTag == ContentType.PHONEBOOK) - || (appParamValue.needTag == ContentType.FAVORITES)) { + if (appParamValue.needTag == ContentType.PHONEBOOK) { String type = ""; if (appParamValue.searchAttr.equals("0")) { type = "name"; @@ -965,7 +948,7 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { checkPbapFeatureSupport(mFolderVersionCounterbitMask); } boolean needSendPhonebookVersionCounters = false; - if (isNameMatchTarget(name, PB) || isNameMatchTarget(name, FAV)) { + if (isNameMatchTarget(name, PB)) { needSendPhonebookVersionCounters = checkPbapFeatureSupport(mFolderVersionCounterbitMask); } @@ -1209,12 +1192,11 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { if (appParamValue.needTag == 0) { Log.w(TAG, "wrong path!"); return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; - } else if ((appParamValue.needTag == ContentType.PHONEBOOK) - || (appParamValue.needTag == ContentType.FAVORITES)) { + } else if (appParamValue.needTag == ContentType.PHONEBOOK) { if (intIndex < 0 || intIndex >= size) { Log.w(TAG, "The requested vcard is not acceptable! name= " + name); return ResponseCodes.OBEX_HTTP_NOT_FOUND; - } else if ((intIndex == 0) && (appParamValue.needTag == ContentType.PHONEBOOK)) { + } else if (intIndex == 0) { // For PB_PATH, 0.vcf is the phone number of this phone. String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21, appParamValue.ignorefilter ? null : appParamValue.propertySelector); @@ -1270,49 +1252,30 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { int requestSize = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize; - /** - * startIndex (resp., lastIndex) corresponds to the index of the first (resp., last) - * vcard entry in the phonebook object. - * PBAP v1.2.3: only pb starts indexing at 0.vcf (owner card), the other phonebook - * objects (e.g., fav) start at 1.vcf. Additionally, the owner card is included in - * pb's pbSize. This means pbSize corresponds to the index of the last vcf in the fav - * phonebook object, but does not for the pb phonebook object. - */ - int startIndex = 1; - int lastIndex = pbSize; - if (appParamValue.needTag == BluetoothPbapObexServer.ContentType.PHONEBOOK) { - startIndex = 0; - lastIndex = pbSize - 1; - } - // [startPoint, endPoint] denote the range of vcf indices to send, inclusive. - int startPoint = startIndex + appParamValue.listStartOffset; - int endPoint = startPoint + requestSize - 1; - if (appParamValue.listStartOffset < 0 || startPoint > lastIndex) { + int startPoint = appParamValue.listStartOffset; + if (startPoint < 0 || startPoint >= pbSize) { Log.w(TAG, "listStartOffset is not correct! " + startPoint); return ResponseCodes.OBEX_HTTP_OK; } - if (endPoint > lastIndex) { - endPoint = lastIndex; - } // Limit the number of call log to CALLLOG_NUM_LIMIT - if ((appParamValue.needTag != BluetoothPbapObexServer.ContentType.PHONEBOOK) - && (appParamValue.needTag != BluetoothPbapObexServer.ContentType.FAVORITES)) { + if (appParamValue.needTag != BluetoothPbapObexServer.ContentType.PHONEBOOK) { if (requestSize > CALLLOG_NUM_LIMIT) { requestSize = CALLLOG_NUM_LIMIT; } } + int endPoint = startPoint + requestSize - 1; + if (endPoint > pbSize - 1) { + endPoint = pbSize - 1; + } if (D) { Log.d(TAG, "pullPhonebook(): requestSize=" + requestSize + " startPoint=" + startPoint + " endPoint=" + endPoint); } boolean vcard21 = appParamValue.vcard21; - boolean favorites = - (appParamValue.needTag == BluetoothPbapObexServer.ContentType.FAVORITES); - if ((appParamValue.needTag == BluetoothPbapObexServer.ContentType.PHONEBOOK) - || favorites) { + if (appParamValue.needTag == BluetoothPbapObexServer.ContentType.PHONEBOOK) { if (startPoint == 0) { String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21, appParamValue.ignorefilter ? null : appParamValue.propertySelector); @@ -1322,13 +1285,13 @@ public class BluetoothPbapObexServer extends ServerRequestHandler { return mVcardManager.composeAndSendPhonebookVcards(op, 1, endPoint, vcard21, ownerVcard, needSendBody, pbSize, appParamValue.ignorefilter, appParamValue.propertySelector, appParamValue.vCardSelector, - appParamValue.vCardSelectorOperator, mVcardSelector, favorites); + appParamValue.vCardSelectorOperator, mVcardSelector); } } else { return mVcardManager.composeAndSendPhonebookVcards(op, startPoint, endPoint, vcard21, null, needSendBody, pbSize, appParamValue.ignorefilter, appParamValue.propertySelector, appParamValue.vCardSelector, - appParamValue.vCardSelectorOperator, mVcardSelector, favorites); + appParamValue.vCardSelectorOperator, mVcardSelector); } } else { return mVcardManager.composeAndSendSelectedCallLogVcards(appParamValue.needTag, op, diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/src/com/android/bluetooth/pbap/BluetoothPbapService.java index 280186f8f..e7dba2a96 100644 --- a/src/com/android/bluetooth/pbap/BluetoothPbapService.java +++ b/src/com/android/bluetooth/pbap/BluetoothPbapService.java @@ -140,8 +140,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect private ObexServerSockets mServerSockets = null; private static final int SDP_PBAP_SERVER_VERSION = 0x0102; - // PBAP v1.2.3, Sec. 7.1.2: local phonebook and favorites - private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0009; + private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0001; private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F; /* PBAP will use Bluetooth notification ID from 1000000 (included) to 2000000 (excluded). diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java index 8801c16fb..5ba2b4b8b 100755 --- a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java +++ b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java @@ -153,8 +153,7 @@ public class BluetoothPbapVcardManager { int size; switch (type) { case BluetoothPbapObexServer.ContentType.PHONEBOOK: - case BluetoothPbapObexServer.ContentType.FAVORITES: - size = getContactsSize(type); + size = getContactsSize(); break; default: size = getCallHistorySize(type); @@ -166,30 +165,16 @@ public class BluetoothPbapVcardManager { return size; } - /** - * Returns the number of contacts (i.e., vcf) in a phonebook object. - * @param type specifies which phonebook object, e.g., pb, fav - * @return - */ - public final int getContactsSize(final int type) { + public final int getContactsSize() { final Uri myUri = DevicePolicyUtils.getEnterprisePhoneUri(mContext); Cursor contactCursor = null; - String selectionClause = null; - if (type == BluetoothPbapObexServer.ContentType.FAVORITES) { - selectionClause = Phone.STARRED + " = 1"; - } try { - contactCursor = mResolver.query(myUri, - new String[]{Phone.CONTACT_ID}, selectionClause, - null, Phone.CONTACT_ID); + contactCursor = mResolver.query(myUri, new String[]{Phone.CONTACT_ID}, null, null, + Phone.CONTACT_ID); if (contactCursor == null) { return 0; } - int contactsSize = getDistinctContactIdSize(contactCursor); - if (type == BluetoothPbapObexServer.ContentType.PHONEBOOK) { - contactsSize += 1; // pb has the 0.vcf owner's card - } - return contactsSize; + return getDistinctContactIdSize(contactCursor) + 1; // always has the 0.vcf } catch (CursorWindowAllocationException e) { Log.e(TAG, "CursorWindowAllocationException while getting Contacts size"); } finally { @@ -566,7 +551,7 @@ public class BluetoothPbapVcardManager { final int composeAndSendPhonebookVcards(Operation op, final int startPoint, final int endPoint, final boolean vcardType21, String ownerVCard, int needSendBody, int pbSize, boolean ignorefilter, byte[] filter, byte[] vcardselector, String vcardselectorop, - boolean vcardselect, boolean favorites) { + boolean vcardselect) { if (startPoint < 1 || startPoint > endPoint) { Log.e(TAG, "internal error: startPoint or endPoint is not correct."); return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; @@ -577,15 +562,9 @@ public class BluetoothPbapVcardManager { Cursor contactIdCursor = new MatrixCursor(new String[]{ Phone.CONTACT_ID }); - - String selectionClause = null; - if (favorites) { - selectionClause = Phone.STARRED + " = 1"; - } - try { - contactCursor = mResolver.query(myUri, PHONES_CONTACTS_PROJECTION, selectionClause, - null, Phone.CONTACT_ID); + contactCursor = mResolver.query(myUri, PHONES_CONTACTS_PROJECTION, null, null, + Phone.CONTACT_ID); if (contactCursor != null) { contactIdCursor = ContactCursorFilter.filterByRange(contactCursor, startPoint, endPoint); -- cgit v1.2.3