• 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.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