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

Commit 29a256a8 authored by Aditi Katragadda's avatar Aditi Katragadda
Browse files

Randomize media ID to correct lost audio focus

When an active device and secondary device are connected,
if the secondary device is disconnected and reconnected, the
active device will lose audio focus. This is because device
media IDs are stored and function is called when the secondary
device reconnects as it is a recognized device, which causes the
active device to lose audio focus and stop playing media, if any.

Tag: #stability
Bug: 215263047
Bug: 332367017
Test: atest BrowseTreeTest.java
Test: atest AvrcpControllerStateMachineTest.java
Change-Id: If8f76e1535d72be921252133c3bfa4ebf251b221
parent ffe9dcdd
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.support.v4.media.MediaBrowserCompat.MediaItem;
import android.util.Log;

import com.android.bluetooth.Utils;
import com.android.bluetooth.flags.Flags;

import com.google.common.annotations.VisibleForTesting;

@@ -80,10 +81,22 @@ public class BrowseTree {
            mRootNode = new BrowseNode(new AvrcpItem.Builder()
                    .setUuid(ROOT).setTitle(ROOT).setBrowsable(true).build());
            mRootNode.setCached(true);
        } else {
        } else if (!Flags.randomizeDeviceLevelMediaIds()) {
            mRootNode = new BrowseNode(new AvrcpItem.Builder().setDevice(device)
                    .setUuid(ROOT + device.getAddress().toString())
                    .setTitle(Utils.getName(device)).setBrowsable(true).build());
        } else {
            mRootNode =
                    new BrowseNode(
                            new AvrcpItem.Builder()
                                    .setDevice(device)
                                    .setUuid(
                                            ROOT
                                                    + device.getAddress().toString()
                                                    + UUID.randomUUID().toString())
                                    .setTitle(Utils.getName(device))
                                    .setBrowsable(true)
                                    .build());
        }

        mRootNode.mBrowseScope = AvrcpControllerService.BROWSE_SCOPE_PLAYER_LIST;
+58 −3
Original line number Diff line number Diff line
@@ -30,7 +30,9 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.media.AudioManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.support.v4.media.session.PlaybackStateCompat;

import androidx.test.InstrumentationRegistry;
@@ -38,9 +40,12 @@ import androidx.test.filters.MediumTest;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService.BrowseResult;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.flags.Flags;

import org.junit.After;
import org.junit.Before;
@@ -61,6 +66,11 @@ import java.util.List;
public class AvrcpControllerServiceTest {
    private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
    private static final byte[] REMOTE_DEVICE_ADDRESS_AS_ARRAY = new byte[]{0, 0, 0, 0, 0, 0};
    private static final String REMOTE_DEVICE_ADDRESS_2 = "11:11:11:11:11:11";
    private static final byte[] REMOTE_DEVICE_ADDRESS_AS_ARRAY_2 =
            new byte[] {11, 11, 11, 11, 11, 11};

    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private AvrcpControllerService mService = null;
    private BluetoothAdapter mAdapter = null;
@@ -70,11 +80,18 @@ public class AvrcpControllerServiceTest {

    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock private A2dpSinkService mA2dpSinkService;
    @Mock private AdapterService mAdapterService;
    @Mock private AvrcpControllerStateMachine mStateMachine;
    @Mock private AvrcpControllerStateMachine mStateMachine2;
    @Mock private AvrcpControllerNativeInterface mNativeInterface;

    @Mock private Resources mMockResources;

    private BluetoothDevice mRemoteDevice;
    private BluetoothDevice mRemoteDevice2;

    @Mock private AvrcpControllerStateMachine mAvrcpStateMachine;

    @Before
    public void setUp() throws Exception {
@@ -86,17 +103,29 @@ public class AvrcpControllerServiceTest {
        // Try getting the Bluetooth adapter
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        assertThat(mAdapter).isNotNull();
        // Set a mock A2dpSinkService for audio focus calls
        A2dpSinkService.setA2dpSinkService(mA2dpSinkService);
        when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus))
                .thenReturn(true);

        mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
        mService.mDeviceStateMap.put(mRemoteDevice, mStateMachine);
        final Intent bluetoothBrowserMediaServiceStartIntent =
                TestUtils.prepareIntentToStartBluetoothBrowserMediaService();
        mBluetoothBrowserMediaServiceTestRule.startService(bluetoothBrowserMediaServiceStartIntent);

        // Set up device and state machine under test
        mRemoteDevice2 = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS_2);
        mService.mDeviceStateMap.put(mRemoteDevice2, mStateMachine2);

