server: Get bluetooth phone vendor and model from Device ID profile (BMC #736)

In the past we have relied on the user-modifiable device name to be
the fingerprint for matching a phone to a template which is unreliable.

This commit changes this in the cases where the phone supports the
Device ID profile (DIP). If support DIP is detected then we extract
the vendor and product id's from the PnPInformation service
record. Then, we attempt to associate the ids with a product and
vendor name by using a newly added lookup table.

The preference for which value to use as the fingerprint is as
follows:
1) If the product is found in the lookup table we use
   that. (The product lookup table is manually updated and currently
   only has a few entries.)
2) Otherwise, we use the vendor name. (All vendors should be in the
   lookup table, so this should always be used if the DIP is supported.
3) If the DIP is not supported by this device we fall back to using
   the user-modifiable device name as before.

Additionaly, because the user-modifiable device name is still
desirable to expose through the dbus interface, it is now made
avaiable through the peerName property. This is intended to be used in
GUIs, for example.
This commit is contained in:
Chris Kühl 2011-07-26 03:12:58 +02:00 committed by Patrick Ohly
parent 5a6a88b096
commit cc3f1b6663
12 changed files with 387 additions and 14 deletions

View File

@ -884,6 +884,10 @@ SYNCEVOLUTION_DEBUG
SYNCEVOLUTION_GNUTLS_DEBUG
Enables additional debugging output when using the libsoup HTTP transport library.
SYNCEVOLUTION_DATA_DIR
Overrides the default path to the bluetooth device lookup table,
normally `/usr/lib/syncevolution/`.
SYNCEVOLUTION_BACKEND_DIR
Overrides the default path to plugins, normally `/usr/lib/syncevolution/backends`.

View File

@ -44,6 +44,8 @@ libsyncevodbusserver_la_SOURCES = \
$(server_cpp_files) \
main.cpp
dist_pkgdata_DATA = bluetooth_products.ini
libsyncevodbusserver_la_LIBADD = $(KEYRING_LIBS) $(LIBNOTIFY_LIBS) $(MLITE_LIBS) $(KDE_KWALLET_LIBS) $(DBUS_LIBS) $(LIBSOUP_LIBS)
libsyncevodbusserver_la_CPPFLAGS = -DHAVE_CONFIG_H -DSYNCEVOLUTION_LOCALEDIR=\"${SYNCEVOLUTION_LOCALEDIR}\" -I$(top_srcdir)/src -I$(top_srcdir)/test -I$(top_srcdir) $(BACKEND_CPPFLAGS)
libsyncevodbusserver_la_CXXFLAGS = $(SYNCEVOLUTION_CXXFLAGS) $(CORE_CXXFLAGS) $(SYNTHESIS_CFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) $(LIBSOUP_CFLAGS) $(KEYRING_CFLAGS) $(LIBNOTIFY_CFLAGS) $(MLITE_CFLAGS) $(KDE_KWALLET_CFLAGS)

View File

