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

#ifndef SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_RECEIVER_H_
#define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_RECEIVER_H_

#include <memory>

#include "base/component_export.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/mojom/connector.mojom.h"
#include "services/service_manager/public/mojom/service.mojom.h"
#include "services/service_manager/public/mojom/service_control.mojom.h"

namespace service_manager {

class Service;

// Encapsulates service-side bindings to Service Manager interfaces. Namely,
// this helps receive and dispatch Service interface events to a service
// implementation, while also exposing a working Connector interface the service
// can use to make outgoing interface requests.
//
// A ServiceReceiver is considered to be "bound" after |Bind()| is invoked with
// a valid Service receiver (or the equivalent constructor is used -- see
// below). Upon connection error or an explicit call to |Close()|, the
// ServiceReceiver will be considered "unbound" until another call to |Bind()|
// is made.
//
// NOTE: A well-behaved service should aim to always close its ServiceReceiver
// gracefully by calling |RequestClose()|. Closing a ServiceReceiver abruptly
// (by either destroying it or explicitly calling |Close()|) introduces inherent
// flakiness into the system unless the Service's |OnDisconnected()| has already
// been invoked, because otherwise the Service Manager may have in-flight
// interface requests directed at your service instance and these will be
// dropped to the dismay of the service instance which issued them. Exceptions
// can reasonably be made for system-wide shutdown situations where even the
// Service Manager itself will be imminently torn down.
class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceReceiver
    : public mojom::Service {
 public:
  // Creates a new ServiceReceiver bound to |service|. The service will not
  // receive any Service interface calls until |Bind()| is called, but its
  // |connector()| is usable immediately upon construction.
  //
  // |service| is not owned and must outlive this ServiceReceiver.
  explicit ServiceReceiver(service_manager::Service* service);

  // Same as above, but behaves as if |Bind(receiver)| is also called
  // immediately after construction. See below.
  ServiceReceiver(service_manager::Service* service,
                  mojo::PendingReceiver<mojom::Service> receiver);

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

  ~ServiceReceiver() override;

  bool is_bound() const { return receiver_.is_bound(); }

  const Identity& identity() const { return identity_; }

  // Returns a usable Connector which can make outgoing interface requests
  // identifying as the service to which this ServiceReceiver is bound.
  Connector* GetConnector();

  // Binds this ServiceReceiver to a new Service receiver. Once a
  // ServiceReceiver is bound, its target Service will begin receiving Service
  // events. The order of events received is:
  //
  //   - OnStart() exactly once
  //   - OnIdentityKnown() exactly once
  //   - OnBindInterface() zero or more times
  //
  // The target Service will be able to receive these events until this
  // ServiceReceiver is either unbound or destroyed.
  //
  // If |receiver| is invalid, this call does nothing.
  //
  // Must only be called on an unbound ServiceReceiver.
  void Bind(mojo::PendingReceiver<mojom::Service> receiver);

  // Asks the Service Manager nicely if it's OK for this service instance to
  // disappear now. If the Service Manager thinks it's OK, it will sever the
  // binding's connection, ultimately triggering an |OnDisconnected()| call on
  // the bound Service object.
  //
  // Must only be called on a bound ServiceReceiver.
  void RequestClose();

  // Immediately severs the connection to the Service Manager. No further
  // incoming interface requests will be received until this ServiceReceiver is
  // bound again. Always prefer |RequestClose()| under normal circumstances,
  // unless |OnDisconnected()| has already been invoked on the Service. See the
  // note in the class documentation above regarding graceful binding closure.
  //
  // Must only be called on a bound ServiceReceiver.
  void Close();

 private:
  void OnConnectionError();

  // mojom::Service:
  void OnStart(const Identity& identity, OnStartCallback callback) override;
  void OnBindInterface(const BindSourceInfo& source_info,
                       const std::string& interface_name,
                       mojo::ScopedMessagePipeHandle interface_pipe,
                       OnBindInterfaceCallback callback) override;
  void CreatePackagedServiceInstance(
      const Identity& identity,
      mojo::PendingReceiver<mojom::Service> receiver,
      mojo::PendingRemote<mojom::ProcessMetadata> metadata) override;

  // The Service instance to which all incoming events from the Service Manager
  // should be directed. Typically this is the object which owns this
  // ServiceReceiver.
  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
  // #union
  RAW_PTR_EXCLUSION service_manager::Service* const service_;

  // A pending Connector request which will eventually be passed to the Service
  // Manager. Created preemptively by every unbound ServiceReceiver so that
  // |connector()| may begin pipelining outgoing requests even before the
  // ServiceReceiver is bound to a Service receiver.
  mojo::PendingReceiver<mojom::Connector> pending_connector_receiver_;

  mojo::Receiver<mojom::Service> receiver_{this};
  Identity identity_;
  std::unique_ptr<Connector> connector_;

  // This instance's control interface to the service manager. Note that this
  // is unbound and therefore invalid until OnStart() is called.
  mojo::AssociatedRemote<mojom::ServiceControl> service_control_;

  // Tracks whether |RequestClose()| has been called at least once prior to
  // receiving |OnStart()| on a bound ServiceReceiver. This ensures that the
  // closure request is actually issued once |OnStart()| is invoked.
  bool request_closure_on_start_ = false;
};

}  // namespace service_manager

#endif  // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_RECEIVER_H_