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

Commit a434429c authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "PhonePolicy: Gate CONNECT_OTHER_PROFILE per device"

parents 24b9a718 67f79faa
Loading
Loading
Loading
Loading
+19 −9
Original line number Diff line number Diff line
@@ -80,13 +80,15 @@ class PhonePolicy {
    private static final int MESSAGE_ADAPTER_STATE_TURNED_ON = 4;

    // Timeouts
    private static final int CONNECT_OTHER_PROFILES_TIMEOUT = 6000; // 6s
    @VisibleForTesting
    static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s

    private final AdapterService mAdapterService;
    private final ServiceFactory mFactory;
    private final Handler mHandler;
    private final HashSet<BluetoothDevice> mHeadsetRetrySet = new HashSet<>();
    private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>();
    private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>();

    // Broadcast receiver for all changes to states of various profiles
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -167,12 +169,14 @@ class PhonePolicy {
                }
                break;

                case MESSAGE_CONNECT_OTHER_PROFILES:
                case MESSAGE_CONNECT_OTHER_PROFILES: {
                    // Called when we try connect some profiles in processConnectOtherProfiles but
                    // we send a delayed message to try connecting the remaining profiles
                    processConnectOtherProfiles((BluetoothDevice) msg.obj);
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    processConnectOtherProfiles(device);
                    mConnectOtherProfilesDeviceSet.remove(device);
                    break;

                }
                case MESSAGE_ADAPTER_STATE_TURNED_ON:
                    // Call auto connect when adapter switches state to ON
                    resetStates();
@@ -341,12 +345,18 @@ class PhonePolicy {
    }

    private void connectOtherProfile(BluetoothDevice device) {
        if ((!mHandler.hasMessages(MESSAGE_CONNECT_OTHER_PROFILES))
                && (!mAdapterService.isQuietModeEnabled())) {
        if (mAdapterService.isQuietModeEnabled()) {
            debugLog("connectOtherProfile: in quiet mode, skip connect other profile " + device);
            return;
        }
        if (mConnectOtherProfilesDeviceSet.contains(device)) {
            debugLog("connectOtherProfile: already scheduled callback for " + device);
            return;
        }
        mConnectOtherProfilesDeviceSet.add(device);
        Message m = mHandler.obtainMessage(MESSAGE_CONNECT_OTHER_PROFILES);
        m.obj = device;
            mHandler.sendMessageDelayed(m, CONNECT_OTHER_PROFILES_TIMEOUT);
        }
        mHandler.sendMessageDelayed(m, sConnectOtherProfilesTimeoutMillis);
    }

    // This function is called whenever a profile is connected.  This allows any other bluetooth
+57 −73
Original line number Diff line number Diff line
@@ -28,8 +28,6 @@ import android.content.BroadcastReceiver;
import android.content.Intent;
import android.os.HandlerThread;
import android.os.ParcelUuid;
import android.os.TestLooperManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;

@@ -50,11 +48,15 @@ import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
public class PhonePolicyTest {
    private static final int MAX_CONNECTED_AUDIO_DEVICES = 5;
    private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250;
    private static final int CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS = 1000;
    private static final int CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS =
            CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS * 3 / 2;

    private HandlerThread mHandlerThread;
    private TestLooperManager mTestLooperManager;
    private BluetoothAdapter mAdapter;
    private BluetoothDevice mTestDevice;
    private PhonePolicy mPhonePolicy;

    @Mock private AdapterService mAdapterService;
    @Mock private ServiceFactory mServiceFactory;
@@ -74,20 +76,19 @@ public class PhonePolicyTest {
        // Start handler thread for this test
        mHandlerThread = new HandlerThread("PhonePolicyTestHandlerThread");
        mHandlerThread.start();
        mTestLooperManager = InstrumentationRegistry.getInstrumentation()
                .acquireLooperManager(mHandlerThread.getLooper());
        // Mock the looper
        doReturn(mHandlerThread.getLooper()).when(mAdapterService).getMainLooper();
        // Tell the AdapterService that it is a mock (see isMock documentation)
        doReturn(true).when(mAdapterService).isMock();
        // Must be called to initialize services
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
        mTestDevice = TestUtils.getTestDevice(mAdapter, 1);
        PhonePolicy.sConnectOtherProfilesTimeoutMillis = CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS;
        mPhonePolicy = new PhonePolicy(mAdapterService, mServiceFactory);
    }

    @After
    public void tearDown() throws Exception {
        mTestLooperManager.release();
        mHandlerThread.quit();
        TestUtils.clearAdapterService(mAdapterService);
    }
@@ -106,26 +107,19 @@ public class PhonePolicyTest {
        // Mock the A2DP service to return undefined priority
        when(mA2dpService.getPriority(mTestDevice)).thenReturn(BluetoothProfile.PRIORITY_UNDEFINED);

        PhonePolicy phPol = new PhonePolicy(mAdapterService, mServiceFactory);

        // Get the broadcast receiver to inject events.
        BroadcastReceiver injector = phPol.getBroadcastReceiver();

        // Inject an event for UUIDs updated for a remote device with only HFP enabled
        Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mTestDevice);
        ParcelUuid[] uuids = new ParcelUuid[2];
        uuids[0] = BluetoothUuid.Handsfree;
        uuids[1] = BluetoothUuid.AudioSink;

        intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids);
        injector.onReceive(null /* context */, intent);
        mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);

        // Check that the priorities of the devices for preferred profiles are set to ON
        executePendingMessages(1);
        verify(mHeadsetService, times(1)).setPriority(eq(mTestDevice),
        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(eq(mTestDevice),
                eq(BluetoothProfile.PRIORITY_ON));
        verify(mA2dpService, times(1)).setPriority(eq(mTestDevice),
        verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(eq(mTestDevice),
                eq(BluetoothProfile.PRIORITY_ON));
    }

@@ -152,20 +146,14 @@ public class PhonePolicyTest {
        when(mA2dpService.getPriority(mTestDevice)).thenReturn(
                BluetoothProfile.PRIORITY_AUTO_CONNECT);

        PhonePolicy phPol = new PhonePolicy(mAdapterService, mServiceFactory);

        // Get the broadcast receiver to inject events
        BroadcastReceiver injector = phPol.getBroadcastReceiver();

        // Inject an event that the adapter is turned on.
        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
        intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
        injector.onReceive(null /* context */, intent);
        mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);

        // Check that we got a request to connect over HFP and A2DP
        executePendingMessages(1);
        verify(mHeadsetService, times(1)).connect(eq(mTestDevice));
        verify(mA2dpService, times(1)).connect(eq(mTestDevice));
        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connect(eq(mTestDevice));
        verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connect(eq(mTestDevice));
    }

    /**
@@ -188,8 +176,6 @@ public class PhonePolicyTest {

        when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON);

        PhonePolicy phPol = new PhonePolicy(mAdapterService, mServiceFactory);

        // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP
        // To enable that we need to make sure that HeadsetService returns the device as list of
        // connected devices
@@ -200,9 +186,6 @@ public class PhonePolicyTest {
        when(mA2dpService.getConnectionState(mTestDevice)).thenReturn(
                BluetoothProfile.STATE_DISCONNECTED);

        // Get the broadcast receiver to inject events
        BroadcastReceiver injector = phPol.getBroadcastReceiver();

        // We send a connection successful for one profile since the re-connect *only* works if we
        // have already connected successfully over one of the profiles
        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
@@ -210,11 +193,11 @@ public class PhonePolicyTest {
        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        injector.onReceive(null /* context */, intent);
        mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);

        // Check that we get a call to A2DP connect
        executePendingMessages(2);
        verify(mA2dpService, times(1)).connect(eq(mTestDevice));
        verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect(
                eq(mTestDevice));
    }

    /**
@@ -222,11 +205,12 @@ public class PhonePolicyTest {
     */
    @Test
    public void testAutoConnectMultipleDevices() {
        final int kMaxTestDevices = 2;
        final int kMaxTestDevices = 3;
        BluetoothDevice[] testDevices = new BluetoothDevice[kMaxTestDevices];
        ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>();
        ArrayList<BluetoothDevice> a2dpConnectedDevices = new ArrayList<>();
        BluetoothDevice a2dpNotConnectedDevice = null;
        BluetoothDevice a2dpNotConnectedDevice1 = null;
        BluetoothDevice a2dpNotConnectedDevice2 = null;

        for (int i = 0; i < kMaxTestDevices; i++) {
            BluetoothDevice testDevice = TestUtils.getTestDevice(mAdapter, i);
@@ -243,36 +227,46 @@ public class PhonePolicyTest {
            // of connected devices.
            hsConnectedDevices.add(testDevice);
            // Connect A2DP for all devices except the last one
            if (i < kMaxTestDevices - 1) {
            if (i < (kMaxTestDevices - 2)) {
                a2dpConnectedDevices.add(testDevice);
            } else {
                a2dpNotConnectedDevice = testDevice;
            }
        }
        a2dpNotConnectedDevice1 = hsConnectedDevices.get(kMaxTestDevices - 1);
        a2dpNotConnectedDevice2 = hsConnectedDevices.get(kMaxTestDevices - 2);

        when(mAdapterService.getBondedDevices()).thenReturn(testDevices);
        when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON);
        when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices);
        when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices);
        // One of the A2DP devices is not connected
        when(mA2dpService.getConnectionState(a2dpNotConnectedDevice)).thenReturn(
        // Two of the A2DP devices are not connected
        when(mA2dpService.getConnectionState(a2dpNotConnectedDevice1)).thenReturn(
                BluetoothProfile.STATE_DISCONNECTED);
        when(mA2dpService.getConnectionState(a2dpNotConnectedDevice2)).thenReturn(
                BluetoothProfile.STATE_DISCONNECTED);

        // Get the broadcast receiver to inject events
        PhonePolicy phPol = new PhonePolicy(mAdapterService, mServiceFactory);
        BroadcastReceiver injector = phPol.getBroadcastReceiver();

        // We send a connection successful for one profile since the re-connect *only* works if we
        // have already connected successfully over one of the profiles
        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, a2dpNotConnectedDevice);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, a2dpNotConnectedDevice1);
        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        injector.onReceive(null /* context */, intent);
        mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);

        // We send a connection successful for one profile since the re-connect *only* works if we
        // have already connected successfully over one of the profiles
        intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, a2dpNotConnectedDevice2);
        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);

        // Check that we get a call to A2DP connect
        executePendingMessages(2);
        verify(mA2dpService, times(1)).connect(eq(a2dpNotConnectedDevice));
        verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect(
                eq(a2dpNotConnectedDevice1));
        verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect(
                eq(a2dpNotConnectedDevice2));
    }

    /**
@@ -312,22 +306,19 @@ public class PhonePolicyTest {
                hsConnectedDevices.add(testDevice);
                when(mHeadsetService.getPriority(testDevice)).thenReturn(
                        BluetoothProfile.PRIORITY_ON);
                when(mA2dpService.getPriority(testDevice)).thenReturn(
                        BluetoothProfile.PRIORITY_ON);
                when(mA2dpService.getPriority(testDevice)).thenReturn(BluetoothProfile.PRIORITY_ON);
            }
            if (i == 2) {
                a2dpConnectedDevices.add(testDevice);
                when(mHeadsetService.getPriority(testDevice)).thenReturn(
                        BluetoothProfile.PRIORITY_ON);
                when(mA2dpService.getPriority(testDevice)).thenReturn(
                        BluetoothProfile.PRIORITY_ON);
                when(mA2dpService.getPriority(testDevice)).thenReturn(BluetoothProfile.PRIORITY_ON);
            }
            if (i == 3) {
                // Device not connected
                when(mHeadsetService.getPriority(testDevice)).thenReturn(
                        BluetoothProfile.PRIORITY_ON);
                when(mA2dpService.getPriority(testDevice)).thenReturn(
                        BluetoothProfile.PRIORITY_ON);
                when(mA2dpService.getPriority(testDevice)).thenReturn(BluetoothProfile.PRIORITY_ON);
            }
        }
        when(mAdapterService.getBondedDevices()).thenReturn(testDevices);
@@ -369,8 +360,8 @@ public class PhonePolicyTest {
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        injector.onReceive(null /* context */, intent);
        // Check that we get a call to A2DP connect
        executePendingMessages(2);
        verify(mA2dpService, times(1)).connect(eq(testDevices[1]));
        verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect(
                eq(testDevices[1]));

        // testDevices[1] auto-connect completed for A2DP
        a2dpConnectedDevices.add(testDevices[1]);
