| // Copyright 2017 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "midis/client_tracker.h" | |
| #include <sys/socket.h> | |
| #include <sys/stat.h> | |
| #include <sys/un.h> | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include <base/check.h> | |
| #include <base/functional/bind.h> | |
| #include <base/location.h> | |
| #include <base/logging.h> | |
| #include <base/task/single_thread_task_runner.h> | |
| #include <brillo/message_loops/message_loop.h> | |
| #include <mojo/core/embedder/embedder.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/system/invitation.h> | |
| namespace { | |
| constexpr char kMidisPipe[] = "arc-midis-pipe"; | |
| // Implementation of the MidisHost interface. This is used to | |
| // get the actual MidisManager interface which is used by the client to | |
| // communicate with midis. | |
| // A request to initialize this should be initiated by the ArcBridgeHost. | |
| // | |
| // NOTE: It is expected that this class should only be instantiated once | |
| // during the lifetime of the service. An error in the Message Pipe associated | |
| // with this class is most likely an unrecoverable error, and will necessitate | |
| // the restart of the midis service from Chrome. | |
| class MidisHostImpl : public arc::mojom::MidisHost { | |
| public: | |
| // |client_tracker|, which must outlive MidisHostImpl, is owned by the caller. | |
| MidisHostImpl(mojo::PendingReceiver<arc::mojom::MidisHost> receiver, | |
| midis::ClientTracker* client_tracker); | |
| MidisHostImpl(const MidisHostImpl&) = delete; | |
| MidisHostImpl& operator=(const MidisHostImpl&) = delete; | |
| ~MidisHostImpl() override = default; | |
| // mojom::MidisHost: | |
| void Connect(mojo::PendingReceiver<arc::mojom::MidisServer> receiver, | |
| mojo::PendingRemote<arc::mojom::MidisClient> client) override; | |
| private: | |
| // It's safe to hold a raw pointer to ClientTracker since we can assume | |
| // that the lifecycle of ClientTracker is a superset of the lifecycle of | |
| // MidisHostImpl. | |
| midis::ClientTracker* client_tracker_; | |
| mojo::Receiver<arc::mojom::MidisHost> receiver_; | |
| }; | |
| MidisHostImpl::MidisHostImpl( | |
| mojo::PendingReceiver<arc::mojom::MidisHost> receiver, | |
| midis::ClientTracker* client_tracker) | |
| : client_tracker_(client_tracker), receiver_(this, std::move(receiver)) {} | |
| void MidisHostImpl::Connect( | |
| mojo::PendingReceiver<arc::mojom::MidisServer> receiver, | |
| mojo::PendingRemote<arc::mojom::MidisClient> client) { | |
| VLOG(1) << "Connect() called."; | |
| client_tracker_->MakeMojoClient(std::move(receiver), std::move(client)); | |
| } | |
| } // namespace | |
| namespace midis { | |
| void ClientTracker::MakeMojoClient( | |
| mojo::PendingReceiver<arc::mojom::MidisServer> receiver, | |
| mojo::PendingRemote<arc::mojom::MidisClient> client) { | |
| client_id_counter_++; | |
| VLOG(1) << "MakeMojoClient called."; | |
| auto new_cli = std::make_unique<Client>( | |
| device_tracker_, client_id_counter_, | |
| base::BindOnce(&ClientTracker::RemoveClient, weak_factory_.GetWeakPtr()), | |
| std::move(receiver), std::move(client)); | |
| if (new_cli) { | |
| clients_.emplace(client_id_counter_, std::move(new_cli)); | |
| } | |
| } | |
| ClientTracker::ClientTracker() : client_id_counter_(0), weak_factory_(this) {} | |
| ClientTracker::~ClientTracker() { | |
| for (auto& client : clients_) { | |
| device_tracker_->RemoveClientFromDevices(client.first); | |
| } | |
| clients_.clear(); | |
| ipc_support_ = nullptr; | |
| } | |
| void ClientTracker::InitClientTracker() { | |
| VLOG(1) << "Start client Mojo server."; | |
| mojo::core::Init(); | |
| ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>( | |
| base::SingleThreadTaskRunner::GetCurrentDefault(), | |
| mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST); | |
| } | |
| void ClientTracker::RemoveClient(uint32_t client_id) { | |
| // First delete all references to this device. | |
| device_tracker_->RemoveClientFromDevices(client_id); | |
| clients_.erase(client_id); | |
| } | |
| void ClientTracker::AcceptProxyConnection(base::ScopedFD fd) { | |
| DCHECK(sequence_checker_.CalledOnValidSequence()); | |
| mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept( | |
| mojo::PlatformChannelEndpoint(mojo::PlatformHandle(std::move(fd)))); | |
| mojo::ScopedMessagePipeHandle child_pipe = | |
| invitation.ExtractMessagePipe(kMidisPipe); | |
| midis_host_ = std::make_unique<MidisHostImpl>( | |
| mojo::PendingReceiver<arc::mojom::MidisHost>(std::move(child_pipe)), | |
| this); | |
| } | |
| bool ClientTracker::IsProxyConnected() { | |
| return midis_host_ != nullptr; | |
| } | |
| } // namespace midis |