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 onCreate(Bundle savedInstanceState)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 createUserEntryDialog()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 createView()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 createConfirmationDialog()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 createConsentDialog()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 createDisplayPasskeyDialog()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 onDestroy()224 protected void onDestroy() { 225 super.onDestroy(); 226 unregisterReceiver(mReceiver); 227 } 228 afterTextChanged(Editable s)229 public void afterTextChanged(Editable s) { 230 if (s.length() > 0) { 231 mOkButton.setEnabled(true); 232 } 233 } 234 dismissDialog()235 private void dismissDialog() { 236 this.dismiss(); 237 } 238 onPair(String value)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 onCancel()262 private void onCancel() { 263 mDevice.cancelPairingUserInput(); 264 } 265 onClick(DialogInterface dialog, int which)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 */ beforeTextChanged(CharSequence s, int start, int count, int after)279 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 280 } 281 282 /* Not used */ onTextChanged(CharSequence s, int start, int before, int count)283 public void onTextChanged(CharSequence s, int start, int before, int count) { 284 } 285 286 } 287