blob: b560a37bb761bf835c7fda2e7686787b7a974acb [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GARNET_DRIVERS_BLUETOOTH_LIB_HCI_LOW_ENERGY_SCANNER_H_
#define GARNET_DRIVERS_BLUETOOTH_LIB_HCI_LOW_ENERGY_SCANNER_H_
#include <set>
#include <lib/async/dispatcher.h>
#include "garnet/drivers/bluetooth/lib/common/byte_buffer.h"
#include "garnet/drivers/bluetooth/lib/common/device_address.h"
#include "garnet/drivers/bluetooth/lib/hci/hci_constants.h"
#include "garnet/drivers/bluetooth/lib/hci/sequential_command_runner.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/ref_ptr.h"
namespace btlib {
namespace hci {
class Transport;
// Represents a discovered Bluetooth Low Energy device.
struct LowEnergyScanResult {
LowEnergyScanResult();
LowEnergyScanResult(const common::DeviceAddress& address, bool resolved,
bool connectable, int8_t rssi);
// The device address of the remote device.
common::DeviceAddress address;
// True if |address| is a static or random identity address resolved by the
// controller.
bool resolved;
// True if this device accepts connections. This is the case if this device
// sent a connectable advertising PDU.
bool connectable;
// The received signal strength of the advertisement packet corresponding to
// this device.
int8_t rssi;
};
// LowEnergyScanner manages Low Energy device scan procedures that are used
// during general and limited device discovery and connection establishment
// procedures. This is an abstract class that provides a common interface
// over 5.0 Extended Advertising and Legacy Advertising features.
//
// Instances of this class are expected to each as a singleton on a
// per-transport basis as multiple instances cannot accurately reflect the state
// of the controller while allowing simultaneous scan operations.
class LowEnergyScanner {
public:
// Value that can be passed to StartScan() to scan indefinitely.
static constexpr int64_t kPeriodInfinite = -1;
enum class State {
// No scan is currently being performed.
kIdle,
// A previously running scan is being stopped.
kStopping,
// A scan is being initiated.
kInitiating,
// An active scan is currently being performed.
kActiveScanning,
// A passive scan is currently being performed.
kPassiveScanning,
};
// Interface for receiving events related to Low Energy device scan.
class Delegate {
public:
virtual ~Delegate() = default;
// Called when a device is found. |data| contains the advertising data, as
// well as any scan response data that was received during an active scan.
virtual void OnDeviceFound(const LowEnergyScanResult& result,
const common::ByteBuffer& data);
// Called when a directed advertising report is received from the device
// with the given address.
virtual void OnDirectedAdvertisement(const LowEnergyScanResult& result);
};
LowEnergyScanner(Delegate* delegate,
fxl::RefPtr<Transport> hci,
async_dispatcher_t* dispatcher);
virtual ~LowEnergyScanner() = default;
// Returns the current Scan state.
State state() const { return state_; }
// True if a device scan is currently being performed.
bool IsActiveScanning() const { return state() == State::kActiveScanning; }
bool IsPassiveScanning() const { return state() == State::kPassiveScanning; }
bool IsScanning() const { return IsActiveScanning() || IsPassiveScanning(); }
// True if no scan procedure is currently enabled.
bool IsIdle() const { return state() == State::kIdle; }
// Initiates a device scan. This is an asynchronous operation that abides by
// the following rules:
//
// - This method synchronously returns false if the procedure could not be
// started, e.g. because discovery is already in progress, or it is in the
// process of being stopped, or the controller does not support discovery,
// etc.
//
// - Synchronously returns true if the procedure was initiated but the it is
// unknown whether or not the procedure has succeeded.
//
// - |callback| is invoked asynchronously to report the status of the
// procedure. In the case of failure, |callback| will be invoked once to
// report the end of the procedure. In the case of success, |callback|
// will be invoked twice: the first time to report that the procedure has
// started, and a second time to report when the procedure ends, either
// due to a timeout or cancellation.
//
// - |period| specifies (in milliseconds) the duration of the scan. If the
// special value of kPeriodInfinite is passed then scanning will continue
// indefinitely and must be explicitly stopped by calling StopScan().
// Otherwise, the value must be non-zero.
//
// Once started, a scan can be terminated at any time by calling the
// StopScan() method. Otherwise, an ongoing scan will terminate at the end of
// the scan period if a finite value for |period_ms| was provided.
//
// If an active scan is being performed, then scannable advertising reports
// will NOT generate an OnDeviceFound event until a scan response is received
// from the corresponding broadcaster. If a scan response from a scannable
// device is never received during a scan period, then an OnDeviceFound event
// (excluding scan response data) will be generated for that device at the end
// of the scan period, UNLESS the scan was explicitly stopped via StopScan().
enum class ScanStatus {
// Reported when the scan could not be started.
kFailed,
// Reported when an active scan was started and is currently in progress.
kActive,
// Reported when a passive scan was started and is currently in progress.
kPassive,
// Called when the scan was terminated naturally at the end of the scan
// period.
kComplete,
// Called when the scan was terminated due to a call to StopScan().
kStopped,
};
using ScanStatusCallback = fit::function<void(ScanStatus)>;
virtual bool StartScan(bool active,
uint16_t scan_interval,
uint16_t scan_window,
bool filter_duplicates,
LEScanFilterPolicy filter_policy,
int64_t period_ms,
ScanStatusCallback callback) = 0;
// Stops a previously started device scan. Returns false if a scan is not in
// progress. Otherwise, cancels any in progress scan procedure and returns
// true.
virtual bool StopScan() = 0;
protected:
async_dispatcher_t* dispatcher() const { return dispatcher_; }
Transport* transport() const { return transport_.get(); }
SequentialCommandRunner* hci_cmd_runner() const {
return hci_cmd_runner_.get();
}
Delegate* delegate() const { return delegate_; }
void set_state(State state) { state_ = state; }
// Returns true if an active scan was most recently requested. This applies to
// the on-going scan only if IsScanning() returns true.
bool active_scan_requested() const { return active_scan_requested_; }
void set_active_scan_requested(bool value) { active_scan_requested_ = value; }
private:
State state_;
bool active_scan_requested_;
Delegate* delegate_; // weak
// Task runner for all asynchronous tasks.
async_dispatcher_t* dispatcher_;
// The HCI transport.
fxl::RefPtr<Transport> transport_;
// Command runner for all HCI commands sent out by implementations.
std::unique_ptr<SequentialCommandRunner> hci_cmd_runner_;
FXL_DISALLOW_COPY_AND_ASSIGN(LowEnergyScanner);
};
} // namespace hci
} // namespace btlib
#endif // GARNET_DRIVERS_BLUETOOTH_LIB_HCI_LOW_ENERGY_SCANNER_H_