Loading android/app/jni/com_android_bluetooth_avrcp_controller.cpp +36 −2 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ static jmethodID method_createFromNativeFolderItem; static jmethodID method_createFromNativePlayerItem; static jmethodID method_handleChangeFolderRsp; static jmethodID method_handleSetBrowsedPlayerRsp; static jmethodID method_handleSetAddressedPlayerRsp; static jclass class_MediaBrowser_MediaItem; static jclass class_AvrcpPlayer; Loading Loading @@ -543,6 +544,17 @@ static void btavrcp_set_browsed_player_callback( sCallbacksObj, method_handleSetBrowsedPlayerRsp, (jint) num_items, (jint) depth); } static void btavrcp_set_addressed_player_callback( bt_bdaddr_t *bd_addr, uint8_t status) { ALOGI("%s status %d", __FUNCTION__, status); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; sCallbackEnv->CallVoidMethod( sCallbacksObj, method_handleSetAddressedPlayerRsp, (jint) status); } static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = { sizeof(sBluetoothAvrcpCallbacks), btavrcp_passthrough_response_callback, Loading @@ -559,7 +571,8 @@ static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = { btavrcp_play_status_changed_callback, btavrcp_get_folder_items_callback, btavrcp_change_path_callback, btavrcp_set_browsed_player_callback btavrcp_set_browsed_player_callback, btavrcp_set_addressed_player_callback }; static void classInitNative(JNIEnv* env, jclass clazz) { Loading Loading @@ -618,6 +631,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { env->GetMethodID(clazz, "handleChangeFolderRsp", "(I)V"); method_handleSetBrowsedPlayerRsp = env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "(II)V"); method_handleSetAddressedPlayerRsp = env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "(I)V"); ALOGI("%s: succeeds", __FUNCTION__); } Loading Loading @@ -931,7 +946,25 @@ static void setBrowsedPlayerNative(JNIEnv *env, jobject object, jbyteArray addre ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); if ((status = sBluetoothAvrcpInterface->set_browsed_player_cmd( (bt_bdaddr_t *) addr, (uint16_t) id)) != BT_STATUS_SUCCESS) { ALOGE("Failed sending changeFolderPathNative command, status: %d", status); ALOGE("Failed sending setBrowsedPlayerNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void setAddressedPlayerNative(JNIEnv *env, jobject object, jbyteArray address, jint id) { bt_status_t status; if (!sBluetoothAvrcpInterface) return; jbyte *addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); if ((status = sBluetoothAvrcpInterface->set_addressed_player_cmd( (bt_bdaddr_t *) addr, (uint16_t) id)) != BT_STATUS_SUCCESS) { ALOGE("Failed sending setAddressedPlayerNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } Loading Loading @@ -980,6 +1013,7 @@ static JNINativeMethod sMethods[] = { {"changeFolderPathNative", "([BB[B)V", (void *) changeFolderPathNative}, {"playItemNative", "([BB[BI)V", (void *) playItemNative}, {"setBrowsedPlayerNative", "([BI)V", (void *) setBrowsedPlayerNative}, {"setAddressedPlayerNative", "([BI)V", (void *) setAddressedPlayerNative}, }; int register_com_android_bluetooth_avrcp_controller(JNIEnv* env) Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +10 −0 Original line number Diff line number Diff line Loading @@ -1046,6 +1046,15 @@ public class AvrcpControllerService extends ProfileService { mAvrcpCtSm.sendMessage(msg); } private void handleSetAddressedPlayerRsp(int status) { if (DBG) { Log.d(TAG, "handleSetAddressedPlayerRsp status: " + status); } Message msg = mAvrcpCtSm.obtainMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ADDRESSED_PLAYER); mAvrcpCtSm.sendMessage(msg); } @Override public void dump(StringBuilder sb) { super.dump(sb); Loading Loading @@ -1110,4 +1119,5 @@ public class AvrcpControllerService extends ProfileService { native static void playItemNative( byte[] address, byte scope, byte[] uid, int uidCounter); native static void setBrowsedPlayerNative(byte[] address, int playerId); native static void setAddressedPlayerNative(byte[] address, int playerId); } android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +74 −10 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ class AvrcpControllerStateMachine extends StateMachine { static final int MESSAGE_PROCESS_GET_PLAYER_ITEMS = 111; static final int MESSAGE_PROCESS_FOLDER_PATH = 112; static final int MESSAGE_PROCESS_SET_BROWSED_PLAYER = 113; static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 114; // commands from A2DP sink static final int MESSAGE_STOP_METADATA_BROADCASTS = 201; Loading Loading @@ -114,6 +115,7 @@ class AvrcpControllerStateMachine extends StateMachine { private final State mDisconnected; private final State mConnected; private final SetBrowsedPlayer mSetBrowsedPlayer; private final SetAddresedPlayerAndPlayItem mSetAddrPlayer; private final ChangeFolderPath mChangeFolderPath; private final GetFolderList mGetFolderList; private final GetPlayerListing mGetPlayerListing; Loading @@ -133,11 +135,7 @@ class AvrcpControllerStateMachine extends StateMachine { private boolean mBroadcastMetadata = false; private int previousPercentageVol = -1; // New addressed player. private String mCurrentPlayer = null; // Depth from root of current browsing. This can be used to move to root directly. // Only valid if mCurrentPlayer != null. private int mBrowseDepth = 0; // Browse tree. Loading @@ -156,6 +154,7 @@ class AvrcpControllerStateMachine extends StateMachine { // Used to change folder path and fetch the new folder listing. mSetBrowsedPlayer = new SetBrowsedPlayer(); mSetAddrPlayer = new SetAddresedPlayerAndPlayItem(); mChangeFolderPath = new ChangeFolderPath(); mGetFolderList = new GetFolderList(); mGetPlayerListing = new GetPlayerListing(); Loading @@ -169,6 +168,7 @@ class AvrcpControllerStateMachine extends StateMachine { // only handle the messages that are relevant to the sub-action. Everything else should be // deferred so that once we transition to the mConnected we can process them hence. addState(mSetBrowsedPlayer, mConnected); addState(mSetAddrPlayer, mConnected); addState(mChangeFolderPath, mConnected); addState(mGetFolderList, mConnected); addState(mGetPlayerListing, mConnected); Loading Loading @@ -295,12 +295,33 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_FETCH_ATTR_AND_PLAY_ITEM: { int scope = msg.arg1; String uid = (String) msg.obj; String playItemUid = (String) msg.obj; BrowseTree.BrowseNode currBrPlayer = mBrowseTree.getCurrentBrowsedPlayer(); BrowseTree.BrowseNode currAddrPlayer = mBrowseTree.getCurrentAddressedPlayer(); if (DBG) { Log.d(TAG, "currBrPlayer " + currBrPlayer + " currAddrPlayer " + currAddrPlayer); } if (currBrPlayer == null || currBrPlayer.equals(currAddrPlayer)) { // String is encoded as a Hex String (mostly for display purposes) // hence convert this back to real byte string. // NOTE: It may be possible that sending play while the same item is // playing leads to reset of track. AvrcpControllerService.playItemNative( mRemoteDevice.getBluetoothAddress(), (byte) msg.arg1, AvrcpControllerService.hexStringToByteUID(uid), (int) 0); mRemoteDevice.getBluetoothAddress(), (byte) scope, AvrcpControllerService.hexStringToByteUID(playItemUid), (int) 0); } else { // Send out the request for setting addressed player. AvrcpControllerService.setAddressedPlayerNative( mRemoteDevice.getBluetoothAddress(), currBrPlayer.getPlayerID()); mSetAddrPlayer.setItemAndScope( currBrPlayer.getID(), playItemUid, scope); transitionTo(mSetAddrPlayer); } break; } Loading Loading @@ -759,6 +780,8 @@ class AvrcpControllerStateMachine extends StateMachine { transitionTo(mMoveToRoot); } mBrowseTree.setCurrentBrowsedFolder(mID); // Also set the browsed player here. mBrowseTree.setCurrentBrowsedPlayer(mID); break; case MESSAGE_INTERNAL_CMD_TIMEOUT: Loading @@ -774,6 +797,47 @@ class AvrcpControllerStateMachine extends StateMachine { } } class SetAddresedPlayerAndPlayItem extends CmdState { private String STATE_TAG = "AVRCPSM.SetAddresedPlayerAndPlayItem"; int mScope; String mPlayItemId; String mAddrPlayerId; public void setItemAndScope(String addrPlayerId, String playItemId, int scope) { mAddrPlayerId = addrPlayerId; mPlayItemId = playItemId; mScope = scope; } @Override public boolean processMessage(Message msg) { Log.d(STATE_TAG, "processMessage " + msg); switch (msg.what) { case MESSAGE_PROCESS_SET_ADDRESSED_PLAYER: // Set the new addressed player. mBrowseTree.setCurrentAddressedPlayer(mAddrPlayerId); // And now play the item. AvrcpControllerService.playItemNative( mRemoteDevice.getBluetoothAddress(), (byte) mScope, AvrcpControllerService.hexStringToByteUID(mPlayItemId), (int) 0); // Transition to connected state here. transitionTo(mConnected); break; case MESSAGE_INTERNAL_CMD_TIMEOUT: transitionTo(mConnected); break; default: Log.d(STATE_TAG, "deferring message " + msg + " to connected!"); deferMessage(msg); } return true; } } // Class template for commands. Each state should do the following: // (a) In enter() send a timeout message which could be tracked in the // processMessage() stage. Loading android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java +30 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ public class BrowseTree { // Static instance of Folder ID <-> Folder Instance (for navigation purposes) private final HashMap<String, BrowseNode> mBrowseMap = new HashMap<String, BrowseNode>(); private BrowseNode mCurrentBrowseNode; private BrowseNode mCurrentBrowsedPlayer; private BrowseNode mCurrentAddressedPlayer; BrowseTree() { } Loading Loading @@ -289,6 +291,34 @@ public class BrowseTree { return mCurrentBrowseNode; } synchronized boolean setCurrentBrowsedPlayer(String uid) { BrowseNode bn = findFolderByIDLocked(uid); if (bn == null) { Log.e(TAG, "Setting an unknown browsed player, ignoring bn " + uid); return false; } mCurrentBrowsedPlayer = bn; return true; } synchronized BrowseNode getCurrentBrowsedPlayer() { return mCurrentBrowsedPlayer; } synchronized boolean setCurrentAddressedPlayer(String uid) { BrowseNode bn = findFolderByIDLocked(uid); if (bn == null) { Log.e(TAG, "Setting an unknown addressed player, ignoring bn " + uid); return false; } mCurrentAddressedPlayer = bn; return true; } synchronized BrowseNode getCurrentAddressedPlayer() { return mCurrentAddressedPlayer; } @Override public String toString() { return mBrowseMap.toString(); Loading Loading
android/app/jni/com_android_bluetooth_avrcp_controller.cpp +36 −2 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ static jmethodID method_createFromNativeFolderItem; static jmethodID method_createFromNativePlayerItem; static jmethodID method_handleChangeFolderRsp; static jmethodID method_handleSetBrowsedPlayerRsp; static jmethodID method_handleSetAddressedPlayerRsp; static jclass class_MediaBrowser_MediaItem; static jclass class_AvrcpPlayer; Loading Loading @@ -543,6 +544,17 @@ static void btavrcp_set_browsed_player_callback( sCallbacksObj, method_handleSetBrowsedPlayerRsp, (jint) num_items, (jint) depth); } static void btavrcp_set_addressed_player_callback( bt_bdaddr_t *bd_addr, uint8_t status) { ALOGI("%s status %d", __FUNCTION__, status); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; sCallbackEnv->CallVoidMethod( sCallbacksObj, method_handleSetAddressedPlayerRsp, (jint) status); } static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = { sizeof(sBluetoothAvrcpCallbacks), btavrcp_passthrough_response_callback, Loading @@ -559,7 +571,8 @@ static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = { btavrcp_play_status_changed_callback, btavrcp_get_folder_items_callback, btavrcp_change_path_callback, btavrcp_set_browsed_player_callback btavrcp_set_browsed_player_callback, btavrcp_set_addressed_player_callback }; static void classInitNative(JNIEnv* env, jclass clazz) { Loading Loading @@ -618,6 +631,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { env->GetMethodID(clazz, "handleChangeFolderRsp", "(I)V"); method_handleSetBrowsedPlayerRsp = env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "(II)V"); method_handleSetAddressedPlayerRsp = env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "(I)V"); ALOGI("%s: succeeds", __FUNCTION__); } Loading Loading @@ -931,7 +946,25 @@ static void setBrowsedPlayerNative(JNIEnv *env, jobject object, jbyteArray addre ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); if ((status = sBluetoothAvrcpInterface->set_browsed_player_cmd( (bt_bdaddr_t *) addr, (uint16_t) id)) != BT_STATUS_SUCCESS) { ALOGE("Failed sending changeFolderPathNative command, status: %d", status); ALOGE("Failed sending setBrowsedPlayerNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void setAddressedPlayerNative(JNIEnv *env, jobject object, jbyteArray address, jint id) { bt_status_t status; if (!sBluetoothAvrcpInterface) return; jbyte *addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); if ((status = sBluetoothAvrcpInterface->set_addressed_player_cmd( (bt_bdaddr_t *) addr, (uint16_t) id)) != BT_STATUS_SUCCESS) { ALOGE("Failed sending setAddressedPlayerNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } Loading Loading @@ -980,6 +1013,7 @@ static JNINativeMethod sMethods[] = { {"changeFolderPathNative", "([BB[B)V", (void *) changeFolderPathNative}, {"playItemNative", "([BB[BI)V", (void *) playItemNative}, {"setBrowsedPlayerNative", "([BI)V", (void *) setBrowsedPlayerNative}, {"setAddressedPlayerNative", "([BI)V", (void *) setAddressedPlayerNative}, }; int register_com_android_bluetooth_avrcp_controller(JNIEnv* env) Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +10 −0 Original line number Diff line number Diff line Loading @@ -1046,6 +1046,15 @@ public class AvrcpControllerService extends ProfileService { mAvrcpCtSm.sendMessage(msg); } private void handleSetAddressedPlayerRsp(int status) { if (DBG) { Log.d(TAG, "handleSetAddressedPlayerRsp status: " + status); } Message msg = mAvrcpCtSm.obtainMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ADDRESSED_PLAYER); mAvrcpCtSm.sendMessage(msg); } @Override public void dump(StringBuilder sb) { super.dump(sb); Loading Loading @@ -1110,4 +1119,5 @@ public class AvrcpControllerService extends ProfileService { native static void playItemNative( byte[] address, byte scope, byte[] uid, int uidCounter); native static void setBrowsedPlayerNative(byte[] address, int playerId); native static void setAddressedPlayerNative(byte[] address, int playerId); }
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +74 −10 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ class AvrcpControllerStateMachine extends StateMachine { static final int MESSAGE_PROCESS_GET_PLAYER_ITEMS = 111; static final int MESSAGE_PROCESS_FOLDER_PATH = 112; static final int MESSAGE_PROCESS_SET_BROWSED_PLAYER = 113; static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 114; // commands from A2DP sink static final int MESSAGE_STOP_METADATA_BROADCASTS = 201; Loading Loading @@ -114,6 +115,7 @@ class AvrcpControllerStateMachine extends StateMachine { private final State mDisconnected; private final State mConnected; private final SetBrowsedPlayer mSetBrowsedPlayer; private final SetAddresedPlayerAndPlayItem mSetAddrPlayer; private final ChangeFolderPath mChangeFolderPath; private final GetFolderList mGetFolderList; private final GetPlayerListing mGetPlayerListing; Loading @@ -133,11 +135,7 @@ class AvrcpControllerStateMachine extends StateMachine { private boolean mBroadcastMetadata = false; private int previousPercentageVol = -1; // New addressed player. private String mCurrentPlayer = null; // Depth from root of current browsing. This can be used to move to root directly. // Only valid if mCurrentPlayer != null. private int mBrowseDepth = 0; // Browse tree. Loading @@ -156,6 +154,7 @@ class AvrcpControllerStateMachine extends StateMachine { // Used to change folder path and fetch the new folder listing. mSetBrowsedPlayer = new SetBrowsedPlayer(); mSetAddrPlayer = new SetAddresedPlayerAndPlayItem(); mChangeFolderPath = new ChangeFolderPath(); mGetFolderList = new GetFolderList(); mGetPlayerListing = new GetPlayerListing(); Loading @@ -169,6 +168,7 @@ class AvrcpControllerStateMachine extends StateMachine { // only handle the messages that are relevant to the sub-action. Everything else should be // deferred so that once we transition to the mConnected we can process them hence. addState(mSetBrowsedPlayer, mConnected); addState(mSetAddrPlayer, mConnected); addState(mChangeFolderPath, mConnected); addState(mGetFolderList, mConnected); addState(mGetPlayerListing, mConnected); Loading Loading @@ -295,12 +295,33 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_FETCH_ATTR_AND_PLAY_ITEM: { int scope = msg.arg1; String uid = (String) msg.obj; String playItemUid = (String) msg.obj; BrowseTree.BrowseNode currBrPlayer = mBrowseTree.getCurrentBrowsedPlayer(); BrowseTree.BrowseNode currAddrPlayer = mBrowseTree.getCurrentAddressedPlayer(); if (DBG) { Log.d(TAG, "currBrPlayer " + currBrPlayer + " currAddrPlayer " + currAddrPlayer); } if (currBrPlayer == null || currBrPlayer.equals(currAddrPlayer)) { // String is encoded as a Hex String (mostly for display purposes) // hence convert this back to real byte string. // NOTE: It may be possible that sending play while the same item is // playing leads to reset of track. AvrcpControllerService.playItemNative( mRemoteDevice.getBluetoothAddress(), (byte) msg.arg1, AvrcpControllerService.hexStringToByteUID(uid), (int) 0); mRemoteDevice.getBluetoothAddress(), (byte) scope, AvrcpControllerService.hexStringToByteUID(playItemUid), (int) 0); } else { // Send out the request for setting addressed player. AvrcpControllerService.setAddressedPlayerNative( mRemoteDevice.getBluetoothAddress(), currBrPlayer.getPlayerID()); mSetAddrPlayer.setItemAndScope( currBrPlayer.getID(), playItemUid, scope); transitionTo(mSetAddrPlayer); } break; } Loading Loading @@ -759,6 +780,8 @@ class AvrcpControllerStateMachine extends StateMachine { transitionTo(mMoveToRoot); } mBrowseTree.setCurrentBrowsedFolder(mID); // Also set the browsed player here. mBrowseTree.setCurrentBrowsedPlayer(mID); break; case MESSAGE_INTERNAL_CMD_TIMEOUT: Loading @@ -774,6 +797,47 @@ class AvrcpControllerStateMachine extends StateMachine { } } class SetAddresedPlayerAndPlayItem extends CmdState { private String STATE_TAG = "AVRCPSM.SetAddresedPlayerAndPlayItem"; int mScope; String mPlayItemId; String mAddrPlayerId; public void setItemAndScope(String addrPlayerId, String playItemId, int scope) { mAddrPlayerId = addrPlayerId; mPlayItemId = playItemId; mScope = scope; } @Override public boolean processMessage(Message msg) { Log.d(STATE_TAG, "processMessage " + msg); switch (msg.what) { case MESSAGE_PROCESS_SET_ADDRESSED_PLAYER: // Set the new addressed player. mBrowseTree.setCurrentAddressedPlayer(mAddrPlayerId); // And now play the item. AvrcpControllerService.playItemNative( mRemoteDevice.getBluetoothAddress(), (byte) mScope, AvrcpControllerService.hexStringToByteUID(mPlayItemId), (int) 0); // Transition to connected state here. transitionTo(mConnected); break; case MESSAGE_INTERNAL_CMD_TIMEOUT: transitionTo(mConnected); break; default: Log.d(STATE_TAG, "deferring message " + msg + " to connected!"); deferMessage(msg); } return true; } } // Class template for commands. Each state should do the following: // (a) In enter() send a timeout message which could be tracked in the // processMessage() stage. Loading
android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java +30 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ public class BrowseTree { // Static instance of Folder ID <-> Folder Instance (for navigation purposes) private final HashMap<String, BrowseNode> mBrowseMap = new HashMap<String, BrowseNode>(); private BrowseNode mCurrentBrowseNode; private BrowseNode mCurrentBrowsedPlayer; private BrowseNode mCurrentAddressedPlayer; BrowseTree() { } Loading Loading @@ -289,6 +291,34 @@ public class BrowseTree { return mCurrentBrowseNode; } synchronized boolean setCurrentBrowsedPlayer(String uid) { BrowseNode bn = findFolderByIDLocked(uid); if (bn == null) { Log.e(TAG, "Setting an unknown browsed player, ignoring bn " + uid); return false; } mCurrentBrowsedPlayer = bn; return true; } synchronized BrowseNode getCurrentBrowsedPlayer() { return mCurrentBrowsedPlayer; } synchronized boolean setCurrentAddressedPlayer(String uid) { BrowseNode bn = findFolderByIDLocked(uid); if (bn == null) { Log.e(TAG, "Setting an unknown addressed player, ignoring bn " + uid); return false; } mCurrentAddressedPlayer = bn; return true; } synchronized BrowseNode getCurrentAddressedPlayer() { return mCurrentAddressedPlayer; } @Override public String toString() { return mBrowseMap.toString(); Loading