1 /* 2 * Copyright (c) 2008-2009, Motorola, Inc. 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * - Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * - Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * - Neither the name of the Motorola, Inc. nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.android.bluetooth.pbap; 34 35 import com.android.bluetooth.R; 36 37 import android.content.BroadcastReceiver; 38 import android.content.Context; 39 import android.content.DialogInterface; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.os.Bundle; 43 import android.os.Handler; 44 import android.os.Message; 45 import android.preference.Preference; 46 import android.text.InputFilter; 47 import android.text.TextWatcher; 48 import android.text.InputFilter.LengthFilter; 49 import android.util.Log; 50 import android.view.View; 51 import android.widget.CheckBox; 52 import android.widget.EditText; 53 import android.widget.TextView; 54 import android.widget.Button; 55 56 import com.android.internal.app.AlertActivity; 57 import com.android.internal.app.AlertController; 58 59 /** 60 * PbapActivity shows two dialogues: One for accepting incoming pbap request and 61 * the other prompts the user to enter a session key for authentication with a 62 * remote Bluetooth device. 63 */ 64 public class BluetoothPbapActivity extends AlertActivity implements 65 DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, TextWatcher { 66 private static final String TAG = "BluetoothPbapActivity"; 67 68 private static final boolean V = BluetoothPbapService.VERBOSE; 69 70 private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16; 71 72 private static final int DIALOG_YES_NO_AUTH = 1; 73 74 private static final String KEY_USER_TIMEOUT = "user_timeout"; 75 76 private View mView; 77 78 private EditText mKeyView; 79 80 private TextView messageView; 81 82 private String mSessionKey = ""; 83 84 private int mCurrentDialog; 85 86 private Button mOkButton; 87 88 private CheckBox mAlwaysAllowed; 89 90 private boolean mTimeout = false; 91 92 private boolean mAlwaysAllowedValue = true; 93 94 private static final int DISMISS_TIMEOUT_DIALOG = 0; 95 96 private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000; 97 98 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 99 @Override 100 public void onReceive(Context context, Intent intent) { 101 if (!BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) { 102 return; 103 } 104 onTimeout(); 105 } 106 }; 107 108 @Override onCreate(Bundle savedInstanceState)109 protected void onCreate(Bundle savedInstanceState) { 110 super.onCreate(savedInstanceState); 111 Intent i = getIntent(); 112 String action = i.getAction(); 113 if (action.equals(BluetoothPbapService.AUTH_CHALL_ACTION)) { 114 showPbapDialog(DIALOG_YES_NO_AUTH); 115 mCurrentDialog = DIALOG_YES_NO_AUTH; 116 } else { 117 Log.e(TAG, "Error: this activity may be started only with intent " 118 + "PBAP_ACCESS_REQUEST or PBAP_AUTH_CHALL "); 119 finish(); 120 } 121 registerReceiver(mReceiver, new IntentFilter( 122 BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION)); 123 } 124 showPbapDialog(int id)125 private void showPbapDialog(int id) { 126 final AlertController.AlertParams p = mAlertParams; 127 switch (id) { 128 case DIALOG_YES_NO_AUTH: 129 p.mTitle = getString(R.string.pbap_session_key_dialog_header); 130 p.mView = createView(DIALOG_YES_NO_AUTH); 131 p.mPositiveButtonText = getString(android.R.string.ok); 132 p.mPositiveButtonListener = this; 133 p.mNegativeButtonText = getString(android.R.string.cancel); 134 p.mNegativeButtonListener = this; 135 setupAlert(); 136 mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); 137 mOkButton.setEnabled(false); 138 break; 139 default: 140 break; 141 } 142 } 143 createDisplayText(final int id)144 private String createDisplayText(final int id) { 145 String mRemoteName = BluetoothPbapService.getRemoteDeviceName(); 146 switch (id) { 147 case DIALOG_YES_NO_AUTH: 148 String mMessage2 = getString(R.string.pbap_session_key_dialog_title, mRemoteName); 149 return mMessage2; 150 default: 151 return null; 152 } 153 } 154 createView(final int id)155 private View createView(final int id) { 156 switch (id) { 157 case DIALOG_YES_NO_AUTH: 158 mView = getLayoutInflater().inflate(R.layout.auth, null); 159 messageView = (TextView)mView.findViewById(R.id.message); 160 messageView.setText(createDisplayText(id)); 161 mKeyView = (EditText)mView.findViewById(R.id.text); 162 mKeyView.addTextChangedListener(this); 163 mKeyView.setFilters(new InputFilter[] { 164 new LengthFilter(BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH) 165 }); 166 return mView; 167 default: 168 return null; 169 } 170 } 171 onPositive()172 private void onPositive() { 173 if (!mTimeout) { 174 if (mCurrentDialog == DIALOG_YES_NO_AUTH) { 175 sendIntentToReceiver(BluetoothPbapService.AUTH_RESPONSE_ACTION, 176 BluetoothPbapService.EXTRA_SESSION_KEY, mSessionKey); 177 mKeyView.removeTextChangedListener(this); 178 } 179 } 180 mTimeout = false; 181 finish(); 182 } 183 onNegative()184 private void onNegative() { 185 if (mCurrentDialog == DIALOG_YES_NO_AUTH) { 186 sendIntentToReceiver(BluetoothPbapService.AUTH_CANCELLED_ACTION, null, null); 187 mKeyView.removeTextChangedListener(this); 188 } 189 finish(); 190 } 191 sendIntentToReceiver(final String intentName, final String extraName, final String extraValue)192 private void sendIntentToReceiver(final String intentName, final String extraName, 193 final String extraValue) { 194 Intent intent = new Intent(intentName); 195 intent.setPackage(BluetoothPbapService.THIS_PACKAGE_NAME); 196 if (extraName != null) { 197 intent.putExtra(extraName, extraValue); 198 } 199 sendBroadcast(intent); 200 } 201 sendIntentToReceiver(final String intentName, final String extraName, final boolean extraValue)202 private void sendIntentToReceiver(final String intentName, final String extraName, 203 final boolean extraValue) { 204 Intent intent = new Intent(intentName); 205 intent.setPackage(BluetoothPbapService.THIS_PACKAGE_NAME); 206 if (extraName != null) { 207 intent.putExtra(extraName, extraValue); 208 } 209 sendBroadcast(intent); 210 } 211 onClick(DialogInterface dialog, int which)212 public void onClick(DialogInterface dialog, int which) { 213 switch (which) { 214 case DialogInterface.BUTTON_POSITIVE: 215 if (mCurrentDialog == DIALOG_YES_NO_AUTH) { 216 mSessionKey = mKeyView.getText().toString(); 217 } 218 onPositive(); 219 break; 220 221 case DialogInterface.BUTTON_NEGATIVE: 222 onNegative(); 223 break; 224 default: 225 break; 226 } 227 } 228 onTimeout()229 private void onTimeout() { 230 mTimeout = true; 231 if (mCurrentDialog == DIALOG_YES_NO_AUTH) { 232 messageView.setText(getString(R.string.pbap_authentication_timeout_message, 233 BluetoothPbapService.getRemoteDeviceName())); 234 mKeyView.setVisibility(View.GONE); 235 mKeyView.clearFocus(); 236 mKeyView.removeTextChangedListener(this); 237 mOkButton.setEnabled(true); 238 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); 239 } 240 241 mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(DISMISS_TIMEOUT_DIALOG), 242 DISMISS_TIMEOUT_DIALOG_VALUE); 243 } 244 245 @Override onRestoreInstanceState(Bundle savedInstanceState)246 protected void onRestoreInstanceState(Bundle savedInstanceState) { 247 super.onRestoreInstanceState(savedInstanceState); 248 mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT); 249 if (V) Log.v(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout); 250 if (mTimeout) { 251 onTimeout(); 252 } 253 } 254 255 @Override onSaveInstanceState(Bundle outState)256 protected void onSaveInstanceState(Bundle outState) { 257 super.onSaveInstanceState(outState); 258 outState.putBoolean(KEY_USER_TIMEOUT, mTimeout); 259 } 260 261 @Override onDestroy()262 protected void onDestroy() { 263 super.onDestroy(); 264 unregisterReceiver(mReceiver); 265 } 266 onPreferenceChange(Preference preference, Object newValue)267 public boolean onPreferenceChange(Preference preference, Object newValue) { 268 return true; 269 } 270 beforeTextChanged(CharSequence s, int start, int before, int after)271 public void beforeTextChanged(CharSequence s, int start, int before, int after) { 272 } 273 onTextChanged(CharSequence s, int start, int before, int count)274 public void onTextChanged(CharSequence s, int start, int before, int count) { 275 } 276 afterTextChanged(android.text.Editable s)277 public void afterTextChanged(android.text.Editable s) { 278 if (s.length() > 0) { 279 mOkButton.setEnabled(true); 280 } 281 } 282 283 private final Handler mTimeoutHandler = new Handler() { 284 @Override 285 public void handleMessage(Message msg) { 286 switch (msg.what) { 287 case DISMISS_TIMEOUT_DIALOG: 288 if (V) Log.v(TAG, "Received DISMISS_TIMEOUT_DIALOG msg."); 289 finish(); 290 break; 291 default: 292 break; 293 } 294 } 295 }; 296 } 297