@@ -404,8 +395,8 @@ public class PhonePolicyTest {
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        injector.onReceive(null /* context */, intent);
        // Check that we get a call to HFP connect
        executePendingMessages(2);
        verify(mHeadsetService, times(1)).connect(eq(testDevices[2]));
        verify(mHeadsetService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect(
                eq(testDevices[2]));

        // testDevices[2] auto-connect completed for HFP
        hsConnectedDevices.add(testDevices[2]);
@@ -473,8 +464,8 @@ public class PhonePolicyTest {
        injector.onReceive(null /* context */, intent);

        // Check that we don't get any calls to reconnect
        executePendingMessages(1);
        verify(mA2dpService, never()).connect(eq(mTestDevice));
        verify(mA2dpService, after(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS).never()).connect(
                eq(mTestDevice));
        verify(mHeadsetService, never()).connect(eq(mTestDevice));
    }

@@ -504,17 +495,10 @@ public class PhonePolicyTest {
        injector.onReceive(null /* context */, intent);

        // Check that we do not crash and not call any setPriority methods
        executePendingMessages(1);
        verify(mHeadsetService, never()).setPriority(eq(mTestDevice),
                eq(BluetoothProfile.PRIORITY_ON));
        verify(mHeadsetService,
                after(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS).never()).setPriority(
                eq(mTestDevice), eq(BluetoothProfile.PRIORITY_ON));
        verify(mA2dpService, never()).setPriority(eq(mTestDevice),
                eq(BluetoothProfile.PRIORITY_ON));
    }

    private void executePendingMessages(int numMessage) {
        while (numMessage > 0) {
            mTestLooperManager.execute(mTestLooperManager.next());
            numMessage--;
        }
    }
}