Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java +3 −6 Original line number Diff line number Diff line Loading @@ -45,8 +45,7 @@ import java.lang.ref.WeakReference; * disconnection has occurred, please create a new client. */ public class AvrcpBipClient { private static final String TAG = "AvrcpBipClient"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final String TAG = AvrcpBipClient.class.getSimpleName(); // AVRCP Controller BIP Image Initiator/Cover Art UUID - AVRCP 1.6 Section 5.14.2.1 private static final byte[] BLUETOOTH_UUID_AVRCP_COVER_ART = new byte[] { Loading Loading @@ -466,10 +465,8 @@ public class AvrcpBipClient { * Print to debug if debug is enabled for this class */ private void debug(String msg) { if (DBG) { Log.d(TAG, "[" + mDevice + "] " + msg); } } /** * Print to warn Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterface.java +56 −98 Original line number Diff line number Diff line Loading @@ -30,8 +30,6 @@ import java.util.UUID; /** Provides Bluetooth AVRCP Controller native interface for the AVRCP Controller service */ public class AvrcpControllerNativeInterface { static final String TAG = AvrcpControllerNativeInterface.class.getSimpleName(); static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); private AvrcpControllerService mAvrcpController; Loading Loading @@ -123,14 +121,12 @@ public class AvrcpControllerNativeInterface { void onConnectionStateChanged( boolean remoteControlConnected, boolean browsingConnected, byte[] address) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d( TAG, "onConnectionStateChanged: " + (" remoteControlConnected=" + remoteControlConnected) + (" browsingConnected=" + browsingConnected) + (" device=" + device)); } mAvrcpController.onConnectionStateChanged( remoteControlConnected, browsingConnected, device); Loading @@ -140,9 +136,7 @@ public class AvrcpControllerNativeInterface { @VisibleForTesting void getRcPsm(byte[] address, int psm) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "getRcPsm: device=" + device + " psm=" + psm); } mAvrcpController.getRcPsm(device, psm); } Loading @@ -150,9 +144,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI to report remote Player's capabilities void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handlePlayerAppSetting: device=" + device + " rspLen=" + rspLen); } mAvrcpController.handlePlayerAppSetting(device, playerAttribRsp, rspLen); } Loading @@ -160,9 +152,7 @@ public class AvrcpControllerNativeInterface { @VisibleForTesting void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onPlayerAppSettingChanged: device=" + device); } mAvrcpController.onPlayerAppSettingChanged(device, playerAttribRsp, rspLen); } Loading @@ -170,9 +160,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI when remote wants to set absolute volume. void handleSetAbsVolume(byte[] address, byte absVol, byte label) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleSetAbsVolume: device=" + device); } mAvrcpController.handleSetAbsVolume(device, absVol, label); } Loading @@ -180,9 +168,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI when remote wants to receive absolute volume notifications. void handleRegisterNotificationAbsVol(byte[] address, byte label) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleRegisterNotificationAbsVol: device=" + device); } mAvrcpController.handleRegisterNotificationAbsVol(device, label); } Loading @@ -190,9 +176,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI when a track changes and local AvrcpController is registered for updates. void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onTrackChanged: device=" + device); } mAvrcpController.onTrackChanged(device, numAttributes, attributes, attribVals); } Loading @@ -200,9 +184,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI periodically based upon timer to update play position void onPlayPositionChanged(byte[] address, int songLen, int currSongPosition) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onPlayPositionChanged: device=" + device + " pos=" + currSongPosition); } mAvrcpController.onPlayPositionChanged(device, songLen, currSongPosition); } Loading @@ -210,9 +192,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI on changes of play status void onPlayStatusChanged(byte[] address, byte playStatus) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onPlayStatusChanged: device=" + device + " playStatus=" + playStatus); } mAvrcpController.onPlayStatusChanged(device, toPlaybackStateFromJni(playStatus)); } Loading @@ -220,27 +200,23 @@ public class AvrcpControllerNativeInterface { // Browsing related JNI callbacks. void handleGetFolderItemsRsp(byte[] address, int status, AvrcpItem[] items) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d( TAG, "handleGetFolderItemsRsp:" + (" device=" + device) + (" status=" + status) + (" NumberOfItems=" + items.length)); } mAvrcpController.handleGetFolderItemsRsp(device, status, items); } void handleGetPlayerItemsRsp(byte[] address, AvrcpPlayer[] items) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d( TAG, "handleGetFolderItemsRsp:" + (" device=" + device) + (" NumberOfItems=" + items.length)); } mAvrcpController.handleGetPlayerItemsRsp(device, Arrays.asList(items)); } Loading @@ -249,7 +225,6 @@ public class AvrcpControllerNativeInterface { static AvrcpItem createFromNativeMediaItem( byte[] address, long uid, int type, String name, int[] attrIds, String[] attrVals) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (VDBG) { Log.d( TAG, "createFromNativeMediaItem:" Loading @@ -259,7 +234,6 @@ public class AvrcpControllerNativeInterface { + (" name=" + name) + (" attrids=" + Arrays.toString(attrIds)) + (" attrVals=" + Arrays.toString(attrVals))); } return new AvrcpItem.Builder() .fromAvrcpAttributeArray(attrIds, attrVals) Loading @@ -275,7 +249,6 @@ public class AvrcpControllerNativeInterface { static AvrcpItem createFromNativeFolderItem( byte[] address, long uid, int type, String name, int playable) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (VDBG) { Log.d( TAG, "createFromNativeFolderItem:" Loading @@ -284,7 +257,6 @@ public class AvrcpControllerNativeInterface { + (" type=" + type) + (" name=" + name) + (" playable=" + playable)); } return new AvrcpItem.Builder() .setDevice(device) Loading @@ -306,7 +278,6 @@ public class AvrcpControllerNativeInterface { int playStatus, int playerType) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (VDBG) { Log.d( TAG, "createFromNativePlayerItem:" Loading @@ -315,7 +286,6 @@ public class AvrcpControllerNativeInterface { + (" transportFlags=" + Arrays.toString(transportFlags)) + (" playStatus=" + playStatus) + (" playerType=" + playerType)); } return new AvrcpPlayer.Builder() .setDevice(device) Loading @@ -328,53 +298,41 @@ public class AvrcpControllerNativeInterface { void handleChangeFolderRsp(byte[] address, int count) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleChangeFolderRsp: device=" + device + " count=" + count); } mAvrcpController.handleChangeFolderRsp(device, count); } void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleSetBrowsedPlayerRsp: device=" + device + " depth=" + depth); } mAvrcpController.handleSetBrowsedPlayerRsp(device, items, depth); } void handleSetAddressedPlayerRsp(byte[] address, int status) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleSetAddressedPlayerRsp device=" + device + " status=" + status); } mAvrcpController.handleSetAddressedPlayerRsp(device, status); } void handleAddressedPlayerChanged(byte[] address, int id) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleAddressedPlayerChanged: device=" + device + " id=" + id); } mAvrcpController.handleAddressedPlayerChanged(device, id); } void handleNowPlayingContentChanged(byte[] address) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleNowPlayingContentChanged: device=" + device); } mAvrcpController.handleNowPlayingContentChanged(device); } void onAvailablePlayerChanged(byte[] address) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onAvailablePlayerChanged: device=" + device); } mAvrcpController.onAvailablePlayerChanged(device); } Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +20 −34 Original line number Diff line number Diff line Loading @@ -54,8 +54,6 @@ import java.util.concurrent.ConcurrentHashMap; */ public class AvrcpControllerService extends ProfileService { static final String TAG = AvrcpControllerService.class.getSimpleName(); static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); static final int MAXIMUM_CONNECTED_DEVICES = 5; Loading Loading @@ -119,10 +117,8 @@ public class AvrcpControllerService extends ProfileService { @Override public void onImageDownloadComplete(BluetoothDevice device, AvrcpCoverArtManager.DownloadEvent event) { if (DBG) { Log.d(TAG, "Image downloaded [device: " + device + ", uuid: " + event.getUuid() + ", uri: " + event.getUri()); } AvrcpControllerStateMachine stateMachine = getStateMachine(device); if (stateMachine == null) { Log.e(TAG, "No state machine found for device " + device); Loading Loading @@ -207,9 +203,7 @@ public class AvrcpControllerService extends ProfileService { */ @VisibleForTesting boolean setActiveDevice(BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice(device=" + device + ")"); } A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { Log.w(TAG, "setActiveDevice(device=" + device + "): A2DP Sink not available"); Loading Loading @@ -274,7 +268,7 @@ public class AvrcpControllerService extends ProfileService { } void playItem(String parentMediaId) { if (DBG) Log.d(TAG, "playItem(" + parentMediaId + ")"); Log.d(TAG, "playItem(" + parentMediaId + ")"); // Check if the requestedNode is a player rather than a song BrowseTree.BrowseNode requestedNode = sBrowseTree.findBrowseNodeByID(parentMediaId); if (requestedNode == null) { Loading @@ -282,7 +276,7 @@ public class AvrcpControllerService extends ProfileService { // Check each state machine for the song and then play it requestedNode = stateMachine.findNode(parentMediaId); if (requestedNode != null) { if (DBG) Log.d(TAG, "Found a node"); Log.d(TAG, "Found a node, node=" + requestedNode); BluetoothDevice device = stateMachine.getDevice(); if (device != null) { setActiveDevice(device); Loading @@ -304,7 +298,7 @@ public class AvrcpControllerService extends ProfileService { * must be performed. */ public synchronized BrowseResult getContents(String parentMediaId) { if (DBG) Log.d(TAG, "getContents(" + parentMediaId + ")"); Log.d(TAG, "getContents(" + parentMediaId + ")"); BrowseTree.BrowseNode requestedNode = sBrowseTree.findBrowseNodeByID(parentMediaId); if (requestedNode == null) { Loading @@ -316,12 +310,10 @@ public class AvrcpControllerService extends ProfileService { } } if (DBG) { Log.d(TAG, "getContents(" + parentMediaId + "): " + (requestedNode == null ? "Failed to find node" : "node=" + requestedNode + ", device=" + requestedNode.getDevice())); } // 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. Loading @@ -340,7 +332,7 @@ public class AvrcpControllerService extends ProfileService { List<MediaItem> contents = requestedNode.getContents(); if (!requestedNode.isCached()) { if (DBG) Log.d(TAG, "getContents(" + parentMediaId + "): node download pending"); Log.d(TAG, "getContents(" + parentMediaId + "): node download pending"); refreshContents(requestedNode); /* Ongoing downloads can have partial results and we want to make sure they get sent * to the client. If a download gets kicked off as a result of this request, the Loading @@ -348,10 +340,8 @@ public class AvrcpControllerService extends ProfileService { */ return new BrowseResult(contents, BrowseResult.DOWNLOAD_PENDING); } if (DBG) { Log.d(TAG, "getContents(" + parentMediaId + "): return node, contents=" + requestedNode.getContents()); } return new BrowseResult(contents, BrowseResult.SUCCESS); } Loading Loading @@ -526,9 +516,7 @@ public class AvrcpControllerService extends ProfileService { * player to stop and start playing. */ public void onAudioFocusStateChanged(int state) { if (DBG) { Log.d(TAG, "onAudioFocusStateChanged(state=" + state + ")"); } // Make sure the active device isn't changed while we're processing the event so play/pause // commands get routed to the correct device Loading Loading @@ -639,9 +627,10 @@ public class AvrcpControllerService extends ProfileService { // Browsing related JNI callbacks. void handleGetFolderItemsRsp(BluetoothDevice device, int status, AvrcpItem[] items) { Log.d(TAG, "handleGetFolderItemsRsp(device=" + device + ", status=" + status); List<AvrcpItem> itemsList = new ArrayList<>(); for (AvrcpItem item : items) { if (VDBG) Log.d(TAG, item.toString()); Log.v(TAG, "handleGetFolderItemsRsp(device=" + device + "): item=" + item.toString()); if (mCoverArtManager != null) { String handle = item.getCoverArtHandle(); if (handle != null) { Loading Loading @@ -718,9 +707,7 @@ public class AvrcpControllerService extends ProfileService { * @return true if disconnect is successful, false otherwise. */ public synchronized boolean disconnect(BluetoothDevice device) { if (DBG) { Log.d(TAG, "disconnect(device=" + device + ")"); } AvrcpControllerStateMachine stateMachine = mDeviceStateMap.get(device); // a map state machine instance doesn't exist. maybe it is already gone? if (stateMachine == null) { Loading Loading @@ -786,21 +773,20 @@ public class AvrcpControllerService extends ProfileService { } List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates" + Arrays.toString(states)); Log.d(TAG, "getDevicesMatchingConnectionStates(states=" + Arrays.toString(states) + ")"); List<BluetoothDevice> deviceList = new ArrayList<>(); BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); int connectionState; for (BluetoothDevice device : bondedDevices) { connectionState = getConnectionState(device); if (DBG) Log.d(TAG, "Device: " + device + "State: " + connectionState); for (int i = 0; i < states.length; i++) { if (connectionState == states[i]) { deviceList.add(device); } } } if (DBG) Log.d(TAG, deviceList.toString()); Log.d(TAG, "GetDevicesDone"); Log.d(TAG, "getDevicesMatchingConnectionStates(states=" + Arrays.toString(states) + "): Found " + deviceList.toString()); return deviceList; } Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +94 −81 File changed.Preview size limit exceeded, changes collapsed. Show changes android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerUtils.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.avrcpcontroller; import android.support.v4.media.session.PlaybackStateCompat; /** * A package global set of utilities for the AVRCP Controller implementation to leverage */ public final class AvrcpControllerUtils { /** * Convert an AVRCP Passthrough command id to a human readable version of the key */ public static String passThruIdToString(int id) { StringBuilder sb = new StringBuilder(); switch (id) { case AvrcpControllerService.PASS_THRU_CMD_ID_PLAY: sb.append("PLAY"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE: sb.append("PAUSE"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_VOL_UP: sb.append("VOL_UP"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_VOL_DOWN: sb.append("VOL_DOWN"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_STOP: sb.append("STOP"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_FF: sb.append("FF"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_REWIND: sb.append("REWIND"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD: sb.append("FORWARD"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD: sb.append("BACKWARD"); break; default: sb.append("UNKNOWN_CMD_" + id); break; } sb.append(" (" + id + ")"); return sb.toString(); } /** * Convert an entire PlaybackStateCompat to a string that contains human readable states */ public static String playbackStateCompatToString(PlaybackStateCompat playbackState) { if (playbackState == null) { return null; } StringBuilder sb = new StringBuilder("PlaybackState {"); sb.append("state=").append(playbackStateToString(playbackState.getState())); sb.append(", position=").append(playbackState.getPosition()); sb.append(", buffered position=").append(playbackState.getBufferedPosition()); sb.append(", speed=").append(playbackState.getPlaybackSpeed()); sb.append(", updated=").append(playbackState.getLastPositionUpdateTime()); sb.append(", actions=").append(playbackState.getActions()); sb.append(", error code=").append(playbackState.getErrorCode()); sb.append(", error message=").append(playbackState.getErrorMessage()); sb.append(", custom actions=").append(playbackState.getCustomActions()); sb.append(", active item id=").append(playbackState.getActiveQueueItemId()); sb.append("}"); return sb.toString(); } /** * Convert a playback state constant to a human readable version of the state */ public static String playbackStateToString(int playbackState) { StringBuilder sb = new StringBuilder(); switch (playbackState) { case PlaybackStateCompat.STATE_NONE: sb.append("STATE_NONE"); break; case PlaybackStateCompat.STATE_STOPPED: sb.append("STATE_STOPPED"); break; case PlaybackStateCompat.STATE_PAUSED: sb.append("STATE_PAUSED"); break; case PlaybackStateCompat.STATE_PLAYING: sb.append("STATE_PLAYING"); break; case PlaybackStateCompat.STATE_FAST_FORWARDING: sb.append("STATE_FAST_FORWARDING"); break; case PlaybackStateCompat.STATE_REWINDING: sb.append("STATE_REWINDING"); break; case PlaybackStateCompat.STATE_BUFFERING: sb.append("STATE_BUFFERING"); break; case PlaybackStateCompat.STATE_ERROR: sb.append("STATE_ERROR"); break; case PlaybackStateCompat.STATE_CONNECTING: sb.append("STATE_CONNECTING"); break; case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: sb.append("STATE_SKIPPING_TO_PREVIOUS"); break; case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: sb.append("STATE_SKIPPING_TO_NEXT"); break; case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM: sb.append("STATE_SKIPPING_TO_QUEUE_ITEM"); break; default: sb.append("UNKNOWN_PLAYBACK_STATE"); break; } sb.append(" (" + playbackState + ")"); return sb.toString(); } } Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java +3 −6 Original line number Diff line number Diff line Loading @@ -45,8 +45,7 @@ import java.lang.ref.WeakReference; * disconnection has occurred, please create a new client. */ public class AvrcpBipClient { private static final String TAG = "AvrcpBipClient"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final String TAG = AvrcpBipClient.class.getSimpleName(); // AVRCP Controller BIP Image Initiator/Cover Art UUID - AVRCP 1.6 Section 5.14.2.1 private static final byte[] BLUETOOTH_UUID_AVRCP_COVER_ART = new byte[] { Loading Loading @@ -466,10 +465,8 @@ public class AvrcpBipClient { * Print to debug if debug is enabled for this class */ private void debug(String msg) { if (DBG) { Log.d(TAG, "[" + mDevice + "] " + msg); } } /** * Print to warn Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterface.java +56 −98 Original line number Diff line number Diff line Loading @@ -30,8 +30,6 @@ import java.util.UUID; /** Provides Bluetooth AVRCP Controller native interface for the AVRCP Controller service */ public class AvrcpControllerNativeInterface { static final String TAG = AvrcpControllerNativeInterface.class.getSimpleName(); static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); private AvrcpControllerService mAvrcpController; Loading Loading @@ -123,14 +121,12 @@ public class AvrcpControllerNativeInterface { void onConnectionStateChanged( boolean remoteControlConnected, boolean browsingConnected, byte[] address) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d( TAG, "onConnectionStateChanged: " + (" remoteControlConnected=" + remoteControlConnected) + (" browsingConnected=" + browsingConnected) + (" device=" + device)); } mAvrcpController.onConnectionStateChanged( remoteControlConnected, browsingConnected, device); Loading @@ -140,9 +136,7 @@ public class AvrcpControllerNativeInterface { @VisibleForTesting void getRcPsm(byte[] address, int psm) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "getRcPsm: device=" + device + " psm=" + psm); } mAvrcpController.getRcPsm(device, psm); } Loading @@ -150,9 +144,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI to report remote Player's capabilities void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handlePlayerAppSetting: device=" + device + " rspLen=" + rspLen); } mAvrcpController.handlePlayerAppSetting(device, playerAttribRsp, rspLen); } Loading @@ -160,9 +152,7 @@ public class AvrcpControllerNativeInterface { @VisibleForTesting void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onPlayerAppSettingChanged: device=" + device); } mAvrcpController.onPlayerAppSettingChanged(device, playerAttribRsp, rspLen); } Loading @@ -170,9 +160,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI when remote wants to set absolute volume. void handleSetAbsVolume(byte[] address, byte absVol, byte label) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleSetAbsVolume: device=" + device); } mAvrcpController.handleSetAbsVolume(device, absVol, label); } Loading @@ -180,9 +168,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI when remote wants to receive absolute volume notifications. void handleRegisterNotificationAbsVol(byte[] address, byte label) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleRegisterNotificationAbsVol: device=" + device); } mAvrcpController.handleRegisterNotificationAbsVol(device, label); } Loading @@ -190,9 +176,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI when a track changes and local AvrcpController is registered for updates. void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onTrackChanged: device=" + device); } mAvrcpController.onTrackChanged(device, numAttributes, attributes, attribVals); } Loading @@ -200,9 +184,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI periodically based upon timer to update play position void onPlayPositionChanged(byte[] address, int songLen, int currSongPosition) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onPlayPositionChanged: device=" + device + " pos=" + currSongPosition); } mAvrcpController.onPlayPositionChanged(device, songLen, currSongPosition); } Loading @@ -210,9 +192,7 @@ public class AvrcpControllerNativeInterface { // Called by JNI on changes of play status void onPlayStatusChanged(byte[] address, byte playStatus) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onPlayStatusChanged: device=" + device + " playStatus=" + playStatus); } mAvrcpController.onPlayStatusChanged(device, toPlaybackStateFromJni(playStatus)); } Loading @@ -220,27 +200,23 @@ public class AvrcpControllerNativeInterface { // Browsing related JNI callbacks. void handleGetFolderItemsRsp(byte[] address, int status, AvrcpItem[] items) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d( TAG, "handleGetFolderItemsRsp:" + (" device=" + device) + (" status=" + status) + (" NumberOfItems=" + items.length)); } mAvrcpController.handleGetFolderItemsRsp(device, status, items); } void handleGetPlayerItemsRsp(byte[] address, AvrcpPlayer[] items) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d( TAG, "handleGetFolderItemsRsp:" + (" device=" + device) + (" NumberOfItems=" + items.length)); } mAvrcpController.handleGetPlayerItemsRsp(device, Arrays.asList(items)); } Loading @@ -249,7 +225,6 @@ public class AvrcpControllerNativeInterface { static AvrcpItem createFromNativeMediaItem( byte[] address, long uid, int type, String name, int[] attrIds, String[] attrVals) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (VDBG) { Log.d( TAG, "createFromNativeMediaItem:" Loading @@ -259,7 +234,6 @@ public class AvrcpControllerNativeInterface { + (" name=" + name) + (" attrids=" + Arrays.toString(attrIds)) + (" attrVals=" + Arrays.toString(attrVals))); } return new AvrcpItem.Builder() .fromAvrcpAttributeArray(attrIds, attrVals) Loading @@ -275,7 +249,6 @@ public class AvrcpControllerNativeInterface { static AvrcpItem createFromNativeFolderItem( byte[] address, long uid, int type, String name, int playable) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (VDBG) { Log.d( TAG, "createFromNativeFolderItem:" Loading @@ -284,7 +257,6 @@ public class AvrcpControllerNativeInterface { + (" type=" + type) + (" name=" + name) + (" playable=" + playable)); } return new AvrcpItem.Builder() .setDevice(device) Loading @@ -306,7 +278,6 @@ public class AvrcpControllerNativeInterface { int playStatus, int playerType) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (VDBG) { Log.d( TAG, "createFromNativePlayerItem:" Loading @@ -315,7 +286,6 @@ public class AvrcpControllerNativeInterface { + (" transportFlags=" + Arrays.toString(transportFlags)) + (" playStatus=" + playStatus) + (" playerType=" + playerType)); } return new AvrcpPlayer.Builder() .setDevice(device) Loading @@ -328,53 +298,41 @@ public class AvrcpControllerNativeInterface { void handleChangeFolderRsp(byte[] address, int count) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleChangeFolderRsp: device=" + device + " count=" + count); } mAvrcpController.handleChangeFolderRsp(device, count); } void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleSetBrowsedPlayerRsp: device=" + device + " depth=" + depth); } mAvrcpController.handleSetBrowsedPlayerRsp(device, items, depth); } void handleSetAddressedPlayerRsp(byte[] address, int status) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleSetAddressedPlayerRsp device=" + device + " status=" + status); } mAvrcpController.handleSetAddressedPlayerRsp(device, status); } void handleAddressedPlayerChanged(byte[] address, int id) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleAddressedPlayerChanged: device=" + device + " id=" + id); } mAvrcpController.handleAddressedPlayerChanged(device, id); } void handleNowPlayingContentChanged(byte[] address) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "handleNowPlayingContentChanged: device=" + device); } mAvrcpController.handleNowPlayingContentChanged(device); } void onAvailablePlayerChanged(byte[] address) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onAvailablePlayerChanged: device=" + device); } mAvrcpController.onAvailablePlayerChanged(device); } Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +20 −34 Original line number Diff line number Diff line Loading @@ -54,8 +54,6 @@ import java.util.concurrent.ConcurrentHashMap; */ public class AvrcpControllerService extends ProfileService { static final String TAG = AvrcpControllerService.class.getSimpleName(); static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); static final int MAXIMUM_CONNECTED_DEVICES = 5; Loading Loading @@ -119,10 +117,8 @@ public class AvrcpControllerService extends ProfileService { @Override public void onImageDownloadComplete(BluetoothDevice device, AvrcpCoverArtManager.DownloadEvent event) { if (DBG) { Log.d(TAG, "Image downloaded [device: " + device + ", uuid: " + event.getUuid() + ", uri: " + event.getUri()); } AvrcpControllerStateMachine stateMachine = getStateMachine(device); if (stateMachine == null) { Log.e(TAG, "No state machine found for device " + device); Loading Loading @@ -207,9 +203,7 @@ public class AvrcpControllerService extends ProfileService { */ @VisibleForTesting boolean setActiveDevice(BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice(device=" + device + ")"); } A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { Log.w(TAG, "setActiveDevice(device=" + device + "): A2DP Sink not available"); Loading Loading @@ -274,7 +268,7 @@ public class AvrcpControllerService extends ProfileService { } void playItem(String parentMediaId) { if (DBG) Log.d(TAG, "playItem(" + parentMediaId + ")"); Log.d(TAG, "playItem(" + parentMediaId + ")"); // Check if the requestedNode is a player rather than a song BrowseTree.BrowseNode requestedNode = sBrowseTree.findBrowseNodeByID(parentMediaId); if (requestedNode == null) { Loading @@ -282,7 +276,7 @@ public class AvrcpControllerService extends ProfileService { // Check each state machine for the song and then play it requestedNode = stateMachine.findNode(parentMediaId); if (requestedNode != null) { if (DBG) Log.d(TAG, "Found a node"); Log.d(TAG, "Found a node, node=" + requestedNode); BluetoothDevice device = stateMachine.getDevice(); if (device != null) { setActiveDevice(device); Loading @@ -304,7 +298,7 @@ public class AvrcpControllerService extends ProfileService { * must be performed. */ public synchronized BrowseResult getContents(String parentMediaId) { if (DBG) Log.d(TAG, "getContents(" + parentMediaId + ")"); Log.d(TAG, "getContents(" + parentMediaId + ")"); BrowseTree.BrowseNode requestedNode = sBrowseTree.findBrowseNodeByID(parentMediaId); if (requestedNode == null) { Loading @@ -316,12 +310,10 @@ public class AvrcpControllerService extends ProfileService { } } if (DBG) { Log.d(TAG, "getContents(" + parentMediaId + "): " + (requestedNode == null ? "Failed to find node" : "node=" + requestedNode + ", device=" + requestedNode.getDevice())); } // 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. Loading @@ -340,7 +332,7 @@ public class AvrcpControllerService extends ProfileService { List<MediaItem> contents = requestedNode.getContents(); if (!requestedNode.isCached()) { if (DBG) Log.d(TAG, "getContents(" + parentMediaId + "): node download pending"); Log.d(TAG, "getContents(" + parentMediaId + "): node download pending"); refreshContents(requestedNode); /* Ongoing downloads can have partial results and we want to make sure they get sent * to the client. If a download gets kicked off as a result of this request, the Loading @@ -348,10 +340,8 @@ public class AvrcpControllerService extends ProfileService { */ return new BrowseResult(contents, BrowseResult.DOWNLOAD_PENDING); } if (DBG) { Log.d(TAG, "getContents(" + parentMediaId + "): return node, contents=" + requestedNode.getContents()); } return new BrowseResult(contents, BrowseResult.SUCCESS); } Loading Loading @@ -526,9 +516,7 @@ public class AvrcpControllerService extends ProfileService { * player to stop and start playing. */ public void onAudioFocusStateChanged(int state) { if (DBG) { Log.d(TAG, "onAudioFocusStateChanged(state=" + state + ")"); } // Make sure the active device isn't changed while we're processing the event so play/pause // commands get routed to the correct device Loading Loading @@ -639,9 +627,10 @@ public class AvrcpControllerService extends ProfileService { // Browsing related JNI callbacks. void handleGetFolderItemsRsp(BluetoothDevice device, int status, AvrcpItem[] items) { Log.d(TAG, "handleGetFolderItemsRsp(device=" + device + ", status=" + status); List<AvrcpItem> itemsList = new ArrayList<>(); for (AvrcpItem item : items) { if (VDBG) Log.d(TAG, item.toString()); Log.v(TAG, "handleGetFolderItemsRsp(device=" + device + "): item=" + item.toString()); if (mCoverArtManager != null) { String handle = item.getCoverArtHandle(); if (handle != null) { Loading Loading @@ -718,9 +707,7 @@ public class AvrcpControllerService extends ProfileService { * @return true if disconnect is successful, false otherwise. */ public synchronized boolean disconnect(BluetoothDevice device) { if (DBG) { Log.d(TAG, "disconnect(device=" + device + ")"); } AvrcpControllerStateMachine stateMachine = mDeviceStateMap.get(device); // a map state machine instance doesn't exist. maybe it is already gone? if (stateMachine == null) { Loading Loading @@ -786,21 +773,20 @@ public class AvrcpControllerService extends ProfileService { } List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates" + Arrays.toString(states)); Log.d(TAG, "getDevicesMatchingConnectionStates(states=" + Arrays.toString(states) + ")"); List<BluetoothDevice> deviceList = new ArrayList<>(); BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); int connectionState; for (BluetoothDevice device : bondedDevices) { connectionState = getConnectionState(device); if (DBG) Log.d(TAG, "Device: " + device + "State: " + connectionState); for (int i = 0; i < states.length; i++) { if (connectionState == states[i]) { deviceList.add(device); } } } if (DBG) Log.d(TAG, deviceList.toString()); Log.d(TAG, "GetDevicesDone"); Log.d(TAG, "getDevicesMatchingConnectionStates(states=" + Arrays.toString(states) + "): Found " + deviceList.toString()); return deviceList; } Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +94 −81 File changed.Preview size limit exceeded, changes collapsed. Show changes
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerUtils.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.avrcpcontroller; import android.support.v4.media.session.PlaybackStateCompat; /** * A package global set of utilities for the AVRCP Controller implementation to leverage */ public final class AvrcpControllerUtils { /** * Convert an AVRCP Passthrough command id to a human readable version of the key */ public static String passThruIdToString(int id) { StringBuilder sb = new StringBuilder(); switch (id) { case AvrcpControllerService.PASS_THRU_CMD_ID_PLAY: sb.append("PLAY"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE: sb.append("PAUSE"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_VOL_UP: sb.append("VOL_UP"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_VOL_DOWN: sb.append("VOL_DOWN"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_STOP: sb.append("STOP"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_FF: sb.append("FF"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_REWIND: sb.append("REWIND"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD: sb.append("FORWARD"); break; case AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD: sb.append("BACKWARD"); break; default: sb.append("UNKNOWN_CMD_" + id); break; } sb.append(" (" + id + ")"); return sb.toString(); } /** * Convert an entire PlaybackStateCompat to a string that contains human readable states */ public static String playbackStateCompatToString(PlaybackStateCompat playbackState) { if (playbackState == null) { return null; } StringBuilder sb = new StringBuilder("PlaybackState {"); sb.append("state=").append(playbackStateToString(playbackState.getState())); sb.append(", position=").append(playbackState.getPosition()); sb.append(", buffered position=").append(playbackState.getBufferedPosition()); sb.append(", speed=").append(playbackState.getPlaybackSpeed()); sb.append(", updated=").append(playbackState.getLastPositionUpdateTime()); sb.append(", actions=").append(playbackState.getActions()); sb.append(", error code=").append(playbackState.getErrorCode()); sb.append(", error message=").append(playbackState.getErrorMessage()); sb.append(", custom actions=").append(playbackState.getCustomActions()); sb.append(", active item id=").append(playbackState.getActiveQueueItemId()); sb.append("}"); return sb.toString(); } /** * Convert a playback state constant to a human readable version of the state */ public static String playbackStateToString(int playbackState) { StringBuilder sb = new StringBuilder(); switch (playbackState) { case PlaybackStateCompat.STATE_NONE: sb.append("STATE_NONE"); break; case PlaybackStateCompat.STATE_STOPPED: sb.append("STATE_STOPPED"); break; case PlaybackStateCompat.STATE_PAUSED: sb.append("STATE_PAUSED"); break; case PlaybackStateCompat.STATE_PLAYING: sb.append("STATE_PLAYING"); break; case PlaybackStateCompat.STATE_FAST_FORWARDING: sb.append("STATE_FAST_FORWARDING"); break; case PlaybackStateCompat.STATE_REWINDING: sb.append("STATE_REWINDING"); break; case PlaybackStateCompat.STATE_BUFFERING: sb.append("STATE_BUFFERING"); break; case PlaybackStateCompat.STATE_ERROR: sb.append("STATE_ERROR"); break; case PlaybackStateCompat.STATE_CONNECTING: sb.append("STATE_CONNECTING"); break; case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: sb.append("STATE_SKIPPING_TO_PREVIOUS"); break; case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: sb.append("STATE_SKIPPING_TO_NEXT"); break; case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM: sb.append("STATE_SKIPPING_TO_QUEUE_ITEM"); break; default: sb.append("UNKNOWN_PLAYBACK_STATE"); break; } sb.append(" (" + playbackState + ")"); return sb.toString(); } }