1 /* 2 * Copyright (C) 2017 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 package com.android.car; 17 18 import static android.car.settings.CarSettings.Secure 19 .KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0; 20 import static android.car.settings.CarSettings.Secure 21 .KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1; 22 import static android.car.settings.CarSettings.Secure 23 .KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0; 24 import static android.car.settings.CarSettings.Secure 25 .KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1; 26 import static android.car.settings.CarSettings.Secure 27 .KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICE_PRIORITY_0; 28 import static android.car.settings.CarSettings.Secure 29 .KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICE_PRIORITY_1; 30 import static android.car.settings.CarSettings.Secure 31 .KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0; 32 import static android.car.settings.CarSettings.Secure 33 .KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1; 34 35 import android.app.ActivityManager; 36 import android.bluetooth.BluetoothDevice; 37 import android.bluetooth.BluetoothProfile; 38 import android.car.CarBluetoothManager; 39 import android.car.ICarBluetooth; 40 import android.content.Context; 41 import android.content.pm.PackageManager; 42 import android.provider.Settings; 43 import android.util.Log; 44 45 import java.io.PrintWriter; 46 47 /** 48 * CarBluetoothService - deals with the automatically connecting to a known device via bluetooth. 49 * Interacts with a policy -{@link BluetoothDeviceConnectionPolicy} -to initiate connections and 50 * update status. 51 * The {@link BluetoothDeviceConnectionPolicy} is responsible for finding the appropriate device to 52 * connect for a specific profile. 53 */ 54 55 public class CarBluetoothService extends ICarBluetooth.Stub implements CarServiceBase { 56 57 private static final String TAG = "CarBluetoothService"; 58 private final Context mContext; 59 private final BluetoothDeviceConnectionPolicy mBluetoothDeviceConnectionPolicy; 60 private static final boolean DBG = false; 61 62 public CarBluetoothService(Context context, CarPropertyService carPropertyService, 63 PerUserCarServiceHelper userSwitchService, CarUxRestrictionsManagerService uxrService) { 64 mContext = context; 65 mBluetoothDeviceConnectionPolicy = BluetoothDeviceConnectionPolicy.create(mContext, 66 carPropertyService, userSwitchService, uxrService, this); 67 } 68 69 @Override 70 public void init() { 71 mBluetoothDeviceConnectionPolicy.init(); 72 } 73 74 @Override 75 public synchronized void release() { 76 mBluetoothDeviceConnectionPolicy.release(); 77 } 78 79 /** 80 * Set the Auto connect priority for a paired Bluetooth Device. 81 * For example, if a device is tagged as a Primary device for a supported Bluetooth Profile, 82 * every new Auto Connect attempt would start with trying to connect to *that* device. 83 * This priority is set at a Bluetooth profile granularity 84 * 85 * @param deviceToSet - Device to set priority (Tag) 86 * @param profileToSet - BluetoothProfile to set priority for. 87 * @param priorityToSet - What priority level to set to 88 * @hide 89 */ 90 public void setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet, 91 int priorityToSet) { 92 setBluetoothDeviceConnectionPriority(deviceToSet.getAddress(), profileToSet, priorityToSet); 93 } 94 95 public void setBluetoothDeviceConnectionPriority(String deviceAddress, int profileToSet, 96 int priorityToSet) { 97 // Check if the caller has Bluetooth Admin Permissions 98 enforceBluetoothAdminPermission(); 99 if (priorityToSet == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1) { 100 if (!isPriorityDevicePresent(profileToSet, 101 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)) { 102 Log.e(TAG, "Secondary Device not allowed without a primary device"); 103 return; 104 } 105 } 106 // Write the priority preference to Secure settings. The Bluetooth device connection policy 107 // will look up the Settings when it initiates a connection 108 Settings.Secure.putStringForUser(mContext.getContentResolver(), 109 getKeyForProfile(profileToSet, priorityToSet), deviceAddress, 110 ActivityManager.getCurrentUser()); 111 112 } 113 114 /** 115 * Unset the Auto connect priority for the given profile 116 * 117 * @param profileToClear - Profile to unset priority 118 * @param priorityToClear - Which priority to clear (Primary or Secondary) 119 * @hide 120 */ 121 public void clearBluetoothDeviceConnectionPriority(int profileToClear, int priorityToClear) { 122 // Check if the caller has Bluetooth Admin F@Permissions 123 enforceBluetoothAdminPermission(); 124 if (priorityToClear == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) { 125 if (isPriorityDevicePresent(profileToClear, 126 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1)) { 127 Log.e(TAG, "Please remove Secondary device before removing Primary Device"); 128 return; 129 } 130 } 131 Settings.Secure.putStringForUser(mContext.getContentResolver(), 132 getKeyForProfile(profileToClear, priorityToClear), 133 CarBluetoothManager.BLUETOOTH_NO_PRIORITY_DEVICE, 134 ActivityManager.getCurrentUser()); 135 } 136 137 /** 138 * Returns if there is a device that has been tagged with the given priority for the given 139 * profile. 140 * 141 * @param profile - BluetoothProfile 142 * @param priorityToCheck - Priority to check 143 * @return true if there is a device present with the given priority, false if not 144 */ 145 public boolean isPriorityDevicePresent(int profile, int priorityToCheck) { 146 String deviceName = getDeviceNameWithPriority(profile, priorityToCheck); 147 if (deviceName != null && !deviceName.equalsIgnoreCase( 148 CarBluetoothManager.BLUETOOTH_NO_PRIORITY_DEVICE)) { 149 return true; 150 } else { 151 if (DBG) { 152 Log.d(TAG, 153 "No device present for priority: " + priorityToCheck + " profile: " 154 + profile); 155 } 156 return false; 157 } 158 } 159 160 /** 161 * Returns the Bluetooth device address as a String that has been tagged with the given priority 162 * for the given profile. 163 * 164 * @param profile - BluetoothProfile 165 * @param priorityToCheck - Priority to check 166 * @return BluetoothDevice address if present, null if absent 167 */ 168 public String getDeviceNameWithPriority(int profile, int priorityToCheck) { 169 String keyToQuery = null; 170 String deviceName = null; 171 enforceBluetoothAdminPermission(); 172 switch (profile) { 173 case BluetoothProfile.A2DP_SINK: 174 keyToQuery = (priorityToCheck 175 == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) 176 ? KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0 177 : KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1; 178 break; 179 case BluetoothProfile.HEADSET_CLIENT: 180 case BluetoothProfile.PBAP_CLIENT: 181 keyToQuery = (priorityToCheck 182 == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) 183 ? KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0 184 : KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1; 185 break; 186 case BluetoothProfile.MAP_CLIENT: 187 keyToQuery = (priorityToCheck 188 == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) 189 ? KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0 190 : KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1; 191 break; 192 case BluetoothProfile.PAN: 193 keyToQuery = (priorityToCheck 194 == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) 195 ? KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICE_PRIORITY_0 196 : KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICE_PRIORITY_1; 197 break; 198 199 default: 200 if (DBG) { 201 Log.d(TAG, "Unknown Bluetooth profile"); 202 } 203 } 204 if (keyToQuery != null) { 205 deviceName = Settings.Secure.getStringForUser(mContext.getContentResolver(), 206 keyToQuery, (int) ActivityManager.getCurrentUser()); 207 } 208 return deviceName; 209 } 210 211 private void enforceBluetoothAdminPermission() { 212 if (mContext != null 213 && PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( 214 android.Manifest.permission.BLUETOOTH_ADMIN)) { 215 return; 216 } 217 if (mContext == null) { 218 Log.e(TAG, "CarBluetoothPrioritySettings does not have a Context"); 219 } 220 throw new SecurityException("requires permission " + android.Manifest.permission.BLUETOOTH_ADMIN); 221 } 222 223 private String getKeyForProfile(int profile, int priority) { 224 String keyToLookup = null; 225 switch (profile) { 226 case BluetoothProfile.A2DP_SINK: 227 keyToLookup = (priority 228 == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) 229 ? KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0 230 : KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1; 231 break; 232 case BluetoothProfile.MAP_CLIENT: 233 keyToLookup = (priority 234 == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) 235 ? KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0 236 : KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1; 237 break; 238 case BluetoothProfile.PBAP_CLIENT: 239 // fall through 240 case BluetoothProfile.HEADSET_CLIENT: 241 keyToLookup = (priority 242 == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) 243 ? KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0 244 : KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1; 245 break; 246 case BluetoothProfile.PAN: 247 keyToLookup = (priority 248 == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) 249 ? KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICE_PRIORITY_0 250 : KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICE_PRIORITY_1; 251 break; 252 253 default: 254 Log.e(TAG, "Unsupported Bluetooth profile to set priority to"); 255 break; 256 } 257 return keyToLookup; 258 } 259 260 @Override 261 public synchronized void dump(PrintWriter writer) { 262 mBluetoothDeviceConnectionPolicy.dump(writer); 263 } 264 265 } 266