Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46b7d26f authored by Sal Savage's avatar Sal Savage Committed by Gerrit Code Review
Browse files

Merge changes from topic "avrcp-ct-cover-art-pts"

* changes:
  Remove cover artwork from storage once it is no longer in use
  Use mapped UUIDs to identify handles and invalidate/delete on disconnect
  Disable browsed cover art by default
  Only trigger a metadata sync if we don't have any cover artwork
  Disconnect and reconnect the OBEX session on change path
parents 70858ce3 e7a9bd16
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -75,6 +75,9 @@
    <!-- For enabling the AVRCP Controller Cover Artwork feature -->
    <bool name="avrcp_controller_enable_cover_art">false</bool>

    <!-- For enabling browsed cover art with the AVRCP Controller Cover Artwork feature -->
    <bool name="avrcp_controller_cover_art_browsed_images">false</bool>

    <!-- For enabling the hfp client connection service -->
    <bool name="hfp_client_connection_service_enabled">false</bool>

+80 −4
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ public class AvrcpBipClient {
    private static final int CONNECT = 0;
    private static final int DISCONNECT = 1;
    private static final int REQUEST = 2;
    private static final int REFRESH_OBEX_SESSION = 3;

    private final Handler mHandler;
    private final HandlerThread mThread;
@@ -140,6 +141,24 @@ public class AvrcpBipClient {
        mHandler.obtainMessage(CONNECT).sendToTarget();
    }

    /**
     * Refreshes this client's OBEX session
     */
    public void refreshSession() {
        debug("Refresh client session");
        if (!isConnected()) {
            error("Tried to do a reconnect operation on a client that is not connected");
            return;
        }
        try {
            mHandler.obtainMessage(REFRESH_OBEX_SESSION).sendToTarget();
        } catch (IllegalStateException e) {
            // Means we haven't been started or we're already stopped. Doing this makes this call
            // always safe no matter the state.
            return;
        }
    }

    /**
     * Safely disconnects the client from the server
     */
@@ -173,6 +192,15 @@ public class AvrcpBipClient {
        return getState() == BluetoothProfile.STATE_CONNECTED;
    }

    /**
     * Return the L2CAP PSM used to connect to the server.
     *
     * @return The L2CAP PSM
     */
    public int getL2capPsm() {
        return mPsm;
    }

    /**
     * Retrieve the image properties associated with the given imageHandle
     */
@@ -239,18 +267,54 @@ public class AvrcpBipClient {
            int responseCode = headerSet.getResponseCode();
            if (responseCode == ResponseCodes.OBEX_HTTP_OK) {
                setConnectionState(BluetoothProfile.STATE_CONNECTED);
                debug("Connection established");
            } else {
                error("Error connecting, code: " + responseCode);
                disconnect();
            }
            debug("Connection established");

        } catch (IOException e) {
            error("Exception while connecting to AVRCP BIP server", e);
            disconnect();
        }
    }

    /**
     * Disconnect and reconnect the OBEX session.
     */
    private synchronized void refreshObexSession() {
        if (mSession == null) return;

        try {
            setConnectionState(BluetoothProfile.STATE_DISCONNECTING);
            mSession.disconnect(null);
            debug("Disconnected from OBEX session");
        } catch (IOException e) {
            error("Exception while disconnecting from AVRCP BIP server", e);
            disconnect();
            return;
        }

        try {
            setConnectionState(BluetoothProfile.STATE_CONNECTING);

            HeaderSet headerSet = new HeaderSet();
            headerSet.setHeader(HeaderSet.TARGET, BLUETOOTH_UUID_AVRCP_COVER_ART);

            headerSet = mSession.connect(headerSet);
            int responseCode = headerSet.getResponseCode();
            if (responseCode == ResponseCodes.OBEX_HTTP_OK) {
                setConnectionState(BluetoothProfile.STATE_CONNECTED);
                debug("Reconnection established");
            } else {
                error("Error reconnecting, code: " + responseCode);
                disconnect();
            }
        } catch (IOException e) {
            error("Exception while reconnecting to AVRCP BIP server", e);
            disconnect();
        }
    }

    /**
     * Permanently disconnects this client from the remote device's BIP server and notifies of the
     * new connection status.
@@ -262,17 +326,23 @@ public class AvrcpBipClient {

            try {
                mSession.disconnect(null);
                debug("Disconnected from OBEX session");
            } catch (IOException e) {
                error("Exception while disconnecting from AVRCP BIP server: " + e.toString());
            }

            try {
                mSession.close();
                mTransport.close();
                mSocket.close();
                debug("Closed underlying session, transport and socket");
            } catch (IOException e) {
                error("Exception while closing AVRCP BIP session: " + e.toString());
                error("Exception while closing AVRCP BIP session: ", e);
            }

            mSession = null;
            mTransport = null;
            mSocket = null;
        }
        setConnectionState(BluetoothProfile.STATE_DISCONNECTED);
    }
@@ -312,7 +382,7 @@ public class AvrcpBipClient {
            case BipRequest.TYPE_GET_IMAGE:
                imageHandle = ((RequestGetImage) request).getImageHandle();
                BipImage image = ((RequestGetImage) request).getImage();
                mCallback.onGetImageComplete(responseCode, imageHandle, image); // TODO: add handle
                mCallback.onGetImageComplete(responseCode, imageHandle, image);
                break;
        }
    }
@@ -344,6 +414,12 @@ public class AvrcpBipClient {
                    }
                    break;

                case REFRESH_OBEX_SESSION:
                    if (inst.isConnected()) {
                        inst.refreshObexSession();
                    }
                    break;

                case REQUEST:
                    if (inst.isConnected()) {
                        inst.executeRequest((BipRequest) msg.obj);
+25 −3
Original line number Diff line number Diff line
@@ -103,13 +103,13 @@ public class AvrcpControllerService extends ProfileService {
        public void onImageDownloadComplete(BluetoothDevice device,
                AvrcpCoverArtManager.DownloadEvent event) {
            if (DBG) {
                Log.d(TAG, "Image downloaded [device: " + device + ", handle: " + event.getHandle()
                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);
                mCoverArtManager.removeImage(device, event.getHandle());
                mCoverArtManager.removeImage(device, event.getUuid());
                return;
            }
            stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_IMAGE_DOWNLOADED,
@@ -167,6 +167,16 @@ public class AvrcpControllerService extends ProfileService {
        return new AvrcpControllerStateMachine(device, this);
    }

    protected void getCurrentMetadataIfNoCoverArt(BluetoothDevice device) {
        if (device == null) return;
        AvrcpControllerStateMachine stateMachine = getStateMachine(device);
        if (stateMachine == null) return;
        AvrcpItem track = stateMachine.getCurrentTrack();
        if (track != null && track.getCoverArtLocation() == null) {
            getCurrentMetadataNative(Utils.getByteAddress(device));
        }
    }

    private void refreshContents(BrowseTree.BrowseNode node) {
        BluetoothDevice device = node.getDevice();
        if (device == null) {
@@ -413,6 +423,12 @@ public class AvrcpControllerService extends ProfileService {
            aib.setItemType(AvrcpItem.TYPE_MEDIA);
            aib.setUuid(UUID.randomUUID().toString());
            AvrcpItem item = aib.build();
            if (mCoverArtManager != null) {
                String handle = item.getCoverArtHandle();
                if (handle != null) {
                    item.setCoverArtUuid(mCoverArtManager.getUuidForHandle(device, handle));
                }
            }
            stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED,
                    item);
        }
@@ -519,13 +535,19 @@ public class AvrcpControllerService extends ProfileService {
                    + items.length + " items.");
        }

        BluetoothDevice device = mAdapter.getRemoteDevice(address);
        List<AvrcpItem> itemsList = new ArrayList<>();
        for (AvrcpItem item : items) {
            if (VDBG) Log.d(TAG, item.toString());
            if (mCoverArtManager != null) {
                String handle = item.getCoverArtHandle();
                if (handle != null) {
                    item.setCoverArtUuid(mCoverArtManager.getUuidForHandle(device, handle));
                }
            }
            itemsList.add(item);
        }

        BluetoothDevice device = mAdapter.getRemoteDevice(address);
        AvrcpControllerStateMachine stateMachine = getStateMachine(device);
        if (stateMachine != null) {
            stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+94 −11
Original line number Diff line number Diff line
@@ -207,6 +207,13 @@ class AvrcpControllerStateMachine extends StateMachine {
        sendMessage(DISCONNECT);
    }

    /**
     * Get the current playing track
     */
    public AvrcpItem getCurrentTrack() {
        return mAddressedPlayer.getCurrentTrack();
    }

    /**
     * Dump the current State Machine to the string builder.
     *
@@ -289,6 +296,8 @@ class AvrcpControllerStateMachine extends StateMachine {
    synchronized void onBrowsingDisconnected() {
        if (!mBrowsingConnected) return;
        mAddressedPlayer.setPlayStatus(PlaybackStateCompat.STATE_ERROR);
        AvrcpItem previousTrack = mAddressedPlayer.getCurrentTrack();
        String previousTrackUuid = previousTrack != null ? previousTrack.getCoverArtUuid() : null;
        mAddressedPlayer.updateCurrentTrack(null);
        mBrowseTree.mNowPlayingNode.setCached(false);
        if (isActive()) {
@@ -298,17 +307,28 @@ class AvrcpControllerStateMachine extends StateMachine {
                mBrowseTree.mRootNode);
        BluetoothMediaBrowserService.notifyChanged(mService
                .sBrowseTree.mRootNode);
        removeUnusedArtwork(previousTrackUuid);
        removeUnusedArtworkFromBrowseTree();
        mBrowsingConnected = false;
    }

    synchronized void connectCoverArt() {
        // Called from "connected" state, which assumes either control or browse is connected
        if (mCoverArtManager != null && mCoverArtPsm != 0) {
        if (mCoverArtManager != null && mCoverArtPsm != 0
                && mCoverArtManager.getState(mDevice) != BluetoothProfile.STATE_CONNECTED) {
            logD("Attempting to connect to AVRCP BIP, psm: " + mCoverArtPsm);
            mCoverArtManager.connect(mDevice, /* psm */ mCoverArtPsm);
        }
    }

    synchronized void refreshCoverArt() {
        if (mCoverArtManager != null && mCoverArtPsm != 0
                && mCoverArtManager.getState(mDevice) == BluetoothProfile.STATE_CONNECTED) {
            logD("Attempting to refresh AVRCP BIP OBEX session, psm: " + mCoverArtPsm);
            mCoverArtManager.refreshSession(mDevice);
        }
    }

    synchronized void disconnectCoverArt() {
        // Safe to call even if we're not connected
        if (mCoverArtManager != null) {
@@ -317,6 +337,40 @@ class AvrcpControllerStateMachine extends StateMachine {
        }
    }

    /**
     * Remove an unused cover art image from storage if it's unused by the browse tree and the
     * current track.
     */
    synchronized void removeUnusedArtwork(String previousTrackUuid) {
        logD("removeUnusedArtwork(" + previousTrackUuid + ")");
        if (mCoverArtManager == null) return;
        AvrcpItem currentTrack = getCurrentTrack();
        String currentTrackUuid = currentTrack != null ? currentTrack.getCoverArtUuid() : null;
        if (previousTrackUuid != null) {
            if (!previousTrackUuid.equals(currentTrackUuid)
                    && mBrowseTree.getNodesUsingCoverArt(previousTrackUuid).isEmpty()) {
                mCoverArtManager.removeImage(mDevice, previousTrackUuid);
            }
        }
    }

    /**
     * Queries the browse tree for unused uuids and removes the associated images from storage
     * if the uuid is not used by the current track.
     */
    synchronized void removeUnusedArtworkFromBrowseTree() {
        logD("removeUnusedArtworkFromBrowseTree()");
        if (mCoverArtManager == null) return;
        AvrcpItem currentTrack = getCurrentTrack();
        String currentTrackUuid = currentTrack != null ? currentTrack.getCoverArtUuid() : null;
        ArrayList<String> unusedArtwork = mBrowseTree.getAndClearUnusedCoverArt();
        for (String uuid : unusedArtwork) {
            if (!uuid.equals(currentTrackUuid)) {
                mCoverArtManager.removeImage(mDevice, uuid);
            }
        }
    }

    private void notifyChanged(BrowseTree.BrowseNode node) {
        // We should only notify now playing content updates if we're the active device. VFS
        // updates are fine at any time
@@ -345,6 +399,7 @@ class AvrcpControllerStateMachine extends StateMachine {

    void nowPlayingContentChanged() {
        mBrowseTree.mNowPlayingNode.setCached(false);
        removeUnusedArtworkFromBrowseTree();
        sendMessage(MESSAGE_GET_FOLDER_ITEMS, mBrowseTree.mNowPlayingNode);
    }

@@ -444,11 +499,16 @@ class AvrcpControllerStateMachine extends StateMachine {

                case MESSAGE_PROCESS_TRACK_CHANGED:
                    AvrcpItem track = (AvrcpItem) msg.obj;
                    AvrcpItem previousTrack = mAddressedPlayer.getCurrentTrack();
                    downloadImageIfNeeded(track);
                    mAddressedPlayer.updateCurrentTrack(track);
                    if (isActive()) {
                        BluetoothMediaBrowserService.trackChanged(track);
                    }
                    if (previousTrack != null) {
                        removeUnusedArtwork(previousTrack.getCoverArtUuid());
                        removeUnusedArtworkFromBrowseTree();
                    }
                    return true;

                case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
@@ -518,6 +578,7 @@ class AvrcpControllerStateMachine extends StateMachine {
                        mBrowseTree.mRootNode.setExpectedChildren(255);
                        BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode);
                    }
                    removeUnusedArtworkFromBrowseTree();
                    return true;

                case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS:
@@ -544,13 +605,13 @@ class AvrcpControllerStateMachine extends StateMachine {
                case MESSAGE_PROCESS_IMAGE_DOWNLOADED:
                    AvrcpCoverArtManager.DownloadEvent event =
                            (AvrcpCoverArtManager.DownloadEvent) msg.obj;
                    String handle = event.getHandle();
                    String uuid = event.getUuid();
                    Uri uri = event.getUri();
                    logD("Received image for " + handle + " at " + uri.toString());
                    logD("Received image for " + uuid + " at " + uri.toString());

                    // Let the addressed player know we got an image so it can see if the current
                    // track now has cover artwork
                    boolean addedArtwork = mAddressedPlayer.notifyImageDownload(handle, uri);
                    boolean addedArtwork = mAddressedPlayer.notifyImageDownload(uuid, uri);
                    if (addedArtwork && isActive()) {
                        BluetoothMediaBrowserService.trackChanged(
                                mAddressedPlayer.getCurrentTrack());
@@ -558,10 +619,17 @@ class AvrcpControllerStateMachine extends StateMachine {

                    // Let the browse tree know of the newly downloaded image so it can attach it to
                    // all the items that need it. Notify of changed nodes accordingly
                    Set<BrowseTree.BrowseNode> nodes = mBrowseTree.notifyImageDownload(handle, uri);
                    Set<BrowseTree.BrowseNode> nodes = mBrowseTree.notifyImageDownload(uuid, uri);
                    for (BrowseTree.BrowseNode node : nodes) {
                        notifyChanged(node);
                    }

                    // Delete images that were downloaded and entirely unused
                    if (!addedArtwork && nodes.isEmpty()) {
                        removeUnusedArtwork(uuid);
                        removeUnusedArtworkFromBrowseTree();
                    }

                    return true;

                case DISCONNECT:
@@ -642,6 +710,7 @@ class AvrcpControllerStateMachine extends StateMachine {
            mBrowseTree.mRootNode.setCached(false);
            mBrowseTree.mRootNode.setExpectedChildren(255);
            BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode);
            removeUnusedArtworkFromBrowseTree();
        }
    }

@@ -688,8 +757,13 @@ class AvrcpControllerStateMachine extends StateMachine {
                            + " received " + folderList.size());

                    // Queue up image download if the item has an image and we don't have it yet
                    // Only do this if the feature is enabled.
                    for (AvrcpItem track : folderList) {
                        if (shouldDownloadBrowsedImages()) {
                            downloadImageIfNeeded(track);
                        } else {
                            track.setCoverArtUuid(null);
                        }
                    }

                    // Always update the node so that the user does not wait forever
@@ -724,6 +798,10 @@ class AvrcpControllerStateMachine extends StateMachine {
                    mBrowseTree.setCurrentBrowsedFolder(mNextStep.getID());
                    mBrowseTree.getCurrentBrowsedFolder().setExpectedChildren(msg.arg1);

                    // AVRCP Specification says, if we're not database aware, we must disconnect and
                    // reconnect our BIP client each time we successfully change path
                    refreshCoverArt();

                    if (mAbort) {
                        transitionTo(mConnected);
                    } else {
@@ -864,7 +942,7 @@ class AvrcpControllerStateMachine extends StateMachine {
                logD("NAVIGATING UP " + mNextStep.toString());
                mNextStep = mBrowseTree.getCurrentBrowsedFolder().getParent();
                mBrowseTree.getCurrentBrowsedFolder().setCached(false);

                removeUnusedArtworkFromBrowseTree();
                mService.changeFolderPathNative(
                        mDeviceAddress,
                        AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_UP,
@@ -954,16 +1032,21 @@ class AvrcpControllerStateMachine extends StateMachine {
        return newIndex;
    }

    private boolean shouldDownloadBrowsedImages() {
        return mService.getResources()
                .getBoolean(R.bool.avrcp_controller_cover_art_browsed_images);
    }

    private void downloadImageIfNeeded(AvrcpItem track) {
        if (mCoverArtManager == null) return;
        String handle = track.getCoverArtHandle();
        String uuid = track.getCoverArtUuid();
        Uri imageUri = null;
        if (handle != null) {
            imageUri = mCoverArtManager.getImageUri(mDevice, handle);
        if (uuid != null) {
            imageUri = mCoverArtManager.getImageUri(mDevice, uuid);
            if (imageUri != null) {
                track.setCoverArtLocation(imageUri);
            } else {
                mCoverArtManager.downloadImage(mDevice, handle);
                mCoverArtManager.downloadImage(mDevice, uuid);
            }
        }
    }
+164 −31

File changed.

Preview size limit exceeded, changes collapsed.

Loading