Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2008 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 
     17 package com.android.settings.bluetooth;
     18 
     19 import android.bluetooth.BluetoothDevice;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.os.Bundle;
     26 import android.text.Editable;
     27 import android.text.InputFilter;
     28 import android.text.InputType;
     29 import android.text.TextWatcher;
     30 import android.text.InputFilter.LengthFilter;
     31 import android.util.Log;
     32 import android.view.View;
     33 import android.widget.Button;
     34 import android.widget.EditText;
     35 import android.widget.TextView;
     36 
     37 import com.android.internal.app.AlertActivity;
     38 import com.android.internal.app.AlertController;
     39 import com.android.settings.R;
     40 
     41 /**
     42  * BluetoothPairingDialog asks the user to enter a PIN / Passkey / simple confirmation
     43  * for pairing with a remote Bluetooth device. It is an activity that appears as a dialog.
     44  */
     45 public class BluetoothPairingDialog extends AlertActivity implements DialogInterface.OnClickListener,
     46         TextWatcher {
     47     private static final String TAG = "BluetoothPairingDialog";
     48 
     49     private final int BLUETOOTH_PIN_MAX_LENGTH = 16;
     50     private final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
     51     private LocalBluetoothManager mLocalManager;
     52     private BluetoothDevice mDevice;
     53     private int mType;
     54     private String mPasskey;
     55     private EditText mPairingView;
     56     private Button mOkButton;
     57 
     58     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
     59         @Override
     60         public void onReceive(Context context, Intent intent) {
     61             if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
     62                 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
     63                                                    BluetoothDevice.ERROR);
     64                 if (bondState == BluetoothDevice.BOND_BONDED ||
     65                         bondState == BluetoothDevice.BOND_NONE) {
     66                     dismissDialog();
     67                 }
     68             } else if(BluetoothDevice.ACTION_PAIRING_CANCEL.equals(intent.getAction())) {
     69                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     70                 if (device == null || device.equals(mDevice)) {
     71                     dismissDialog();
     72                 }
     73             }
     74         }
     75     };
     76 
     77     @Override
     78     protected void onCreate(Bundle savedInstanceState) {
     79         super.onCreate(savedInstanceState);
     80 
     81         Intent intent = getIntent();
     82         if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST))
     83         {
     84             Log.e(TAG,
     85                   "Error: this activity may be started only with intent " +
     86                   BluetoothDevice.ACTION_PAIRING_REQUEST);
     87             finish();
     88         }
     89 
     90         mLocalManager = LocalBluetoothManager.getInstance(this);
     91         mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     92         mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
     93         if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
     94             createUserEntryDialog();
     95         } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY) {
     96             createUserEntryDialog();
     97         } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION){
     98             int passkey =
     99                 intent.getIntExtra(BluetoothDevice.EXTRA_PASSKEY, BluetoothDevice.ERROR);
    100             if (passkey == BluetoothDevice.ERROR) {
    101                 Log.e(TAG, "Invalid ConfirmationPasskey received, not showing any dialog");
    102                 return;
    103             }
    104             mPasskey = String.format("%06d", passkey);
    105             createConfirmationDialog();
    106         } else if (mType == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
    107             createConsentDialog();
    108         } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
    109             int passkey =
    110                 intent.getIntExtra(BluetoothDevice.EXTRA_PASSKEY, BluetoothDevice.ERROR);
    111             if (passkey == BluetoothDevice.ERROR) {
    112                 Log.e(TAG, "Invalid ConfirmationPasskey received, not showing any dialog");
    113                 return;
    114             }
    115             mPasskey = String.format("%06d", passkey);
    116             createDisplayPasskeyDialog();
    117         } else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
    118             createConsentDialog();
    119         } else {
    120             Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
    121         }
    122 
    123         /*
    124          * Leave this registered through pause/resume since we still want to
    125          * finish the activity in the background if pairing is canceled.
    126          */
    127         registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_CANCEL));
    128         registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
    129     }
    130 
    131     private void createUserEntryDialog() {
    132         final AlertController.AlertParams p = mAlertParams;
    133         p.mIconId = android.R.drawable.ic_dialog_info;
    134         p.mTitle = getString(R.string.bluetooth_pairing_request);
    135         p.mView = createView();
    136         p.mPositiveButtonText = getString(android.R.string.ok);
    137         p.mPositiveButtonListener = this;
    138         p.mNegativeButtonText = getString(android.R.string.cancel);
    139         p.mNegativeButtonListener = this;
    140         setupAlert();
    141 
    142         mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
    143         mOkButton.setEnabled(false);
    144     }
    145 
    146     private View createView() {
    147         View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
    148 
    149         String name = mLocalManager.getCachedDeviceManager().getName(mDevice);
    150         TextView messageView = (TextView) view.findViewById(R.id.message);
    151         mPairingView = (EditText) view.findViewById(R.id.text);
    152         mPairingView.addTextChangedListener(this);
    153 
    154         if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
    155             messageView.setText(getString(R.string.bluetooth_enter_pin_msg, name));
    156             // Maximum of 16 characters in a PIN adb sync
    157             mPairingView.setFilters(new InputFilter[] {
    158                     new LengthFilter(BLUETOOTH_PIN_MAX_LENGTH) });
    159         } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY){
    160             messageView.setText(getString(R.string.bluetooth_enter_passkey_msg, name));
    161             // Maximum of 6 digits for passkey
    162             mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER |
    163                     InputType.TYPE_NUMBER_FLAG_SIGNED);
    164             mPairingView.setFilters(new InputFilter[] {
    165                     new LengthFilter(BLUETOOTH_PASSKEY_MAX_LENGTH)});
    166         } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
    167             mPairingView.setVisibility(View.GONE);
    168             messageView.setText(getString(R.string.bluetooth_confirm_passkey_msg, name,
    169                     mPasskey));
    170         } else if (mType == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
    171             mPairingView.setVisibility(View.GONE);
    172             messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
    173         } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
    174             mPairingView.setVisibility(View.GONE);
    175             messageView.setText(getString(R.string.bluetooth_display_passkey_msg, name, mPasskey));
    176         } else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
    177             mPairingView.setVisibility(View.GONE);
    178             messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
    179         } else {
    180             Log.e(TAG, "Incorrect pairing type received, not creating view");
    181         }
    182         return view;
    183     }
    184 
    185     private void createConfirmationDialog() {
    186         final AlertController.AlertParams p = mAlertParams;
    187         p.mIconId = android.R.drawable.ic_dialog_info;
    188         p.mTitle = getString(R.string.bluetooth_pairing_request);
    189         p.mView = createView();
    190         p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
    191         p.mPositiveButtonListener = this;
    192         p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
    193         p.mNegativeButtonListener = this;
    194         setupAlert();
    195     }
    196 
    197     private void createConsentDialog() {
    198         final AlertController.AlertParams p = mAlertParams;
    199         p.mIconId = android.R.drawable.ic_dialog_info;
    200         p.mTitle = getString(R.string.bluetooth_pairing_request);
    201         p.mView = createView();
    202         p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
    203         p.mPositiveButtonListener = this;
    204         p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
    205         p.mNegativeButtonListener = this;
    206         setupAlert();
    207     }
    208 
    209     private void createDisplayPasskeyDialog() {
    210         final AlertController.AlertParams p = mAlertParams;
    211         p.mIconId = android.R.drawable.ic_dialog_info;
    212         p.mTitle = getString(R.string.bluetooth_pairing_request);
    213         p.mView = createView();
    214         p.mPositiveButtonText = getString(android.R.string.ok);
    215         p.mPositiveButtonListener = this;
    216         setupAlert();
    217 
    218         // Since its only a notification, send an OK to the framework,
    219         // indicating that the dialog has been displayed.
    220         mDevice.setPairingConfirmation(true);
    221     }
    222 
    223     @Override
    224     protected void onDestroy() {
    225         super.onDestroy();
    226         unregisterReceiver(mReceiver);
    227     }
    228 
    229     public void afterTextChanged(Editable s) {
    230         if (s.length() > 0) {
    231             mOkButton.setEnabled(true);
    232         }
    233     }
    234 
    235     private void dismissDialog() {
    236         this.dismiss();
    237     }
    238 
    239     private void onPair(String value) {
    240         if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
    241             byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
    242             if (pinBytes == null) {
    243                 return;
    244             }
    245             mDevice.setPin(pinBytes);
    246         } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY) {
    247             int passkey = Integer.parseInt(value);
    248             mDevice.setPasskey(passkey);
    249         } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
    250             mDevice.setPairingConfirmation(true);
    251         } else if (mType ==  BluetoothDevice.PAIRING_VARIANT_CONSENT) {
    252             mDevice.setPairingConfirmation(true);
    253         } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
    254             // Do Nothing.
    255         } else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
    256             mDevice.setRemoteOutOfBandData();
    257         } else {
    258             Log.e(TAG, "Incorrect pairing type received");
    259         }
    260     }
    261 
    262     private void onCancel() {
    263         mDevice.cancelPairingUserInput();
    264     }
    265 
    266     public void onClick(DialogInterface dialog, int which) {
    267         switch (which) {
    268             case DialogInterface.BUTTON_POSITIVE:
    269                 onPair(mPairingView.getText().toString());
    270                 break;
    271 
    272             case DialogInterface.BUTTON_NEGATIVE:
    273                 onCancel();
    274                 break;
    275         }
    276     }
    277 
    278     /* Not used */
    279     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    280     }
    281 
    282     /* Not used */
    283     public void onTextChanged(CharSequence s, int start, int before, int count) {
    284     }
    285 
    286 }
    287