summaryrefslogtreecommitdiffstats
path: root/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc
diff options
context:
space:
mode:
authorarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-24 21:51:10 +0000
committerarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-24 21:51:10 +0000
commit95d00f3223b240ef386e185e24bfae2d662dd5bd (patch)
tree31877c666f0ee6074ee14edb9fecca661682ad57 /chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc
parentf215a25fe8773b80f624e1b667e0cb30ec9a7f35 (diff)
downloadchromium_src-95d00f3223b240ef386e185e24bfae2d662dd5bd.zip
chromium_src-95d00f3223b240ef386e185e24bfae2d662dd5bd.tar.gz
chromium_src-95d00f3223b240ef386e185e24bfae2d662dd5bd.tar.bz2
chromeos/dbus: Add fake D-Bus clients for GATT client-mode.
Added fake implementations of GATT service, characteristic, and descriptor clients. A new "Low Energy" fake device is introduced, which uses the fake GATT clients to simulate the Heart Rate Service. BUG=351229 TEST=1. device_unittests, chromeos_unittests. 2. Build and run chrome for Linux with "chromeos=1". Run Bluetooth device discovery, connect to the device named "Bluetooth 4.0 Heart Rate Monitor". Watch the debug output (vmodule="*bluetooth_gatt*=2") for the GATT clients, which should create fake heart rate service and descriptors and update the measurement value at regular intervals. R=keybuk@chromium.org, stevenjb@chromium.org Review URL: https://codereview.chromium.org/206443009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@259022 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc')
-rw-r--r--chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc342
1 files changed, 342 insertions, 0 deletions
diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc
new file mode 100644
index 0000000..85408b8
--- /dev/null
+++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc
@@ -0,0 +1,342 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "base/time/time.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+const int kHeartRateMeasurementNotificationIntervalMs = 2000;
+
+} // namespace
+
+// static
+const char FakeBluetoothGattCharacteristicClient::
+ kHeartRateMeasurementPathComponent[] = "char0000";
+const char FakeBluetoothGattCharacteristicClient::
+ kBodySensorLocationPathComponent[] = "char0001";
+const char FakeBluetoothGattCharacteristicClient::
+ kHeartRateControlPointPathComponent[] = "char0002";
+
+// static
+const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] =
+ "00002a37-0000-1000-8000-00805f9b34fb";
+const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] =
+ "00002a38-0000-1000-8000-00805f9b34fb";
+const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] =
+ "00002a39-0000-1000-8000-00805f9b34fb";
+
+FakeBluetoothGattCharacteristicClient::Properties::Properties(
+ const PropertyChangedCallback& callback)
+ : BluetoothGattCharacteristicClient::Properties(
+ NULL,
+ bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
+ callback) {
+}
+
+FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
+}
+
+void FakeBluetoothGattCharacteristicClient::Properties::Get(
+ dbus::PropertyBase* property,
+ dbus::PropertySet::GetCallback callback) {
+ VLOG(1) << "Get " << property->name();
+ callback.Run(false);
+}
+
+void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
+ VLOG(1) << "GetAll";
+}
+
+void FakeBluetoothGattCharacteristicClient::Properties::Set(
+ dbus::PropertyBase* property,
+ dbus::PropertySet::SetCallback callback) {
+ VLOG(1) << "Set " << property->name();
+ if (property->name() != value.name()) {
+ callback.Run(false);
+ return;
+ }
+ // Allow writing to only certain characteristics that are defined with the
+ // write permission.
+ // TODO(armansito): Actually check against the permissions property instead of
+ // UUID, once that property is fully defined in the API.
+ if (uuid.value() != kHeartRateControlPointUUID) {
+ callback.Run(false);
+ return;
+ }
+ callback.Run(true);
+ property->ReplaceValueWithSetValue();
+}
+
+FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
+ : heart_rate_visible_(false),
+ calories_burned_(0),
+ weak_ptr_factory_(this) {
+}
+
+FakeBluetoothGattCharacteristicClient::
+ ~FakeBluetoothGattCharacteristicClient() {
+}
+
+void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
+}
+
+void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+std::vector<dbus::ObjectPath>
+FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
+ std::vector<dbus::ObjectPath> paths;
+ if (IsHeartRateVisible()) {
+ paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_));
+ paths.push_back(dbus::ObjectPath(body_sensor_location_path_));
+ paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_));
+ }
+ return paths;
+}
+
+FakeBluetoothGattCharacteristicClient::Properties*
+FakeBluetoothGattCharacteristicClient::GetProperties(
+ const dbus::ObjectPath& object_path) {
+ if (object_path.value() == heart_rate_measurement_path_) {
+ DCHECK(heart_rate_measurement_properties_.get());
+ return heart_rate_measurement_properties_.get();
+ }
+ if (object_path.value() == body_sensor_location_path_) {
+ DCHECK(heart_rate_measurement_properties_.get());
+ return heart_rate_measurement_properties_.get();
+ }
+ if (object_path.value() == heart_rate_control_point_path_) {
+ DCHECK(heart_rate_control_point_properties_.get());
+ return heart_rate_control_point_properties_.get();
+ }
+ return NULL;
+}
+
+void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
+ const dbus::ObjectPath& service_path) {
+ if (IsHeartRateVisible()) {
+ VLOG(2) << "Fake Heart Rate characteristics are already visible.";
+ return;
+ }
+
+ VLOG(2) << "Exposing fake Heart Rate characteristics.";
+
+ // ==== Heart Rate Measurement Characteristic ====
+ heart_rate_measurement_path_ =
+ service_path.value() + "/" + kHeartRateMeasurementPathComponent;
+ heart_rate_measurement_properties_.reset(new Properties(base::Bind(
+ &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ dbus::ObjectPath(heart_rate_measurement_path_))));
+ heart_rate_measurement_properties_->uuid.ReplaceValue(
+ kHeartRateMeasurementUUID);
+ heart_rate_measurement_properties_->service.ReplaceValue(service_path);
+
+ // TODO(armansito): Fill out the flags field once bindings for the values have
+ // been added. For now, leave it empty.
+
+ std::vector<uint8> measurement_value = GetHeartRateMeasurementValue();
+ heart_rate_measurement_properties_->value.ReplaceValue(measurement_value);
+
+ // ==== Body Sensor Location Characteristic ====
+ body_sensor_location_path_ =
+ service_path.value() + "/" + kBodySensorLocationPathComponent;
+ body_sensor_location_properties_.reset(new Properties(base::Bind(
+ &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ dbus::ObjectPath(body_sensor_location_path_))));
+ body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID);
+ body_sensor_location_properties_->service.ReplaceValue(service_path);
+
+ // TODO(armansito): Fill out the flags field once bindings for the values have
+ // been added. For now, leave it empty.
+
+ // The sensor is in the "Other" location.
+ std::vector<uint8> body_sensor_location_value;
+ body_sensor_location_value.push_back(0);
+ body_sensor_location_properties_->value.ReplaceValue(
+ body_sensor_location_value);
+
+ // ==== Heart Rate Control Point Characteristic ====
+ heart_rate_control_point_path_ =
+ service_path.value() + "/" + kHeartRateControlPointPathComponent;
+ heart_rate_control_point_properties_.reset(new Properties(base::Bind(
+ &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ dbus::ObjectPath(heart_rate_control_point_path_))));
+ heart_rate_control_point_properties_->uuid.ReplaceValue(
+ kHeartRateControlPointUUID);
+ heart_rate_control_point_properties_->service.ReplaceValue(service_path);
+
+ // TODO(armansito): Fill out the flags field once bindings for the values have
+ // been added. For now, leave it empty.
+
+ // Set the initial value to 0. Whenever this gets set to 1, we will reset the
+ // total calories burned and change the value back to 0.
+ std::vector<uint8> heart_rate_control_point_value;
+ heart_rate_control_point_value.push_back(0);
+ heart_rate_control_point_properties_->value.ReplaceValue(
+ heart_rate_control_point_value);
+
+ heart_rate_visible_ = true;
+
+ NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_));
+ NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_));
+ NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_));
+
+ // Set up notifications for heart rate measurement.
+ // TODO(armansito): Do this based on the value of the "client characteristic
+ // configuration" descriptor. Since it's still unclear how descriptors will
+ // be handled by BlueZ, automatically set up notifications for now.
+ ScheduleHeartRateMeasurementValueChange();
+
+ // TODO(armansito): Add descriptors.
+}
+
+void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
+ VLOG(2) << "Hiding fake Heart Rate characteristics.";
+ heart_rate_measurement_properties_.reset();
+ body_sensor_location_properties_.reset();
+ heart_rate_control_point_properties_.reset();
+
+ std::string hrm_path = heart_rate_measurement_path_;
+ heart_rate_measurement_path_.clear();
+ std::string bsl_path = body_sensor_location_path_;
+ body_sensor_location_path_.clear();
+ std::string hrcp_path = heart_rate_control_point_path_;
+ heart_rate_control_point_path_.clear();
+ heart_rate_visible_ = false;
+
+ NotifyCharacteristicRemoved(dbus::ObjectPath(hrm_path));
+ NotifyCharacteristicRemoved(dbus::ObjectPath(bsl_path));
+ NotifyCharacteristicRemoved(dbus::ObjectPath(hrcp_path));
+}
+
+void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ VLOG(2) << "Characteristic property changed: " << object_path.value()
+ << ": " << property_name;
+
+ FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
+ GattCharacteristicPropertyChanged(
+ object_path, property_name));
+
+ // If the heart rate control point was set, reset the calories burned.
+ if (object_path.value() != heart_rate_control_point_path_)
+ return;
+ DCHECK(heart_rate_control_point_properties_.get());
+ dbus::Property<std::vector<uint8> >* value_prop =
+ &heart_rate_control_point_properties_->value;
+ if (property_name != value_prop->name())
+ return;
+
+ std::vector<uint8> value = value_prop->value();
+ DCHECK(value.size() == 1);
+ if (value[0] == 0)
+ return;
+
+ DCHECK(value[0] == 1);
+ calories_burned_ = 0;
+ value[0] = 0;
+ value_prop->ReplaceValue(value);
+}
+
+void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
+ const dbus::ObjectPath& object_path) {
+ VLOG(2) << "GATT characteristic added: " << object_path.value();
+ FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
+ GattCharacteristicAdded(object_path));
+}
+
+void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
+ const dbus::ObjectPath& object_path) {
+ VLOG(2) << "GATT characteristic removed: " << object_path.value();
+ FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
+ GattCharacteristicRemoved(object_path));
+}
+
+void FakeBluetoothGattCharacteristicClient::
+ ScheduleHeartRateMeasurementValueChange() {
+ if (!IsHeartRateVisible())
+ return;
+ VLOG(2) << "Updating heart rate value.";
+ std::vector<uint8> measurement = GetHeartRateMeasurementValue();
+ heart_rate_measurement_properties_->value.ReplaceValue(measurement);
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&FakeBluetoothGattCharacteristicClient::
+ ScheduleHeartRateMeasurementValueChange,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(
+ kHeartRateMeasurementNotificationIntervalMs));
+}
+
+std::vector<uint8>
+FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
+ // TODO(armansito): We should make sure to properly pack this struct to ensure
+ // correct byte alignment and endianness. It doesn't matter too much right now
+ // as this is a fake and GCC on Linux seems to do the right thing.
+ struct {
+ uint8 flags;
+ uint8 bpm;
+ uint16 energy_expanded;
+ uint16 rr_interval;
+ } value;
+
+ // Flags in LSB: 0 11 1 1 000
+ // | | | | |
+ // 8-bit bpm format -- | | | |
+ // Sensor contact supported -- | | |
+ // Energy expanded field present -- | |
+ // RR-Interval values present ------- |
+ // Reserved for future use ------------
+ value.flags = 0x0;
+ value.flags |= (0x03 << 1);
+ value.flags |= (0x01 << 3);
+ value.flags |= (0x01 << 4);
+
+ // Pick a value between 117 bpm and 153 bpm for heart rate.
+ value.bpm = static_cast<uint8>(base::RandInt(117, 153));
+
+ // Total calories burned in kJoules since the last reset. Increment this by 1
+ // every time. It's fine if it overflows: it becomes 0 when the user resets
+ // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
+ value.energy_expanded = calories_burned_++;
+
+ // Include one RR-Interval value, in seconds.
+ value.rr_interval = 60/value.bpm;
+
+ // Return the bytes in an array.
+ uint8* bytes = reinterpret_cast<uint8*>(&value);
+ std::vector<uint8> return_value;
+ return_value.assign(bytes, bytes + sizeof(value));
+ return return_value;
+}
+
+bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
+ DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty());
+ DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty());
+ DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty());
+ DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get());
+ DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get());
+ DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get());
+ return heart_rate_visible_;
+}
+
+} // namespace chromeos