chromium/device/bluetooth/bluetooth_socket_mac.h

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

#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_

#import <IOBluetooth/IOBluetooth.h>
#import <IOKit/IOReturn.h>
#include <stddef.h>

#include <memory>
#include <string>

#include "base/containers/queue.h"
#include "base/memory/scoped_refptr.h"
#include "base/threading/thread_checker.h"
#include "base/timer/timer.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"

@class BluetoothRfcommConnectionListener;
@class BluetoothL2capConnectionListener;
@class SDPQueryListener;

namespace net {
class IOBuffer;
class IOBufferWithSize;
}

namespace device {

class BluetoothAdapterMac;
class BluetoothChannelMac;

// Implements the BluetoothSocket class for the macOS platform.
class BluetoothSocketMac : public BluetoothSocket {
 public:
  static scoped_refptr<BluetoothSocketMac> CreateSocket();

  BluetoothSocketMac(const BluetoothSocketMac&) = delete;
  BluetoothSocketMac& operator=(const BluetoothSocketMac&) = delete;

  // Connects this socket to the service on |device| published as UUID |uuid|.
  // The underlying protocol and PSM or Channel is obtained through service
  // discovery. On a successful connection, the socket properties will be
  // updated and |success_callback| called. On failure, |error_callback| will be
  // called with a message explaining the cause of failure.
  void Connect(IOBluetoothDevice* device,
               const BluetoothUUID& uuid,
               base::OnceClosure success_callback,
               ErrorCompletionCallback error_callback);

  // Listens for incoming RFCOMM connections using this socket: Publishes an
  // RFCOMM service on the |adapter| as UUID |uuid| with Channel
  // |options.channel|, or an automatically allocated Channel if
  // |options.channel| is left null. The service is published with English name
  // |options.name| if that is non-null. |success_callback| will be called if
  // the service is successfully registered, |error_callback| on failure with a
  // message explaining the cause.
  void ListenUsingRfcomm(scoped_refptr<BluetoothAdapterMac> adapter,
                         const BluetoothUUID& uuid,
                         const BluetoothAdapter::ServiceOptions& options,
                         base::OnceClosure success_callback,
                         ErrorCompletionCallback error_callback);

  // Listens for incoming L2CAP connections using this socket: Publishes an
  // L2CAP service on the |adapter| as UUID |uuid| with PSM |options.psm|, or an
  // automatically allocated PSM if |options.psm| is left null. The service is
  // published with English name |options.name| if that is non-null.
  // |success_callback| will be called if the service is successfully
  // registered, |error_callback| on failure with a message explaining the
  // cause.
  void ListenUsingL2cap(scoped_refptr<BluetoothAdapterMac> adapter,
                        const BluetoothUUID& uuid,
                        const BluetoothAdapter::ServiceOptions& options,
                        base::OnceClosure success_callback,
                        ErrorCompletionCallback error_callback);

  // BluetoothSocket:
  void Disconnect(base::OnceClosure callback) override;
  void Receive(int /* buffer_size */,
               ReceiveCompletionCallback success_callback,
               ReceiveErrorCompletionCallback error_callback) override;
  void Send(scoped_refptr<net::IOBuffer> buffer,
            int buffer_size,
            SendCompletionCallback success_callback,
            ErrorCompletionCallback error_callback) override;
  void Accept(AcceptCompletionCallback success_callback,
              ErrorCompletionCallback error_callback) override;

  // Callback that is invoked when the OS completes an SDP query.
  // |status| is the returned status from the SDP query, |device| is the
  // IOBluetoothDevice for which the query was made. The remaining
  // parameters are those from |Connect()|.
  void OnSDPQueryComplete(IOReturn status,
                          IOBluetoothDevice* device,
                          base::OnceClosure success_callback,
                          ErrorCompletionCallback error_callback);

  // Called by BluetoothRfcommConnectionListener and
  // BluetoothL2capConnectionListener.
  void OnChannelOpened(std::unique_ptr<BluetoothChannelMac> channel);

  // Called by |channel_|.
  // Note: OnChannelOpenComplete might be called before the |channel_| is set.
  void OnChannelOpenComplete(const std::string& device_address,
                             IOReturn status);
  void OnChannelClosed();
  void OnChannelDataReceived(void* data, size_t length);
  void OnChannelWriteComplete(void* refcon, IOReturn status);

  void OnChannelOpeningTimeout();
  void OnSDPQueryTimeout();

 private:
  struct AcceptRequest {
    AcceptRequest();
    ~AcceptRequest();

    AcceptCompletionCallback success_callback;
    ErrorCompletionCallback error_callback;
  };

  struct SendRequest {
    SendRequest();
    ~SendRequest();
    int buffer_size;
    SendCompletionCallback success_callback;
    ErrorCompletionCallback error_callback;
    IOReturn status = kIOReturnSuccess;
    int active_async_writes = 0;
    bool error_signaled = false;
  };

  struct ReceiveCallbacks {
    ReceiveCallbacks();
    ~ReceiveCallbacks();
    ReceiveCompletionCallback success_callback;
    ReceiveErrorCompletionCallback error_callback;
  };

  struct ConnectCallbacks {
    ConnectCallbacks();
    ~ConnectCallbacks();
    base::OnceClosure success_callback;
    ErrorCompletionCallback error_callback;
  };

  BluetoothSocketMac();
  ~BluetoothSocketMac() override;

  // Accepts a single incoming connection.
  void AcceptConnectionRequest();

  void ReleaseChannel();
  void ReleaseListener();

  bool is_connecting() const { return !!connect_callbacks_; }

  // Used to verify that all methods are called on the same thread.
  base::ThreadChecker thread_checker_;

  // Adapter the socket is registered against. This is only present when the
  // socket is listening.
  scoped_refptr<BluetoothAdapterMac> adapter_;

  // UUID of the profile being connected to, or that the socket is listening on.
  device::BluetoothUUID uuid_;

  // Simple helpers that register for OS notifications and forward them to
  // |this| profile.
  BluetoothRfcommConnectionListener* __strong rfcomm_connection_listener_;
  BluetoothL2capConnectionListener* __strong l2cap_connection_listener_;
  SDPQueryListener* __strong sdp_query_listener_;

  // The service record registered in the system SDP server, used to
  // eventually unregister the service.
  IOBluetoothSDPServiceRecord* __strong service_record_;

  // The channel used to issue commands.
  std::unique_ptr<BluetoothChannelMac> channel_;

  // Connection callbacks -- when a pending async connection is active.
  std::unique_ptr<ConnectCallbacks> connect_callbacks_;

  // Packets received while there is no pending "receive" callback.
  base::queue<scoped_refptr<net::IOBufferWithSize>> receive_queue_;

  // Receive callbacks -- when a receive call is active.
  std::unique_ptr<ReceiveCallbacks> receive_callbacks_;

  // Send queue -- one entry per pending send operation.
  base::queue<std::unique_ptr<SendRequest>> send_queue_;

  // The pending request to an Accept() call, or null if there is no pending
  // request.
  std::unique_ptr<AcceptRequest> accept_request_;

  // Queue of incoming connections.
  base::queue<std::unique_ptr<BluetoothChannelMac>> accept_queue_;

  // One shot timer for detecting SDP query or channel opening timeout.
  base::OneShotTimer timer_;
};

}  // namespace device

#endif  // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_