chromium/device/bluetooth/public/mojom/adapter.mojom

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module bluetooth.mojom;

import "device/bluetooth/public/mojom/device.mojom";
import "device/bluetooth/public/mojom/uuid.mojom";
import "device/bluetooth/public/mojom/gatt_characteristic_permissions.mojom";
import "device/bluetooth/public/mojom/gatt_characteristic_properties.mojom";
import "device/bluetooth/public/mojom/gatt_service_error_code.mojom";

// Possible errors sent as a response by Adapter.ConnectToDevice on a Device
// connection request.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. This enum should be kept in sync
// with the NearbyConnectionsConnectResult enum in
// src/tools/metrics/histograms/metadata/nearby/enums.xml.
enum ConnectResult {
  SUCCESS,
  AUTH_CANCELED,
  AUTH_FAILED,
  AUTH_REJECTED,
  AUTH_TIMEOUT,
  FAILED,
  INPROGRESS,
  UNKNOWN,
  UNSUPPORTED_DEVICE,
  DEVICE_NO_LONGER_IN_RANGE,
  NOT_READY,
  ALREADY_CONNECTED,
  ALREADY_EXISTS,
  NOT_CONNECTED,
  DOES_NOT_EXIST,
  INVALID_ARGS,
  NON_AUTH_TIMEOUT,
  NO_MEMORY,
  JNI_ENVIRONMENT,
  JNI_THREAD_ATTACH,
  WAKELOCK,
  UNEXPECTED_STATE,
  SOCKET,
};

union LocalCharacteristicReadResult {
  GattServiceErrorCode error_code;
  array<uint8> data;
};

// The results of a successful connection via ConnectToServiceInsecurely().
struct ConnectToServiceResult {
  pending_remote<Socket> socket;
  handle<data_pipe_consumer> receive_stream;
  handle<data_pipe_producer> send_stream;
};

// The results of a successful connection via ServerSocket::Accept().
struct AcceptConnectionResult {
  DeviceInfo device;
  pending_remote<Socket> socket;
  handle<data_pipe_consumer> receive_stream;
  handle<data_pipe_producer> send_stream;
};

struct AdapterInfo {
  // TODO(crbug.com/40709957): Use fixed-size array, not string, for address.
  string address;
  string name;
  string system_name;
  // Whether the new CrOS Bluetooth stack (aka floss) is in use. See
  // go/project-floss for more info.
  bool floss;
  // Whether the adapter supports BLE extended advertisements, which were
  // added in Bluetooth 5.
  bool extended_advertisement_support;
  bool initialized;
  bool present;
  bool powered;
  bool discoverable;
  bool discovering;
};

// Represents an ongoing BLE advertisement. Releasing it will release the
// underlying object and stop the advertisement, but callers should prefer to
// let a call to Unregister() to finish first.
// Note: Methods which are declared [Sync] are for use by
// //chrome/services/sharing/nearby; all other usage of their synchronous
// signatures is strongly discouraged.
interface Advertisement {
  // Use to gracefully stop advertising before destroying the message pipe. The
  // reply callback can be used to synchronize an attempt to re-register an
  // advertisement; attempting to register an advertisement without first
  // releasing limited advertisement slots in the hardware may fail with a
  // busy error.
  [Sync]
  Unregister() => ();
};

// Represents a request to discover nearby devices.
// Note: Methods which are declared [Sync] are for use by
// //chrome/services/sharing/nearby; all other usage of their synchronous
// signatures is strongly discouraged.
interface DiscoverySession {
  // Returns true if the session is active, false otherwise. If false, the
  // adapter might still be discovering as there might still be other active
  // sessions; this just means that this instance no longer has a say in
  // whether or not discovery should continue. In this case, a new
  // DiscoverySession should be started to make sure that device discovery
  // continues.
  [Sync]
  IsActive() => (bool active);

  // Requests this discovery session instance to stop. If this instance is
  // active, the session will stop. After a successful invocation, the
  // adapter may or may not stop device discovery, depending on whether or not
  // other active discovery sessions are present. Users are highly encouraged
  // to call this method to end a discovery session, instead of relying on
  // disconnecting the message pipe, so that they can respond to the result.
  // Returns true on success. Returns false if this session is inactive or an
  // error occurs while stopping the session.
  [Sync]
  Stop() => (bool success);
};

