blob: fd25da4d06be519b6405f4b6f89f722864d1c8a4 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.bluetooth.btservice.storage;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;
import android.util.StatsLog;
import com.android.bluetooth.btservice.AdapterService;
import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* The active device manager is responsible to handle a Room database
* for Bluetooth persistent data.
*/
public class DatabaseManager {
private static final boolean DBG = true;
private static final boolean VERBOSE = true;
private static final String TAG = "BluetoothDatabase";
private AdapterService mAdapterService = null;
private HandlerThread mHandlerThread = null;
private Handler mHandler = null;
private MetadataDatabase mDatabase = null;
private boolean mMigratedFromSettingsGlobal = false;
@VisibleForTesting
final Map<String, Metadata> mMetadataCache = new HashMap<>();
private final Semaphore mSemaphore = new Semaphore(1);
private static final int LOAD_DATABASE_TIMEOUT = 500; // milliseconds
private static final int MSG_LOAD_DATABASE = 0;
private static final int MSG_UPDATE_DATABASE = 1;
private static final int MSG_DELETE_DATABASE = 2;
private static final int MSG_CLEAR_DATABASE = 100;
private static final String LOCAL_STORAGE = "LocalStorage";
/**
* Constructor of the DatabaseManager
*/
public DatabaseManager(AdapterService service) {
mAdapterService = service;
}
class DatabaseHandler extends Handler {
DatabaseHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_LOAD_DATABASE: {
synchronized (mDatabase) {
List<Metadata> list = mDatabase.load();
cacheMetadata(list);
}
break;
}
case MSG_UPDATE_DATABASE: {
Metadata data = (Metadata) msg.obj;
synchronized (mDatabase) {
mDatabase.insert(data);
}
break;
}
case MSG_DELETE_DATABASE: {
String address = (String) msg.obj;
synchronized (mDatabase) {
mDatabase.delete(address);
}
break;
}
case MSG_CLEAR_DATABASE: {
synchronized (mDatabase) {
mDatabase.deleteAll();
}
break;
}
}
}
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) {
Log.e(TAG, "Received intent with null action");
return;
}
switch (action) {
case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
BluetoothDevice.ERROR);
BluetoothDevice device =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Objects.requireNonNull(device,
"ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
bondStateChanged(device, state);
break;
}
case BluetoothAdapter.ACTION_STATE_CHANGED: {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.STATE_OFF);
if (!mMigratedFromSettingsGlobal
&& state == BluetoothAdapter.STATE_TURNING_ON) {
migrateSettingsGlobal();
}
break;
}
}
}
};
void bondStateChanged(BluetoothDevice device, int state) {
synchronized (mMetadataCache) {
String address = device.getAddress();
if (state != BluetoothDevice.BOND_NONE) {
if (mMetadataCache.containsKey(address)) {
return;
}
createMetadata(address);
} else {
Metadata metadata = mMetadataCache.get(address);
if (metadata != null) {
mMetadataCache.remove(address);
deleteDatabase(metadata);
}
}
}
}
boolean isValidMetaKey(int key) {
switch (key) {
case BluetoothDevice.METADATA_MANUFACTURER_NAME:
case BluetoothDevice.METADATA_MODEL_NAME:
case BluetoothDevice.METADATA_SOFTWARE_VERSION:
case BluetoothDevice.METADATA_HARDWARE_VERSION:
case BluetoothDevice.METADATA_COMPANION_APP:
case BluetoothDevice.METADATA_MAIN_ICON:
case BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET:
case BluetoothDevice.METADATA_UNTHETHERED_LEFT_ICON:
case BluetoothDevice.METADATA_UNTHETHERED_RIGHT_ICON:
case BluetoothDevice.METADATA_UNTHETHERED_CASE_ICON:
case BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY:
case BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY:
case BluetoothDevice.METADATA_UNTHETHERED_CASE_BATTERY:
case BluetoothDevice.METADATA_UNTHETHERED_LEFT_CHARGING:
case BluetoothDevice.METADATA_UNTHETHERED_RIGHT_CHARGING:
case BluetoothDevice.METADATA_UNTHETHERED_CASE_CHARGING:
case BluetoothDevice.METADATA_ENHANCED_SETTINGS_UI_URI:
return true;
}
Log.w(TAG, "Invalid metadata key " + key);
return false;
}
/**
* Set customized metadata to database with requested key
*/
@VisibleForTesting
public boolean setCustomMeta(BluetoothDevice device, int key, String newValue) {
synchronized (mMetadataCache) {
if (device == null) {
Log.e(TAG, "setCustomMeta: device is null");
return false;
}
if (!isValidMetaKey(key)) {
Log.e(TAG, "setCustomMeta: meta key invalid " + key);
return false;
}
String address = device.getAddress();
if (VERBOSE) {
Log.d(TAG, "setCustomMeta: " + address + ", key=" + key);
}
if (!mMetadataCache.containsKey(address)) {
createMetadata(address);
}
Metadata data = mMetadataCache.get(address);
String oldValue = data.getCustomizedMeta(key);
if (oldValue != null && oldValue.equals(newValue)) {
if (VERBOSE) {
Log.d(TAG, "setCustomMeta: metadata not changed.");
}
return true;
}
logManufacturerInfo(device, key, newValue);
data.setCustomizedMeta(key, newValue);
updateDatabase(data);
mAdapterService.metadataChanged(address, key, newValue);
return true;
}
}
/**
* Get customized metadata from database with requested key
*/
@VisibleForTesting
public String getCustomMeta(BluetoothDevice device, int key) {
synchronized (mMetadataCache) {
if (device == null) {
Log.e(TAG, "getCustomMeta: device is null");
return null;
}
if (!isValidMetaKey(key)) {
Log.e(TAG, "getCustomMeta: meta key invalid " + key);
return null;
}
String address = device.getAddress();
if (!mMetadataCache.containsKey(address)) {
Log.e(TAG, "getCustomMeta: device " + address + " is not in cache");
return null;
}
Metadata data = mMetadataCache.get(address);
String value = data.getCustomizedMeta(key);
return value;
}
}
/**
* Set the device profile prioirty
*
* @param device {@link BluetoothDevice} wish to set
* @param profile The Bluetooth profile; one of {@link BluetoothProfile#HEADSET},
* {@link BluetoothProfile#HEADSET_CLIENT}, {@link BluetoothProfile#A2DP},
* {@link BluetoothProfile#A2DP_SINK}, {@link BluetoothProfile#HID_HOST},
* {@link BluetoothProfile#PAN}, {@link BluetoothProfile#PBAP},
* {@link BluetoothProfile#MAP}, {@link BluetoothProfile#MAP_CLIENT},
* {@link BluetoothProfile#SAP}, {@link BluetoothProfile#HEARING_AID}
* @param newPriority the priority to set; one of
* {@link BluetoothProfile#PRIORITY_UNDEFINED},
* {@link BluetoothProfile#PRIORITY_OFF},
* {@link BluetoothProfile#PRIORITY_ON},
* {@link BluetoothProfile#PRIORITY_AUTO_CONNECT}
*/
@VisibleForTesting
public boolean setProfilePriority(BluetoothDevice device, int profile, int newPriority) {
synchronized (mMetadataCache) {
if (device == null) {
Log.e(TAG, "setProfilePriority: device is null");
return false;
}
if (newPriority != BluetoothProfile.PRIORITY_UNDEFINED
&& newPriority != BluetoothProfile.PRIORITY_OFF
&& newPriority != BluetoothProfile.PRIORITY_ON
&& newPriority != BluetoothProfile.PRIORITY_AUTO_CONNECT) {
Log.e(TAG, "setProfilePriority: invalid priority " + newPriority);
return false;
}
String address = device.getAddress();
if (VERBOSE) {
Log.v(TAG, "setProfilePriority: " + address + ", profile=" + profile
+ ", priority = " + newPriority);
}
if (!mMetadataCache.containsKey(address)) {
if (newPriority == BluetoothProfile.PRIORITY_UNDEFINED) {
return true;
}
createMetadata(address);
}
Metadata data = mMetadataCache.get(address);
int oldPriority = data.getProfilePriority(profile);
if (oldPriority == newPriority) {
if (VERBOSE) {
Log.v(TAG, "setProfilePriority priority not changed.");
}
return true;
}
data.setProfilePriority(profile, newPriority);
updateDatabase(data);
return true;
}
}
/**
* Get the device profile prioirty
*
* @param device {@link BluetoothDevice} wish to get
* @param profile The Bluetooth profile; one of {@link BluetoothProfile#HEADSET},
* {@link BluetoothProfile#HEADSET_CLIENT}, {@link BluetoothProfile#A2DP},
* {@link BluetoothProfile#A2DP_SINK}, {@link BluetoothProfile#HID_HOST},
* {@link BluetoothProfile#PAN}, {@link BluetoothProfile#PBAP},
* {@link BluetoothProfile#MAP}, {@link BluetoothProfile#MAP_CLIENT},
* {@link BluetoothProfile#SAP}, {@link BluetoothProfile#HEARING_AID}
* @return the profile priority of the device; one of
* {@link BluetoothProfile#PRIORITY_UNDEFINED},
* {@link BluetoothProfile#PRIORITY_OFF},
* {@link BluetoothProfile#PRIORITY_ON},
* {@link BluetoothProfile#PRIORITY_AUTO_CONNECT}
*/
@VisibleForTesting
public int getProfilePriority(BluetoothDevice device, int profile) {
synchronized (mMetadataCache) {
if (device == null) {
Log.e(TAG, "getProfilePriority: device is null");
return BluetoothProfile.PRIORITY_UNDEFINED;
}
String address = device.getAddress();
if (!mMetadataCache.containsKey(address)) {
Log.e(TAG, "getProfilePriority: device " + address + " is not in cache");
return BluetoothProfile.PRIORITY_UNDEFINED;
}
Metadata data = mMetadataCache.get(address);
int priority = data.getProfilePriority(profile);
if (VERBOSE) {
Log.v(TAG, "getProfilePriority: " + address + ", profile=" + profile
+ ", priority = " + priority);
}
return priority;
}
}
/**
* Set the A2DP optional coedc support value
*
* @param device {@link BluetoothDevice} wish to set
* @param newValue the new A2DP optional coedc support value, one of
* {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORT_UNKNOWN},
* {@link BluetoothA2dp#OPTIONAL_CODECS_NOT_SUPPORTED},
* {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORTED}
*/
@VisibleForTesting
public void setA2dpSupportsOptionalCodecs(BluetoothDevice device, int newValue) {
synchronized (mMetadataCache) {
if (device == null) {
Log.e(TAG, "setA2dpOptionalCodec: device is null");
return;
}
if (newValue != BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN
&& newValue != BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED
&& newValue != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
Log.e(TAG, "setA2dpSupportsOptionalCodecs: invalid value " + newValue);
return;
}
String address = device.getAddress();
if (!mMetadataCache.containsKey(address)) {
return;
}
Metadata data = mMetadataCache.get(address);
int oldValue = data.a2dpSupportsOptionalCodecs;
if (oldValue == newValue) {
return;
}
data.a2dpSupportsOptionalCodecs = newValue;
updateDatabase(data);
}
}
/**
* Get the A2DP optional coedc support value
*
* @param device {@link BluetoothDevice} wish to get
* @return the A2DP optional coedc support value, one of
* {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORT_UNKNOWN},
* {@link BluetoothA2dp#OPTIONAL_CODECS_NOT_SUPPORTED},
* {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORTED},
*/
@VisibleForTesting
public int getA2dpSupportsOptionalCodecs(BluetoothDevice device) {
synchronized (mMetadataCache) {
if (device == null) {
Log.e(TAG, "setA2dpOptionalCodec: device is null");
return BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
}
String address = device.getAddress();
if (!mMetadataCache.containsKey(address)) {
Log.e(TAG, "getA2dpOptionalCodec: device " + address + " is not in cache");
return BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
}
Metadata data = mMetadataCache.get(address);
return data.a2dpSupportsOptionalCodecs;
}
}
/**
* Set the A2DP optional coedc enabled value
*
* @param device {@link BluetoothDevice} wish to set
* @param newValue the new A2DP optional coedc enabled value, one of
* {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_UNKNOWN},
* {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_DISABLED},
* {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_ENABLED}
*/
@VisibleForTesting
public void setA2dpOptionalCodecsEnabled(BluetoothDevice device, int newValue) {
synchronized (mMetadataCache) {
if (device == null) {
Log.e(TAG, "setA2dpOptionalCodecEnabled: device is null");
return;
}
if (newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
&& newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
&& newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
Log.e(TAG, "setA2dpOptionalCodecsEnabled: invalid value " + newValue);
return;
}
String address = device.getAddress();
if (!mMetadataCache.containsKey(address)) {
return;
}
Metadata data = mMetadataCache.get(address);
int oldValue = data.a2dpOptionalCodecsEnabled;
if (oldValue == newValue) {
return;
}
data.a2dpOptionalCodecsEnabled = newValue;
updateDatabase(data);
}
}
/**
* Get the A2DP optional coedc enabled value
*
* @param device {@link BluetoothDevice} wish to get
* @return the A2DP optional coedc enabled value, one of
* {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_UNKNOWN},
* {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_DISABLED},
* {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_ENABLED}
*/
@VisibleForTesting
public int getA2dpOptionalCodecsEnabled(BluetoothDevice device) {
synchronized (mMetadataCache) {
if (device == null) {
Log.e(TAG, "getA2dpOptionalCodecEnabled: device is null");
return BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED;
}
String address = device.getAddress();
if (!mMetadataCache.containsKey(address)) {
Log.e(TAG, "getA2dpOptionalCodecEnabled: device " + address + " is not in cache");
return BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED;
}
Metadata data = mMetadataCache.get(address);
return data.a2dpOptionalCodecsEnabled;
}
}
/**
* Get the {@link Looper} for the handler thread. This is used in testing and helper
* objects
*
* @return {@link Looper} for the handler thread
*/
@VisibleForTesting
public Looper getHandlerLooper() {
if (mHandlerThread == null) {
return null;
}
return mHandlerThread.getLooper();
}
/**
* Start and initialize the DatabaseManager
*
* @param database the Bluetooth storage {@link MetadataDatabase}
*/
public void start(MetadataDatabase database) {
if (DBG) {
Log.d(TAG, "start()");
}
if (mAdapterService == null) {
Log.e(TAG, "stat failed, mAdapterService is null.");
return;
}
if (database == null) {
Log.e(TAG, "stat failed, database is null.");
return;
}
mDatabase = database;
mHandlerThread = new HandlerThread("BluetoothDatabaseManager");
mHandlerThread.start();
mHandler = new DatabaseHandler(mHandlerThread.getLooper());
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mAdapterService.registerReceiver(mReceiver, filter);
loadDatabase();
}
String getDatabaseAbsolutePath() {
//TODO backup database when Bluetooth turn off and FOTA?
return mAdapterService.getDatabasePath(MetadataDatabase.DATABASE_NAME)
.getAbsolutePath();
}
/**
* Clear all persistence data in database
*/
public void factoryReset() {
Log.w(TAG, "factoryReset");
Message message = mHandler.obtainMessage(MSG_CLEAR_DATABASE);
mHandler.sendMessage(message);
}
/**
* Close and de-init the DatabaseManager
*/
public void cleanup() {
removeUnusedMetadata();
mAdapterService.unregisterReceiver(mReceiver);
if (mHandlerThread != null) {
mHandlerThread.quit();
mHandlerThread = null;
}
mMetadataCache.clear();
}
void createMetadata(String address) {
if (VERBOSE) {
Log.v(TAG, "createMetadata " + address);
}
Metadata data = new Metadata(address);
mMetadataCache.put(address, data);
updateDatabase(data);
}
@VisibleForTesting
void removeUnusedMetadata() {
BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
synchronized (mMetadataCache) {
mMetadataCache.forEach((address, metadata) -> {
for (BluetoothDevice device : bondedDevices) {
if (!device.getAddress().equals(address)
&& !address.equals(LOCAL_STORAGE)) {
// Report metadata change to null
List<Integer> list = metadata.getChangedCustomizedMeta();
for (int key : list) {
mAdapterService.metadataChanged(address, key, null);
}
Log.i(TAG, "remove unpaired device from database " + address);
deleteDatabase(mMetadataCache.get(address));
}
}
});
}
}
void cacheMetadata(List<Metadata> list) {
synchronized (mMetadataCache) {
Log.i(TAG, "cacheMetadata");
// Unlock the main thread.
mSemaphore.release();
if (!isMigrated(list)) {
// Wait for data migrate from Settings Global
mMigratedFromSettingsGlobal = false;
return;
}
mMigratedFromSettingsGlobal = true;
for (Metadata data : list) {
String address = data.getAddress();
if (VERBOSE) {
Log.v(TAG, "cacheMetadata: found device " + address);
}
mMetadataCache.put(address, data);
}
if (VERBOSE) {
Log.v(TAG, "cacheMetadata: Database is ready");
}
}
}
boolean isMigrated(List<Metadata> list) {
for (Metadata data : list) {
String address = data.getAddress();
if (address.equals(LOCAL_STORAGE) && data.migrated) {
return true;
}
}
return false;
}
void migrateSettingsGlobal() {
Log.i(TAG, "migrateSettingGlobal");
BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
ContentResolver contentResolver = mAdapterService.getContentResolver();
for (BluetoothDevice device : bondedDevices) {
int a2dpPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int a2dpSinkPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothA2dpSrcPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int hearingaidPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothHearingAidPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int headsetPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int headsetClientPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int hidHostPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothHidHostPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int mapPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothMapPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int mapClientPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothMapClientPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int panPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothPanPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int pbapPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int sapPriority = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothSapPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
int a2dpSupportsOptionalCodec = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()),
BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
int a2dpOptionalCodecEnabled = Settings.Global.getInt(contentResolver,
Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()),
BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
String address = device.getAddress();
Metadata data = new Metadata(address);
data.setProfilePriority(BluetoothProfile.A2DP, a2dpPriority);
data.setProfilePriority(BluetoothProfile.A2DP_SINK, a2dpSinkPriority);
data.setProfilePriority(BluetoothProfile.HEADSET, headsetPriority);
data.setProfilePriority(BluetoothProfile.HEADSET_CLIENT, headsetClientPriority);
data.setProfilePriority(BluetoothProfile.HID_HOST, hidHostPriority);
data.setProfilePriority(BluetoothProfile.PAN, panPriority);
data.setProfilePriority(BluetoothProfile.PBAP, pbapPriority);
data.setProfilePriority(BluetoothProfile.MAP, mapPriority);
data.setProfilePriority(BluetoothProfile.MAP_CLIENT, mapClientPriority);
data.setProfilePriority(BluetoothProfile.SAP, sapPriority);
data.setProfilePriority(BluetoothProfile.HEARING_AID, hearingaidPriority);
data.a2dpSupportsOptionalCodecs = a2dpSupportsOptionalCodec;
data.a2dpOptionalCodecsEnabled = a2dpOptionalCodecEnabled;
mMetadataCache.put(address, data);
updateDatabase(data);
}
// Mark database migrated from Settings Global
Metadata localData = new Metadata(LOCAL_STORAGE);
localData.migrated = true;
mMetadataCache.put(LOCAL_STORAGE, localData);
updateDatabase(localData);
// Reload database after migration is completed
loadDatabase();
}
private void loadDatabase() {
if (DBG) {
Log.d(TAG, "Load Database");
}
Message message = mHandler.obtainMessage(MSG_LOAD_DATABASE);
mHandler.sendMessage(message);
try {
// Lock the thread until handler thread finish loading database.
mSemaphore.tryAcquire(LOAD_DATABASE_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Log.e(TAG, "loadDatabase: semaphore acquire failed");
}
}
private void updateDatabase(Metadata data) {
if (data.getAddress() == null) {
Log.e(TAG, "updateDatabase: address is null");
return;
}
if (DBG) {
Log.d(TAG, "updateDatabase " + data.getAddress());
}
Message message = mHandler.obtainMessage(MSG_UPDATE_DATABASE);
message.obj = data;
mHandler.sendMessage(message);
}
private void deleteDatabase(Metadata data) {
if (data.getAddress() == null) {
Log.e(TAG, "deleteDatabase: address is null");
return;
}
Log.d(TAG, "deleteDatabase: " + data.getAddress());
Message message = mHandler.obtainMessage(MSG_DELETE_DATABASE);
message.obj = data.getAddress();
mHandler.sendMessage(message);
}
private void logManufacturerInfo(BluetoothDevice device, int key, String value) {
String callingApp = mAdapterService.getPackageManager().getNameForUid(
Binder.getCallingUid());
String manufacturerName = "";
String modelName = "";
String hardwareVersion = "";
String softwareVersion = "";
switch (key) {
case BluetoothDevice.METADATA_MANUFACTURER_NAME:
manufacturerName = value;
break;
case BluetoothDevice.METADATA_MODEL_NAME:
modelName = value;
break;
case BluetoothDevice.METADATA_HARDWARE_VERSION:
hardwareVersion = value;
break;
case BluetoothDevice.METADATA_SOFTWARE_VERSION:
softwareVersion = value;
break;
default:
// Do not log anything if metadata doesn't fall into above categories
return;
}
StatsLog.write(StatsLog.BLUETOOTH_DEVICE_INFO_REPORTED,
mAdapterService.obfuscateAddress(device),
BluetoothProtoEnums.DEVICE_INFO_EXTERNAL, callingApp, manufacturerName, modelName,
hardwareVersion, softwareVersion);
}
}