// // Copyright (C) 2015 Google, Inc. // // 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. // #include #include #include #include #include #include #include #include "constants.h" #include "heart_rate_server.h" using android::binder::Status; using android::String8; using android::String16; using android::bluetooth::IBluetoothLeAdvertiser; using android::bluetooth::BluetoothGattService; namespace heart_rate { class CLIBluetoothLeAdvertiserCallback : public android::bluetooth::BnBluetoothLeAdvertiserCallback { public: explicit CLIBluetoothLeAdvertiserCallback( android::sp bt) : bt_(bt) {} // IBluetoothLeAdvertiserCallback overrides: Status OnAdvertiserRegistered(int status, int advertiser_id) { if (status != bluetooth::BLE_STATUS_SUCCESS) { LOG(ERROR) << "Failed to register BLE advertiser, will not start advertising"; return Status::ok(); } LOG(INFO) << "Registered BLE advertiser with ID: " << advertiser_id; String16 name_param; bt_->GetName(&name_param); std::string name(String8(name_param).string()); /* Advertising data: 16-bit Service UUID: Heart Rate Service, Tx power*/ std::vector data{0x03, bluetooth::kEIRTypeComplete16BitUUIDs, 0x0D, 0x18, 0x02, bluetooth::kEIRTypeTxPower, 0x00}; data.push_back(name.length() + 1); data.push_back(bluetooth::kEIRTypeCompleteLocalName); data.insert(data.end(), name.c_str(), name.c_str() + name.length()); base::TimeDelta timeout; bluetooth::AdvertiseSettings settings( bluetooth::AdvertiseSettings::MODE_LOW_POWER, timeout, bluetooth::AdvertiseSettings::TX_POWER_LEVEL_MEDIUM, true); bluetooth::AdvertiseData adv_data(data); bluetooth::AdvertiseData scan_rsp; android::sp ble; bt_->GetLeAdvertiserInterface(&ble); bool start_status; ble->StartMultiAdvertising(advertiser_id, adv_data, scan_rsp, settings, &start_status); return Status::ok(); } Status OnMultiAdvertiseCallback( int status, bool is_start, const android::bluetooth::AdvertiseSettings& /* settings */) { LOG(INFO) << "Advertising" << (is_start ? " started" : " stopped"); return Status::ok(); }; private: android::sp bt_; DISALLOW_COPY_AND_ASSIGN(CLIBluetoothLeAdvertiserCallback); }; HeartRateServer::HeartRateServer( android::sp bluetooth, scoped_refptr main_task_runner, bool advertise) : simulation_started_(false), bluetooth_(bluetooth), server_if_(-1), hr_notification_count_(0), energy_expended_(0), advertise_(advertise), main_task_runner_(main_task_runner), weak_ptr_factory_(this) { CHECK(bluetooth_.get()); } HeartRateServer::~HeartRateServer() { std::lock_guard lock(mutex_); if (!gatt_.get() || server_if_ == -1) return; if (!android::IInterface::asBinder(gatt_.get())->isBinderAlive()) return; // Manually unregister ourselves from the daemon. It's good practice to do // this, even though the daemon will automatically unregister us if this // process exits. gatt_->UnregisterServer(server_if_); } bool HeartRateServer::Run(const RunCallback& callback) { std::lock_guard lock(mutex_); if (pending_run_cb_) { LOG(ERROR) << "Already started"; return false; } // Grab the IBluetoothGattServer binder from the Bluetooth daemon. bluetooth_->GetGattServerInterface(&gatt_); if (!gatt_.get()) { LOG(ERROR) << "Failed to obtain handle to IBluetoothGattServer interface"; return false; } // Register this instance as a GATT server. If this call succeeds, we will // asynchronously receive a server ID via the OnServerRegistered callback. bool status; gatt_->RegisterServer(this, &status); if (!status) { LOG(ERROR) << "Failed to register with the server interface"; return false; } pending_run_cb_ = callback; return true; } void HeartRateServer::ScheduleNextMeasurement() { main_task_runner_->PostDelayedTask( FROM_HERE, base::Bind(&HeartRateServer::SendHeartRateMeasurement, weak_ptr_factory_.GetWeakPtr()), base::TimeDelta::FromSeconds(1)); } void HeartRateServer::SendHeartRateMeasurement() { std::lock_guard lock(mutex_); // Send a notification or indication to all enabled devices. bool found = false; for (const auto& iter : device_ccc_map_) { uint8_t ccc_val = iter.second; if (!ccc_val) continue; found = true; // Don't send a notification if one is already pending for this device. if (pending_notification_map_[iter.first]) continue; std::vector value; BuildHeartRateMeasurementValue(&value); bool status; gatt_->SendNotification(server_if_, String16(String8(iter.first.c_str())), hr_measurement_handle_, false, value, &status); if (status) pending_notification_map_[iter.first] = true; } // Still enabled! if (found) { ScheduleNextMeasurement(); return; } // All clients disabled notifications. simulation_started_ = false; // TODO(armansito): We should keep track of closed connections here so that we // don't send notifications to uninterested clients. } void HeartRateServer::BuildHeartRateMeasurementValue( std::vector* out_value) { CHECK(out_value); // Assert that |out_value| is not nullptr. // Default flags field. Here is what we put in there: // Bit 0: 0 - 8-bit Heart Rate value // Bits 1 & 2: 11 - Sensor contact feature supported and contact detected. uint8_t flags = kHRValueFormat8Bit | kHRSensorContactDetected; // Our demo's heart rate. Pick a value between 90 and 130. uint8_t heart_rate = base::RandInt(90, 130); // On every tenth beat we include the Energy Expended value. bool include_ee = false; if (!(hr_notification_count_ % 10)) { include_ee = true; flags |= kHREnergyExpendedPresent; } hr_notification_count_++; energy_expended_ = std::min(UINT16_MAX, (int)energy_expended_ + 1); // Add all the value bytes. out_value->push_back(flags); out_value->push_back(heart_rate); if (include_ee) { out_value->push_back(energy_expended_); out_value->push_back(energy_expended_ >> 8); } } Status HeartRateServer::OnServerRegistered(int status, int server_if) { std::lock_guard lock(mutex_); if (status != bluetooth::BLE_STATUS_SUCCESS) { LOG(ERROR) << "Failed to register GATT server"; pending_run_cb_(false); return Status::ok(); } // Registration succeeded. Store our ID, as we need it for GATT server // operations. server_if_ = server_if; LOG(INFO) << "Heart Rate server registered - server_if: " << server_if_; bluetooth::Service hrService( 0, true, kHRServiceUUID, {{0, kHRMeasurementUUID, bluetooth::kCharacteristicPropertyNotify, 0, {{0, kCCCDescriptorUUID, (bluetooth::kAttributePermissionRead | bluetooth::kAttributePermissionWrite)}}}, {0, kBodySensorLocationUUID, bluetooth::kCharacteristicPropertyRead, bluetooth::kAttributePermissionRead, {}}, {0, kHRControlPointUUID, bluetooth::kCharacteristicPropertyWrite, bluetooth::kAttributePermissionWrite, {}}}, {}); bool op_status = true; Status stat = gatt_->AddService(server_if_, (BluetoothGattService)hrService, &op_status); if (!stat.isOk()) { LOG(ERROR) << "Failed to add service, status is: " /*<< stat*/; pending_run_cb_(false); return Status::ok(); } if (!op_status) { LOG(ERROR) << "Failed to add service"; pending_run_cb_(false); return Status::ok(); } LOG(INFO) << "Initiated AddService request"; return Status::ok(); } Status HeartRateServer::OnServiceAdded( int status, const android::bluetooth::BluetoothGattService& service) { std::lock_guard lock(mutex_); if (status != bluetooth::BLE_STATUS_SUCCESS) { LOG(ERROR) << "Failed to add Heart Rate service"; pending_run_cb_(false); return Status::ok(); } hr_service_handle_ = service.handle(); hr_measurement_handle_ = service.characteristics()[0].handle(); hr_measurement_cccd_handle_ = service.characteristics()[0].descriptors()[0].handle(); body_sensor_loc_handle_ = service.characteristics()[1].handle(); hr_control_point_handle_ = service.characteristics()[2].handle(); LOG(INFO) << "Heart Rate service added"; pending_run_cb_(true); if (advertise_) { android::sp ble; bluetooth_->GetLeAdvertiserInterface(&ble); bool status; ble->RegisterAdvertiser(new CLIBluetoothLeAdvertiserCallback(bluetooth_), &status); } return Status::ok(); } Status HeartRateServer::OnCharacteristicReadRequest( const String16& device_address, int request_id, int offset, bool /* is_long */, int handle) { std::lock_guard lock(mutex_); // This is where we handle an incoming characteristic read. Only the body // sensor location characteristic is readable. CHECK(handle == body_sensor_loc_handle_); std::vector value; bluetooth::GATTError error = bluetooth::GATT_ERROR_NONE; if (offset > 1) error = bluetooth::GATT_ERROR_INVALID_OFFSET; else if (offset == 0) value.push_back(kHRBodyLocationFoot); bool status; gatt_->SendResponse(server_if_, device_address, request_id, error, offset, value, &status); return Status::ok(); } Status HeartRateServer::OnDescriptorReadRequest(const String16& device_address, int request_id, int offset, bool /* is_long */, int handle) { std::lock_guard lock(mutex_); // This is where we handle an incoming characteristic descriptor read. There // is only one descriptor. if (handle != hr_measurement_cccd_handle_) { std::vector value; bool status; gatt_->SendResponse(server_if_, device_address, request_id, bluetooth::GATT_ERROR_ATTRIBUTE_NOT_FOUND, offset, value, &status); return Status::ok(); } // 16-bit value encoded as little-endian. const uint8_t value_bytes[] = { device_ccc_map_[std::string(String8(device_address).string())], 0x00}; std::vector value; bluetooth::GATTError error = bluetooth::GATT_ERROR_NONE; if (offset > 2) error = bluetooth::GATT_ERROR_INVALID_OFFSET; else value.insert(value.begin(), value_bytes + offset, value_bytes + 2 - offset); bool status; gatt_->SendResponse(server_if_, device_address, request_id, error, offset, value, &status); return Status::ok(); } Status HeartRateServer::OnCharacteristicWriteRequest( const String16& device_address, int request_id, int offset, bool is_prepare_write, bool need_response, const std::vector& value, int handle) { std::lock_guard lock(mutex_); std::vector dummy; // This is where we handle an incoming characteristic write. The Heart Rate // service doesn't really support prepared writes, so we just reject them to // keep things simple. if (is_prepare_write) { bool status; gatt_->SendResponse(server_if_, device_address, request_id, bluetooth::GATT_ERROR_REQUEST_NOT_SUPPORTED, offset, dummy, &status); return Status::ok(); } // Heart Rate Control point is the only writable characteristic. CHECK(handle == hr_control_point_handle_); // Writes to the Heart Rate Control Point characteristic must contain a single // byte with the value 0x01. if (value.size() != 1 || value[0] != 0x01) { bool status; gatt_->SendResponse(server_if_, device_address, request_id, bluetooth::GATT_ERROR_OUT_OF_RANGE, offset, dummy, &status); return Status::ok(); } LOG(INFO) << "Heart Rate Control Point written; Enery Expended reset!"; energy_expended_ = 0; if (!need_response) return Status::ok(); bool status; gatt_->SendResponse(server_if_, device_address, request_id, bluetooth::GATT_ERROR_NONE, offset, dummy, &status); return Status::ok(); } Status HeartRateServer::OnDescriptorWriteRequest( const String16& device_address, int request_id, int offset, bool is_prepare_write, bool need_response, const std::vector& value, int handle) { std::lock_guard lock(mutex_); std::vector dummy; // This is where we handle an incoming characteristic write. The Heart Rate // service doesn't really support prepared writes, so we just reject them to // keep things simple. if (is_prepare_write) { bool status; gatt_->SendResponse(server_if_, device_address, request_id, bluetooth::GATT_ERROR_REQUEST_NOT_SUPPORTED, offset, dummy, &status); return Status::ok(); } // CCC is the only descriptor we have. CHECK(handle == hr_measurement_cccd_handle_); // CCC must contain 2 bytes for a 16-bit value in little-endian. The only // allowed values here are 0x0000 and 0x0001. if (value.size() != 2 || value[1] != 0x00 || value[0] > 0x01) { bool status; gatt_->SendResponse(server_if_, device_address, request_id, bluetooth::GATT_ERROR_CCCD_IMPROPERLY_CONFIGURED, offset, dummy, &status); return Status::ok(); } device_ccc_map_[std::string(String8(device_address).string())] = value[0]; LOG(INFO) << "Heart Rate Measurement CCC written - device: " << device_address << " value: " << (int)value[0]; // Start the simulation. if (!simulation_started_ && value[0]) { simulation_started_ = true; ScheduleNextMeasurement(); } if (!need_response) return Status::ok(); bool status; gatt_->SendResponse(server_if_, device_address, request_id, bluetooth::GATT_ERROR_NONE, offset, dummy, &status); return Status::ok(); } Status HeartRateServer::OnExecuteWriteRequest(const String16& device_address, int request_id, bool /* is_execute */) { // We don't support Prepared Writes so, simply return Not Supported error. std::vector dummy; bool status; gatt_->SendResponse(server_if_, device_address, request_id, bluetooth::GATT_ERROR_REQUEST_NOT_SUPPORTED, 0, dummy, &status); return Status::ok(); } Status HeartRateServer::OnNotificationSent(const String16& device_address, int status) { LOG(INFO) << "Notification was sent - device: " << device_address << " status: " << status; std::lock_guard lock(mutex_); pending_notification_map_[std::string(String8(device_address).string())] = false; return Status::ok(); } Status HeartRateServer::OnConnectionStateChanged(const String16& device_address, bool connected) { LOG(INFO) << "Connection state changed - device: " << device_address << " connected: " << (connected ? "true" : "false"); return Status::ok(); } } // namespace heart_rate