// Represents an open connection to a remote device. Releasing it will destroy
// the underlying connection, but if callers want to try again soon, they should
// call Disconnect() first and wait for completion to ensure that the resource
// has been completely released.
// Note: Methods which are declared [Sync] are for use by
// //chrome/services/sharing/nearby; all other usage of their synchronous
// signatures is strongly discouraged.
interface Socket {
  // Use to gracefully close the underlying connection before destroying. The
  // reply callback can be used to synchronize a reconnection attempt;
  // attempting to reconnect to the same service on this device may fail with a
  // busy error until the reply callback is invoked.
  [Sync]
  Disconnect() => ();
};

// Represents a pending connecting from a remote device. Releasing it will
// stop listening for an incoming connection, but if callers want to start
// listening again soon, they should call Disconnect() first and wait for
// completion to ensure that the resource has been completely released.
// Note: Methods which are declared [Sync] are for use by
// //chrome/services/sharing/nearby; all other usage of their synchronous
// signatures is strongly discouraged.
interface ServerSocket {
  // Accept the next incoming connection from a remote device. If something
  // goes wrong, the returned |result| will be null.
  [Sync]
  Accept() => (AcceptConnectionResult? result);

  // Use to gracefully close the underlying resource before destroying. The
  // reply callback can be used to synchronize an attempt to re-initialize
  // a server socket; attempting to listen on the same server socket on this
  // device may fail with a busy error until the reply callback is invoked.
  [Sync]
  Disconnect() => ();
};

// Representation of a BLE GATT service. A `GattService` is created by
// `Adapter::CreateLocalGattService()`. To use this interface, first create all
// characteristics belonging to the service via `CreateCharacteristic()`. Then,
// when ready to publish the service and its characteristics on the local
// adapter's GATT database, call `Register()`. Requests from GATT clients to
// read/write values on the characteristics are executed via the
// `GattServiceObserver`. When finished with the `GattService`, callers should
// delete their Mojo remote, which triggers the underlying GATT service in
// the local adapter to be deleted.
interface GattService {
  // Creates a characteristic and adds it to the GATT service under the given
  // characteristic uuid, using the service UUID tied to the `GattService`.
  // This method will fail if the characteristic already exists at
  // `characteristic_uuid`; it is expected that callers will only call
  // when they want to create a characteristic that does not already exist.
  [Sync]
  CreateCharacteristic(UUID characteristic_uuid,
    GattCharacteristicPermissions permissions,
    GattCharacteristicProperties properties) => (bool success);

  // Registers this `GattService` with the local Bluetooth Adapter.
  // Calling `Register()` will make this service and all of its associated
  // attributes available on the local adapter's GATT database. This should be
  // called once all the requested characteristics have been created, before
  // advertising begins. No `error_code` indicates success.
  Register() => (GattServiceErrorCode? error_code);
};

// Listener on `GattService` events.
// TODO(b/311430390): Implement additional methods that communicate write
// requests on GATT characteristics.
interface GattServiceObserver {
  // Called when a remote peripheral is reading from the characteristic.
  // On failures, |read_result| will be the error code. On success,
  // |read_result| will be the read result value.
  OnLocalCharacteristicRead(DeviceInfo remote_device,
    UUID characteristic_uuid, UUID service_uuid, uint32 offset) =>
    (LocalCharacteristicReadResult read_result);
};

// Handles requests to either query Bluetooth adapter capabilities or state, or
// find or connect to remote devices. Backed by //device/bluetooth.
// Note: Methods which are declared [Sync] are for use by
// //chrome/services/sharing/nearby; all other usage of their synchronous
// signatures is strongly discouraged.
interface Adapter {
  // Creates a GATT connection to the device with |address| and returns a
  // Device if the connection was succesful. The GATT connection is tied to the
  // the lifetime of the Device message pipe.
  // TODO(crbug.com/40709957): Use fixed-size array, not string, for address.
  ConnectToDevice(string address) =>
      (ConnectResult result, pending_remote<Device>? device);

  // Retrieves the list of the devices known by the adapter including Connected
  // Devices, GATT Connected Devices, Paired Devices and Devices discovered
  // during a classic or low-energy scan.
  GetDevices() => (array<DeviceInfo> devices);

  // Gets basic information about the adapter.
  [Sync]
  GetInfo() => (AdapterInfo info);

  // Adds an observer that listens for the adapter's events.
  [Sync]
  AddObserver(pending_remote<AdapterObserver> observer) => ();

