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

Commit 1d45a8a1 authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 6611069 from e262cc96 to mainline-release

Change-Id: I6c5c406787889cfd3056ad3f0f18ce7054a83363
parents 0768f931 e262cc96
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,
+137 −38
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections
@@ -206,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.
     *
@@ -231,25 +239,39 @@ class AvrcpControllerStateMachine extends StateMachine {
        if (sActiveDevice == null
                || BluetoothMediaBrowserService.getPlaybackState()
                != PlaybackStateCompat.STATE_PLAYING) {
            return setActive();
            return setActive(true);
        }
        return false;
    }

    /*
     * setActive
     *
     * Set this state machine as the active device and update media browse service
    /**
     * Attempt to set the active status for this device
     */
    private boolean setActive() {
        if (DBG) Log.d(TAG, "setActive" + mDevice);
        if (A2dpSinkService.getA2dpSinkService().setActiveDeviceNative(mDeviceAddress)) {
    boolean setActive(boolean becomeActive) {
        logD("setActive(" + becomeActive + ")");
        if (becomeActive) {
            if (isActive()) {
                return true;
            }

            A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
            if (a2dpSinkService == null) {
                return false;
            }

            if (a2dpSinkService.setActiveDeviceNative(mDeviceAddress)) {
                sActiveDevice = mDevice;
                BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks);
                BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
                BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
            }
            return mDevice == sActiveDevice;
        } else if (isActive()) {
            sActiveDevice = null;
            BluetoothMediaBrowserService.trackChanged(null);
            BluetoothMediaBrowserService.addressedPlayerChanged(null);
        }
        return true;
    }

    @Override
@@ -274,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()) {
@@ -283,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) {
@@ -302,9 +337,50 @@ 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
        int scope = node.getScope();
        if (scope != AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING
                || (scope == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING
                && isActive())) {
            BluetoothMediaBrowserService.notifyChanged(node);
        }
    }

    private void notifyChanged(PlaybackStateCompat state) {
        if (isActive()) {
@@ -323,6 +399,7 @@ class AvrcpControllerStateMachine extends StateMachine {

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

@@ -372,8 +449,6 @@ class AvrcpControllerStateMachine extends StateMachine {
        public void enter() {
            if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) {
                requestActive();
                BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks);
                BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
                broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED);
                connectCoverArt(); // only works if we have a valid PSM
            } else {
@@ -424,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:
@@ -468,10 +548,7 @@ class AvrcpControllerStateMachine extends StateMachine {
                case MESSAGE_PROCESS_PLAY_POS_CHANGED:
                    if (msg.arg2 != -1) {
                        mAddressedPlayer.setPlayTime(msg.arg2);
                        if (isActive()) {
                            BluetoothMediaBrowserService.notifyChanged(
                                    mAddressedPlayer.getPlaybackState());
                        }
                        notifyChanged(mAddressedPlayer.getPlaybackState());
                    }
                    return true;

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

                case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS:
@@ -527,21 +605,31 @@ 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());
                    }

                    // Let the browse tree know of the newly downloaded image so it can attach it to
                    // all the items that need it
                    mBrowseTree.notifyImageDownload(handle, uri);
                    // all the items that need it. Notify of changed nodes accordingly
                    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:
@@ -555,7 +643,7 @@ class AvrcpControllerStateMachine extends StateMachine {
        }

        private void processPlayItem(BrowseTree.BrowseNode node) {
            setActive();
            setActive(true);
            if (node == null) {
                Log.w(TAG, "Invalid item to play");
            } else {
@@ -622,6 +710,7 @@ class AvrcpControllerStateMachine extends StateMachine {
            mBrowseTree.mRootNode.setCached(false);
            mBrowseTree.mRootNode.setExpectedChildren(255);
            BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode);
            removeUnusedArtworkFromBrowseTree();
        }
    }

@@ -668,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
@@ -704,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 {
@@ -844,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,
@@ -872,11 +970,7 @@ class AvrcpControllerStateMachine extends StateMachine {
        public void enter() {
            disconnectCoverArt();
            onBrowsingDisconnected();
            if (isActive()) {
                sActiveDevice = null;
                BluetoothMediaBrowserService.trackChanged(null);
                BluetoothMediaBrowserService.addressedPlayerChanged(null);
            }
            setActive(false);
            broadcastConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING);
            transitionTo(mDisconnected);
        }
@@ -938,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