2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.bluetooth;
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.preference.Preference;
27 import android.util.Log;
28 import android.view.View;
29 import android.widget.CheckBox;
30 import android.widget.CompoundButton;
31 import android.widget.TextView;
32 import android.widget.Button;
33 import android.widget.CompoundButton.OnCheckedChangeListener;
35 import com.android.internal.app.AlertActivity;
36 import com.android.internal.app.AlertController;
38 import com.android.settings.R;
41 * BluetoothPermissionActivity shows a dialog for accepting incoming
42 * profile connection request from untrusted devices.
43 * It is also used to show a dialogue for accepting incoming phonebook
44 * read request. The request could be initiated by PBAP PCE or by HF AT+CPBR.
46 public class BluetoothPermissionActivity extends AlertActivity implements
47 DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener {
48 private static final String TAG = "BluetoothPermissionActivity";
49 private static final boolean DEBUG = Utils.D;
52 private TextView messageView;
53 private Button mOkButton;
54 private BluetoothDevice mDevice;
55 private String mReturnPackage = null;
56 private String mReturnClass = null;
58 private int mRequestType = 0;
59 private BroadcastReceiver mReceiver = new BroadcastReceiver() {
61 public void onReceive(Context context, Intent intent) {
62 String action = intent.getAction();
63 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) {
64 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
65 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
66 if (requestType != mRequestType) return;
67 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
68 if (mDevice.equals(device)) dismissDialog();
72 private boolean mReceiverRegistered = false;
74 private void dismissDialog() {
79 protected void onCreate(Bundle savedInstanceState) {
80 super.onCreate(savedInstanceState);
82 Intent i = getIntent();
83 String action = i.getAction();
84 if (!action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) {
85 Log.e(TAG, "Error: this activity may be started only with intent "
86 + "ACTION_CONNECTION_ACCESS_REQUEST");
91 mDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
92 mReturnPackage = i.getStringExtra(BluetoothDevice.EXTRA_PACKAGE_NAME);
93 mReturnClass = i.getStringExtra(BluetoothDevice.EXTRA_CLASS_NAME);
94 mRequestType = i.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
95 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
97 if(DEBUG) Log.i(TAG, "onCreate() Request type: " + mRequestType);
99 if (mRequestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION) {
100 showDialog(getString(R.string.bluetooth_connection_permission_request), mRequestType);
101 } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
102 showDialog(getString(R.string.bluetooth_phonebook_request), mRequestType);
103 } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
104 showDialog(getString(R.string.bluetooth_map_request), mRequestType);
107 Log.e(TAG, "Error: bad request type: " + mRequestType);
111 registerReceiver(mReceiver,
112 new IntentFilter(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL));
113 mReceiverRegistered = true;
117 private void showDialog(String title, int requestType)
119 final AlertController.AlertParams p = mAlertParams;
121 if(DEBUG) Log.i(TAG, "showDialog() Request type: " + mRequestType + " this: " + this);
124 case BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION:
125 p.mView = createConnectionDialogView();
127 case BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS:
128 p.mView = createPhonebookDialogView();
130 case BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS:
131 p.mView = createMapDialogView();
134 p.mPositiveButtonText = getString(R.string.yes);
135 p.mPositiveButtonListener = this;
136 p.mNegativeButtonText = getString(R.string.no);
137 p.mNegativeButtonListener = this;
138 mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
143 public void onBackPressed() {
144 /*we need an answer so ignore back button presses during auth */
145 if(DEBUG) Log.i(TAG, "Back button pressed! ignoring");
148 private String createRemoteName()
150 String mRemoteName = mDevice != null ? mDevice.getAliasName() : null;
152 if (mRemoteName == null) mRemoteName = getString(R.string.unknown);
156 // TODO(edjee): createConnectionDialogView, createPhonebookDialogView and createMapDialogView
157 // are similar. Refactor them into one method.
158 // Also, the string resources bluetooth_remember_choice and bluetooth_pb_remember_choice should
160 private View createConnectionDialogView() {
161 String mRemoteName = createRemoteName();
162 mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null);
163 messageView = (TextView)mView.findViewById(R.id.message);
164 messageView.setText(getString(R.string.bluetooth_connection_dialog_text,
169 private View createPhonebookDialogView() {
170 String mRemoteName = createRemoteName();
171 mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null);
172 messageView = (TextView)mView.findViewById(R.id.message);
173 messageView.setText(getString(R.string.bluetooth_pb_acceptance_dialog_text,
174 mRemoteName, mRemoteName));
178 private View createMapDialogView() {
179 String mRemoteName = createRemoteName();
180 mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null);
181 messageView = (TextView)mView.findViewById(R.id.message);
182 messageView.setText(getString(R.string.bluetooth_map_acceptance_dialog_text,
183 mRemoteName, mRemoteName));
187 private void onPositive() {
188 if (DEBUG) Log.d(TAG, "onPositive");
189 savePermissionChoice(mRequestType, CachedBluetoothDevice.ACCESS_ALLOWED);
190 // TODO(edjee): Now that we always save the user's choice,
191 // we can get rid of BluetoothDevice#EXTRA_ALWAYS_ALLOWED.
192 sendIntentToReceiver(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, true,
193 BluetoothDevice.EXTRA_ALWAYS_ALLOWED, true);
197 private void onNegative() {
198 if (DEBUG) Log.d(TAG, "onNegative");
199 savePermissionChoice(mRequestType, CachedBluetoothDevice.ACCESS_REJECTED);
200 sendIntentToReceiver(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, false,
201 null, false // dummy value, no effect since last param is null
206 private void sendIntentToReceiver(final String intentName, final boolean allowed,
207 final String extraName, final boolean extraValue) {
208 Intent intent = new Intent(intentName);
210 if (mReturnPackage != null && mReturnClass != null) {
211 intent.setClassName(mReturnPackage, mReturnClass);
213 if(DEBUG) Log.i(TAG, "sendIntentToReceiver() Request type: " + mRequestType +
214 " mReturnPackage" + mReturnPackage + " mReturnClass" + mReturnClass);
216 intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
217 allowed ? BluetoothDevice.CONNECTION_ACCESS_YES :
218 BluetoothDevice.CONNECTION_ACCESS_NO);
220 if (extraName != null) {
221 intent.putExtra(extraName, extraValue);
223 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
224 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType);
225 sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_ADMIN);
228 public void onClick(DialogInterface dialog, int which) {
230 case DialogInterface.BUTTON_POSITIVE:
234 case DialogInterface.BUTTON_NEGATIVE:
243 protected void onDestroy() {
245 if (mReceiverRegistered) {
246 unregisterReceiver(mReceiver);
247 mReceiverRegistered = false;
251 public boolean onPreferenceChange(Preference preference, Object newValue) {
255 private void savePermissionChoice(int permissionType, int permissionChoice) {
256 LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(this);
257 CachedBluetoothDeviceManager cachedDeviceManager =
258 bluetoothManager.getCachedDeviceManager();
259 CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice);
260 if (DEBUG) Log.d(TAG, "savePermissionChoice permissionType: " + permissionType);
261 if (cachedDevice == null ) {
262 cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(),
263 bluetoothManager.getProfileManager(),
266 if(permissionType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS){
267 cachedDevice.setPhonebookPermissionChoice(permissionChoice);
268 }else if (permissionType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS){
269 cachedDevice.setMessagePermissionChoice(permissionChoice);