@ -0,0 +1,133 @@
# Vendor lookup table
#
# The key is the VendorID and the value the Vendor name. This list
# was obtained from
# http://www.bluetooth.org/Technical/AssignedNumbers/identifiers.htm
#
# FIXME: This is available from Bluez. Use that.
[Vendors]
0x0000=Sony Ericsson
0x0001=Nokia
0x0002=Intel Corp.
0x0003=IBM Corp.
0x0004=Toshiba Corp.
0x0005=3Com
0x0006=Microsoft
0x0007=Lucent
0x0008=Motorola
0x0009=Infineon Technologies AG
0x000A=Cambridge Silicon Radio
0x000B=Silicon Wave
0x000C=Digianswer A/S
0x000D=Texas Instruments Inc.
0x000E=Parthus Technologies Inc.
0x000F=Broadcom Corporation
0x0010=Mitel Semiconductor
0x0011=Widcomm, Inc.
0x0012=Zeevo, Inc.
0x0013=Atmel Corporation
0x0014=Mitsubishi Electric Corporation
0x0015=RTX Telecom A/S
0x0016=KC Technology Inc.
0x0017=Newlogic
0x0018=Transilica, Inc.
0x0019=Rohde & Schwarz GmbH & Co. KG
0x001A=TTPCom Limited
0x001B=Signia Technologies, Inc.
0x001C=Conexant Systems Inc.
0x001D=Qualcomm
0x001E=Inventel
0x001F=AVM Berlin
0x0020=BandSpeed, Inc.
0x0021=Mansella Ltd
0x0022=NEC Corporation
0x0023=WavePlus Technology Co., Ltd.
0x0024=Alcatel
0x0025=Philips Semiconductors
0x0026=C Technologies
0x0027=Open Interface
0x0028=R F Micro Devices
0x0029=Hitachi Ltd
0x002A=Symbol Technologies, Inc.
0x002B=Tenovis
0x002C=Macronix International Co. Ltd.
0x002D=GCT Semiconductor
0x002E=Norwood Systems
0x002F=MewTel Technology Inc.
0x0030=ST Microelectronics
0x0031=Synopsys
0x0032=Red-M (Communications) Ltd
0x0033=Commil Ltd
0x0034=Computer Access Technology Corporation (CATC)
0x0035=Eclipse (HQ Espana) S.L.
0x0036=Renesas Technology Corp.
0x0037=Mobilian Corporation
0x0038=Terax
0x0039=Integrated System Solution Corp.
0x003A=Matsushita Electric Industrial Co., Ltd.
0x003B=Gennum Corporation
0x003C=Research In Motion
0x003D=IPextreme, Inc.
0x003E=Systems and Chips, Inc
0x003F=Bluetooth SIG, Inc
0x0040=Seiko Epson Corporation
0x0041=Integrated Silicon Solution Taiwan, Inc.
0x0042=CONWISE Technology Corporation Ltd
0x0043=PARROT SA
0x0044=Socket Mobile
0x0045=Atheros Communications, Inc.
0x0046=MediaTek, Inc.
0x0047=Bluegiga
0x0048=Marvell Technology Group Ltd.
0x0049=3DSP Corporation
0x004A=Accel Semiconductor Ltd.
0x004B=Continental Automotive Systems
0x004C=Apple, Inc.
0x004D=Staccato Communications, Inc.
0x004E=Avago Technologies
0x004F=APT Licensing Ltd.
0x0050=SiRF Technology, Inc.
0x0051=Tzero Technologies, Inc.
0x0052=J&M Corporation
0x0053=Free2move AB
0x0054=3DiJoy Corporation
0x0055=Plantronics, Inc.
0x0056=Sony Ericsson
0x0057=Harman International Industries, Inc.
0x0058=Vizio, Inc.
0x0059=Nordic Semiconductor ASA
0x005A=EM Microelectronic-Marin SA
0x005B=Ralink Technology Corporation
0x005C=Belkin International, Inc.
0x005D=Realtek Semiconductor Corporation
0x005E=Stonestreet One, LLC
0x005F=Wicentric, Inc.
0x0060=RivieraWaves S.A.S
0x0061=RDA Microelectronics
0x0062=Gibson Guitars
0x0063=MiCommand Inc.
0x0064=Band XI International, LLC
0x0065=Hewlett-Packard Company
0x0066=9Solutions Oy
0x0067=GN Netcom A/S
0x0068=General Motors
0x0069=A&D Engineering, Inc.
0x006A=MindTree Ltd.
0x006B=Polar Electro OY
0x006C=Beautiful Enterprise Co., Ltd.
0x006D=BriarTek, Inc.
0x006E=Summit Data Communications, Inc.
0x006F=Sound ID
0x0070=Monster, LLC
0x0071=connectBlue AB
# Product lookup table
#
# The keys are of the form "VendorID_ProductID", and the value
# "Vendor Model". The VendorID needs to be used as a prefix because
# ProductIDs are only unique per vender. Currently we have no list of
# product IDs from vendors so we add them as we find them.
[Products]
0x0000_0xc112=Sony Ericsson W995
0x0001_0x0084=Nokia N85
0x0001_0x00e7=Nokia 5230

View File

