OSDN Git Service

Merge changes from topic "am-882196d9-9fac-4e85-b0bf-2018fa4b68ae" into oc-dev am...
[android-x86/packages-apps-Settings.git] / src / com / android / settings / bluetooth / BluetoothPairingController.java
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.android.settings.bluetooth;
17
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;
31
32 /**
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.
36  */
37 public class BluetoothPairingController implements OnCheckedChangeListener,
38         BluetoothPairingDialogListener {
39
40     private static final String TAG = "BTPairingController";
41
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;
47
48     private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
49     private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
50
51     // Bluetooth dependencies for the connection we are trying to establish
52     private LocalBluetoothManager mBluetoothManager;
53     private BluetoothDevice mDevice;
54     private int mType;
55     private String mUserInput;
56     private String mPasskeyFormatted;
57     private int mPasskey;
58     private String mDeviceName;
59     private LocalBluetoothProfile mPbapClientProfile;
60     private boolean mPbapAllowed;
61
62     /**
63      * Creates an instance of a BluetoothPairingController.
64      *
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.
68      */
69     public BluetoothPairingController(Intent intent, Context context) {
70         mBluetoothManager = Utils.getLocalBtManager(context);
71         mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
72
73         String message = "";
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");
78         }
79
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);
85
86     }
87
88     @Override
89     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
90         if (isChecked) {
91             mPbapAllowed = true;
92         } else {
93             mPbapAllowed = false;
94         }
95     }
96
97     @Override
98     public void onDialogPositiveClick(BluetoothPairingDialogFragment dialog) {
99         if (getDialogType() == USER_ENTRY_DIALOG) {
100             if (mPbapAllowed) {
101                 mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
102             } else {
103                 mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
104             }
105             onPair(mUserInput);
106         } else {
107             onPair(null);
108         }
109     }
110
111     @Override
112     public void onDialogNegativeClick(BluetoothPairingDialogFragment dialog) {
113         mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
114         onCancel();
115     }
116
117     /**
118      * A method for querying which bluetooth pairing dialog fragment variant this device requires.
119      *
120      * @return - The dialog view variant needed for this device.
121      */
122     public int getDialogType() {
123         switch (mType) {
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;
128
129             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
130             case BluetoothDevice.PAIRING_VARIANT_CONSENT:
131             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
132                 return CONFIRMATION_DIALOG;
133
134             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
135             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
136                 return DISPLAY_PASSKEY_DIALOG;
137
138             default:
139                 return INVALID_DIALOG_TYPE;
140         }
141     }
142
143     /**
144      * @return - A string containing the name provided by the device.
145      */
146     public String getDeviceName() {
147         return mDeviceName;
148     }
149
150     /**
151      * A method for querying if the bluetooth device has a profile already set up on this device.
152      *
153      * @return - A boolean indicating if the device has previous knowledge of a profile for this
154      * device.
155      */
156     public boolean isProfileReady() {
157         return mPbapClientProfile != null && mPbapClientProfile.isProfileReady();
158     }
159
160     /**
161      * A method for querying if the bluetooth device has access to contacts on the device.
162      *
163      * @return - A boolean indicating if the bluetooth device has permission to access the device
164      * contacts
165      */
166     public boolean getContactSharingState() {
167         switch (mDevice.getPhonebookAccessPermission()) {
168             case BluetoothDevice.ACCESS_ALLOWED:
169                 return true;
170             case BluetoothDevice.ACCESS_REJECTED:
171                 return false;
172             default:
173                 if (mDevice.getBluetoothClass().getDeviceClass()
174                         == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
175                     return true;
176                 }
177                 return false;
178         }
179     }
180
181     /**
182      * Update Phone book permission
183      *
184      */
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);
191             } else {
192                 onCheckedChanged(null, false);
193             }
194         }
195     }
196
197     /**
198      * A method for querying if the provided editable is a valid passkey/pin format for this device.
199      *
200      * @param s - The passkey/pin
201      * @return - A boolean indicating if the passkey/pin is of the correct format.
202      */
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;
206     }
207
208     /**
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.
211      *
212      * @return - The message ID to show the user.
213      */
214     public int getDeviceVariantMessageId() {
215         switch (mType) {
216             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
217             case BluetoothDevice.PAIRING_VARIANT_PIN:
218                 return R.string.bluetooth_enter_pin_other_device;
219
220             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
221                 return R.string.bluetooth_enter_passkey_other_device;
222
223             default:
224                 return INVALID_DIALOG_TYPE;
225         }
226     }
227
228     /**
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.
231      *
232      * @return - The message ID to show the user.
233      */
234     public int getDeviceVariantMessageHintId() {
235         switch (mType) {
236             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
237                 return R.string.bluetooth_pin_values_hint_16_digits;
238
239             case BluetoothDevice.PAIRING_VARIANT_PIN:
240             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
241                 return R.string.bluetooth_pin_values_hint;
242
243             default:
244                 return INVALID_DIALOG_TYPE;
245         }
246     }
247
248     /**
249      * A method for querying the maximum passkey/pin length for this device.
250      *
251      * @return - An int indicating the maximum length
252      */
253     public int getDeviceMaxPasskeyLength() {
254         switch (mType) {
255             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
256             case BluetoothDevice.PAIRING_VARIANT_PIN:
257                 return BLUETOOTH_PIN_MAX_LENGTH;
258
259             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
260                 return BLUETOOTH_PASSKEY_MAX_LENGTH;
261
262             default:
263                 return 0;
264         }
265
266     }
267
268     /**
269      * A method for querying if the device uses an alphanumeric passkey.
270      *
271      * @return - a boolean indicating if the passkey can be alphanumeric.
272      */
273     public boolean pairingCodeIsAlphanumeric() {
274         switch (mType) {
275             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
276                 return false;
277
278             default:
279                 return true;
280         }
281     }
282
283     /**
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.
286      */
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);
294         }
295     }
296
297     /**
298      * A method for querying if this bluetooth device type has a key it would like displayed
299      * to the user.
300      *
301      * @return - A boolean indicating if a key exists which should be displayed to the user.
302      */
303     public boolean isDisplayPairingKeyVariant() {
304         switch (mType) {
305             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
306             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
307             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
308                 return true;
309             default:
310                 return false;
311         }
312     }
313
314     /**
315      * A method for querying if this bluetooth device type has other content it would like displayed
316      * to the user.
317      *
318      * @return - A boolean indicating if content exists which should be displayed to the user.
319      */
320     public boolean hasPairingContent() {
321         switch (mType) {
322             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
323             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
324             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
325                 return true;
326
327             default:
328                 return false;
329         }
330     }
331
332     /**
333      * A method for obtaining any additional content this bluetooth device has for displaying to the
334      * user.
335      *
336      * @return - A string containing the additional content, null if none exists.
337      * @see {@link BluetoothPairingController#hasPairingContent()}
338      */
339     public String getPairingContent() {
340         if (hasPairingContent()) {
341             return mPasskeyFormatted;
342         } else {
343             return null;
344         }
345     }
346
347     /**
348      * A method that exists to allow the fragment to update the controller with input the user has
349      * provided in the fragment.
350      *
351      * @param input - A string containing the user input.
352      */
353     protected void updateUserInput(String input) {
354         mUserInput = input;
355     }
356
357     /**
358      * Returns the provided passkey in a format that this device expects. Only works for numeric
359      * passkeys/pins.
360      *
361      * @param passkey - An integer containing the passkey to format.
362      * @return - A string containing the formatted passkey/pin
363      */
364     private String formatKey(int passkey) {
365         switch (mType) {
366             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
367             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
368                 return String.format(Locale.US, "%06d", passkey);
369
370             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
371                 return String.format("%04d", passkey);
372
373             default:
374                 return null;
375         }
376     }
377
378     /**
379      * handles the necessary communication with the bluetooth device to establish a successful
380      * pairing
381      *
382      * @param passkey - The passkey we will attempt to pair to the device with.
383      */
384     private void onPair(String passkey) {
385         Log.d(TAG, "Pairing dialog accepted");
386         switch (mType) {
387             case BluetoothDevice.PAIRING_VARIANT_PIN:
388             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
389                 byte[] pinBytes = BluetoothDevice.convertPinToBytes(passkey);
390                 if (pinBytes == null) {
391                     return;
392                 }
393                 mDevice.setPin(pinBytes);
394                 break;
395
396             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
397                 int pass = Integer.parseInt(passkey);
398                 mDevice.setPasskey(pass);
399                 break;
400
401             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
402             case BluetoothDevice.PAIRING_VARIANT_CONSENT:
403                 mDevice.setPairingConfirmation(true);
404                 break;
405
406             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
407             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
408                 // Do nothing.
409                 break;
410
411             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
412                 mDevice.setRemoteOutOfBandData();
413                 break;
414
415             default:
416                 Log.e(TAG, "Incorrect pairing type received");
417         }
418     }
419
420     /**
421      * A method for properly ending communication with the bluetooth device. Will be called by the
422      * {@link BluetoothPairingDialogFragment} when it is dismissed.
423      */
424     public void onCancel() {
425         Log.d(TAG, "Pairing dialog canceled");
426         mDevice.cancelPairingUserInput();
427     }
428
429     /**
430      * A method for checking if this device is equal to another device.
431      *
432      * @param device - The other device being compared to this device.
433      * @return - A boolean indicating if the devices were equal.
434      */
435     public boolean deviceEquals(BluetoothDevice device) {
436         return mDevice == device;
437     }
438 }