2 * Copyright (C) 2016 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.android.settings.bluetooth;
18 import android.bluetooth.BluetoothClass;
19 import android.bluetooth.BluetoothDevice;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.text.Editable;
23 import android.util.Log;
24 import android.widget.CompoundButton;
25 import android.widget.CompoundButton.OnCheckedChangeListener;
26 import com.android.settings.R;
27 import com.android.settings.bluetooth.BluetoothPairingDialogFragment.BluetoothPairingDialogListener;
28 import com.android.settingslib.bluetooth.LocalBluetoothManager;
29 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
30 import java.util.Locale;
33 * A controller used by {@link BluetoothPairingDialog} to manage connection state while we try to
34 * pair with a bluetooth device. It includes methods that allow the
35 * {@link BluetoothPairingDialogFragment} to interrogate the current state as well.
37 public class BluetoothPairingController implements OnCheckedChangeListener,
38 BluetoothPairingDialogListener {
40 private static final String TAG = "BTPairingController";
42 // Different types of dialogs we can map to
43 public static final int INVALID_DIALOG_TYPE = -1;
44 public static final int USER_ENTRY_DIALOG = 0;
45 public static final int CONFIRMATION_DIALOG = 1;
46 public static final int DISPLAY_PASSKEY_DIALOG = 2;
48 private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
49 private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
51 // Bluetooth dependencies for the connection we are trying to establish
52 private LocalBluetoothManager mBluetoothManager;
53 private BluetoothDevice mDevice;
55 private String mUserInput;
56 private String mPasskeyFormatted;
58 private String mDeviceName;
59 private LocalBluetoothProfile mPbapClientProfile;
60 private boolean mPbapAllowed;
63 * Creates an instance of a BluetoothPairingController.
65 * @param intent - must contain {@link BluetoothDevice#EXTRA_PAIRING_VARIANT}, {@link
66 * BluetoothDevice#EXTRA_PAIRING_KEY}, and {@link BluetoothDevice#EXTRA_DEVICE}. Missing extra
67 * will lead to undefined behavior.
69 public BluetoothPairingController(Intent intent, Context context) {
70 mBluetoothManager = Utils.getLocalBtManager(context);
71 mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
74 if (mBluetoothManager == null) {
75 throw new IllegalStateException("Could not obtain LocalBluetoothManager");
76 } else if (mDevice == null) {
77 throw new IllegalStateException("Could not find BluetoothDevice");
80 mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
81 mPasskey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
82 mDeviceName = mBluetoothManager.getCachedDeviceManager().getName(mDevice);
83 mPbapClientProfile = mBluetoothManager.getProfileManager().getPbapClientProfile();
84 mPasskeyFormatted = formatKey(mPasskey);
89 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
98 public void onDialogPositiveClick(BluetoothPairingDialogFragment dialog) {
99 if (getDialogType() == USER_ENTRY_DIALOG) {
101 mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
103 mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
112 public void onDialogNegativeClick(BluetoothPairingDialogFragment dialog) {
113 mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
118 * A method for querying which bluetooth pairing dialog fragment variant this device requires.
120 * @return - The dialog view variant needed for this device.
122 public int getDialogType() {
124 case BluetoothDevice.PAIRING_VARIANT_PIN:
125 case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
126 case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
127 return USER_ENTRY_DIALOG;
129 case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
130 case BluetoothDevice.PAIRING_VARIANT_CONSENT:
131 case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
132 return CONFIRMATION_DIALOG;
134 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
135 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
136 return DISPLAY_PASSKEY_DIALOG;
139 return INVALID_DIALOG_TYPE;
144 * @return - A string containing the name provided by the device.
146 public String getDeviceName() {
151 * A method for querying if the bluetooth device has a profile already set up on this device.
153 * @return - A boolean indicating if the device has previous knowledge of a profile for this
156 public boolean isProfileReady() {
157 return mPbapClientProfile != null && mPbapClientProfile.isProfileReady();
161 * A method for querying if the bluetooth device has access to contacts on the device.
163 * @return - A boolean indicating if the bluetooth device has permission to access the device
166 public boolean getContactSharingState() {
167 switch (mDevice.getPhonebookAccessPermission()) {
168 case BluetoothDevice.ACCESS_ALLOWED:
170 case BluetoothDevice.ACCESS_REJECTED:
173 if (mDevice.getBluetoothClass().getDeviceClass()
174 == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
182 * Update Phone book permission
185 public void setContactSharingState() {
186 if ((mDevice.getPhonebookAccessPermission() != BluetoothDevice.ACCESS_ALLOWED)
187 && (mDevice.getPhonebookAccessPermission() != BluetoothDevice.ACCESS_REJECTED)) {
188 if (mDevice.getBluetoothClass().getDeviceClass()
189 == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
190 onCheckedChanged(null, true);
192 onCheckedChanged(null, false);
198 * A method for querying if the provided editable is a valid passkey/pin format for this device.
200 * @param s - The passkey/pin
201 * @return - A boolean indicating if the passkey/pin is of the correct format.
203 public boolean isPasskeyValid(Editable s) {
204 boolean requires16Digits = mType == BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS;
205 return s.length() >= 16 && requires16Digits || s.length() > 0 && !requires16Digits;
209 * A method for querying what message should be shown to the user as additional text in the
210 * dialog for this device. Returns -1 to indicate a device type that does not use this message.
212 * @return - The message ID to show the user.
214 public int getDeviceVariantMessageId() {
216 case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
217 case BluetoothDevice.PAIRING_VARIANT_PIN:
218 return R.string.bluetooth_enter_pin_other_device;
220 case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
221 return R.string.bluetooth_enter_passkey_other_device;
224 return INVALID_DIALOG_TYPE;
229 * A method for querying what message hint should be shown to the user as additional text in the
230 * dialog for this device. Returns -1 to indicate a device type that does not use this message.
232 * @return - The message ID to show the user.
234 public int getDeviceVariantMessageHintId() {
236 case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
237 return R.string.bluetooth_pin_values_hint_16_digits;
239 case BluetoothDevice.PAIRING_VARIANT_PIN:
240 case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
241 return R.string.bluetooth_pin_values_hint;
244 return INVALID_DIALOG_TYPE;
249 * A method for querying the maximum passkey/pin length for this device.
251 * @return - An int indicating the maximum length
253 public int getDeviceMaxPasskeyLength() {
255 case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
256 case BluetoothDevice.PAIRING_VARIANT_PIN:
257 return BLUETOOTH_PIN_MAX_LENGTH;
259 case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
260 return BLUETOOTH_PASSKEY_MAX_LENGTH;
269 * A method for querying if the device uses an alphanumeric passkey.
271 * @return - a boolean indicating if the passkey can be alphanumeric.
273 public boolean pairingCodeIsAlphanumeric() {
275 case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
284 * A method used by the dialogfragment to notify the controller that the dialog has been
285 * displayed for bluetooth device types that just care about it being displayed.
287 protected void notifyDialogDisplayed() {
288 // send an OK to the framework, indicating that the dialog has been displayed.
289 if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
290 mDevice.setPairingConfirmation(true);
291 } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
292 byte[] pinBytes = BluetoothDevice.convertPinToBytes(mPasskeyFormatted);
293 mDevice.setPin(pinBytes);
298 * A method for querying if this bluetooth device type has a key it would like displayed
301 * @return - A boolean indicating if a key exists which should be displayed to the user.
303 public boolean isDisplayPairingKeyVariant() {
305 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
306 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
307 case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
315 * A method for querying if this bluetooth device type has other content it would like displayed
318 * @return - A boolean indicating if content exists which should be displayed to the user.
320 public boolean hasPairingContent() {
322 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
323 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
324 case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
333 * A method for obtaining any additional content this bluetooth device has for displaying to the
336 * @return - A string containing the additional content, null if none exists.
337 * @see {@link BluetoothPairingController#hasPairingContent()}
339 public String getPairingContent() {
340 if (hasPairingContent()) {
341 return mPasskeyFormatted;
348 * A method that exists to allow the fragment to update the controller with input the user has
349 * provided in the fragment.
351 * @param input - A string containing the user input.
353 protected void updateUserInput(String input) {
358 * Returns the provided passkey in a format that this device expects. Only works for numeric
361 * @param passkey - An integer containing the passkey to format.
362 * @return - A string containing the formatted passkey/pin
364 private String formatKey(int passkey) {
366 case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
367 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
368 return String.format(Locale.US, "%06d", passkey);
370 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
371 return String.format("%04d", passkey);
379 * handles the necessary communication with the bluetooth device to establish a successful
382 * @param passkey - The passkey we will attempt to pair to the device with.
384 private void onPair(String passkey) {
385 Log.d(TAG, "Pairing dialog accepted");
387 case BluetoothDevice.PAIRING_VARIANT_PIN:
388 case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
389 byte[] pinBytes = BluetoothDevice.convertPinToBytes(passkey);
390 if (pinBytes == null) {
393 mDevice.setPin(pinBytes);
396 case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
397 int pass = Integer.parseInt(passkey);
398 mDevice.setPasskey(pass);
401 case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
402 case BluetoothDevice.PAIRING_VARIANT_CONSENT:
403 mDevice.setPairingConfirmation(true);
406 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
407 case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
411 case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
412 mDevice.setRemoteOutOfBandData();
416 Log.e(TAG, "Incorrect pairing type received");
421 * A method for properly ending communication with the bluetooth device. Will be called by the
422 * {@link BluetoothPairingDialogFragment} when it is dismissed.
424 public void onCancel() {
425 Log.d(TAG, "Pairing dialog canceled");
426 mDevice.cancelPairingUserInput();
430 * A method for checking if this device is equal to another device.
432 * @param device - The other device being compared to this device.
433 * @return - A boolean indicating if the devices were equal.
435 public boolean deviceEquals(BluetoothDevice device) {
436 return mDevice == device;