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