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

Commit 4f069f0d authored by Aditi Katragadda's avatar Aditi Katragadda Committed by Automerger Merge Worker
Browse files

Merge "Display Bluetooth Disconnected Message" am: fc17bea0

parents bf546700 fc17bea0
Loading
Loading
Loading
Loading
+27 −17
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import com.android.bluetooth.BluetoothPrefs;
import com.android.bluetooth.R;
import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService.BrowseResult;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.internal.annotations.VisibleForTesting;
@@ -321,10 +322,10 @@ public class AvrcpControllerService extends ProfileService {
     * Get a List of MediaItems that are children of the specified media Id
     *
     * @param parentMediaId The player or folder to get the contents of
     * @return List of Children if available, an empty list if there are none,
     * or null if a search must be performed.
     * @return List of Children if available, an empty list if there are none, or null if a search
     *     must be performed.
     */
    public synchronized List<MediaItem> getContents(String parentMediaId) {
    public synchronized BrowseResult getContents(String parentMediaId) {
        if (DBG) Log.d(TAG, "getContents(" + parentMediaId + ")");

        BrowseTree.BrowseNode requestedNode = sBrowseTree.findBrowseNodeByID(parentMediaId);
@@ -337,28 +338,37 @@ public class AvrcpControllerService extends ProfileService {
                }
            }
        }

        // If we don't find a node in the tree then do not have any way to browse for the contents.
        // Return an empty list instead.
        if (requestedNode == null) {
            if (DBG) Log.d(TAG, "Didn't find a node");
            return new ArrayList(0);
        } else {
            return new BrowseResult(new ArrayList(0), BrowseResult.ERROR_MEDIA_ID_INVALID);
        }
        if (parentMediaId.equals(BrowseTree.ROOT) && requestedNode.getChildrenCount() == 0) {
            return new BrowseResult(null, BrowseResult.NO_DEVICE_CONNECTED);
        }
        // If we found a node and it belongs to a device then go ahead and make it active
        BluetoothDevice device = requestedNode.getDevice();
        if (device != null) {
            setActiveDevice(device);
        }

        List<MediaItem> contents = requestedNode.getContents();

        if (DBG) Log.d(TAG, "Returning contents");
        if (!requestedNode.isCached()) {
            if (DBG) Log.d(TAG, "node is not cached");
            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
             * contents will be null until the first results arrive.
             */
            return new BrowseResult(contents, BrowseResult.DOWNLOAD_PENDING);
        }
            if (DBG) Log.d(TAG, "Returning contents");
            return requestedNode.getContents();
        }
        return new BrowseResult(contents, BrowseResult.SUCCESS);
    }


    @Override
    protected IProfileServiceBinder initBinder() {
        return new AvrcpControllerServiceBinder(this);
+66 −6
Original line number Diff line number Diff line
@@ -132,11 +132,66 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
        mReceiver = null;
    }

    List<MediaItem> getContents(final String parentMediaId) {
    /**
     * BrowseResult is used to return the contents of a node along with a status. The status is
     * used to indicate success, a pending download, or error conditions. BrowseResult is used in
     * onLoadChildren() and getContents() in BluetoothMediaBrowserService and in getContents() in
     * AvrcpControllerService.
     * The following statuses have been implemented:
     * 1. SUCCESS - Contents have been retrieved successfully.
     * 2. DOWNLOAD_PENDING - Download is in progress and may or may not have contents to return.
     * 3. NO_DEVICE_CONNECTED - If no device is connected there are no contents to be retrieved.
     * 4. ERROR_MEDIA_ID_INVALID - Contents could not be retrieved as the media ID is invalid.
     * 5. ERROR_NO_AVRCP_SERVICE - Contents could not be retrieved as AvrcpControllerService is not
     *                             connected.
     */
    public static class BrowseResult {
        // Possible statuses for onLoadChildren
        public static final byte SUCCESS = 0x00;
        public static final byte DOWNLOAD_PENDING = 0x01;
        public static final byte NO_DEVICE_CONNECTED = 0x02;
        public static final byte ERROR_MEDIA_ID_INVALID = 0x03;
        public static final byte ERROR_NO_AVRCP_SERVICE = 0x04;

        private List<MediaItem> mResults;
        private final byte mStatus;

        List<MediaItem> getResults() {
            return mResults;
        }

        byte getStatus() {
            return mStatus;
        }

        String getStatusString() {
            switch (mStatus) {
                case DOWNLOAD_PENDING:
                    return "DOWNLOAD_PENDING";
                case SUCCESS:
                    return "SUCCESS";
                case NO_DEVICE_CONNECTED:
                    return "NO_DEVICE_CONNECTED";
                case ERROR_MEDIA_ID_INVALID:
                    return "ERROR_MEDIA_ID_INVALID";
                case ERROR_NO_AVRCP_SERVICE:
                    return "ERROR_NO_AVRCP_SERVICE";
                default:
                    return "UNDEFINED_ERROR_CASE";
            }
        }

        BrowseResult(List<MediaItem> results, byte status) {
            mResults = results;
            mStatus = status;
        }
    }

    BrowseResult getContents(final String parentMediaId) {
        AvrcpControllerService avrcpControllerService =
                AvrcpControllerService.getAvrcpControllerService();
        if (avrcpControllerService == null) {
            return new ArrayList(0);
            return new BrowseResult(new ArrayList(0), BrowseResult.ERROR_NO_AVRCP_SERVICE);
        } else {
            return avrcpControllerService.getContents(parentMediaId);
        }
@@ -173,11 +228,16 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
    public synchronized void onLoadChildren(final String parentMediaId,
            final Result<List<MediaItem>> result) {
        if (DBG) Log.d(TAG, "onLoadChildren parentMediaId= " + parentMediaId);
        List<MediaItem> contents = getContents(parentMediaId);
        if (contents == null) {
        BrowseResult contents = getContents(parentMediaId);
        byte status = contents.getStatus();
        if (status == BrowseResult.DOWNLOAD_PENDING && contents == null) {
            Log.i(TAG, "Download pending - no contents, id= " + parentMediaId);
            result.detach();
        } else {
            result.sendResult(contents);
            if (DBG) {
                Log.d(TAG, "id= " + parentMediaId + ", status= " + contents.getStatusString());
            }
            result.sendResult(contents.getResults());
        }
    }

+87 −1
Original line number Diff line number Diff line
@@ -19,9 +19,11 @@ import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -36,6 +38,7 @@ import androidx.test.filters.MediumTest;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService.BrowseResult;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;

@@ -174,7 +177,90 @@ public class AvrcpControllerServiceTest {

        mService.getContents(parentMediaId);

        verify(node).getContents();
        verify(node, atLeastOnce()).getContents();
    }

    /**
     * Pre-conditions: No node in BrowseTree for specified media ID
     * Test: Call AvrcpControllerService.getContents()
     * Expected Output: BrowseResult object with status ERROR_MEDIA_ID_INVALID
     */
    @Test
    public void testGetContentsNoNode_returnInvalidMediaIdStatus() {
        String parentMediaId = "test_parent_media_id";
        when(mStateMachine.findNode(parentMediaId)).thenReturn(null);
        BrowseResult result = mService.getContents(parentMediaId);

        assertThat(result.getStatus()).isEqualTo(BrowseResult.ERROR_MEDIA_ID_INVALID);
    }

    /**
     * Pre-conditions: No device is connected - parent media ID is at the root of the BrowseTree
     * Test: Call AvrcpControllerService.getContents()
     * Expected Output: BrowseResult object with status NO_DEVICE_CONNECTED
     */
    @Test
    public void getContentsNoDeviceConnected_returnNoDeviceConnectedStatus() {
        String parentMediaId = BrowseTree.ROOT;
        BrowseResult result = mService.getContents(parentMediaId);

        assertThat(result.getStatus()).isEqualTo(BrowseResult.NO_DEVICE_CONNECTED);
    }

    /**
     * Pre-conditions: At least one device is connected
     * Test: Call AvrcpControllerService.getContents()
     * Expected Output: BrowseResult object with status SUCCESS
     */
    @Test
    public void getContentsOneDeviceConnected_returnSuccessStatus() {
        String parentMediaId = BrowseTree.ROOT;
        mService.sBrowseTree.onConnected(mRemoteDevice);
        BrowseResult result = mService.getContents(parentMediaId);

        assertThat(result.getStatus()).isEqualTo(BrowseResult.SUCCESS);
    }

    /**
     * Pre-conditions: Node for specified media ID is not cached
     * Test: {@link BrowseTree.BrowseNode#getContents} returns {@code null} when the node has no
     * children/items and the node is not cached.
     * When {@link AvrcpControllerService#getContents} receives a node that is not cached,
     * it should interpret the status as `DOWNLOAD_PENDING`.
     * Expected Output: BrowseResult object with status DOWNLOAD_PENDING; verify that a download
     * request has been sent by checking if mStateMachine.requestContents() is called
     */
    @Test
    public void getContentsNodeNotCached_returnDownloadPendingStatus() {
        String parentMediaId = "test_parent_media_id";
        BrowseTree.BrowseNode node = mock(BrowseTree.BrowseNode.class);
        when(mStateMachine.findNode(parentMediaId)).thenReturn(node);
        when(node.isCached()).thenReturn(false);
        when(node.getDevice()).thenReturn(mRemoteDevice);
        when(node.getID()).thenReturn(parentMediaId);

        BrowseResult result = mService.getContents(parentMediaId);

        verify(mStateMachine, times(1)).requestContents(eq(node));
        assertThat(result.getStatus()).isEqualTo(BrowseResult.DOWNLOAD_PENDING);
    }

    /**
     * Pre-conditions: Parent media ID that is not BrowseTree.ROOT; isCached returns true
     * Test: Call AvrcpControllerService.getContents()
     * Expected Output: BrowseResult object with status SUCCESS
     */
    @Test
    public void getContentsNoErrorConditions_returnsSuccessStatus() {
        String parentMediaId = "test_parent_media_id";
        BrowseTree.BrowseNode node = mock(BrowseTree.BrowseNode.class);
        when(mStateMachine.findNode(parentMediaId)).thenReturn(node);
        when(node.getContents()).thenReturn(new ArrayList(0));
        when(node.isCached()).thenReturn(true);

        BrowseResult result = mService.getContents(parentMediaId);

        assertThat(result.getStatus()).isEqualTo(BrowseResult.SUCCESS);
    }

    @Test