        when(mA2dpSinkService.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
    }

    @After
    public void tearDown() throws Exception {
        mService.stop();
        AvrcpControllerNativeInterface.setInstance(null);
        A2dpSinkService.setA2dpSinkService(null);
        mService = AvrcpControllerService.getAvrcpControllerService();
        assertThat(mService).isNull();
        TestUtils.clearAdapterService(mAdapterService);
@@ -142,6 +171,7 @@ public class AvrcpControllerServiceTest {

    @Test
    public void setActiveDevice_whenA2dpSinkServiceIsNotInitailized_returnsFalse() {
        A2dpSinkService.setA2dpSinkService(null);
        assertThat(mService.setActiveDevice(mRemoteDevice)).isFalse();

        assertThat(mService.getActiveDevice()).isNull();
@@ -465,7 +495,8 @@ public class AvrcpControllerServiceTest {

        mService.handleGetPlayerItemsRsp(mRemoteDevice, items);

        verify(mStateMachine).sendMessage(
        verify(mStateMachine)
                .sendMessage(
                        eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS),
                        eq(items));
    }
@@ -487,4 +518,28 @@ public class AvrcpControllerServiceTest {
        mService.onAudioFocusStateChanged(AudioManager.AUDIOFOCUS_LOSS);
        assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
    }

    /**
     * Connect first device and check that it is the active device. Pair a second device, then
     * disconnect and repair this known second device. Confirm that audio focus is maintained by
     * first device by checking that it has remained as the active device.
     */
    @Test
    public void testActiveDeviceMaintainsAudioFocusWhenOtherDeviceConnects_audioFocusMaintained() {
        mSetFlagsRule.enableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS);

        mService.onConnectionStateChanged(true, true, mRemoteDevice);
        // check set active device is called
        verify(mA2dpSinkService).setActiveDevice(mRemoteDevice);
        when(mA2dpSinkService.getActiveDevice()).thenReturn(mRemoteDevice);

        // connect another phone
        mService.onConnectionStateChanged(true, true, mRemoteDevice2);
        verify(mA2dpSinkService, times(0)).setActiveDevice(mRemoteDevice2);

        // disconnect and reconnect other phone
        mService.onConnectionStateChanged(false, false, mRemoteDevice2);
        mService.onConnectionStateChanged(true, true, mRemoteDevice2);
        verify(mA2dpSinkService, times(0)).setActiveDevice(mRemoteDevice2);
    }
}
+46 −34
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@ package com.android.bluetooth.avrcpcontroller;

import static android.Manifest.permission.BLUETOOTH_CONNECT;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.*;

import android.bluetooth.BluetoothAdapter;
@@ -29,6 +31,7 @@ import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
@@ -44,6 +47,7 @@ import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.flags.Flags;

import org.hamcrest.core.IsInstanceOf;
import org.junit.After;
@@ -68,6 +72,10 @@ public class AvrcpControllerStateMachineTest {
    private static final int CONNECT_TIMEOUT_TEST_MILLIS = 1000;
    private static final int KEY_DOWN = 0;
    private static final int KEY_UP = 1;
    private static final int UUID_START = 0;
    private static final int UUID_LENGTH = 25;

    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private BluetoothAdapter mAdapter;

@@ -135,6 +143,7 @@ public class AvrcpControllerStateMachineTest {
        // Set up device and state machine under test
        mTestDevice = mAdapter.getRemoteDevice(mTestAddress);
        mAvrcpStateMachine = makeStateMachine(mTestDevice);

        setActiveDevice(mTestDevice);
    }

@@ -461,6 +470,27 @@ public class AvrcpControllerStateMachineTest {
        verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
    }

    /** Get the root of the device */
    @Test
    public void testGetDeviceRootNode_flagRandomDeviceIdDisabled_rootNodeMatchesUuidFormat() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();
        // Get the root of the device
        BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName);
        Assert.assertEquals(rootName, results.getID());
    }

    /** Get the root of the device */
    @Test
    public void testGetDeviceRootNode_flagRandomDeviceIdEnabled_rootNodeMatchesUuidFormat() {
        mSetFlagsRule.enableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS);
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();
        // Get the root of the device
        BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;
        assertThat((results.getID()).substring(UUID_START, UUID_LENGTH)).isEqualTo(rootName);
    }

    /**
     * Test to make sure the state machine is tracking the correct device
     */
