• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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.storage;
18 
19 import android.bluetooth.BluetoothA2dp;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothProfile;
23 import android.bluetooth.BluetoothProtoEnums;
24 import android.content.BroadcastReceiver;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.os.Binder;
30 import android.os.Handler;
31 import android.os.HandlerThread;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.provider.Settings;
35 import android.util.Log;
36 import android.util.StatsLog;
37 
38 import com.android.bluetooth.Utils;
39 import com.android.bluetooth.btservice.AdapterService;
40 import com.android.internal.annotations.VisibleForTesting;
41 
42 import java.util.Arrays;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Objects;
47 import java.util.concurrent.Semaphore;
48 import java.util.concurrent.TimeUnit;
49 
50 /**
51  * The active device manager is responsible to handle a Room database
52  * for Bluetooth persistent data.
53  */
54 public class DatabaseManager {
55     private static final boolean DBG = true;
56     private static final boolean VERBOSE = true;
57     private static final String TAG = "BluetoothDatabase";
58 
59     private AdapterService mAdapterService = null;
60     private HandlerThread mHandlerThread = null;
61     private Handler mHandler = null;
62     private MetadataDatabase mDatabase = null;
63     private boolean mMigratedFromSettingsGlobal = false;
64 
65     @VisibleForTesting
66     final Map<String, Metadata> mMetadataCache = new HashMap<>();
67     private final Semaphore mSemaphore = new Semaphore(1);
68 
69     private static final int LOAD_DATABASE_TIMEOUT = 500; // milliseconds
70     private static final int MSG_LOAD_DATABASE = 0;
71     private static final int MSG_UPDATE_DATABASE = 1;
72     private static final int MSG_DELETE_DATABASE = 2;
73     private static final int MSG_CLEAR_DATABASE = 100;
74     private static final String LOCAL_STORAGE = "LocalStorage";
75 
76     /**
77      * Constructor of the DatabaseManager
78      */
DatabaseManager(AdapterService service)79     public DatabaseManager(AdapterService service) {
80         mAdapterService = service;
81     }
82 
83     class DatabaseHandler extends Handler {
DatabaseHandler(Looper looper)84         DatabaseHandler(Looper looper) {
85             super(looper);
86         }
87 
88         @Override
handleMessage(Message msg)89         public void handleMessage(Message msg) {
90             switch (msg.what) {
91                 case MSG_LOAD_DATABASE: {
92                     synchronized (mDatabase) {
93                         List<Metadata> list;
94                         try {
95                             list = mDatabase.load();
96                         } catch (IllegalStateException e) {
97                             Log.e(TAG, "Unable to open database: " + e);
98                             mDatabase = MetadataDatabase
99                                     .createDatabaseWithoutMigration(mAdapterService);
100                             list = mDatabase.load();
101                         }
102                         cacheMetadata(list);
103                     }
104                     break;
105                 }
106                 case MSG_UPDATE_DATABASE: {
107                     Metadata data = (Metadata) msg.obj;
108                     synchronized (mDatabase) {
109                         mDatabase.insert(data);
110                     }
111                     break;
112                 }
113                 case MSG_DELETE_DATABASE: {
114                     String address = (String) msg.obj;
115                     synchronized (mDatabase) {
116                         mDatabase.delete(address);
117                     }
118                     break;
119                 }
120                 case MSG_CLEAR_DATABASE: {
121                     synchronized (mDatabase) {
122                         mDatabase.deleteAll();
123                     }
124                     break;
125                 }
126             }
127         }
128     }
129 
130     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
131         @Override
132         public void onReceive(Context context, Intent intent) {
133             String action = intent.getAction();
134             if (action == null) {
135                 Log.e(TAG, "Received intent with null action");
136                 return;
137             }
138             switch (action) {
139                 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
140                     int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
141                             BluetoothDevice.ERROR);
142                     BluetoothDevice device =
143                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
144                     Objects.requireNonNull(device,
145                             "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
146                     bondStateChanged(device, state);
147                     break;
148                 }
149                 case BluetoothAdapter.ACTION_STATE_CHANGED: {
150                     int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
151                             BluetoothAdapter.STATE_OFF);
152                     if (!mMigratedFromSettingsGlobal
153                             && state == BluetoothAdapter.STATE_TURNING_ON) {
154                         migrateSettingsGlobal();
155                     }
156                     break;
157                 }
158             }
159         }
160     };
161 
bondStateChanged(BluetoothDevice device, int state)162     void bondStateChanged(BluetoothDevice device, int state) {
163         synchronized (mMetadataCache) {
164             String address = device.getAddress();
165             if (state != BluetoothDevice.BOND_NONE) {
166                 if (mMetadataCache.containsKey(address)) {
167                     return;
168                 }
169                 createMetadata(address);
170             } else {
171                 Metadata metadata = mMetadataCache.get(address);
172                 if (metadata != null) {
173                     mMetadataCache.remove(address);
174                     deleteDatabase(metadata);
175                 }
176             }
177         }
178     }
179 
isValidMetaKey(int key)180     boolean isValidMetaKey(int key) {
181         switch (key) {
182             case BluetoothDevice.METADATA_MANUFACTURER_NAME:
183             case BluetoothDevice.METADATA_MODEL_NAME:
184             case BluetoothDevice.METADATA_SOFTWARE_VERSION:
185             case BluetoothDevice.METADATA_HARDWARE_VERSION:
186             case BluetoothDevice.METADATA_COMPANION_APP:
187             case BluetoothDevice.METADATA_MAIN_ICON:
188             case BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET:
189             case BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON:
190             case BluetoothDevice.METADATA_UNTETHERED_RIGHT_ICON:
191             case BluetoothDevice.METADATA_UNTETHERED_CASE_ICON:
192             case BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY:
193             case BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY:
194             case BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY:
195             case BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING:
196             case BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING:
197             case BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING:
198             case BluetoothDevice.METADATA_ENHANCED_SETTINGS_UI_URI:
199                 return true;
200         }
201         Log.w(TAG, "Invalid metadata key " + key);
202         return false;
203     }
204 
205     /**
206      * Set customized metadata to database with requested key
207      */
208     @VisibleForTesting
setCustomMeta(BluetoothDevice device, int key, byte[] newValue)209     public boolean setCustomMeta(BluetoothDevice device, int key, byte[] newValue) {
210         synchronized (mMetadataCache) {
211             if (device == null) {
212                 Log.e(TAG, "setCustomMeta: device is null");
213                 return false;
214             }
215             if (!isValidMetaKey(key)) {
216                 Log.e(TAG, "setCustomMeta: meta key invalid " + key);
217                 return false;
218             }
219 
220             String address = device.getAddress();
221             if (VERBOSE) {
222                 Log.d(TAG, "setCustomMeta: " + address + ", key=" + key);
223             }
224             if (!mMetadataCache.containsKey(address)) {
225                 createMetadata(address);
226             }
227             Metadata data = mMetadataCache.get(address);
228             byte[] oldValue = data.getCustomizedMeta(key);
229             if (oldValue != null && Arrays.equals(oldValue, newValue)) {
230                 if (VERBOSE) {
231                     Log.d(TAG, "setCustomMeta: metadata not changed.");
232                 }
233                 return true;
234             }
235             logManufacturerInfo(device, key, newValue);
236             data.setCustomizedMeta(key, newValue);
237 
238             updateDatabase(data);
239             mAdapterService.metadataChanged(address, key, newValue);
240             return true;
241         }
242     }
243 
244     /**
245      * Get customized metadata from database with requested key
246      */
247     @VisibleForTesting
getCustomMeta(BluetoothDevice device, int key)248     public byte[] getCustomMeta(BluetoothDevice device, int key) {
249         synchronized (mMetadataCache) {
250             if (device == null) {
251                 Log.e(TAG, "getCustomMeta: device is null");
252                 return null;
253             }
254             if (!isValidMetaKey(key)) {
255                 Log.e(TAG, "getCustomMeta: meta key invalid " + key);
256                 return null;
257             }
258 
259             String address = device.getAddress();
260 
261             if (!mMetadataCache.containsKey(address)) {
262                 Log.e(TAG, "getCustomMeta: device " + address + " is not in cache");
263                 return null;
264             }
265 
266             Metadata data = mMetadataCache.get(address);
267             return data.getCustomizedMeta(key);
268         }
269     }
270 
271     /**
272      * Set the device profile prioirty
273      *
274      * @param device {@link BluetoothDevice} wish to set
275      * @param profile The Bluetooth profile; one of {@link BluetoothProfile#HEADSET},
276      * {@link BluetoothProfile#HEADSET_CLIENT}, {@link BluetoothProfile#A2DP},
277      * {@link BluetoothProfile#A2DP_SINK}, {@link BluetoothProfile#HID_HOST},
278      * {@link BluetoothProfile#PAN}, {@link BluetoothProfile#PBAP},
279      * {@link BluetoothProfile#PBAP_CLIENT}, {@link BluetoothProfile#MAP},
280      * {@link BluetoothProfile#MAP_CLIENT}, {@link BluetoothProfile#SAP},
281      * {@link BluetoothProfile#HEARING_AID}
282      * @param newPriority the priority to set; one of
283      * {@link BluetoothProfile#PRIORITY_UNDEFINED},
284      * {@link BluetoothProfile#PRIORITY_OFF},
285      * {@link BluetoothProfile#PRIORITY_ON},
286      * {@link BluetoothProfile#PRIORITY_AUTO_CONNECT}
287      */
288     @VisibleForTesting
setProfilePriority(BluetoothDevice device, int profile, int newPriority)289     public boolean setProfilePriority(BluetoothDevice device, int profile, int newPriority) {
290         synchronized (mMetadataCache) {
291             if (device == null) {
292                 Log.e(TAG, "setProfilePriority: device is null");
293                 return false;
294             }
295 
296             if (newPriority != BluetoothProfile.PRIORITY_UNDEFINED
297                     && newPriority != BluetoothProfile.PRIORITY_OFF
298                     && newPriority != BluetoothProfile.PRIORITY_ON
299                     && newPriority != BluetoothProfile.PRIORITY_AUTO_CONNECT) {
300                 Log.e(TAG, "setProfilePriority: invalid priority " + newPriority);
301                 return false;
302             }
303 
304             String address = device.getAddress();
305             if (VERBOSE) {
306                 Log.v(TAG, "setProfilePriority: " + address + ", profile=" + profile
307                         + ", priority = " + newPriority);
308             }
309             if (!mMetadataCache.containsKey(address)) {
310                 if (newPriority == BluetoothProfile.PRIORITY_UNDEFINED) {
311                     return true;
312                 }
313                 createMetadata(address);
314             }
315             Metadata data = mMetadataCache.get(address);
316             int oldPriority = data.getProfilePriority(profile);
317             if (oldPriority == newPriority) {
318                 if (VERBOSE) {
319                     Log.v(TAG, "setProfilePriority priority not changed.");
320                 }
321                 return true;
322             }
323 
324             data.setProfilePriority(profile, newPriority);
325             updateDatabase(data);
326             return true;
327         }
328     }
329 
330     /**
331      * Get the device profile prioirty
332      *
333      * @param device {@link BluetoothDevice} wish to get
334      * @param profile The Bluetooth profile; one of {@link BluetoothProfile#HEADSET},
335      * {@link BluetoothProfile#HEADSET_CLIENT}, {@link BluetoothProfile#A2DP},
336      * {@link BluetoothProfile#A2DP_SINK}, {@link BluetoothProfile#HID_HOST},
337      * {@link BluetoothProfile#PAN}, {@link BluetoothProfile#PBAP},
338      * {@link BluetoothProfile#PBAP_CLIENT}, {@link BluetoothProfile#MAP},
339      * {@link BluetoothProfile#MAP_CLIENT}, {@link BluetoothProfile#SAP},
340      * {@link BluetoothProfile#HEARING_AID}
341      * @return the profile priority of the device; one of
342      * {@link BluetoothProfile#PRIORITY_UNDEFINED},
343      * {@link BluetoothProfile#PRIORITY_OFF},
344      * {@link BluetoothProfile#PRIORITY_ON},
345      * {@link BluetoothProfile#PRIORITY_AUTO_CONNECT}
346      */
347     @VisibleForTesting
getProfilePriority(BluetoothDevice device, int profile)348     public int getProfilePriority(BluetoothDevice device, int profile) {
349         synchronized (mMetadataCache) {
350             if (device == null) {
351                 Log.e(TAG, "getProfilePriority: device is null");
352                 return BluetoothProfile.PRIORITY_UNDEFINED;
353             }
354 
355             String address = device.getAddress();
356 
357             if (!mMetadataCache.containsKey(address)) {
358                 Log.e(TAG, "getProfilePriority: device " + address + " is not in cache");
359                 return BluetoothProfile.PRIORITY_UNDEFINED;
360             }
361 
362             Metadata data = mMetadataCache.get(address);
363             int priority = data.getProfilePriority(profile);
364             if (VERBOSE) {
365                 Log.v(TAG, "getProfilePriority: " + address + ", profile=" + profile
366                         + ", priority = " + priority);
367             }
368             return priority;
369         }
370     }
371 
372     /**
373      * Set the A2DP optional coedc support value
374      *
375      * @param device {@link BluetoothDevice} wish to set
376      * @param newValue the new A2DP optional coedc support value, one of
377      * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORT_UNKNOWN},
378      * {@link BluetoothA2dp#OPTIONAL_CODECS_NOT_SUPPORTED},
379      * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORTED}
380      */
381     @VisibleForTesting
setA2dpSupportsOptionalCodecs(BluetoothDevice device, int newValue)382     public void setA2dpSupportsOptionalCodecs(BluetoothDevice device, int newValue) {
383         synchronized (mMetadataCache) {
384             if (device == null) {
385                 Log.e(TAG, "setA2dpOptionalCodec: device is null");
386                 return;
387             }
388             if (newValue != BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN
389                     && newValue != BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED
390                     && newValue != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
391                 Log.e(TAG, "setA2dpSupportsOptionalCodecs: invalid value " + newValue);
392                 return;
393             }
394 
395             String address = device.getAddress();
396 
397             if (!mMetadataCache.containsKey(address)) {
398                 return;
399             }
400             Metadata data = mMetadataCache.get(address);
401             int oldValue = data.a2dpSupportsOptionalCodecs;
402             if (oldValue == newValue) {
403                 return;
404             }
405 
406             data.a2dpSupportsOptionalCodecs = newValue;
407             updateDatabase(data);
408         }
409     }
410 
411     /**
412      * Get the A2DP optional coedc support value
413      *
414      * @param device {@link BluetoothDevice} wish to get
415      * @return the A2DP optional coedc support value, one of
416      * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORT_UNKNOWN},
417      * {@link BluetoothA2dp#OPTIONAL_CODECS_NOT_SUPPORTED},
418      * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORTED},
419      */
420     @VisibleForTesting
getA2dpSupportsOptionalCodecs(BluetoothDevice device)421     public int getA2dpSupportsOptionalCodecs(BluetoothDevice device) {
422         synchronized (mMetadataCache) {
423             if (device == null) {
424                 Log.e(TAG, "setA2dpOptionalCodec: device is null");
425                 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
426             }
427 
428             String address = device.getAddress();
429 
430             if (!mMetadataCache.containsKey(address)) {
431                 Log.e(TAG, "getA2dpOptionalCodec: device " + address + " is not in cache");
432                 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
433             }
434 
435             Metadata data = mMetadataCache.get(address);
436             return data.a2dpSupportsOptionalCodecs;
437         }
438     }
439 
440     /**
441      * Set the A2DP optional coedc enabled value
442      *
443      * @param device {@link BluetoothDevice} wish to set
444      * @param newValue the new A2DP optional coedc enabled value, one of
445      * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_UNKNOWN},
446      * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_DISABLED},
447      * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_ENABLED}
448      */
449     @VisibleForTesting
setA2dpOptionalCodecsEnabled(BluetoothDevice device, int newValue)450     public void setA2dpOptionalCodecsEnabled(BluetoothDevice device, int newValue) {
451         synchronized (mMetadataCache) {
452             if (device == null) {
453                 Log.e(TAG, "setA2dpOptionalCodecEnabled: device is null");
454                 return;
455             }
456             if (newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
457                     && newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
458                     && newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
459                 Log.e(TAG, "setA2dpOptionalCodecsEnabled: invalid value " + newValue);
460                 return;
461             }
462 
463             String address = device.getAddress();
464 
465             if (!mMetadataCache.containsKey(address)) {
466                 return;
467             }
468             Metadata data = mMetadataCache.get(address);
469             int oldValue = data.a2dpOptionalCodecsEnabled;
470             if (oldValue == newValue) {
471                 return;
472             }
473 
474             data.a2dpOptionalCodecsEnabled = newValue;
475             updateDatabase(data);
476         }
477     }
478 
479     /**
480      * Get the A2DP optional coedc enabled value
481      *
482      * @param device {@link BluetoothDevice} wish to get
483      * @return the A2DP optional coedc enabled value, one of
484      * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_UNKNOWN},
485      * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_DISABLED},
486      * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_ENABLED}
487      */
488     @VisibleForTesting
getA2dpOptionalCodecsEnabled(BluetoothDevice device)489     public int getA2dpOptionalCodecsEnabled(BluetoothDevice device) {
490         synchronized (mMetadataCache) {
491             if (device == null) {
492                 Log.e(TAG, "getA2dpOptionalCodecEnabled: device is null");
493                 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
494             }
495 
496             String address = device.getAddress();
497 
498             if (!mMetadataCache.containsKey(address)) {
499                 Log.e(TAG, "getA2dpOptionalCodecEnabled: device " + address + " is not in cache");
500                 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
501             }
502 
503             Metadata data = mMetadataCache.get(address);
504             return data.a2dpOptionalCodecsEnabled;
505         }
506     }
507 
508     /**
509      * Get the {@link Looper} for the handler thread. This is used in testing and helper
510      * objects
511      *
512      * @return {@link Looper} for the handler thread
513      */
514     @VisibleForTesting
getHandlerLooper()515     public Looper getHandlerLooper() {
516         if (mHandlerThread == null) {
517             return null;
518         }
519         return mHandlerThread.getLooper();
520     }
521 
522     /**
523      * Start and initialize the DatabaseManager
524      *
525      * @param database the Bluetooth storage {@link MetadataDatabase}
526      */
start(MetadataDatabase database)527     public void start(MetadataDatabase database) {
528         if (DBG) {
529             Log.d(TAG, "start()");
530         }
531 
532         if (mAdapterService == null) {
533             Log.e(TAG, "stat failed, mAdapterService is null.");
534             return;
535         }
536 
537         if (database == null) {
538             Log.e(TAG, "stat failed, database is null.");
539             return;
540         }
541 
542         mDatabase = database;
543 
544         mHandlerThread = new HandlerThread("BluetoothDatabaseManager");
545         mHandlerThread.start();
546         mHandler = new DatabaseHandler(mHandlerThread.getLooper());
547 
548         IntentFilter filter = new IntentFilter();
549         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
550         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
551         mAdapterService.registerReceiver(mReceiver, filter);
552 
553         loadDatabase();
554     }
555 
getDatabaseAbsolutePath()556     String getDatabaseAbsolutePath() {
557         //TODO backup database when Bluetooth turn off and FOTA?
558         return mAdapterService.getDatabasePath(MetadataDatabase.DATABASE_NAME)
559                 .getAbsolutePath();
560     }
561 
562     /**
563      * Clear all persistence data in database
564      */
factoryReset()565     public void factoryReset() {
566         Log.w(TAG, "factoryReset");
567         Message message = mHandler.obtainMessage(MSG_CLEAR_DATABASE);
568         mHandler.sendMessage(message);
569     }
570 
571     /**
572      * Close and de-init the DatabaseManager
573      */
cleanup()574     public void cleanup() {
575         removeUnusedMetadata();
576         mAdapterService.unregisterReceiver(mReceiver);
577         if (mHandlerThread != null) {
578             mHandlerThread.quit();
579             mHandlerThread = null;
580         }
581         mMetadataCache.clear();
582     }
583 
createMetadata(String address)584     void createMetadata(String address) {
585         if (VERBOSE) {
586             Log.v(TAG, "createMetadata " + address);
587         }
588         Metadata data = new Metadata(address);
589         mMetadataCache.put(address, data);
590         updateDatabase(data);
591     }
592 
593     @VisibleForTesting
removeUnusedMetadata()594     void removeUnusedMetadata() {
595         BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
596         synchronized (mMetadataCache) {
597             mMetadataCache.forEach((address, metadata) -> {
598                 if (!address.equals(LOCAL_STORAGE)
599                         && !Arrays.asList(bondedDevices).stream().anyMatch(device ->
600                         address.equals(device.getAddress()))) {
601                     List<Integer> list = metadata.getChangedCustomizedMeta();
602                     for (int key : list) {
603                         mAdapterService.metadataChanged(address, key, null);
604                     }
605                     Log.i(TAG, "remove unpaired device from database " + address);
606                     deleteDatabase(mMetadataCache.get(address));
607                 }
608             });
609         }
610     }
611 
cacheMetadata(List<Metadata> list)612     void cacheMetadata(List<Metadata> list) {
613         synchronized (mMetadataCache) {
614             Log.i(TAG, "cacheMetadata");
615             // Unlock the main thread.
616             mSemaphore.release();
617 
618             if (!isMigrated(list)) {
619                 // Wait for data migrate from Settings Global
620                 mMigratedFromSettingsGlobal = false;
621                 return;
622             }
623             mMigratedFromSettingsGlobal = true;
624             for (Metadata data : list) {
625                 String address = data.getAddress();
626                 if (VERBOSE) {
627                     Log.v(TAG, "cacheMetadata: found device " + address);
628                 }
629                 mMetadataCache.put(address, data);
630             }
631             if (VERBOSE) {
632                 Log.v(TAG, "cacheMetadata: Database is ready");
633             }
634         }
635     }
636 
isMigrated(List<Metadata> list)637     boolean isMigrated(List<Metadata> list) {
638         for (Metadata data : list) {
639             String address = data.getAddress();
640             if (address.equals(LOCAL_STORAGE) && data.migrated) {
641                 return true;
642             }
643         }
644         return false;
645     }
646 
migrateSettingsGlobal()647     void migrateSettingsGlobal() {
648         Log.i(TAG, "migrateSettingGlobal");
649 
650         BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
651         ContentResolver contentResolver = mAdapterService.getContentResolver();
652 
653         for (BluetoothDevice device : bondedDevices) {
654             int a2dpPriority = Settings.Global.getInt(contentResolver,
655                     Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
656                     BluetoothProfile.PRIORITY_UNDEFINED);
657             int a2dpSinkPriority = Settings.Global.getInt(contentResolver,
658                     Settings.Global.getBluetoothA2dpSrcPriorityKey(device.getAddress()),
659                     BluetoothProfile.PRIORITY_UNDEFINED);
660             int hearingaidPriority = Settings.Global.getInt(contentResolver,
661                     Settings.Global.getBluetoothHearingAidPriorityKey(device.getAddress()),
662                     BluetoothProfile.PRIORITY_UNDEFINED);
663             int headsetPriority = Settings.Global.getInt(contentResolver,
664                     Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
665                     BluetoothProfile.PRIORITY_UNDEFINED);
666             int headsetClientPriority = Settings.Global.getInt(contentResolver,
667                     Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
668                     BluetoothProfile.PRIORITY_UNDEFINED);
669             int hidHostPriority = Settings.Global.getInt(contentResolver,
670                     Settings.Global.getBluetoothHidHostPriorityKey(device.getAddress()),
671                     BluetoothProfile.PRIORITY_UNDEFINED);
672             int mapPriority = Settings.Global.getInt(contentResolver,
673                     Settings.Global.getBluetoothMapPriorityKey(device.getAddress()),
674                     BluetoothProfile.PRIORITY_UNDEFINED);
675             int mapClientPriority = Settings.Global.getInt(contentResolver,
676                     Settings.Global.getBluetoothMapClientPriorityKey(device.getAddress()),
677                     BluetoothProfile.PRIORITY_UNDEFINED);
678             int panPriority = Settings.Global.getInt(contentResolver,
679                     Settings.Global.getBluetoothPanPriorityKey(device.getAddress()),
680                     BluetoothProfile.PRIORITY_UNDEFINED);
681             int pbapPriority = Settings.Global.getInt(contentResolver,
682                     Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
683                     BluetoothProfile.PRIORITY_UNDEFINED);
684             int pbapClientPriority = Settings.Global.getInt(contentResolver,
685                     Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
686                     BluetoothProfile.PRIORITY_UNDEFINED);
687             int sapPriority = Settings.Global.getInt(contentResolver,
688                     Settings.Global.getBluetoothSapPriorityKey(device.getAddress()),
689                     BluetoothProfile.PRIORITY_UNDEFINED);
690             int a2dpSupportsOptionalCodec = Settings.Global.getInt(contentResolver,
691                     Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()),
692                     BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
693             int a2dpOptionalCodecEnabled = Settings.Global.getInt(contentResolver,
694                     Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()),
695                     BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
696 
697             String address = device.getAddress();
698             Metadata data = new Metadata(address);
699             data.setProfilePriority(BluetoothProfile.A2DP, a2dpPriority);
700             data.setProfilePriority(BluetoothProfile.A2DP_SINK, a2dpSinkPriority);
701             data.setProfilePriority(BluetoothProfile.HEADSET, headsetPriority);
702             data.setProfilePriority(BluetoothProfile.HEADSET_CLIENT, headsetClientPriority);
703             data.setProfilePriority(BluetoothProfile.HID_HOST, hidHostPriority);
704             data.setProfilePriority(BluetoothProfile.PAN, panPriority);
705             data.setProfilePriority(BluetoothProfile.PBAP, pbapPriority);
706             data.setProfilePriority(BluetoothProfile.PBAP_CLIENT, pbapClientPriority);
707             data.setProfilePriority(BluetoothProfile.MAP, mapPriority);
708             data.setProfilePriority(BluetoothProfile.MAP_CLIENT, mapClientPriority);
709             data.setProfilePriority(BluetoothProfile.SAP, sapPriority);
710             data.setProfilePriority(BluetoothProfile.HEARING_AID, hearingaidPriority);
711             data.a2dpSupportsOptionalCodecs = a2dpSupportsOptionalCodec;
712             data.a2dpOptionalCodecsEnabled = a2dpOptionalCodecEnabled;
713             mMetadataCache.put(address, data);
714             updateDatabase(data);
715         }
716 
717         // Mark database migrated from Settings Global
718         Metadata localData = new Metadata(LOCAL_STORAGE);
719         localData.migrated = true;
720         mMetadataCache.put(LOCAL_STORAGE, localData);
721         updateDatabase(localData);
722 
723         // Reload database after migration is completed
724         loadDatabase();
725 
726     }
727 
loadDatabase()728     private void loadDatabase() {
729         if (DBG) {
730             Log.d(TAG, "Load Database");
731         }
732         Message message = mHandler.obtainMessage(MSG_LOAD_DATABASE);
733         mHandler.sendMessage(message);
734         try {
735             // Lock the thread until handler thread finish loading database.
736             mSemaphore.tryAcquire(LOAD_DATABASE_TIMEOUT, TimeUnit.MILLISECONDS);
737         } catch (InterruptedException e) {
738             Log.e(TAG, "loadDatabase: semaphore acquire failed");
739         }
740     }
741 
updateDatabase(Metadata data)742     private void updateDatabase(Metadata data) {
743         if (data.getAddress() == null) {
744             Log.e(TAG, "updateDatabase: address is null");
745             return;
746         }
747         if (DBG) {
748             Log.d(TAG, "updateDatabase " + data.getAddress());
749         }
750         Message message = mHandler.obtainMessage(MSG_UPDATE_DATABASE);
751         message.obj = data;
752         mHandler.sendMessage(message);
753     }
754 
deleteDatabase(Metadata data)755     private void deleteDatabase(Metadata data) {
756         if (data.getAddress() == null) {
757             Log.e(TAG, "deleteDatabase: address is null");
758             return;
759         }
760         Log.d(TAG, "deleteDatabase: " + data.getAddress());
761         Message message = mHandler.obtainMessage(MSG_DELETE_DATABASE);
762         message.obj = data.getAddress();
763         mHandler.sendMessage(message);
764     }
765 
logManufacturerInfo(BluetoothDevice device, int key, byte[] bytesValue)766     private void logManufacturerInfo(BluetoothDevice device, int key, byte[] bytesValue) {
767         String callingApp = mAdapterService.getPackageManager().getNameForUid(
768                 Binder.getCallingUid());
769         String manufacturerName = "";
770         String modelName = "";
771         String hardwareVersion = "";
772         String softwareVersion = "";
773         String value = Utils.byteArrayToUtf8String(bytesValue);
774         switch (key) {
775             case BluetoothDevice.METADATA_MANUFACTURER_NAME:
776                 manufacturerName = value;
777                 break;
778             case BluetoothDevice.METADATA_MODEL_NAME:
779                 modelName = value;
780                 break;
781             case BluetoothDevice.METADATA_HARDWARE_VERSION:
782                 hardwareVersion = value;
783                 break;
784             case BluetoothDevice.METADATA_SOFTWARE_VERSION:
785                 softwareVersion = value;
786                 break;
787             default:
788                 // Do not log anything if metadata doesn't fall into above categories
789                 return;
790         }
791         StatsLog.write(StatsLog.BLUETOOTH_DEVICE_INFO_REPORTED,
792                 mAdapterService.obfuscateAddress(device),
793                 BluetoothProtoEnums.DEVICE_INFO_EXTERNAL, callingApp, manufacturerName, modelName,
794                 hardwareVersion, softwareVersion);
795     }
796 }
797