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.NotificationChannel; 21 import android.app.NotificationManager; 22 import android.app.PendingIntent; 23 import android.bluetooth.BluetoothDevice; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.os.PowerManager; 29 import android.os.UserManager; 30 import android.util.Log; 31 32 import com.android.settings.R; 33 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 34 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; 35 import com.android.settingslib.bluetooth.LocalBluetoothManager; 36 37 /** 38 * BluetoothPermissionRequest is a receiver to receive Bluetooth connection 39 * access request. 40 */ 41 public final class BluetoothPermissionRequest extends BroadcastReceiver { 42 43 private static final String TAG = "BluetoothPermissionRequest"; 44 private static final boolean DEBUG = Utils.V; 45 private static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth; 46 47 private static final String NOTIFICATION_TAG_PBAP = "Phonebook Access" ; 48 private static final String NOTIFICATION_TAG_MAP = "Message Access"; 49 private static final String NOTIFICATION_TAG_SAP = "SIM Access"; 50 /* TODO: Consolidate this multiple defined but common channel ID with other 51 * handlers that declare and use the same channel ID */ 52 private static final String BLUETOOTH_NOTIFICATION_CHANNEL = 53 "bluetooth_notification_channel"; 54 55 private NotificationChannel mNotificationChannel = null; 56 57 Context mContext; 58 int mRequestType; 59 BluetoothDevice mDevice; 60 61 @Override onReceive(Context context, Intent intent)62 public void onReceive(Context context, Intent intent) { 63 mContext = context; 64 String action = intent.getAction(); 65 66 if (DEBUG) Log.d(TAG, "onReceive" + action); 67 68 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) { 69 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 70 // skip the notification for managed profiles. 71 if (um.isManagedProfile()) { 72 if (DEBUG) Log.d(TAG, "Blocking notification for managed profile."); 73 return; 74 } 75 // convert broadcast intent into activity intent (same action string) 76 mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 77 mRequestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 78 BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION); 79 80 if (DEBUG) { 81 Log.d(TAG, "onReceive request type: " + mRequestType); 82 } 83 84 // Even if the user has already made the choice, Bluetooth still may not know that if 85 // the user preference data have not been migrated from Settings app's shared 86 // preferences to Bluetooth app's. In that case, Bluetooth app broadcasts an 87 // ACTION_CONNECTION_ACCESS_REQUEST intent to ask to Settings app. 88 // 89 // If that happens, 'checkUserChoice()' here will do migration because it finds or 90 // creates a 'CachedBluetoothDevice' object for the device. 91 // 92 // After migration is done, 'checkUserChoice()' replies to the request by sending an 93 // ACTION_CONNECTION_ACCESS_REPLY intent. And we don't need to start permission activity 94 // dialog or notification. 95 if (checkUserChoice()) { 96 return; 97 } 98 99 Intent connectionAccessIntent = new Intent(action); 100 connectionAccessIntent.setClass(context, BluetoothPermissionActivity.class); 101 // We use the FLAG_ACTIVITY_MULTIPLE_TASK since we can have multiple concurrent access 102 // requests. 103 connectionAccessIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 104 | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 105 // This is needed to create two pending intents to the same activity. The value is not 106 // used in the activity. 107 connectionAccessIntent.setType(Integer.toString(mRequestType)); 108 connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 109 mRequestType); 110 connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 111 112 String title = null; 113 String message = null; 114 PowerManager powerManager = 115 (PowerManager) context.getSystemService(Context.POWER_SERVICE); 116 117 if (powerManager.isScreenOn() 118 && LocalBluetoothPreferences.shouldShowDialogInForeground( 119 context, mDevice)) { 120 context.startActivity(connectionAccessIntent); 121 } else { 122 // Put up a notification that leads to the dialog 123 124 // Create an intent triggered by clicking on the 125 // "Clear All Notifications" button 126 127 String bluetoothName; 128 try { 129 bluetoothName = Utils.findBluetoothPackageName(context); 130 } catch (NameNotFoundException e) { 131 e.printStackTrace(); 132 return; 133 } 134 Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 135 deleteIntent.setPackage(bluetoothName); 136 deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 137 deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 138 BluetoothDevice.CONNECTION_ACCESS_NO); 139 deleteIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType); 140 String deviceAlias = Utils.createRemoteName(context, mDevice); 141 switch (mRequestType) { 142 case BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS: 143 title = context.getString(R.string.bluetooth_phonebook_request); 144 message = context.getString( 145 R.string.bluetooth_phonebook_access_notification_content); 146 break; 147 case BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS: 148 title = context.getString(R.string.bluetooth_map_request); 149 message = context.getString( 150 R.string.bluetooth_message_access_notification_content); 151 break; 152 case BluetoothDevice.REQUEST_TYPE_SIM_ACCESS: 153 title = context.getString( 154 R.string.bluetooth_sim_card_access_notification_title); 155 message = context.getString( 156 R.string.bluetooth_sim_card_access_notification_content); 157 break; 158 default: 159 title = context.getString( 160 R.string.bluetooth_connect_access_notification_title); 161 message = context.getString( 162 R.string.bluetooth_connect_access_notification_content); 163 break; 164 } 165 NotificationManager notificationManager = 166 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 167 if (mNotificationChannel == null) { 168 mNotificationChannel = new NotificationChannel(BLUETOOTH_NOTIFICATION_CHANNEL, 169 context.getString(R.string.bluetooth), 170 NotificationManager.IMPORTANCE_HIGH); 171 notificationManager.createNotificationChannel(mNotificationChannel); 172 } 173 Notification notification = new Notification.Builder(context, 174 BLUETOOTH_NOTIFICATION_CHANNEL) 175 .setContentTitle(title) 176 .setTicker(message) 177 .setContentText(message) 178 .setStyle(new Notification.BigTextStyle().bigText(message)) 179 .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) 180 .setAutoCancel(true) 181 .setPriority(Notification.PRIORITY_MAX) 182 .setOnlyAlertOnce(false) 183 .setDefaults(Notification.DEFAULT_ALL) 184 .setContentIntent(PendingIntent.getActivity(context, 0, 185 connectionAccessIntent, PendingIntent.FLAG_IMMUTABLE)) 186 .setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent, 187 PendingIntent.FLAG_IMMUTABLE)) 188 .setColor(context.getColor( 189 com.android.internal.R.color.system_notification_accent_color)) 190 .setLocalOnly(true) 191 .build(); 192 193 notification.flags |= Notification.FLAG_NO_CLEAR; // Cannot be set with the builder. 194 195 notificationManager.notify(getNotificationTag(mRequestType), NOTIFICATION_ID, 196 notification); 197 } 198 } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) { 199 // Remove the notification 200 NotificationManager manager = (NotificationManager) context 201 .getSystemService(Context.NOTIFICATION_SERVICE); 202 mRequestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 203 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 204 manager.cancel(getNotificationTag(mRequestType), NOTIFICATION_ID); 205 } 206 } 207 getNotificationTag(int requestType)208 private String getNotificationTag(int requestType) { 209 if(requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 210 return NOTIFICATION_TAG_PBAP; 211 } else if(mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) { 212 return NOTIFICATION_TAG_MAP; 213 } else if(mRequestType == BluetoothDevice.REQUEST_TYPE_SIM_ACCESS) { 214 return NOTIFICATION_TAG_SAP; 215 } 216 return null; 217 } 218 219 /** 220 * @return true user had made a choice, this method replies to the request according 221 * to user's previous decision 222 * false user hadnot made any choice on this device 223 */ checkUserChoice()224 private boolean checkUserChoice() { 225 boolean processed = false; 226 227 // ignore if it is something else than phonebook/message settings it wants us to remember 228 if (mRequestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS 229 && mRequestType != BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS 230 && mRequestType != BluetoothDevice.REQUEST_TYPE_SIM_ACCESS) { 231 if (DEBUG) Log.d(TAG, "checkUserChoice(): Unknown RequestType " + mRequestType); 232 return processed; 233 } 234 235 LocalBluetoothManager bluetoothManager = Utils.getLocalBtManager(mContext); 236 CachedBluetoothDeviceManager cachedDeviceManager = 237 bluetoothManager.getCachedDeviceManager(); 238 CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice); 239 if (cachedDevice == null) { 240 cachedDevice = cachedDeviceManager.addDevice(mDevice); 241 } 242 243 String intentName = BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY; 244 245 if (mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 246 int phonebookPermission = mDevice.getPhonebookAccessPermission(); 247 248 if (phonebookPermission == BluetoothDevice.ACCESS_UNKNOWN) { 249 // Leave 'processed' as false. 250 } else if (phonebookPermission == BluetoothDevice.ACCESS_ALLOWED) { 251 sendReplyIntentToReceiver(true); 252 processed = true; 253 } else if (phonebookPermission == BluetoothDevice.ACCESS_REJECTED) { 254 sendReplyIntentToReceiver(false); 255 processed = true; 256 } else { 257 Log.e(TAG, "Bad phonebookPermission: " + phonebookPermission); 258 } 259 } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) { 260 int messagePermission = mDevice.getMessageAccessPermission(); 261 262 if (messagePermission == BluetoothDevice.ACCESS_UNKNOWN) { 263 // Leave 'processed' as false. 264 } else if (messagePermission == BluetoothDevice.ACCESS_ALLOWED) { 265 sendReplyIntentToReceiver(true); 266 processed = true; 267 } else if (messagePermission == BluetoothDevice.ACCESS_REJECTED) { 268 sendReplyIntentToReceiver(false); 269 processed = true; 270 } else { 271 Log.e(TAG, "Bad messagePermission: " + messagePermission); 272 } 273 } else if(mRequestType == BluetoothDevice.REQUEST_TYPE_SIM_ACCESS) { 274 int simPermission = mDevice.getSimAccessPermission(); 275 276 if (simPermission == BluetoothDevice.ACCESS_UNKNOWN) { 277 // Leave 'processed' as false. 278 } else if (simPermission == BluetoothDevice.ACCESS_ALLOWED) { 279 sendReplyIntentToReceiver(true); 280 processed = true; 281 } else if (simPermission == BluetoothDevice.ACCESS_REJECTED) { 282 sendReplyIntentToReceiver(false); 283 processed = true; 284 } else { 285 Log.e(TAG, "Bad simPermission: " + simPermission); 286 } 287 } 288 if (DEBUG) Log.d(TAG,"checkUserChoice(): returning " + processed); 289 return processed; 290 } 291 sendReplyIntentToReceiver(final boolean allowed)292 private void sendReplyIntentToReceiver(final boolean allowed) { 293 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 294 295 intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 296 allowed ? BluetoothDevice.CONNECTION_ACCESS_YES 297 : BluetoothDevice.CONNECTION_ACCESS_NO); 298 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 299 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType); 300 mContext.sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_CONNECT); 301 } 302 } 303