summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorjosephsih <josephsih@chromium.org>2016-02-24 02:17:23 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-24 10:18:54 +0000
commit098a1cd80c1398b4bea2c1bc83c5dca686b5b42d (patch)
tree8551038fb7319e42b419d444651827ead956a6a5 /device
parent3f96177578cf0511174d42546a70660cc8d6eeeb (diff)
downloadchromium_src-098a1cd80c1398b4bea2c1bc83c5dca686b5b42d.zip
chromium_src-098a1cd80c1398b4bea2c1bc83c5dca686b5b42d.tar.gz
chromium_src-098a1cd80c1398b4bea2c1bc83c5dca686b5b42d.tar.bz2
Determine device type via bluetooth appearance in OOBE
In OOBE, the device type is determined exclusively based on the bluetooth class. However, some keyboards do not advertise the bluetooth class. Instead, the bluetooth appearance is advertised. This patch utilizes the bluetooth appearance to determine the device type if the bluetooth class information is missing. Note that this patch only takes effect on keyboard-less devices such as Mickey and Monroe. BUG=578337, 546616 TEST=Execute the steps below: 1. Let a keyboard-less device boot into OOBE. 2. Unfold Microsoft Universal Foldable Keyboard to power it on. 3. Press Func 1 key on the keyboard for a few seconds until its back light begins blinking. 4. Press "OS" key on the top right corner a few times until Android logo lights on. 3. Observe the pairing takes effect and asks the user to type the pin code. (Without this patch, the pairing never takes effect since the particular keyboard is not considered as a keyboard.) Review URL: https://codereview.chromium.org/1656253002 Cr-Commit-Position: refs/heads/master@{#377257}
Diffstat (limited to 'device')
-rw-r--r--device/bluetooth/bluetooth_adapter_bluez.cc1
-rw-r--r--device/bluetooth/bluetooth_bluez_unittest.cc112
-rw-r--r--device/bluetooth/bluetooth_classic_device_mac.h1
-rw-r--r--device/bluetooth/bluetooth_classic_device_mac.mm7
-rw-r--r--device/bluetooth/bluetooth_device.cc33
-rw-r--r--device/bluetooth/bluetooth_device.h10
-rw-r--r--device/bluetooth/bluetooth_device_android.cc7
-rw-r--r--device/bluetooth/bluetooth_device_android.h1
-rw-r--r--device/bluetooth/bluetooth_device_bluez.cc12
-rw-r--r--device/bluetooth/bluetooth_device_bluez.h1
-rw-r--r--device/bluetooth/bluetooth_device_win.cc7
-rw-r--r--device/bluetooth/bluetooth_device_win.h1
-rw-r--r--device/bluetooth/bluetooth_low_energy_device_mac.h1
-rw-r--r--device/bluetooth/bluetooth_low_energy_device_mac.mm7
-rw-r--r--device/bluetooth/test/mock_bluetooth_device.h1
15 files changed, 202 insertions, 0 deletions
diff --git a/device/bluetooth/bluetooth_adapter_bluez.cc b/device/bluetooth/bluetooth_adapter_bluez.cc
index f8df754..1d22ef7 100644
--- a/device/bluetooth/bluetooth_adapter_bluez.cc
+++ b/device/bluetooth/bluetooth_adapter_bluez.cc
@@ -469,6 +469,7 @@ void BluetoothAdapterBlueZ::DevicePropertyChanged(
}
if (property_name == properties->bluetooth_class.name() ||
+ property_name == properties->appearance.name() ||
property_name == properties->address.name() ||
property_name == properties->alias.name() ||
property_name == properties->paired.name() ||
diff --git a/device/bluetooth/bluetooth_bluez_unittest.cc b/device/bluetooth/bluetooth_bluez_unittest.cc
index 309aec0..f99caab 100644
--- a/device/bluetooth/bluetooth_bluez_unittest.cc
+++ b/device/bluetooth/bluetooth_bluez_unittest.cc
@@ -2160,6 +2160,118 @@ TEST_F(BluetoothBlueZTest, DeviceClassChanged) {
EXPECT_EQ(BluetoothDevice::DEVICE_MOUSE, devices[idx]->GetDeviceType());
}
+TEST_F(BluetoothBlueZTest, DeviceAppearance) {
+ // Simulate a device with appearance.
+ GetAdapter();
+
+ BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+ ASSERT_EQ(2U, devices.size());
+
+ int idx = GetDeviceIndexByAddress(
+ devices, bluez::FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_NE(-1, idx);
+ ASSERT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[idx]->GetDeviceType());
+
+ // Install an observer; expect the DeviceChanged method to be called when
+ // we change the appearance of the device.
+ TestBluetoothAdapterObserver observer(adapter_);
+
+ bluez::FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(dbus::ObjectPath(
+ bluez::FakeBluetoothDeviceClient::kPairedDevicePath));
+
+ // Let the device come without bluetooth_class.
+ properties->appearance.ReplaceValue(0); // DeviceChanged method called
+ EXPECT_EQ(1, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+
+ // Set the device appearance as keyboard (961).
+ properties->appearance.ReplaceValue(961); // DeviceChanged method called
+ properties->appearance.set_valid(true);
+ EXPECT_EQ(2, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+ EXPECT_EQ(961, devices[idx]->GetAppearance());
+ // When discovery is over, the value should be invalidated.
+ properties->appearance.set_valid(false);
+ // DeviceChanged method called by NotifyPropertyChanged()
+ properties->NotifyPropertyChanged(properties->appearance.name());
+ EXPECT_EQ(3, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+ EXPECT_EQ((int) BluetoothDevice::kAppearanceNotPresent,
+ devices[idx]->GetAppearance());
+
+ // Change the device appearance to mouse (962).
+ properties->appearance.ReplaceValue(962); // DeviceChanged method called
+ properties->appearance.set_valid(true);
+ EXPECT_EQ(4, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+ EXPECT_EQ(962, devices[idx]->GetAppearance());
+ // When discovery is over, the value should be invalidated.
+ properties->appearance.set_valid(false);
+ // DeviceChanged method called by NotifyPropertyChanged()
+ properties->NotifyPropertyChanged(properties->appearance.name());
+ EXPECT_EQ(5, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+ EXPECT_EQ((int) BluetoothDevice::kAppearanceNotPresent,
+ devices[idx]->GetAppearance());
+}
+
+TEST_F(BluetoothBlueZTest, DeviceTypebyAppearanceNotBluetoothClass) {
+ // Test device type of a device with appearance but without bluetooth class.
+ GetAdapter();
+
+ BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+ ASSERT_EQ(2U, devices.size());
+
+ int idx = GetDeviceIndexByAddress(
+ devices, bluez::FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_NE(-1, idx);
+ ASSERT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[idx]->GetDeviceType());
+
+ // Install an observer; expect the DeviceChanged method to be called when
+ // we change the appearance of the device.
+ TestBluetoothAdapterObserver observer(adapter_);
+
+ bluez::FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(dbus::ObjectPath(
+ bluez::FakeBluetoothDeviceClient::kPairedDevicePath));
+
+ // Let the device come without bluetooth_class.
+ properties->bluetooth_class.ReplaceValue(0); // DeviceChanged method called
+ properties->appearance.ReplaceValue(0); // DeviceChanged method called
+ EXPECT_EQ(BluetoothDevice::DEVICE_UNKNOWN, devices[idx]->GetDeviceType());
+ EXPECT_EQ(2, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+
+ // Set the device appearance as keyboard.
+ properties->appearance.ReplaceValue(961); // DeviceChanged method called
+ properties->appearance.set_valid(true);
+ EXPECT_EQ(BluetoothDevice::DEVICE_KEYBOARD, devices[idx]->GetDeviceType());
+ EXPECT_EQ(3, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+ // When discovery is over, the value should be invalidated.
+ properties->appearance.set_valid(false);
+ // DeviceChanged method called by NotifyPropertyChanged()
+ properties->NotifyPropertyChanged(properties->appearance.name());
+ EXPECT_EQ(4, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+ EXPECT_EQ(BluetoothDevice::DEVICE_UNKNOWN, devices[idx]->GetDeviceType());
+
+ // Change the device appearance to mouse.
+ properties->appearance.ReplaceValue(962); // DeviceChanged method called
+ properties->appearance.set_valid(true);
+ EXPECT_EQ(BluetoothDevice::DEVICE_MOUSE, devices[idx]->GetDeviceType());
+ EXPECT_EQ(5, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+ // When discovery is over, the value should be invalidated.
+ properties->appearance.set_valid(false);
+ // DeviceChanged method called by NotifyPropertyChanged()
+ properties->NotifyPropertyChanged(properties->appearance.name());
+ EXPECT_EQ(6, observer.device_changed_count());
+ EXPECT_EQ(devices[idx], observer.last_device());
+ EXPECT_EQ(BluetoothDevice::DEVICE_UNKNOWN, devices[idx]->GetDeviceType());
+}
+
TEST_F(BluetoothBlueZTest, DeviceNameChanged) {
// Simulate a change of name of a device.
GetAdapter();
diff --git a/device/bluetooth/bluetooth_classic_device_mac.h b/device/bluetooth/bluetooth_classic_device_mac.h
index c582d6d..acd1dca 100644
--- a/device/bluetooth/bluetooth_classic_device_mac.h
+++ b/device/bluetooth/bluetooth_classic_device_mac.h
@@ -34,6 +34,7 @@ class BluetoothClassicDeviceMac : public BluetoothDeviceMac {
uint16_t GetVendorID() const override;
uint16_t GetProductID() const override;
uint16_t GetDeviceID() const override;
+ uint16_t GetAppearance() const override;
bool IsPaired() const override;
bool IsConnected() const override;
bool IsGattConnected() const override;
diff --git a/device/bluetooth/bluetooth_classic_device_mac.mm b/device/bluetooth/bluetooth_classic_device_mac.mm
index d1c3f63..985596e 100644
--- a/device/bluetooth/bluetooth_classic_device_mac.mm
+++ b/device/bluetooth/bluetooth_classic_device_mac.mm
@@ -102,6 +102,13 @@ uint16_t BluetoothClassicDeviceMac::GetDeviceID() const {
return 0;
}
+uint16_t BluetoothClassicDeviceMac::GetAppearance() const {
+ // TODO(crbug.com/588083): Implementing GetAppearance()
+ // on mac, win, and android platforms for chrome
+ NOTIMPLEMENTED();
+ return 0;
+}
+
bool BluetoothClassicDeviceMac::IsPaired() const {
return [device_ isPaired];
}
diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc
index c85af11..5696632 100644
--- a/device/bluetooth/bluetooth_device.cc
+++ b/device/bluetooth/bluetooth_device.cc
@@ -171,6 +171,39 @@ BluetoothDevice::DeviceType BluetoothDevice::GetDeviceType() const {
break;
}
+ // Some bluetooth devices, e.g., Microsoft Universal Foldable Keyboard,
+ // do not expose its bluetooth class. Use its appearance as a work-around.
+ // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml
+ uint16_t appearance = GetAppearance();
+ // appearance: 10-bit category and 6-bit sub-category
+ switch ((appearance & 0xffc0) >> 6) {
+ case 0x01:
+ // Generic phone
+ return DEVICE_PHONE;
+ case 0x02:
+ // Generic computer
+ return DEVICE_COMPUTER;
+ case 0x0f:
+ // HID subtype
+ switch (appearance & 0x3f) {
+ case 0x01:
+ // Keyboard.
+ return DEVICE_KEYBOARD;
+ case 0x02:
+ // Mouse
+ return DEVICE_MOUSE;
+ case 0x03:
+ // Joystick
+ return DEVICE_JOYSTICK;
+ case 0x04:
+ // Gamepad
+ return DEVICE_GAMEPAD;
+ case 0x05:
+ // Digitizer tablet
+ return DEVICE_TABLET;
+ }
+ }
+
return DEVICE_UNKNOWN;
}
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index 2bf6f35..82cc1ce 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -78,6 +78,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// The value returned if the RSSI or transmit power cannot be read.
static const int kUnknownPower = 127;
+ // The value returned if the appearance is not present.
+ static const uint16_t kAppearanceNotPresent = 0xffc0;
struct DEVICE_BLUETOOTH_EXPORT ConnectionInfo {
int rssi;
@@ -223,6 +225,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// number in BCD format, where available.
virtual uint16_t GetDeviceID() const = 0;
+ // Returns the appearance of the device.
+ virtual uint16_t GetAppearance() const = 0;
+
// Returns the name of the device suitable for displaying, this may
// be a synthesized string containing the address and localized type name
// if the device has no obtained name.
@@ -232,6 +237,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// aware of, by decoding the bluetooth class information. The returned
// values are unique, and do not overlap, so DEVICE_KEYBOARD is not also
// DEVICE_PERIPHERAL.
+ //
+ // Returns the type of the device, limited to those we support or are aware
+ // of, by decoding the bluetooth class information for Classic devices or
+ // by decoding the device's appearance for LE devices. For example,
+ // Microsoft Universal Foldable Keyboard only advertises the appearance.
DeviceType GetDeviceType() const;
// Indicates whether the device is known to support pairing based on its
diff --git a/device/bluetooth/bluetooth_device_android.cc b/device/bluetooth/bluetooth_device_android.cc
index 27fdbaf..6149732 100644
--- a/device/bluetooth/bluetooth_device_android.cc
+++ b/device/bluetooth/bluetooth_device_android.cc
@@ -96,6 +96,13 @@ uint16_t BluetoothDeviceAndroid::GetDeviceID() const {
return 0;
}
+uint16_t BluetoothDeviceAndroid::GetAppearance() const {
+ // TODO(crbug.com/588083): Implementing GetAppearance()
+ // on mac, win, and android platforms for chrome
+ NOTIMPLEMENTED();
+ return 0;
+}
+
bool BluetoothDeviceAndroid::IsPaired() const {
return Java_ChromeBluetoothDevice_isPaired(AttachCurrentThread(),
j_device_.obj());
diff --git a/device/bluetooth/bluetooth_device_android.h b/device/bluetooth/bluetooth_device_android.h
index 2fc634d..84e47bb 100644
--- a/device/bluetooth/bluetooth_device_android.h
+++ b/device/bluetooth/bluetooth_device_android.h
@@ -58,6 +58,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceAndroid final
uint16_t GetVendorID() const override;
uint16_t GetProductID() const override;
uint16_t GetDeviceID() const override;
+ uint16_t GetAppearance() const override;
bool IsPaired() const override;
bool IsConnected() const override;
bool IsGattConnected() const override;
diff --git a/device/bluetooth/bluetooth_device_bluez.cc b/device/bluetooth/bluetooth_device_bluez.cc
index 4001f90..5ee49a4 100644
--- a/device/bluetooth/bluetooth_device_bluez.cc
+++ b/device/bluetooth/bluetooth_device_bluez.cc
@@ -244,6 +244,18 @@ uint16_t BluetoothDeviceBlueZ::GetDeviceID() const {
return device_id;
}
+uint16_t BluetoothDeviceBlueZ::GetAppearance() const {
+ bluez::BluetoothDeviceClient::Properties* properties =
+ bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
+ object_path_);
+ DCHECK(properties);
+
+ if (!properties->appearance.is_valid())
+ return kAppearanceNotPresent;
+
+ return properties->appearance.value();
+}
+
bool BluetoothDeviceBlueZ::IsPaired() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
diff --git a/device/bluetooth/bluetooth_device_bluez.h b/device/bluetooth/bluetooth_device_bluez.h
index 595bab4..4b15aea 100644
--- a/device/bluetooth/bluetooth_device_bluez.h
+++ b/device/bluetooth/bluetooth_device_bluez.h
@@ -47,6 +47,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceBlueZ
uint16_t GetVendorID() const override;
uint16_t GetProductID() const override;
uint16_t GetDeviceID() const override;
+ uint16_t GetAppearance() const override;
bool IsPaired() const override;
bool IsConnected() const override;
bool IsGattConnected() const override;
diff --git a/device/bluetooth/bluetooth_device_win.cc b/device/bluetooth/bluetooth_device_win.cc
index d63b9d0..310aa9d 100644
--- a/device/bluetooth/bluetooth_device_win.cc
+++ b/device/bluetooth/bluetooth_device_win.cc
@@ -70,6 +70,13 @@ uint16_t BluetoothDeviceWin::GetDeviceID() const {
return 0;
}
+uint16_t BluetoothDeviceWin::GetAppearance() const {
+ // TODO(crbug.com/588083): Implementing GetAppearance()
+ // on mac, win, and android platforms for chrome
+ NOTIMPLEMENTED();
+ return 0;
+}
+
bool BluetoothDeviceWin::IsPaired() const {
return paired_;
}
diff --git a/device/bluetooth/bluetooth_device_win.h b/device/bluetooth/bluetooth_device_win.h
index 6166032..bba7fb3 100644
--- a/device/bluetooth/bluetooth_device_win.h
+++ b/device/bluetooth/bluetooth_device_win.h
@@ -40,6 +40,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWin : public BluetoothDevice {
uint16_t GetVendorID() const override;
uint16_t GetProductID() const override;
uint16_t GetDeviceID() const override;
+ uint16_t GetAppearance() const override;
bool IsPaired() const override;
bool IsConnected() const override;
bool IsGattConnected() const override;
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.h b/device/bluetooth/bluetooth_low_energy_device_mac.h
index f2517dd..ad713a5 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.h
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.h
@@ -46,6 +46,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac
uint16_t GetVendorID() const override;
uint16_t GetProductID() const override;
uint16_t GetDeviceID() const override;
+ uint16_t GetAppearance() const override;
bool IsPaired() const override;
bool IsConnected() const override;
bool IsGattConnected() const override;
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
index ac0357e..00712ae 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -106,6 +106,13 @@ uint16_t BluetoothLowEnergyDeviceMac::GetDeviceID() const {
return 0;
}
+uint16_t BluetoothLowEnergyDeviceMac::GetAppearance() const {
+ // TODO(crbug.com/588083): Implementing GetAppearance()
+ // on mac, win, and android platforms for chrome
+ NOTIMPLEMENTED();
+ return 0;
+}
+
int BluetoothLowEnergyDeviceMac::GetRSSI() const {
return rssi_;
}
diff --git a/device/bluetooth/test/mock_bluetooth_device.h b/device/bluetooth/test/mock_bluetooth_device.h
index 5aab092..801a936 100644
--- a/device/bluetooth/test/mock_bluetooth_device.h
+++ b/device/bluetooth/test/mock_bluetooth_device.h
@@ -41,6 +41,7 @@ class MockBluetoothDevice : public BluetoothDevice {
MOCK_CONST_METHOD0(GetDeviceID, uint16_t());
MOCK_CONST_METHOD0(GetName, base::string16());
MOCK_CONST_METHOD0(GetDeviceType, BluetoothDevice::DeviceType());
+ MOCK_CONST_METHOD0(GetAppearance, uint16_t());
MOCK_CONST_METHOD0(IsPaired, bool());
MOCK_CONST_METHOD0(IsConnected, bool());
MOCK_CONST_METHOD0(IsGattConnected, bool());