@@ -716,12 +746,8 @@ public class AvrcpControllerStateMachineTest {
    @FlakyTest
    public void testBrowsingCommands() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();
        final String playerName = "Player 1";

        //Get the root of the device
        BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName);
        Assert.assertEquals(rootName, results.getID());
        BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;

        //Request fetch the list of players
        BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
@@ -764,14 +790,13 @@ public class AvrcpControllerStateMachineTest {
    @Test
    public void testAvailablePlayersChanged() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();

        // Send an available players have changed event
        mAvrcpStateMachine.sendMessage(
                AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);

        // Verify we've uncached our browse root and made the call to fetch new players
        Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
        Assert.assertFalse(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .getPlayerList(eq(mTestAddress), eq(0), eq(19));
    }
@@ -783,7 +808,6 @@ public class AvrcpControllerStateMachineTest {
    @Test
    public void testAvailablePlayersReceived_AddressedPlayerExists() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();

        // Set an addressed player that will be in the available players set. A new player triggers
        // a now playing list download, so send back nothing.
@@ -801,7 +825,7 @@ public class AvrcpControllerStateMachineTest {
                AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);

        // Verify we've uncached our browse root and made the call to fetch new players
        Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
        Assert.assertFalse(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .getPlayerList(eq(mTestAddress), eq(0), eq(19));

@@ -821,7 +845,7 @@ public class AvrcpControllerStateMachineTest {

        // Verify we processed the first players properly. Note the addressed player should always
        // be in the available player set.
        Assert.assertTrue(mAvrcpStateMachine.findNode(rootName).isCached());
        Assert.assertTrue(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
        SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
        Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId()));
        Assert.assertEquals(testPlayers.size(), players.size());
@@ -846,14 +870,13 @@ public class AvrcpControllerStateMachineTest {
    @Test
    public void testAvailablePlayersReceived_AddressedPlayerDoesNotExist() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();

        // Send an available players have changed event
        mAvrcpStateMachine.sendMessage(
                AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);

        // Verify we've uncached our browse root and made the call to fetch new players
        Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
        Assert.assertFalse(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .getPlayerList(eq(mTestAddress), eq(0), eq(19));

@@ -874,7 +897,7 @@ public class AvrcpControllerStateMachineTest {
        // Verify we processed the players properly. Note the addressed player is currently the
        // default player and is not in the available player set sent. This means we'll have an
        // extra player at ID -1.
        Assert.assertTrue(mAvrcpStateMachine.findNode(rootName).isCached());
        Assert.assertTrue(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
        SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
        Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId()));
        Assert.assertEquals(testPlayers.size() + 1, players.size());
@@ -899,11 +922,8 @@ public class AvrcpControllerStateMachineTest {
    @Test
    public void testAddressedPlayerChangedToNewKnownPlayer() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();

        // Get the root of the device
        BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName);
        Assert.assertEquals(rootName, results.getID());
        BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;

        //Request fetch the list of players
        BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
@@ -962,11 +982,9 @@ public class AvrcpControllerStateMachineTest {
    @Test
    public void testAddressedPlayerChangedToUnknownPlayer() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();

        // Get the root of the device
        BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.findNode(rootName);
        Assert.assertEquals(rootName, rootNode.getID());
        BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.mBrowseTree.mRootNode;

        //Request fetch the list of players
        BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(rootNode.getID());
@@ -1009,7 +1027,6 @@ public class AvrcpControllerStateMachineTest {
    @Test
    public void testAddressedPlayerChangedToSamePlayerId() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();

        // Set the addressed player so we can change to the same one
        mAvrcpStateMachine.sendMessage(
@@ -1022,8 +1039,7 @@ public class AvrcpControllerStateMachineTest {
        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());

        // Get the root of the device
        BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.findNode(rootName);
        Assert.assertEquals(rootName, rootNode.getID());
        BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.mBrowseTree.mRootNode;

        //Request fetch the list of players
        BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(rootNode.getID());
@@ -1080,12 +1096,10 @@ public class AvrcpControllerStateMachineTest {
    @Test
    public void testPlayWhileBrowsing() {
        setUpConnectedState(true, true);
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();
        final String playerName = "Player 1";

        // Get the root of the device
        BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName);
        Assert.assertEquals(rootName, results.getID());
        BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;

        //Request fetch the list of players
        BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
@@ -2083,12 +2097,11 @@ public class AvrcpControllerStateMachineTest {
     */
    @Test
    public void testBrowseRequestWhileDisconnected_requestDropped() {
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();
        setUpConnectedState(true, false);
        sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
        clearInvocations(mAvrcpControllerService);
        clearInvocations(mNativeInterface);
        BrowseTree.BrowseNode deviceRoot = mAvrcpStateMachine.findNode(rootName);
        BrowseTree.BrowseNode deviceRoot = mAvrcpStateMachine.mBrowseTree.mRootNode;
        mAvrcpStateMachine.requestContents(deviceRoot);
        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
        verifyNoMoreInteractions(mAvrcpControllerService);
@@ -2101,12 +2114,11 @@ public class AvrcpControllerStateMachineTest {
     */
    @Test
    public void testBrowseRequestWhileDisconnectedThenRequestWhileConnected_secondRequestSent() {
        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();
        setUpConnectedState(true, false);
        sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
        clearInvocations(mAvrcpControllerService);
        clearInvocations(mNativeInterface);
        BrowseTree.BrowseNode deviceRoot = mAvrcpStateMachine.findNode(rootName);
        BrowseTree.BrowseNode deviceRoot = mAvrcpStateMachine.mBrowseTree.mRootNode;
        mAvrcpStateMachine.requestContents(deviceRoot);
        // issues a player list fetch
        mAvrcpStateMachine.connect(StackEvent.connectionStateChanged(true, true));
+33 −0
Original line number Diff line number Diff line
@@ -20,10 +20,13 @@ import static com.google.common.truth.Truth.assertThat;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.platform.test.flag.junit.SetFlagsRule;

import com.android.bluetooth.avrcpcontroller.BrowseTree.BrowseNode;
import com.android.bluetooth.flags.Flags;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import java.util.Set;
@@ -33,6 +36,8 @@ public class BrowseTreeTest {
    private static final String TEST_HANDLE = "test_handle";
    private static final String TEST_NODE_ID = "test_node_id";

    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private final byte[] mTestAddress = new byte[]{01, 01, 01, 01, 01, 01};
    private BluetoothAdapter mAdapter;
    private BluetoothDevice mTestDevice = null;
@@ -89,6 +94,18 @@ public class BrowseTreeTest {
        assertThat(browseTree.mRootNode.getChildrenCount()).isEqualTo(1);
    }

    @Test
    public void sameDeviceDifferentBrowseTrees_uniqueMediaIds() {
        mSetFlagsRule.enableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS);
        BrowseTree browseTree1 = new BrowseTree(mTestDevice);
        BrowseTree browseTree2 = new BrowseTree(mTestDevice);

        String mediaId1 = browseTree1.mRootNode.getID();
        String mediaId2 = browseTree2.mRootNode.getID();

        assertThat(mediaId1).isNotEqualTo(mediaId2);
    }

    @Test
    public void findBrowseNodeByIDForRoot() {
        BrowseTree browseTree = new BrowseTree(null);
@@ -102,6 +119,14 @@ public class BrowseTreeTest {
        assertThat(browseTree.findBrowseNodeByID(deviceId)).isEqualTo(browseTree.mRootNode);
    }

    @Test
    public void findBrowseNodeByIDForDevice_flagEnabled() {
        mSetFlagsRule.enableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS);
        BrowseTree browseTree = new BrowseTree(mTestDevice);
        final String deviceId = browseTree.mRootNode.getID();
        assertThat(browseTree.findBrowseNodeByID(deviceId)).isEqualTo(browseTree.mRootNode);
    }

    @Test
    public void findBrowseNodeByIDForIllegalId() {
        BrowseTree browseTree = new BrowseTree(mTestDevice);
@@ -117,6 +142,14 @@ public class BrowseTreeTest {
        assertThat(browseTree.getCurrentBrowsedFolder()).isEqualTo(browseTree.mNowPlayingNode);
    }

    @Test
    public void findBrowseNodeByIDForDevice_withRandomDeviceID_nodeIsFound() {
        mSetFlagsRule.enableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS);
        BrowseTree browseTree = new BrowseTree(mTestDevice);
        final String deviceId = browseTree.mRootNode.getID();
        assertThat(browseTree.findBrowseNodeByID(deviceId)).isEqualTo(browseTree.mRootNode);
    }

    @Test
    public void setAndGetCurrentBrowsedPlayer() {
        BrowseTree browseTree = new BrowseTree(mTestDevice);