1 /* 2 * Copyright (C) 2022 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.bluetooth.btservice; 18 19 import android.annotation.SuppressLint; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothGatt; 23 import android.content.Context; 24 import android.content.SharedPreferences; 25 import android.os.SystemProperties; 26 import android.util.Log; 27 28 import androidx.annotation.VisibleForTesting; 29 30 import com.android.bluetooth.R; 31 import com.android.bluetooth.Utils; 32 33 import java.util.HashSet; 34 import java.util.Set; 35 36 /** 37 * A CompanionManager to specify parameters between companion devices and regular devices. 38 * 39 * <p>1. A paired device is recognized as a companion device if its METADATA_SOFTWARE_VERSION is set 40 * to BluetoothDevice.COMPANION_TYPE_PRIMARY or BluetoothDevice.COMPANION_TYPE_SECONDARY. 2. Only 41 * can have one companion device at a time. 3. Remove bond does not remove the companion device 42 * record. 4. Factory reset Bluetooth removes the companion device. 5. Companion device has 43 * individual GATT connection parameters. 44 */ 45 public class CompanionManager { 46 private static final String TAG = 47 Utils.TAG_PREFIX_BLUETOOTH + CompanionManager.class.getSimpleName(); 48 49 private BluetoothDevice mCompanionDevice; 50 private int mCompanionType; 51 52 private final int[] mGattConnHighPrimary; 53 private final int[] mGattConnBalancePrimary; 54 private final int[] mGattConnLowPrimary; 55 private final int[] mGattConnHighSecondary; 56 private final int[] mGattConnBalanceSecondary; 57 private final int[] mGattConnLowSecondary; 58 private final int[] mGattConnHighDefault; 59 private final int[] mGattConnBalanceDefault; 60 private final int[] mGattConnLowDefault; 61 private final int[] mGattConnDckDefault; 62 63 @VisibleForTesting static final int COMPANION_TYPE_NONE = 0; 64 @VisibleForTesting static final int COMPANION_TYPE_PRIMARY = 1; 65 @VisibleForTesting static final int COMPANION_TYPE_SECONDARY = 2; 66 67 public static final int GATT_CONN_INTERVAL_MIN = 0; 68 public static final int GATT_CONN_INTERVAL_MAX = 1; 69 public static final int GATT_CONN_LATENCY = 2; 70 71 @VisibleForTesting static final String COMPANION_INFO = "bluetooth_companion_info"; 72 @VisibleForTesting static final String COMPANION_DEVICE_KEY = "companion_device"; 73 @VisibleForTesting static final String COMPANION_TYPE_KEY = "companion_type"; 74 75 static final String PROPERTY_HIGH_MIN_INTERVAL = "bluetooth.gatt.high_priority.min_interval"; 76 static final String PROPERTY_HIGH_MAX_INTERVAL = "bluetooth.gatt.high_priority.max_interval"; 77 static final String PROPERTY_HIGH_LATENCY = "bluetooth.gatt.high_priority.latency"; 78 static final String PROPERTY_BALANCED_MIN_INTERVAL = 79 "bluetooth.gatt.balanced_priority.min_interval"; 80 static final String PROPERTY_BALANCED_MAX_INTERVAL = 81 "bluetooth.gatt.balanced_priority.max_interval"; 82 static final String PROPERTY_BALANCED_LATENCY = "bluetooth.gatt.balanced_priority.latency"; 83 static final String PROPERTY_LOW_MIN_INTERVAL = "bluetooth.gatt.low_priority_min.interval"; 84 static final String PROPERTY_LOW_MAX_INTERVAL = "bluetooth.gatt.low_priority_max.interval"; 85 static final String PROPERTY_LOW_LATENCY = "bluetooth.gatt.low_priority.latency"; 86 static final String PROPERTY_DCK_MIN_INTERVAL = "bluetooth.gatt.dck_priority_min.interval"; 87 static final String PROPERTY_DCK_MAX_INTERVAL = "bluetooth.gatt.dck_priority_max.interval"; 88 static final String PROPERTY_DCK_LATENCY = "bluetooth.gatt.dck_priority.latency"; 89 static final String PROPERTY_SUFFIX_PRIMARY = ".primary"; 90 static final String PROPERTY_SUFFIX_SECONDARY = ".secondary"; 91 92 private final AdapterService mAdapterService; 93 private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); 94 private final Set<BluetoothDevice> mMetadataListeningDevices = new HashSet<>(); 95 CompanionManager(AdapterService service, ServiceFactory factory)96 public CompanionManager(AdapterService service, ServiceFactory factory) { 97 mAdapterService = service; 98 99 mGattConnHighDefault = 100 new int[] { 101 getGattConfig( 102 PROPERTY_HIGH_MIN_INTERVAL, R.integer.gatt_high_priority_min_interval), 103 getGattConfig( 104 PROPERTY_HIGH_MAX_INTERVAL, R.integer.gatt_high_priority_max_interval), 105 getGattConfig(PROPERTY_HIGH_LATENCY, R.integer.gatt_high_priority_latency) 106 }; 107 mGattConnBalanceDefault = 108 new int[] { 109 getGattConfig( 110 PROPERTY_BALANCED_MIN_INTERVAL, 111 R.integer.gatt_balanced_priority_min_interval), 112 getGattConfig( 113 PROPERTY_BALANCED_MAX_INTERVAL, 114 R.integer.gatt_balanced_priority_max_interval), 115 getGattConfig( 116 PROPERTY_BALANCED_LATENCY, R.integer.gatt_balanced_priority_latency) 117 }; 118 mGattConnLowDefault = 119 new int[] { 120 getGattConfig(PROPERTY_LOW_MIN_INTERVAL, R.integer.gatt_low_power_min_interval), 121 getGattConfig(PROPERTY_LOW_MAX_INTERVAL, R.integer.gatt_low_power_max_interval), 122 getGattConfig(PROPERTY_LOW_LATENCY, R.integer.gatt_low_power_latency) 123 }; 124 mGattConnDckDefault = 125 new int[] { 126 getGattConfig( 127 PROPERTY_DCK_MIN_INTERVAL, R.integer.gatt_dck_priority_min_interval), 128 getGattConfig( 129 PROPERTY_DCK_MAX_INTERVAL, R.integer.gatt_dck_priority_max_interval), 130 getGattConfig(PROPERTY_DCK_LATENCY, R.integer.gatt_dck_priority_latency) 131 }; 132 133 mGattConnHighPrimary = 134 new int[] { 135 getGattConfig( 136 PROPERTY_HIGH_MIN_INTERVAL + PROPERTY_SUFFIX_PRIMARY, 137 R.integer.gatt_high_priority_min_interval_primary), 138 getGattConfig( 139 PROPERTY_HIGH_MAX_INTERVAL + PROPERTY_SUFFIX_PRIMARY, 140 R.integer.gatt_high_priority_max_interval_primary), 141 getGattConfig( 142 PROPERTY_HIGH_LATENCY + PROPERTY_SUFFIX_PRIMARY, 143 R.integer.gatt_high_priority_latency_primary) 144 }; 145 mGattConnBalancePrimary = 146 new int[] { 147 getGattConfig( 148 PROPERTY_BALANCED_MIN_INTERVAL + PROPERTY_SUFFIX_PRIMARY, 149 R.integer.gatt_balanced_priority_min_interval_primary), 150 getGattConfig( 151 PROPERTY_BALANCED_MAX_INTERVAL + PROPERTY_SUFFIX_PRIMARY, 152 R.integer.gatt_balanced_priority_max_interval_primary), 153 getGattConfig( 154 PROPERTY_BALANCED_LATENCY + PROPERTY_SUFFIX_PRIMARY, 155 R.integer.gatt_balanced_priority_latency_primary) 156 }; 157 mGattConnLowPrimary = 158 new int[] { 159 getGattConfig( 160 PROPERTY_LOW_MIN_INTERVAL + PROPERTY_SUFFIX_PRIMARY, 161 R.integer.gatt_low_power_min_interval_primary), 162 getGattConfig( 163 PROPERTY_LOW_MAX_INTERVAL + PROPERTY_SUFFIX_PRIMARY, 164 R.integer.gatt_low_power_max_interval_primary), 165 getGattConfig( 166 PROPERTY_LOW_LATENCY + PROPERTY_SUFFIX_PRIMARY, 167 R.integer.gatt_low_power_latency_primary) 168 }; 169 170 mGattConnHighSecondary = 171 new int[] { 172 getGattConfig( 173 PROPERTY_HIGH_MIN_INTERVAL + PROPERTY_SUFFIX_SECONDARY, 174 R.integer.gatt_high_priority_min_interval_secondary), 175 getGattConfig( 176 PROPERTY_HIGH_MAX_INTERVAL + PROPERTY_SUFFIX_SECONDARY, 177 R.integer.gatt_high_priority_max_interval_secondary), 178 getGattConfig( 179 PROPERTY_HIGH_LATENCY + PROPERTY_SUFFIX_SECONDARY, 180 R.integer.gatt_high_priority_latency_secondary) 181 }; 182 mGattConnBalanceSecondary = 183 new int[] { 184 getGattConfig( 185 PROPERTY_BALANCED_MIN_INTERVAL + PROPERTY_SUFFIX_SECONDARY, 186 R.integer.gatt_balanced_priority_min_interval_secondary), 187 getGattConfig( 188 PROPERTY_BALANCED_MAX_INTERVAL + PROPERTY_SUFFIX_SECONDARY, 189 R.integer.gatt_balanced_priority_max_interval_secondary), 190 getGattConfig( 191 PROPERTY_BALANCED_LATENCY + PROPERTY_SUFFIX_SECONDARY, 192 R.integer.gatt_balanced_priority_latency_secondary) 193 }; 194 mGattConnLowSecondary = 195 new int[] { 196 getGattConfig( 197 PROPERTY_LOW_MIN_INTERVAL + PROPERTY_SUFFIX_SECONDARY, 198 R.integer.gatt_low_power_min_interval_secondary), 199 getGattConfig( 200 PROPERTY_LOW_MAX_INTERVAL + PROPERTY_SUFFIX_SECONDARY, 201 R.integer.gatt_low_power_max_interval_secondary), 202 getGattConfig( 203 PROPERTY_LOW_LATENCY + PROPERTY_SUFFIX_SECONDARY, 204 R.integer.gatt_low_power_latency_secondary) 205 }; 206 } 207 getGattConfig(String property, int resId)208 private int getGattConfig(String property, int resId) { 209 return SystemProperties.getInt(property, mAdapterService.getResources().getInteger(resId)); 210 } 211 loadCompanionInfo()212 void loadCompanionInfo() { 213 synchronized (mMetadataListeningDevices) { 214 String address = getCompanionPreferences().getString(COMPANION_DEVICE_KEY, ""); 215 216 try { 217 mCompanionDevice = mAdapter.getRemoteDevice(address); 218 mCompanionType = 219 getCompanionPreferences().getInt(COMPANION_TYPE_KEY, COMPANION_TYPE_NONE); 220 } catch (IllegalArgumentException e) { 221 mCompanionDevice = null; 222 mCompanionType = COMPANION_TYPE_NONE; 223 } 224 } 225 226 if (mCompanionDevice == null) { 227 // We don't have any companion phone registered, try look from the bonded devices 228 for (BluetoothDevice device : mAdapterService.getBondedDevices()) { 229 byte[] metadata = 230 mAdapterService.getMetadata( 231 device, BluetoothDevice.METADATA_SOFTWARE_VERSION); 232 if (metadata == null) { 233 continue; 234 } 235 String valueStr = new String(metadata); 236 if ((valueStr.equals(BluetoothDevice.COMPANION_TYPE_PRIMARY) 237 || valueStr.equals(BluetoothDevice.COMPANION_TYPE_SECONDARY))) { 238 // found the companion device, store and unregister all listeners 239 Log.i(TAG, "Found companion device from the database!"); 240 setCompanionDevice(device, valueStr); 241 break; 242 } 243 registerMetadataListener(device); 244 } 245 } 246 Log.i(TAG, "Companion device is " + mCompanionDevice + ", type=" + mCompanionType); 247 } 248 249 final BluetoothAdapter.OnMetadataChangedListener mMetadataListener = 250 new BluetoothAdapter.OnMetadataChangedListener() { 251 @Override 252 public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) { 253 if (value == null) { 254 Log.d(TAG, "onMetadataChanged(device, " + key + ", null)"); 255 return; 256 } 257 String valueStr = new String(value); 258 Log.d(TAG, "Metadata updated in " + device + ": " + key + "=" + valueStr); 259 if (key == BluetoothDevice.METADATA_SOFTWARE_VERSION 260 && (valueStr.equals(BluetoothDevice.COMPANION_TYPE_PRIMARY) 261 || valueStr.equals(BluetoothDevice.COMPANION_TYPE_SECONDARY))) { 262 setCompanionDevice(device, valueStr); 263 } 264 } 265 }; 266 267 @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/350563786 setCompanionDevice(BluetoothDevice companionDevice, String type)268 private void setCompanionDevice(BluetoothDevice companionDevice, String type) { 269 synchronized (mMetadataListeningDevices) { 270 Log.i(TAG, "setCompanionDevice: " + companionDevice + ", type=" + type); 271 mCompanionDevice = companionDevice; 272 mCompanionType = 273 type.equals(BluetoothDevice.COMPANION_TYPE_PRIMARY) 274 ? COMPANION_TYPE_PRIMARY 275 : COMPANION_TYPE_SECONDARY; 276 277 // unregister all metadata listeners 278 for (BluetoothDevice device : mMetadataListeningDevices) { 279 try { 280 mAdapter.removeOnMetadataChangedListener(device, mMetadataListener); 281 } catch (IllegalArgumentException e) { 282 Log.e(TAG, "failed to unregister metadata listener for " + device + " " + e); 283 } 284 } 285 mMetadataListeningDevices.clear(); 286 287 SharedPreferences.Editor pref = getCompanionPreferences().edit(); 288 pref.putString(COMPANION_DEVICE_KEY, mCompanionDevice.getAddress()); 289 pref.putInt(COMPANION_TYPE_KEY, mCompanionType); 290 pref.apply(); 291 } 292 } 293 getCompanionPreferences()294 private SharedPreferences getCompanionPreferences() { 295 return mAdapterService.getSharedPreferences(COMPANION_INFO, Context.MODE_PRIVATE); 296 } 297 298 /** 299 * Bond state change event from the AdapterService 300 * 301 * @param device the Bluetooth device 302 * @param state the new Bluetooth bond state of the device 303 */ onBondStateChanged(BluetoothDevice device, int state)304 public void onBondStateChanged(BluetoothDevice device, int state) { 305 synchronized (mMetadataListeningDevices) { 306 if (mCompanionDevice != null) { 307 // We already have the companion device, do not care bond state change any more. 308 return; 309 } 310 switch (state) { 311 case BluetoothDevice.BOND_BONDING: 312 registerMetadataListener(device); 313 break; 314 case BluetoothDevice.BOND_NONE: 315 removeMetadataListener(device); 316 break; 317 default: 318 break; 319 } 320 } 321 } 322 323 @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/350563786 registerMetadataListener(BluetoothDevice device)324 private void registerMetadataListener(BluetoothDevice device) { 325 synchronized (mMetadataListeningDevices) { 326 Log.d(TAG, "register metadata listener: " + device); 327 try { 328 mAdapter.addOnMetadataChangedListener( 329 device, mAdapterService.getMainExecutor(), mMetadataListener); 330 } catch (IllegalArgumentException e) { 331 Log.e(TAG, "failed to register metadata listener for " + device + " " + e); 332 } 333 mMetadataListeningDevices.add(device); 334 } 335 } 336 337 @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/350563786 removeMetadataListener(BluetoothDevice device)338 private void removeMetadataListener(BluetoothDevice device) { 339 synchronized (mMetadataListeningDevices) { 340 if (!mMetadataListeningDevices.contains(device)) return; 341 342 Log.d(TAG, "remove metadata listener: " + device); 343 try { 344 mAdapter.removeOnMetadataChangedListener(device, mMetadataListener); 345 } catch (IllegalArgumentException e) { 346 Log.e(TAG, "failed to unregister metadata listener for " + device + " " + e); 347 } 348 mMetadataListeningDevices.remove(device); 349 } 350 } 351 352 /** 353 * Method to get the stored companion device 354 * 355 * @return the companion Bluetooth device 356 */ getCompanionDevice()357 public BluetoothDevice getCompanionDevice() { 358 return mCompanionDevice; 359 } 360 361 /** 362 * Method to check whether it is a companion device 363 * 364 * @param address the address of the device 365 * @return true if the address is a companion device, otherwise false 366 */ isCompanionDevice(String address)367 public boolean isCompanionDevice(String address) { 368 try { 369 return isCompanionDevice(mAdapter.getRemoteDevice(address)); 370 } catch (IllegalArgumentException e) { 371 return false; 372 } 373 } 374 375 /** 376 * Method to check whether it is a companion device 377 * 378 * @param device the Bluetooth device 379 * @return true if the device is a companion device, otherwise false 380 */ isCompanionDevice(BluetoothDevice device)381 public boolean isCompanionDevice(BluetoothDevice device) { 382 if (device == null) return false; 383 return device.equals(mCompanionDevice); 384 } 385 386 /** Method to reset the stored companion info */ factoryReset()387 public void factoryReset() { 388 synchronized (mMetadataListeningDevices) { 389 mCompanionDevice = null; 390 mCompanionType = COMPANION_TYPE_NONE; 391 392 SharedPreferences.Editor pref = getCompanionPreferences().edit(); 393 pref.remove(COMPANION_DEVICE_KEY); 394 pref.remove(COMPANION_TYPE_KEY); 395 pref.apply(); 396 } 397 } 398 399 /** 400 * Gets the GATT connection parameters of the device 401 * 402 * @param address the address of the Bluetooth device 403 * @param type type of the parameter, can be GATT_CONN_INTERVAL_MIN, GATT_CONN_INTERVAL_MAX or 404 * GATT_CONN_LATENCY 405 * @param priority the priority of the connection, can be 406 * BluetoothGatt.CONNECTION_PRIORITY_HIGH, BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER or 407 * BluetoothGatt.CONNECTION_PRIORITY_BALANCED 408 * @return the connection parameter in integer 409 */ getGattConnParameters(String address, int type, int priority)410 public int getGattConnParameters(String address, int type, int priority) { 411 int companionType = isCompanionDevice(address) ? mCompanionType : COMPANION_TYPE_NONE; 412 int parameter; 413 switch (companionType) { 414 case COMPANION_TYPE_PRIMARY: 415 parameter = getGattConnParameterPrimary(type, priority); 416 break; 417 case COMPANION_TYPE_SECONDARY: 418 parameter = getGattConnParameterSecondary(type, priority); 419 break; 420 default: 421 parameter = getGattConnParameterDefault(type, priority); 422 break; 423 } 424 return parameter; 425 } 426 getGattConnParameterPrimary(int type, int priority)427 private int getGattConnParameterPrimary(int type, int priority) { 428 switch (priority) { 429 case BluetoothGatt.CONNECTION_PRIORITY_HIGH: 430 return mGattConnHighPrimary[type]; 431 case BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER: 432 return mGattConnLowPrimary[type]; 433 } 434 return mGattConnBalancePrimary[type]; 435 } 436 getGattConnParameterSecondary(int type, int priority)437 private int getGattConnParameterSecondary(int type, int priority) { 438 switch (priority) { 439 case BluetoothGatt.CONNECTION_PRIORITY_HIGH: 440 return mGattConnHighSecondary[type]; 441 case BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER: 442 return mGattConnLowSecondary[type]; 443 } 444 return mGattConnBalanceSecondary[type]; 445 } 446 getGattConnParameterDefault(int type, int mode)447 private int getGattConnParameterDefault(int type, int mode) { 448 switch (mode) { 449 case BluetoothGatt.CONNECTION_PRIORITY_HIGH: 450 return mGattConnHighDefault[type]; 451 case BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER: 452 return mGattConnLowDefault[type]; 453 case BluetoothGatt.CONNECTION_PRIORITY_DCK: 454 return mGattConnDckDefault[type]; 455 } 456 return mGattConnBalanceDefault[type]; 457 } 458 } 459