1 /* 2 * Copyright (C) 2011 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.app.Notification; 20 import android.app.NotificationManager; 21 import android.app.PendingIntent; 22 import android.bluetooth.BluetoothDevice; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.PowerManager; 27 import android.util.Log; 28 29 import com.android.settings.R; 30 31 /** 32 * BluetoothPermissionRequest is a receiver to receive Bluetooth connection 33 * access request. 34 */ 35 public final class BluetoothPermissionRequest extends BroadcastReceiver { 36 37 private static final String TAG = "BluetoothPermissionRequest"; 38 private static final boolean DEBUG = Utils.V; 39 private static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth; 40 41 private static final String NOTIFICATION_TAG_PBAP = "Phonebook Access" ; 42 private static final String NOTIFICATION_TAG_MAP = "Message Access"; 43 44 45 Context mContext; 46 int mRequestType; 47 BluetoothDevice mDevice; 48 String mReturnPackage = null; 49 String mReturnClass = null; 50 51 @Override 52 public void onReceive(Context context, Intent intent) { 53 mContext = context; 54 String action = intent.getAction(); 55 56 if (DEBUG) Log.d(TAG, "onReceive" + action); 57 58 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) { 59 // convert broadcast intent into activity intent (same action string) 60 mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 61 mRequestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 62 BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION); 63 mReturnPackage = intent.getStringExtra(BluetoothDevice.EXTRA_PACKAGE_NAME); 64 mReturnClass = intent.getStringExtra(BluetoothDevice.EXTRA_CLASS_NAME); 65 66 if (DEBUG) Log.d(TAG, "onReceive request type: " + mRequestType + " return " 67 + mReturnPackage + "," + mReturnClass); 68 69 // Check if user had made decisions on accepting or rejecting the phonebook access 70 // request. If there is, reply the request and return, no need to start permission 71 // activity dialog or notification. 72 if (checkUserChoice()) { 73 return; 74 } 75 76 Intent connectionAccessIntent = new Intent(action); 77 connectionAccessIntent.setClass(context, BluetoothPermissionActivity.class); 78 // We use the FLAG_ACTIVITY_MULTIPLE_TASK since we can have multiple concurrent access requests 79 connectionAccessIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 80 connectionAccessIntent.setType(Integer.toString(mRequestType)); /* This is needed to create two pending 81 intents to the same activity. 82 The value is not used in the activity. */ 83 connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 84 mRequestType); 85 connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 86 connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, mReturnPackage); 87 connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, mReturnClass); 88 89 String deviceAddress = mDevice != null ? mDevice.getAddress() : null; 90 String title = null; 91 String message = null; 92 PowerManager powerManager = 93 (PowerManager) context.getSystemService(Context.POWER_SERVICE); 94 95 if (powerManager.isScreenOn() && 96 LocalBluetoothPreferences.shouldShowDialogInForeground(context, deviceAddress) ) { 97 context.startActivity(connectionAccessIntent); 98 } else { 99 // Put up a notification that leads to the dialog 100 101 // Create an intent triggered by clicking on the 102 // "Clear All Notifications" button 103 104 Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 105 deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 106 deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 107 BluetoothDevice.CONNECTION_ACCESS_NO); 108 deleteIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType); 109 String deviceName = mDevice != null ? mDevice.getAliasName() : null; 110 switch (mRequestType) { 111 case BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS: 112 title = context.getString(R.string.bluetooth_phonebook_request); 113 message = context.getString(R.string.bluetooth_pb_acceptance_dialog_text, deviceName, deviceName); 114 break; 115 case BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS: 116 title = context.getString(R.string.bluetooth_map_request); 117 message = context.getString(R.string.bluetooth_map_acceptance_dialog_text, deviceName, deviceName); 118 break; 119 default: 120 title = context.getString(R.string.bluetooth_connection_permission_request); 121 message = context.getString(R.string.bluetooth_connection_dialog_text, deviceName, deviceName); 122 break; 123 } 124 Notification notification = new Notification.Builder(context) 125 .setContentTitle(title) 126 .setTicker(message) 127 .setContentText(message) 128 .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) 129 .setAutoCancel(true) 130 .setPriority(Notification.PRIORITY_MAX) 131 .setOnlyAlertOnce(false) 132 .setDefaults(Notification.DEFAULT_ALL) 133 .setContentIntent(PendingIntent.getActivity(context, 0, connectionAccessIntent, 0)) 134 .setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent, 0)) 135 .build(); 136 137 notification.flags |= Notification.FLAG_NO_CLEAR; /* cannot be set with the builder */ 138 139 NotificationManager notificationManager = 140 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 141 142 notificationManager.notify(getNotificationTag(mRequestType),NOTIFICATION_ID, notification); 143 } 144 } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) { 145 // Remove the notification 146 NotificationManager manager = (NotificationManager) context 147 .getSystemService(Context.NOTIFICATION_SERVICE); 148 mRequestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 149 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 150 manager.cancel(getNotificationTag(mRequestType), NOTIFICATION_ID); 151 } 152 } 153 154 private String getNotificationTag(int requestType) { 155 if(requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 156 return NOTIFICATION_TAG_PBAP; 157 } else if(mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) { 158 return NOTIFICATION_TAG_MAP; 159 } 160 return null; 161 } 162 163 /** 164 * @return true user had made a choice, this method replies to the request according 165 * to user's previous decision 166 * false user hadnot made any choice on this device 167 */ 168 private boolean checkUserChoice() { 169 boolean processed = false; 170 171 // ignore if it is something else than phonebook/message settings it wants us to remember 172 if (mRequestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS 173 && mRequestType != BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) { 174 if (DEBUG) Log.d(TAG, "Unknown RequestType: " + mRequestType); 175 return processed; 176 } 177 178 LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(mContext); 179 CachedBluetoothDeviceManager cachedDeviceManager = 180 bluetoothManager.getCachedDeviceManager(); 181 CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice); 182 183 if (cachedDevice == null) { 184 cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(), 185 bluetoothManager.getProfileManager(), mDevice); 186 } 187 188 if(mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 189 190 int phonebookPermission = cachedDevice.getPhonebookPermissionChoice(); 191 192 if (phonebookPermission == CachedBluetoothDevice.ACCESS_UNKNOWN) { 193 return processed; 194 } 195 196 String intentName = BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY; 197 if (phonebookPermission == CachedBluetoothDevice.ACCESS_ALLOWED) { 198 sendIntentToReceiver(intentName, true, BluetoothDevice.EXTRA_ALWAYS_ALLOWED, true); 199 processed = true; 200 } else if (phonebookPermission == CachedBluetoothDevice.ACCESS_REJECTED) { 201 sendIntentToReceiver(intentName, false, 202 null, false ); // dummy value, no effect since previous param is null 203 processed = true; 204 } else { 205 Log.e(TAG, "Bad phonebookPermission: " + phonebookPermission); 206 } 207 208 } else if(mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) { 209 210 int messagePermission = cachedDevice.getMessagePermissionChoice(); 211 212 if (messagePermission == CachedBluetoothDevice.ACCESS_UNKNOWN) { 213 return processed; 214 } 215 216 String intentName = BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY; 217 if (messagePermission == CachedBluetoothDevice.ACCESS_ALLOWED) { 218 sendIntentToReceiver(intentName, true, BluetoothDevice.EXTRA_ALWAYS_ALLOWED, true); 219 processed = true; 220 } else if (messagePermission == CachedBluetoothDevice.ACCESS_REJECTED) { 221 sendIntentToReceiver(intentName, false, 222 null, false); // dummy value, no effect since previous param is null 223 processed = true; 224 } else { 225 Log.e(TAG, "Bad messagePermission: " + messagePermission); 226 } 227 } 228 if(DEBUG) Log.d(TAG,"checkUserChoice(): returning " + processed); 229 return processed; 230 } 231 232 private void sendIntentToReceiver(final String intentName, final boolean allowed, 233 final String extraName, final boolean extraValue) { 234 Intent intent = new Intent(intentName); 235 236 if (mReturnPackage != null && mReturnClass != null) { 237 intent.setClassName(mReturnPackage, mReturnClass); 238 } 239 240 intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 241 allowed ? BluetoothDevice.CONNECTION_ACCESS_YES : 242 BluetoothDevice.CONNECTION_ACCESS_NO); 243 244 if (extraName != null) { 245 intent.putExtra(extraName, extraValue); 246 } 247 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 248 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType); 249 mContext.sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_ADMIN); 250 } 251 } 252