OSDN Git Service

Merge "update localepicker test package to sdk 26"
[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
61     /**
62      * Creates an instance of a BluetoothPairingController.
63      *
64      * @param intent - must contain {@link BluetoothDevice#EXTRA_PAIRING_VARIANT}, {@link
65      * BluetoothDevice#EXTRA_PAIRING_KEY}, and {@link BluetoothDevice#EXTRA_DEVICE}. Missing extra
66      * will lead to undefined behavior.
67      */
68     public BluetoothPairingController(Intent intent, Context context) {
69         mBluetoothManager = Utils.getLocalBtManager(context);
70         mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
71
72         String message = "";
73         if (mBluetoothManager == null) {
74             throw new IllegalStateException("Could not obtain LocalBluetoothManager");
75         } else if (mDevice == null) {
76             throw new IllegalStateException("Could not find BluetoothDevice");
77         }
78
79         mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
80         mPasskey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
81         mDeviceName = mBluetoothManager.getCachedDeviceManager().getName(mDevice);
82         mPbapClientProfile = mBluetoothManager.getProfileManager().getPbapClientProfile();
83         mPasskeyFormatted = formatKey(mPasskey);
84
85     }
86
87     @Override
88     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
89         if (isChecked) {
90             mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
91         } else {
92             mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
93         }
94     }
95
96     @Override
97     public void onDialogPositiveClick(BluetoothPairingDialogFragment dialog) {
98         if (getDialogType() == USER_ENTRY_DIALOG) {
99             onPair(mUserInput);
100         } else {
101             onPair(null);
102         }
103     }
104
105     @Override
106     public void onDialogNegativeClick(BluetoothPairingDialogFragment dialog) {
107         onCancel();
108     }
109
110     /**
111      * A method for querying which bluetooth pairing dialog fragment variant this device requires.
112      *
113      * @return - The dialog view variant needed for this device.
114      */
115     public int getDialogType() {
116         switch (mType) {
117             case BluetoothDevice.PAIRING_VARIANT_PIN:
118             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
119             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
120                 return USER_ENTRY_DIALOG;
121
122             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
123             case BluetoothDevice.PAIRING_VARIANT_CONSENT:
124             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
125                 return CONFIRMATION_DIALOG;
126
127             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
128             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
129                 return DISPLAY_PASSKEY_DIALOG;
130
131             default:
132                 return INVALID_DIALOG_TYPE;
133         }
134     }
135
136     /**
137      * @return - A string containing the name provided by the device.
138      */
139     public String getDeviceName() {
140         return mDeviceName;
141     }
142
143     /**
144      * A method for querying if the bluetooth device has a profile already set up on this device.
145      *
146      * @return - A boolean indicating if the device has previous knowledge of a profile for this
147      * device.
148      */
149     public boolean isProfileReady() {
150         return mPbapClientProfile != null && mPbapClientProfile.isProfileReady();
151     }
152
153     /**
154      * A method for querying if the bluetooth device has access to contacts on the device.
155      *
156      * @return - A boolean indicating if the bluetooth device has permission to access the device
157      * contacts
158      */
159     public boolean getContactSharingState() {
160         switch (mDevice.getPhonebookAccessPermission()) {
161             case BluetoothDevice.ACCESS_ALLOWED:
162                 return true;
163             case BluetoothDevice.ACCESS_REJECTED:
164                 return false;
165             default:
166                 if (mDevice.getBluetoothClass().getDeviceClass()
167                         == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
168                     return true;
169                 }
170                 return false;
171         }
172     }
173
174     /**
175      * Update Phone book permission
176      *
177      */
178      public void  setContactSharingState() {
179         if ((mDevice.getPhonebookAccessPermission() != BluetoothDevice.ACCESS_ALLOWED)
180                 && (mDevice.getPhonebookAccessPermission() != BluetoothDevice.ACCESS_REJECTED)) {
181                  if (mDevice.getBluetoothClass().getDeviceClass()
182                         == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
183                     onCheckedChanged(null, true);
184             } else {
185                 onCheckedChanged(null, false);
186             }
187         }
188     }
189
190     /**
191      * A method for querying if the provided editable is a valid passkey/pin format for this device.
192      *
193      * @param s - The passkey/pin
194      * @return - A boolean indicating if the passkey/pin is of the correct format.
195      */
196     public boolean isPasskeyValid(Editable s) {
197         boolean requires16Digits = mType == BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS;
198         return s.length() >= 16 && requires16Digits || s.length() > 0 && !requires16Digits;
199     }
200
201     /**
202      * A method for querying what message should be shown to the user as additional text in the
203      * dialog for this device. Returns -1 to indicate a device type that does not use this message.
204      *
205      * @return - The message ID to show the user.
206      */
207     public int getDeviceVariantMessageId() {
208         switch (mType) {
209             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
210             case BluetoothDevice.PAIRING_VARIANT_PIN:
211                 return R.string.bluetooth_enter_pin_other_device;
212
213             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
214                 return R.string.bluetooth_enter_passkey_other_device;
215
216             default:
217                 return INVALID_DIALOG_TYPE;
218         }
219     }
220
221     /**
222      * A method for querying what message hint should be shown to the user as additional text in the
223      * dialog for this device. Returns -1 to indicate a device type that does not use this message.
224      *
225      * @return - The message ID to show the user.
226      */
227     public int getDeviceVariantMessageHintId() {
228         switch (mType) {
229             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
230                 return R.string.bluetooth_pin_values_hint_16_digits;
231
232             case BluetoothDevice.PAIRING_VARIANT_PIN:
233             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
234                 return R.string.bluetooth_pin_values_hint;
235
236             default:
237                 return INVALID_DIALOG_TYPE;
238         }
239     }
240
241     /**
242      * A method for querying the maximum passkey/pin length for this device.
243      *
244      * @return - An int indicating the maximum length
245      */
246     public int getDeviceMaxPasskeyLength() {
247         switch (mType) {
248             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
249             case BluetoothDevice.PAIRING_VARIANT_PIN:
250                 return BLUETOOTH_PIN_MAX_LENGTH;
251
252             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
253                 return BLUETOOTH_PASSKEY_MAX_LENGTH;
254
255             default:
256                 return 0;
257         }
258
259     }
260
261     /**
262      * A method for querying if the device uses an alphanumeric passkey.
263      *
264      * @return - a boolean indicating if the passkey can be alphanumeric.
265      */
266     public boolean pairingCodeIsAlphanumeric() {
267         switch (mType) {
268             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
269                 return false;
270
271             default:
272                 return true;
273         }
274     }
275
276     /**
277      * A method used by the dialogfragment to notify the controller that the dialog has been
278      * displayed for bluetooth device types that just care about it being displayed.
279      */
280     protected void notifyDialogDisplayed() {
281         // send an OK to the framework, indicating that the dialog has been displayed.
282         if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
283             mDevice.setPairingConfirmation(true);
284         } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
285             byte[] pinBytes = BluetoothDevice.convertPinToBytes(mPasskeyFormatted);
286             mDevice.setPin(pinBytes);
287         }
288     }
289
290     /**
291      * A method for querying if this bluetooth device type has a key it would like displayed
292      * to the user.
293      *
294      * @return - A boolean indicating if a key exists which should be displayed to the user.
295      */
296     public boolean isDisplayPairingKeyVariant() {
297         switch (mType) {
298             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
299             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
300             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
301                 return true;
302             default:
303                 return false;
304         }
305     }
306
307     /**
308      * A method for querying if this bluetooth device type has other content it would like displayed
309      * to the user.
310      *
311      * @return - A boolean indicating if content exists which should be displayed to the user.
312      */
313     public boolean hasPairingContent() {
314         switch (mType) {
315             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
316             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
317             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
318                 return true;
319
320             default:
321                 return false;
322         }
323     }
324
325     /**
326      * A method for obtaining any additional content this bluetooth device has for displaying to the
327      * user.
328      *
329      * @return - A string containing the additional content, null if none exists.
330      * @see {@link BluetoothPairingController#hasPairingContent()}
331      */
332     public String getPairingContent() {
333         if (hasPairingContent()) {
334             return mPasskeyFormatted;
335         } else {
336             return null;
337         }
338     }
339
340     /**
341      * A method that exists to allow the fragment to update the controller with input the user has
342      * provided in the fragment.
343      *
344      * @param input - A string containing the user input.
345      */
346     protected void updateUserInput(String input) {
347         mUserInput = input;
348     }
349
350     /**
351      * Returns the provided passkey in a format that this device expects. Only works for numeric
352      * passkeys/pins.
353      *
354      * @param passkey - An integer containing the passkey to format.
355      * @return - A string containing the formatted passkey/pin
356      */
357     private String formatKey(int passkey) {
358         switch (mType) {
359             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
360             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
361                 return String.format(Locale.US, "%06d", passkey);
362
363             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
364                 return String.format("%04d", passkey);
365
366             default:
367                 return null;
368         }
369     }
370
371     /**
372      * handles the necessary communication with the bluetooth device to establish a successful
373      * pairing
374      *
375      * @param passkey - The passkey we will attempt to pair to the device with.
376      */
377     private void onPair(String passkey) {
378         Log.d(TAG, "Pairing dialog accepted");
379         switch (mType) {
380             case BluetoothDevice.PAIRING_VARIANT_PIN:
381             case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
382                 byte[] pinBytes = BluetoothDevice.convertPinToBytes(passkey);
383                 if (pinBytes == null) {
384                     return;
385                 }
386                 mDevice.setPin(pinBytes);
387                 break;
388
389             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
390                 int pass = Integer.parseInt(passkey);
391                 mDevice.setPasskey(pass);
392                 break;
393
394             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
395             case BluetoothDevice.PAIRING_VARIANT_CONSENT:
396                 mDevice.setPairingConfirmation(true);
397                 break;
398
399             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
400             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
401                 // Do nothing.
402                 break;
403
404             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
405                 mDevice.setRemoteOutOfBandData();
406                 break;
407
408             default:
409                 Log.e(TAG, "Incorrect pairing type received");
410         }
411     }
412
413     /**
414      * A method for properly ending communication with the bluetooth device. Will be called by the
415      * {@link BluetoothPairingDialogFragment} when it is dismissed.
416      */
417     public void onCancel() {
418         Log.d(TAG, "Pairing dialog canceled");
419         mDevice.cancelPairingUserInput();
420     }
421
422     /**
423      * A method for checking if this device is equal to another device.
424      *
425      * @param device - The other device being compared to this device.
426      * @return - A boolean indicating if the devices were equal.
427      */
428     public boolean deviceEquals(BluetoothDevice device) {
429         return mDevice == device;
430     }
431 }