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

Commit 2a67cf04 authored by Jack He's avatar Jack He
Browse files

Bluetooth: Dismiss pairing dialog on user click

* Existing pairing dialog should be dismissed when user clicks on Yes/No
* In a pairing session with multiple pairing dialogs, this is necessary
  as otherwise the second pairing dialog will not be shown
* Modified unit test to test this behavior
* Launch pairing dialog as UserHandle.CURRENT to avoid Context warnings

Bug: 35833536
Test: make, unit test, pair with Bluetooth devices
Change-Id: I1823b78d287134505f59eab7caca2329ecc3a36f
parent d796fda4
Loading
Loading
Loading
Loading
+24 −13
Original line number Diff line number Diff line
@@ -24,16 +24,17 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;

/**
 * BluetoothPairingDialog asks the user to enter a PIN / Passkey / simple confirmation
 * for pairing with a remote Bluetooth device. It is an activity that appears as a dialog.
 */
public final class BluetoothPairingDialog extends Activity {
public class BluetoothPairingDialog extends Activity {
    public static final String FRAGMENT_TAG = "bluetooth.pairing.fragment";

    private BluetoothPairingController mBluetoothPairingController;
    private boolean mReceiverRegistered;
    private boolean mReceiverRegistered = false;

    /**
     * Dismiss the dialog if the bond state changes to bonded or none,
@@ -62,23 +63,26 @@ public final class BluetoothPairingDialog extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        boolean fragmentFound = true;

        BluetoothPairingDialogFragment bluetoothFragment =
                (BluetoothPairingDialogFragment) getFragmentManager()
                        .findFragmentByTag(FRAGMENT_TAG);
        Intent intent = getIntent();
        mBluetoothPairingController = new BluetoothPairingController(intent, this);

        // check if the fragment exists already
        // build the dialog fragment
        boolean fragmentFound = true;
        // check if the fragment has been preloaded
        BluetoothPairingDialogFragment bluetoothFragment =
            (BluetoothPairingDialogFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
        // dismiss the fragment if it is already used
        if (bluetoothFragment != null && (bluetoothFragment.isPairingControllerSet()
            || bluetoothFragment.isPairingDialogActivitySet())) {
            bluetoothFragment.dismiss();
            bluetoothFragment = null;
        }
        // build a new fragment if it is null
        if (bluetoothFragment == null) {
            fragmentFound = false;
            bluetoothFragment = new BluetoothPairingDialogFragment();
        }

        // set the controller
        bluetoothFragment.setPairingController(mBluetoothPairingController);

        bluetoothFragment.setPairingDialogActivity(this);
        // pass the fragment to the manager when it is created from scratch
        if (!fragmentFound) {
            bluetoothFragment.show(getFragmentManager(), FRAGMENT_TAG);
@@ -101,8 +105,15 @@ public final class BluetoothPairingDialog extends Activity {
        }
    }

    private void dismiss() {
    @VisibleForTesting
    void dismiss() {
        if (!isFinishing()) {
            BluetoothPairingDialogFragment bluetoothFragment =
                (BluetoothPairingDialogFragment) getFragmentManager()
                    .findFragmentByTag(FRAGMENT_TAG);
            if (bluetoothFragment != null) {
                bluetoothFragment.dismiss();
            }
            finish();
        }
    }
+37 −4
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i
    private AlertDialog.Builder mBuilder;
    private AlertDialog mDialog;
    private BluetoothPairingController mPairingController;
    private BluetoothPairingDialog mPairingDialogActivity;
    private EditText mPairingView;
    /**
     * The interface we expect a listener to implement. Typically this should be done by
@@ -61,10 +62,14 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        if (mPairingController == null) {
        if (!isPairingControllerSet()) {
            throw new IllegalStateException(
                "Must call setPairingController() before showing dialog");
        }
        if (!isPairingDialogActivitySet()) {
            throw new IllegalStateException(
                "Must call setPairingDialogActivity() before showing dialog");
        }
        mBuilder = new AlertDialog.Builder(getActivity());
        mDialog = setupDialog();
        mDialog.setCanceledOnTouchOutside(false);
@@ -97,6 +102,7 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i
        } else if (which == DialogInterface.BUTTON_NEGATIVE) {
            mPairingController.onDialogNegativeClick(this);
        }
        mPairingDialogActivity.dismiss();
    }

    @Override
@@ -119,14 +125,41 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i
     * controller may not be substituted once it is assigned. Forcibly switching a
     * controller for a new one will lead to undefined behavior.
     */
    public void setPairingController(BluetoothPairingController pairingController) {
        if (mPairingController != null) {
    void setPairingController(BluetoothPairingController pairingController) {
        if (isPairingControllerSet()) {
            throw new IllegalStateException("The controller can only be set once. "
                    + "Forcibly replacing it will lead to undefined behavior");
        }
        mPairingController = pairingController;
    }

    /**
     * Checks whether mPairingController is set
     * @return True when mPairingController is set, False otherwise
     */
    boolean isPairingControllerSet() {
        return mPairingController != null;
    }

    /**
     * Sets the BluetoothPairingDialog activity that started this fragment
     * @param pairingDialogActivity The pairing dialog activty that started this fragment
     */
    void setPairingDialogActivity(BluetoothPairingDialog pairingDialogActivity) {
        if (isPairingDialogActivitySet()) {
            throw new IllegalStateException("The pairing dialog activity can only be set once");
        }
        mPairingDialogActivity = pairingDialogActivity;
    }

    /**
     * Checks whether mPairingDialogActivity is set
     * @return True when mPairingDialogActivity is set, False otherwise
     */
    boolean isPairingDialogActivitySet() {
        return mPairingDialogActivity != null;
    }

    /**
     * Creates the appropriate type of dialog and returns it.
     */
+1 −1
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ public final class BluetoothPairingRequest extends BroadcastReceiver {
    if (powerManager.isInteractive() && shouldShowDialog) {
      // Since the screen is on and the BT-related activity is in the foreground,
      // just open the dialog
      context.startActivity(pairingIntent);
      context.startActivityAsUser(pairingIntent, UserHandle.CURRENT);
    } else {
      // Put up a notification that leads to the dialog
      intent.setClass(context, BluetoothPairingService.class);
+56 −0
Original line number Diff line number Diff line
@@ -54,9 +54,13 @@ public class BluetoothPairingDialogTest {
    @Mock
    private BluetoothPairingController controller;

    @Mock
    private BluetoothPairingDialog dialogActivity;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        doNothing().when(dialogActivity).dismiss();
    }

    @Test
@@ -214,6 +218,17 @@ public class BluetoothPairingDialogTest {
        fail("Setting the controller multiple times should throw an exception.");
    }

    @Test(expected = IllegalStateException.class)
    public void dialogDoesNotAllowSwappingActivity() {
        // instantiate a fragment
        BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
        frag.setPairingDialogActivity(dialogActivity);

        // this should throw an error
        frag.setPairingDialogActivity(dialogActivity);
        fail("Setting the dialog activity multiple times should throw an exception.");
    }

    @Test
    public void dialogPositiveButtonDisabledWhenUserInputInvalid() {
        // set the correct dialog type
@@ -342,11 +357,52 @@ public class BluetoothPairingDialogTest {
                .contains(device);
    }

    @Test
    public void pairingDialogDismissedOnPositiveClick() {
        // set the dialog variant to confirmation/consent
        when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);

        // we don't care what this does, just that it is called
        doNothing().when(controller).onDialogPositiveClick(any());

        // build the fragment
        BluetoothPairingDialogFragment frag = makeFragment();

        // click the button and verify that the controller hook was called
        frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_POSITIVE);

        verify(controller, times(1)).onDialogPositiveClick(any());
        verify(dialogActivity, times(1)).dismiss();
    }

    @Test
    public void pairingDialogDismissedOnNegativeClick() {
        // set the dialog variant to confirmation/consent
        when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);

        // we don't care what this does, just that it is called
        doNothing().when(controller).onDialogNegativeClick(any());

        // build the fragment
        BluetoothPairingDialogFragment frag = makeFragment();

        // click the button and verify that the controller hook was called
        frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_NEGATIVE);

        verify(controller, times(1)).onDialogNegativeClick(any());
        verify(dialogActivity, times(1)).dismiss();
    }

    private BluetoothPairingDialogFragment makeFragment() {
        BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
        assertThat(frag.isPairingControllerSet()).isFalse();
        frag.setPairingController(controller);
        assertThat(frag.isPairingDialogActivitySet()).isFalse();
        frag.setPairingDialogActivity(dialogActivity);
        FragmentTestUtil.startFragment(frag);
        assertThat(frag.getmDialog()).isNotNull();
        assertThat(frag.isPairingControllerSet()).isTrue();
        assertThat(frag.isPairingDialogActivitySet()).isTrue();
        return frag;
    }
}