• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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