  // Requests the adapter to broadcast a BLE advertisement on |service_id| with
  // the associated packet |service_data|. Returns null if advertisement is not
  // registered successfully.
  //
  // When |use_scan_response| is true, the |service_data| is added to the scan
  // response instead of the initial advertisement. When |connectable| is true,
  // the advertisement is created as type PERIPHERAL; else it is type BROADCAST.
  // A few assumptions are made:
  //
  // 1) The |service_id| provided is a valid 16-bit UUID in 128-bit format.
  //    The identifying 16-bits will be extracted for the short service id used
  //    in the scan response.
  // 2) The Ad Type is assumed to be 0x16 as that is all that is supported by
  //    the Platform right now.
  //
  // Important notes:
  // * Bluetooth chips generally can only broadcast a few advertisements,
  //   sometimes even only one, simultaneously. This can be mitigated by
  //   operating systems "rotating" advertisements in the higher software layer,
  //   as Chrome OS does. Non-Chrome OS clients of this API are responsible for
  //   understanding their host OS's and/or hardware's limitations.
  [Sync]
  RegisterAdvertisement(UUID service_id, array<uint8> service_data,
      bool use_scan_response, bool connectable)
      => (pending_remote<Advertisement>? advertisement);

  // Requests the local device to make itself discoverable to nearby remote
  // devices.
  [Sync]
  SetDiscoverable(bool discoverable) => (bool success);

  // Change the name of the local device as it appears to remote devices.
  // TODO(crbug.com/40145221): Implement a mechanism to request this resource
  // before being able to use it.
  [Sync]
  SetName(string name) => (bool success);

  // Requests the adapter to start a new discovery session. Returns null if
  // session not created successfully.
  [Sync]
  StartDiscoverySession(string client_name) => (pending_remote<DiscoverySession>? session);

  // Attempts to initiate an insecure outgoing L2CAP or RFCOMM connection to the
  // advertised service on this device matching |service_uuid|. This method is
  // marked as "Insecurely" because the outgoing connection will not request
  // bonding (pairing) to the remote device. If the connection attempt fails,
  // the returned |result| will be null. See Socket for details on how to close
  // the connection.
  //
  // If the local device is already bonded and the connection attempt fails,
  // when |should_unbond_on_error| is true the local device will forget the
  // remote device. (see b/204274786)
  // TODO(crbug.com/40709957): Use fixed-size array, not string, for address.
  [Sync]
  ConnectToServiceInsecurely(string address, UUID service_uuid,
      bool should_unbond_on_error) => (ConnectToServiceResult? result);

  // Creates an RFCOMM service on this adapter advertised with |service_name|
  // and |service_uuid|. This method is marked as "Insecurely" because the local
  // device will not request bonding (pairing) with the remote device: clients
  // are responsible for securing the connection at the application-level. If
  // the service creation attempt fails, the returned |server_socket| will be
  // null. See ServerSocket for details on how to stop listening.
  [Sync]
  CreateRfcommServiceInsecurely(string service_name, UUID service_uuid)
      => (pending_remote<ServerSocket>? server_socket);

  // Creates a local GATT service corresponding to |service_id|, and
  // communicates events to the |observer|. This call is only expected to be
  // used to create a local GATT service that does not exist at |service_id|.
  // Callers should first consult |IsLeScatternetDualRoleSupported()| before
  // calling this method to determine if a GATT service can be safely started on
  // the local device.
  [Sync]
  CreateLocalGattService(UUID service_id,
       pending_remote<GattServiceObserver> observer)
      => (pending_remote<GattService> gatt_service);

  // Retrieves the status of whether or not the LE Scatternet Dual Role
  // (simultaneous use of the LE peripheral and central roles) is supported on
  // the Adapter. If |is_supported| is true, then callers can call
  // |CreateLocalGattService()| without being concerned about performance or
  // stability issues.
  [Sync]
  IsLeScatternetDualRoleSupported() => (bool is_supported);
};

// Listener on Bluetooth events. Register as an observer via AddObserver().
interface AdapterObserver {
  // Called when the presence of the adapter changes.
  PresentChanged(bool present);

  // Called when the radio power state of the adapter changes.
  PoweredChanged(bool powered);

  // Called when the discoverability state of the adapter changes.
  DiscoverableChanged(bool discoverable);

  // Called when the discovering state of the adapter changes.
  DiscoveringChanged(bool discovering);

  // Called the first time a device is discovered.
  DeviceAdded(DeviceInfo device);

  // Called when one of the following properties of a device changes:
  //   Address, appearance, Bluetooth class, Inquiry RSSI, Inquiry TX Power,
  //   Service UUIDs, Connectionable state, Connection state, Pairing state,
  //   Trustable state.
  // Generally called for each advertisement packet recevied, but this is not
  // guaranteed on ChromeOS or Linux. Because the RSSI is always changing,
  // it's very likely this will be called for each advertising packet.
  DeviceChanged(DeviceInfo device);

  // Called after the device hasn't been seen for 3 minutes.
  DeviceRemoved(DeviceInfo device);
};