@ -20,8 +20,13 @@
#include "bluez-manager.h"
#include "server.h"
#include <algorithm>
#include <boost/assign/list_of.hpp>
using namespace GDBusCXX;
SE_BEGIN_CXX
BluezManager::BluezManager(Server &server) :
@ -121,6 +126,22 @@ BluezManager::BluezDevice::BluezDevice (BluezAdapter &adapter, const string &pat
m_propertyChanged.activate(boost::bind(&BluezDevice::propertyChanged, this, _1, _2));
}
/**
* check whether the current device has the PnP Information attribute.
*/
static bool hasPnpInfoService(const std::vector<std::string> &uuids)
{
// The UUID that indicates the PnPInformation attribute is available.
static const char * PNPINFOMATION_ATTRIBUTE_UUID = "00001200-0000-1000-8000-00805f9b34fb";
// Note: GetProperties appears to return this list sorted which binary_search requires.
if(std::binary_search(uuids.begin(), uuids.end(), PNPINFOMATION_ATTRIBUTE_UUID)) {
return true;
}
return false;
}
void BluezManager::BluezDevice::checkSyncService(const std::vector<std::string> &uuids)
{
static const char * SYNCML_CLIENT_UUID = "00000002-0000-1000-8000-0002ee000002";
@ -131,7 +152,17 @@ void BluezManager::BluezDevice::checkSyncService(const std::vector<std::string>
if(boost::iequals(uuid, SYNCML_CLIENT_UUID)) {
hasSyncService = true;
if(!m_mac.empty()) {
server.addDevice(SyncConfig::DeviceDescription(m_mac, m_name, SyncConfig::MATCH_FOR_SERVER_MODE));
SyncConfig::DeviceDescription deviceDesc(m_mac, m_name,
SyncConfig::MATCH_FOR_SERVER_MODE);
server.addDevice(deviceDesc);
if(hasPnpInfoService(uuids)) {
DBusClientCall1<ServiceDict> discoverServices(*this,
"DiscoverServices");
static const std::string PNP_INFO_UUID("0x1200");
discoverServices(PNP_INFO_UUID,
boost::bind(&BluezDevice::discoverServicesCb,
this, _1, _2));
}
}
break;
}
@ -142,6 +173,121 @@ void BluezManager::BluezDevice::checkSyncService(const std::vector<std::string>
}
}
/*
* Parse the XML-formatted service record.
*/
bool extractValuefromServiceRecord(const std::string &serviceRecord,
const std::string &attributeId,
std::string &attributeValue)
{
// Find atribute
size_t pos = serviceRecord.find(attributeId);
// Only proceed if the attribute id was found.
if(pos != std::string::npos) {
pos = serviceRecord.find("value", pos + attributeId.size());
pos = serviceRecord.find("\"", pos) + 1;
int valLen = serviceRecord.find("\"", pos) - pos;
attributeValue = serviceRecord.substr(pos, valLen);
return true;
}
return false;
}
/*
* Get the names of the PnpInformation vendor and product from their
* respective ids. At a minimum we need a matching vendor id for this
* function to return true. If the product id is not found then we set
* it to "", an empty string.
*/
static bool getPnpInfoNamesFromValues(const std::string &vendorValue, std::string &vendorName,
const std::string &productValue, std::string &productName)
{
static GKeyFile *bt_key_vals = NULL;
if(!bt_key_vals) {
bt_key_vals = g_key_file_new();
GError *err = NULL;
string filePath(SyncEvolutionDataDir() + "/bluetooth_products.ini");
if(!g_key_file_load_from_file(bt_key_vals, filePath.c_str(),
G_KEY_FILE_NONE, &err)) {
SE_LOG_DEBUG(NULL, NULL, "%s[%d]: %s - filePath = %s, error = %s",
__FILE__, __LINE__, "Bluetooth products File not loaded",
filePath.c_str(), err->message);
return false;
}
}
const char *VENDOR_GROUP = "Vendors";
const char *PRODUCT_GROUP = "Products";
char *vendor = g_key_file_get_string(bt_key_vals, VENDOR_GROUP,
vendorValue.c_str(), NULL);
if(vendor) {
vendorName = vendor;
} else {
// We at least need a vendor id match.
return false;
}
char *product = g_key_file_get_string(bt_key_vals, PRODUCT_GROUP,
productValue.c_str(), NULL);
if(product) {
productName = product;
} else {
// If the product is not in the look-up table, the product is
// set to an empty string.
productName = "";
}
return true;
}
void BluezManager::BluezDevice::discoverServicesCb(const ServiceDict &serviceDict,
const string &error)
{
ServiceDict::const_iterator iter = serviceDict.begin();
if(iter != serviceDict.end()) {
std::string serviceRecord = (*iter).second;
if(!serviceRecord.empty()) {
static const std::string SOURCE_ATTRIBUTE_ID("0x0205");
std::string sourceId;
extractValuefromServiceRecord(serviceRecord, SOURCE_ATTRIBUTE_ID, sourceId);
// A sourceId of 0x001 indicates that the vendor ID was
// assigned by the Bluetooth SIG.
// TODO: A sourceId of 0x002, means the vendor id was assigned by
// the USB Implementor's forum. We do nothing in this case but
// should do that look up as well.
if(!boost::iequals(sourceId, "0x0001")) { return; }
std::string vendorId, productId;
static const std::string VENDOR_ATTRIBUTE_ID ("0x0201");
static const std::string PRODUCT_ATTRIBUTE_ID("0x0202");
extractValuefromServiceRecord(serviceRecord, VENDOR_ATTRIBUTE_ID, vendorId);
extractValuefromServiceRecord(serviceRecord, PRODUCT_ATTRIBUTE_ID, productId);
std::string vendorName, productName;
if (!getPnpInfoNamesFromValues(vendorId, vendorName,
vendorId + "_" + productId, productName)) {
return;
}
Server &server = m_adapter.m_manager.m_server;
SyncConfig::DeviceDescription devDesc;
if (server.getDevice(m_mac, devDesc)) {
devDesc.m_pnpInformation =
boost::shared_ptr<SyncConfig::PnpInformation>(
new SyncConfig::PnpInformation(vendorName, productName));
server.updateDevice(m_mac, devDesc);
}
}
}
}
void BluezManager::BluezDevice::getPropertiesCb(const PropDict &props, const string &error)
{
m_adapter.m_devReplies++;
@ -175,7 +321,7 @@ void BluezManager::BluezDevice::propertyChanged(const string &name,
m_name = boost::get<std::string>(prop);
SyncConfig::DeviceDescription device;
if(server.getDevice(m_mac, device)) {
device.m_fingerprint = m_name;
device.m_deviceName = m_name;
server.updateDevice(m_mac, device);
}
} else if(boost::iequals(name, "UUIDs")) {

View File

@ -124,6 +124,7 @@ private:
{
public:
typedef std::map<std::string, boost::variant<std::vector<std::string>, std::string > > PropDict;
typedef std::map<uint32_t, std::string> ServiceDict;
BluezDevice (BluezAdapter &adapter, const std::string &path);
@ -134,8 +135,8 @@ private:
std::string getMac() { return m_mac; }
/**
* check whether the current device has sync service
* if yes, put it in the adapter's sync devices listn
* check whether the current device has sync service if yes,
* put it in the adapter's sync devices list
*/
void checkSyncService(const std::vector<std::string> &uuids);
@ -143,6 +144,9 @@ private:
/** callback of 'GetProperties' method. The properties of the device is gotten */
void getPropertiesCb(const PropDict &props, const std::string &error);
/** callback of 'DiscoverServices' method. The service records are retrieved */
void discoverServicesCb(const ServiceDict &serviceDict, const std::string &error);
/** callback of 'PropertyChanged' signal. Changed property is tracked */
void propertyChanged(const std::string &name, const boost::variant<std::vector<std::string>, std::string> &prop);

View File

@ -120,6 +120,8 @@ void ReadOperations::getConfig(bool getTemplate,
localConfigs.insert(pair<string, string>("score", score.str()));
// Actually this fingerprint is transferred by getConfigs, which refers to device name
localConfigs.insert(pair<string, string>("deviceName", peerTemplate->m_fingerprint));
// This is the user-modifiable device name. Could be shown in GUIs, for example
localConfigs.insert(pair<string, string>("peerName", peerTemplate->m_peerName));
// This is the fingerprint of the template
localConfigs.insert(pair<string, string>("fingerPrint", peerTemplate->m_matchedModel));
// This is the template name presented to UI (or device class)

View File

@ -661,6 +661,11 @@ bool Server::getDevice(const string &deviceId, SyncConfig::DeviceDescription &de
for(syncDevIt = m_syncDevices.begin(); syncDevIt != m_syncDevices.end(); ++syncDevIt) {
if(boost::equals(syncDevIt->m_deviceId, deviceId)) {
device = *syncDevIt;
if(syncDevIt->m_pnpInformation) {
device.m_pnpInformation = boost::shared_ptr<SyncConfig::PnpInformation>(
new SyncConfig::PnpInformation(syncDevIt->m_pnpInformation->m_vendor,
syncDevIt->m_pnpInformation->m_product));
}
return true;
}
}

View File

@ -170,6 +170,7 @@ CLEANFILES = SyncEvolutionXML.c
libsyncevolution_la_LIBADD = @EPACKAGE_LIBS@ @GIO_LIBS@ @GTHREAD_LIBS@ @GLIB_LIBS@ $(SYNTHESIS_LIBS) $(TRANSPORT_LIBS) @LIBS@ $(SYNCEVOLUTION_LDADD) $(NSS_LIBS)
libsyncevolution_la_CXXFLAGS = $(TRANSPORT_CFLAGS) $(SYNCEVOLUTION_CXXFLAGS) $(SYNTHESIS_CFLAGS) $(NSS_CFLAGS)
libsyncevolution_la_CPPFLAGS = $(AM_CPPFLAGS) \
-DDATA_DIR=\""$(pkgdatadir)"\" \
-DXML_CONFIG_DIR=\""$(datadir)/syncevolution/xml"\" \
-DTEMPLATE_DIR=\""$(datadir)/syncevolution/templates"\" \
-DLIBDIR=\""$(libdir)"\"

View File

@ -219,6 +219,27 @@ string SyncConfig::normalizeConfigString(const string &config, NormalizeFlags fl
return normal;
}
std::string SyncConfig::DeviceDescription::getFingerprint() const
{
std::string fingerprint;
/** In the case that we have the PnpInformation we prefer it over
* the mutable device name. The is true even if we only found the
* vendor component of the PnpInformation.
*/
if (m_pnpInformation) {
if(m_pnpInformation->isKnownProduct())
fingerprint = m_pnpInformation->m_product;
else
fingerprint = m_pnpInformation->m_vendor;
}
else {
fingerprint = m_deviceName;
}
return fingerprint;
}
bool SyncConfig::splitConfigString(const string &config, string &peer, string &context)
{
string::size_type at = config.rfind('@');
@ -692,15 +713,17 @@ SyncConfig::TemplateList SyncConfig::matchPeerTemplates(const DeviceList &peers,
continue;
}
BOOST_FOREACH (const DeviceList::value_type &entry, peers){
int rank = templateConf.metaMatch (entry.m_fingerprint, entry.m_matchMode);
std:string fingerprint(entry.getFingerprint());
int rank = templateConf.metaMatch (fingerprint, entry.m_matchMode);
if (fuzzyMatch){
if (rank > TemplateConfig::NO_MATCH) {
result.push_back (boost::shared_ptr<TemplateDescription>(
new TemplateDescription(templateConf.getTemplateId(),
templateConf.getDescription(),
rank,
entry.m_deviceName,
entry.m_deviceId,
entry.m_fingerprint,
fingerprint,
sDir,
templateConf.getFingerprint(),
templateConf.getTemplateName()
@ -712,9 +735,10 @@ SyncConfig::TemplateList SyncConfig::matchPeerTemplates(const DeviceList &peers,
new TemplateDescription(templateConf.getTemplateId(),
templateConf.getDescription(),
rank,
entry.m_deviceName,
entry.m_deviceId,
entry.m_fingerprint,
sDir,
fingerprint,
sDir,
templateConf.getFingerprint(),
templateConf.getTemplateName())
));
@ -1462,6 +1486,8 @@ void SyncConfig::setConfigVersion(ConfigLevel level, ConfigLimit limit, int vers
}
}
/**
* This constructor updates some of the properties above and then adds
* them to the registry. This cannot be done inside getRegistry()

View File

@ -1032,6 +1032,10 @@ class SyncConfig {
// The matched percentage of the template, larger the better.
int m_rank;
// A string that can be shown in GUIs. For bluetooth devices
// this is the user-modifiable device name.
std::string m_peerName;
//a unique identity of the device that the template is for, used by caller
std::string m_deviceId;
@ -1049,11 +1053,14 @@ class SyncConfig {
// The template name (device class) presented
std::string m_templateName;
TemplateDescription (const std::string &templateId, const std::string &description,
const int rank, const std::string deviceId, const std::string &fingerprint, const std::string &path, const std::string &model, const std::string &templateName)
TemplateDescription (const std::string &templateId, const std::string &description,
const int rank, const std::string &peerName, const std::string &deviceId,
const std::string &fingerprint, const std::string &path,
const std::string &model, const std::string &templateName)
: m_templateId (templateId),
m_description (description),
m_rank (rank),
m_peerName (peerName),
m_deviceId (deviceId),
m_fingerprint (fingerprint),
m_path (path),
@ -1079,17 +1086,41 @@ class SyncConfig {
typedef std::list<boost::shared_ptr <TemplateDescription> > TemplateList;
/* This information is available if the device supports the
* Device Id Profile.
*/
struct PnpInformation
{
const std::string m_vendor;
const std::string m_product;
bool isKnownProduct() const {return !m_product.empty();}
PnpInformation(const std::string &vendor,
const std::string &product)
:m_vendor(vendor), m_product(product)
{}
};
struct DeviceDescription {
/** the id of the device */
std::string m_deviceId;
/** the finger print of the device used for matching templates */
std::string m_fingerprint;
/* The user-modifiable name of the device. This will be used
* as the fingerprint if the PnpInformation is not
* available. */
std::string m_deviceName;
/* For bluetooth devices, we use PnpInformation's immutable
* product id which provides a more reliable fingerprint than
* the user-modifiable device string. The fingerprint of the
* device is used for matching templates. */
std::string getFingerprint() const;
/** match mode used for matching templates */
MatchMode m_matchMode;
/** the PnPInformation for the device if available */
boost::shared_ptr<PnpInformation> m_pnpInformation;
DeviceDescription(const std::string &deviceId,
const std::string &fingerprint,
const std::string &deviceName,
MatchMode mode)
:m_deviceId(deviceId), m_fingerprint(fingerprint), m_matchMode(mode)
:m_deviceId(deviceId), m_deviceName(deviceName), m_matchMode(mode)
{}
DeviceDescription() : m_matchMode(INVALID)
{}

View File

@ -859,6 +859,16 @@ std::string Flags2String(int flags, const Flag *descr, const std::string &sep)
return boost::join(tmp, ", ");
}
std::string SyncEvolutionDataDir()
{
std::string dataDir(DATA_DIR);
const char *envvar = getenv("SYNCEVOLUTION_DATA_DIR");
if (envvar) {
dataDir = envvar;
}
return dataDir;
}
ScopedEnvChange::ScopedEnvChange(const string &var, const string &value) :
m_var(var)
{

View File

@ -496,6 +496,15 @@ struct Flag {
*/
std::string Flags2String(int flags, const Flag *descr, const std::string &sep = ", ");
/**
* Returns the path to the data directory. This is generally
* /usr/share/syncevolution/ but can be overridden by setting the
* SYNCEVOLUTION_DATA_DIR environment variable.
*
* @retval dataDir the path to the data directory
*/
std::string SyncEvolutionDataDir();
/**
* Temporarily set env variable, restore old value on destruction.
* Useful for unit tests which depend on the environment.