| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "ClientHandler" |
| |
| #include "host/frontend/webrtc/lib/client_handler.h" |
| |
| #include <vector> |
| |
| #include <json/json.h> |
| #include <json/writer.h> |
| #include <netdb.h> |
| #include <openssl/rand.h> |
| |
| #include <android-base/logging.h> |
| |
| #include "host/frontend/webrtc/lib/keyboard.h" |
| #include "host/frontend/webrtc/lib/utils.h" |
| #include "host/libs/config/cuttlefish_config.h" |
| |
| namespace cuttlefish { |
| namespace webrtc_streaming { |
| |
| namespace { |
| |
| static constexpr auto kInputChannelLabel = "input-channel"; |
| static constexpr auto kAdbChannelLabel = "adb-channel"; |
| static constexpr auto kBluetoothChannelLabel = "bluetooth-channel"; |
| static constexpr auto kCameraDataChannelLabel = "camera-data-channel"; |
| static constexpr auto kCameraDataEof = "EOF"; |
| |
| class CvdCreateSessionDescriptionObserver |
| : public webrtc::CreateSessionDescriptionObserver { |
| public: |
| CvdCreateSessionDescriptionObserver( |
| std::weak_ptr<ClientHandler> client_handler) |
| : client_handler_(client_handler) {} |
| |
| void OnSuccess(webrtc::SessionDescriptionInterface *desc) override { |
| auto client_handler = client_handler_.lock(); |
| if (client_handler) { |
| client_handler->OnCreateSDPSuccess(desc); |
| } |
| } |
| void OnFailure(webrtc::RTCError error) override { |
| auto client_handler = client_handler_.lock(); |
| if (client_handler) { |
| client_handler->OnCreateSDPFailure(error); |
| } |
| } |
| |
| private: |
| std::weak_ptr<ClientHandler> client_handler_; |
| }; |
| |
| class CvdSetSessionDescriptionObserver |
| : public webrtc::SetSessionDescriptionObserver { |
| public: |
| CvdSetSessionDescriptionObserver(std::weak_ptr<ClientHandler> client_handler) |
| : client_handler_(client_handler) {} |
| |
| void OnSuccess() override { |
| // local description set, nothing else to do |
| } |
| void OnFailure(webrtc::RTCError error) override { |
| auto client_handler = client_handler_.lock(); |
| if (client_handler) { |
| client_handler->OnSetSDPFailure(error); |
| } |
| } |
| |
| private: |
| std::weak_ptr<ClientHandler> client_handler_; |
| }; |
| |
| class CvdOnSetRemoteDescription |
| : public webrtc::SetRemoteDescriptionObserverInterface { |
| public: |
| CvdOnSetRemoteDescription( |
| std::function<void(webrtc::RTCError error)> on_error) |
| : on_error_(on_error) {} |
| |
| void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override { |
| on_error_(error); |
| } |
| |
| private: |
| std::function<void(webrtc::RTCError error)> on_error_; |
| }; |
| |
| } // namespace |
| |
| class InputChannelHandler : public webrtc::DataChannelObserver { |
| public: |
| InputChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> input_channel, |
| std::shared_ptr<ConnectionObserver> observer); |
| ~InputChannelHandler() override; |
| |
| void OnStateChange() override; |
| void OnMessage(const webrtc::DataBuffer &msg) override; |
| |
| private: |
| rtc::scoped_refptr<webrtc::DataChannelInterface> input_channel_; |
| std::shared_ptr<ConnectionObserver> observer_; |
| }; |
| |
| class AdbChannelHandler : public webrtc::DataChannelObserver { |
| public: |
| AdbChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> adb_channel, |
| std::shared_ptr<ConnectionObserver> observer); |
| ~AdbChannelHandler() override; |
| |
| void OnStateChange() override; |
| void OnMessage(const webrtc::DataBuffer &msg) override; |
| |
| private: |
| rtc::scoped_refptr<webrtc::DataChannelInterface> adb_channel_; |
| std::shared_ptr<ConnectionObserver> observer_; |
| bool channel_open_reported_ = false; |
| }; |
| |
| class ControlChannelHandler : public webrtc::DataChannelObserver { |
| public: |
| ControlChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> control_channel, |
| std::shared_ptr<ConnectionObserver> observer); |
| ~ControlChannelHandler() override; |
| |
| void OnStateChange() override; |
| void OnMessage(const webrtc::DataBuffer &msg) override; |
| |
| void Send(const Json::Value &message); |
| void Send(const uint8_t *msg, size_t size, bool binary); |
| |
| private: |
| rtc::scoped_refptr<webrtc::DataChannelInterface> control_channel_; |
| std::shared_ptr<ConnectionObserver> observer_; |
| }; |
| |
| class BluetoothChannelHandler : public webrtc::DataChannelObserver { |
| public: |
| BluetoothChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel, |
| std::shared_ptr<ConnectionObserver> observer); |
| ~BluetoothChannelHandler() override; |
| |
| void OnStateChange() override; |
| void OnMessage(const webrtc::DataBuffer &msg) override; |
| |
| private: |
| rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel_; |
| std::shared_ptr<ConnectionObserver> observer_; |
| bool channel_open_reported_ = false; |
| }; |
| |
| class CameraChannelHandler : public webrtc::DataChannelObserver { |
| public: |
| CameraChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel, |
| std::shared_ptr<ConnectionObserver> observer); |
| ~CameraChannelHandler() override; |
| |
| void OnStateChange() override; |
| void OnMessage(const webrtc::DataBuffer &msg) override; |
| |
| private: |
| rtc::scoped_refptr<webrtc::DataChannelInterface> camera_channel_; |
| std::shared_ptr<ConnectionObserver> observer_; |
| std::vector<char> receive_buffer_; |
| }; |
| |
| InputChannelHandler::InputChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> input_channel, |
| std::shared_ptr<ConnectionObserver> observer) |
| : input_channel_(input_channel), observer_(observer) { |
| input_channel->RegisterObserver(this); |
| } |
| |
| InputChannelHandler::~InputChannelHandler() { |
| input_channel_->UnregisterObserver(); |
| } |
| |
| void InputChannelHandler::OnStateChange() { |
| LOG(VERBOSE) << "Input channel state changed to " |
| << webrtc::DataChannelInterface::DataStateString( |
| input_channel_->state()); |
| } |
| |
| void InputChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { |
| if (msg.binary) { |
| // TODO (jemoreira) consider binary protocol to avoid JSON parsing overhead |
| LOG(ERROR) << "Received invalid (binary) data on input channel"; |
| return; |
| } |
| auto size = msg.size(); |
| |
| Json::Value evt; |
| Json::CharReaderBuilder builder; |
| std::unique_ptr<Json::CharReader> json_reader(builder.newCharReader()); |
| std::string errorMessage; |
| auto str = msg.data.cdata<char>(); |
| if (!json_reader->parse(str, str + size, &evt, &errorMessage) < 0) { |
| LOG(ERROR) << "Received invalid JSON object over input channel: " |
| << errorMessage; |
| return; |
| } |
| if (!evt.isMember("type") || !evt["type"].isString()) { |
| LOG(ERROR) << "Input event doesn't have a valid 'type' field: " |
| << evt.toStyledString(); |
| return; |
| } |
| auto event_type = evt["type"].asString(); |
| if (event_type == "mouse") { |
| auto result = |
| ValidationResult::ValidateJsonObject(evt, "mouse", |
| {{"down", Json::ValueType::intValue}, |
| {"x", Json::ValueType::intValue}, |
| {"y", Json::ValueType::intValue}, |
| {"display_label", Json::ValueType::stringValue}}); |
| if (!result.ok()) { |
| LOG(ERROR) << result.error(); |
| return; |
| } |
| auto label = evt["display_label"].asString(); |
| int32_t down = evt["down"].asInt(); |
| int32_t x = evt["x"].asInt(); |
| int32_t y = evt["y"].asInt(); |
| |
| observer_->OnTouchEvent(label, x, y, down); |
| } else if (event_type == "multi-touch") { |
| auto result = |
| ValidationResult::ValidateJsonObject(evt, "multi-touch", |
| {{"id", Json::ValueType::arrayValue}, |
| {"down", Json::ValueType::intValue}, |
| {"x", Json::ValueType::arrayValue}, |
| {"y", Json::ValueType::arrayValue}, |
| {"slot", Json::ValueType::arrayValue}, |
| {"display_label", Json::ValueType::stringValue}}); |
| if (!result.ok()) { |
| LOG(ERROR) << result.error(); |
| return; |
| } |
| |
| auto label = evt["display_label"].asString(); |
| auto idArr = evt["id"]; |
| int32_t down = evt["down"].asInt(); |
| auto xArr = evt["x"]; |
| auto yArr = evt["y"]; |
| auto slotArr = evt["slot"]; |
| int size = evt["id"].size(); |
| |
| observer_->OnMultiTouchEvent(label, idArr, slotArr, xArr, yArr, down, size); |
| } else if (event_type == "keyboard") { |
| auto result = |
| ValidationResult::ValidateJsonObject(evt, "keyboard", |
| {{"event_type", Json::ValueType::stringValue}, |
| {"keycode", Json::ValueType::stringValue}}); |
| if (!result.ok()) { |
| LOG(ERROR) << result.error(); |
| return; |
| } |
| auto down = evt["event_type"].asString() == std::string("keydown"); |
| auto code = DomKeyCodeToLinux(evt["keycode"].asString()); |
| observer_->OnKeyboardEvent(code, down); |
| } else { |
| LOG(ERROR) << "Unrecognized event type: " << event_type; |
| return; |
| } |
| } |
| |
| AdbChannelHandler::AdbChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> adb_channel, |
| std::shared_ptr<ConnectionObserver> observer) |
| : adb_channel_(adb_channel), observer_(observer) { |
| adb_channel->RegisterObserver(this); |
| } |
| |
| AdbChannelHandler::~AdbChannelHandler() { adb_channel_->UnregisterObserver(); } |
| |
| void AdbChannelHandler::OnStateChange() { |
| LOG(VERBOSE) << "Adb channel state changed to " |
| << webrtc::DataChannelInterface::DataStateString( |
| adb_channel_->state()); |
| } |
| |
| void AdbChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { |
| // Report the adb channel as open on the first message received instead of at |
| // channel open, this avoids unnecessarily connecting to the adb daemon for |
| // clients that don't use ADB. |
| if (!channel_open_reported_) { |
| observer_->OnAdbChannelOpen([this](const uint8_t *msg, size_t size) { |
| webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(msg, size), |
| true /*binary*/); |
| // TODO (b/185832105): When the SCTP channel is congested data channel |
| // messages are buffered up to 16MB, when the buffer is full the channel |
| // is abruptly closed. Keep track of the buffered data to avoid losing the |
| // adb data channel. |
| adb_channel_->Send(buffer); |
| return true; |
| }); |
| channel_open_reported_ = true; |
| } |
| observer_->OnAdbMessage(msg.data.cdata(), msg.size()); |
| } |
| |
| ControlChannelHandler::ControlChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> control_channel, |
| std::shared_ptr<ConnectionObserver> observer) |
| : control_channel_(control_channel), observer_(observer) { |
| control_channel->RegisterObserver(this); |
| observer_->OnControlChannelOpen([this](const Json::Value& message) { |
| this->Send(message); |
| return true; |
| }); |
| } |
| |
| ControlChannelHandler::~ControlChannelHandler() { |
| control_channel_->UnregisterObserver(); |
| } |
| |
| void ControlChannelHandler::OnStateChange() { |
| LOG(VERBOSE) << "Control channel state changed to " |
| << webrtc::DataChannelInterface::DataStateString( |
| control_channel_->state()); |
| } |
| |
| void ControlChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { |
| observer_->OnControlMessage(msg.data.cdata(), msg.size()); |
| } |
| |
| void ControlChannelHandler::Send(const Json::Value& message) { |
| Json::StreamWriterBuilder factory; |
| std::string message_string = Json::writeString(factory, message); |
| Send(reinterpret_cast<const uint8_t*>(message_string.c_str()), |
| message_string.size(), /*binary=*/false); |
| } |
| |
| void ControlChannelHandler::Send(const uint8_t *msg, size_t size, bool binary) { |
| webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(msg, size), binary); |
| control_channel_->Send(buffer); |
| } |
| |
| BluetoothChannelHandler::BluetoothChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel, |
| std::shared_ptr<ConnectionObserver> observer) |
| : bluetooth_channel_(bluetooth_channel), observer_(observer) { |
| bluetooth_channel_->RegisterObserver(this); |
| } |
| |
| BluetoothChannelHandler::~BluetoothChannelHandler() { |
| bluetooth_channel_->UnregisterObserver(); |
| } |
| |
| void BluetoothChannelHandler::OnStateChange() { |
| LOG(VERBOSE) << "Bluetooth channel state changed to " |
| << webrtc::DataChannelInterface::DataStateString( |
| bluetooth_channel_->state()); |
| } |
| |
| void BluetoothChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { |
| // Notify bluetooth channel opening when actually using the channel, |
| // it has the same reason with AdbChannelHandler::OnMessage, |
| // to avoid unnecessarily connection for Rootcanal. |
| if (channel_open_reported_ == false) { |
| channel_open_reported_ = true; |
| observer_->OnBluetoothChannelOpen([this](const uint8_t *msg, size_t size) { |
| webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(msg, size), |
| true /*binary*/); |
| // TODO (b/185832105): When the SCTP channel is congested data channel |
| // messages are buffered up to 16MB, when the buffer is full the channel |
| // is abruptly closed. Keep track of the buffered data to avoid losing the |
| // adb data channel. |
| bluetooth_channel_->Send(buffer); |
| return true; |
| }); |
| } |
| |
| observer_->OnBluetoothMessage(msg.data.cdata(), msg.size()); |
| } |
| |
| CameraChannelHandler::CameraChannelHandler( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> camera_channel, |
| std::shared_ptr<ConnectionObserver> observer) |
| : camera_channel_(camera_channel), observer_(observer) { |
| camera_channel_->RegisterObserver(this); |
| } |
| |
| CameraChannelHandler::~CameraChannelHandler() { |
| camera_channel_->UnregisterObserver(); |
| } |
| |
| void CameraChannelHandler::OnStateChange() { |
| LOG(VERBOSE) << "Camera channel state changed to " |
| << webrtc::DataChannelInterface::DataStateString( |
| camera_channel_->state()); |
| } |
| |
| void CameraChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { |
| auto msg_data = msg.data.cdata<char>(); |
| if (msg.size() == strlen(kCameraDataEof) && |
| !strncmp(msg_data, kCameraDataEof, msg.size())) { |
| // Send complete buffer to observer on EOF marker |
| observer_->OnCameraData(receive_buffer_); |
| receive_buffer_.clear(); |
| return; |
| } |
| // Otherwise buffer up data |
| receive_buffer_.insert(receive_buffer_.end(), msg_data, |
| msg_data + msg.size()); |
| } |
| |
| std::shared_ptr<ClientHandler> ClientHandler::Create( |
| int client_id, std::shared_ptr<ConnectionObserver> observer, |
| std::function<void(const Json::Value &)> send_to_client_cb, |
| std::function<void(bool)> on_connection_changed_cb) { |
| return std::shared_ptr<ClientHandler>(new ClientHandler( |
| client_id, observer, send_to_client_cb, on_connection_changed_cb)); |
| } |
| |
| ClientHandler::ClientHandler( |
| int client_id, std::shared_ptr<ConnectionObserver> observer, |
| std::function<void(const Json::Value &)> send_to_client_cb, |
| std::function<void(bool)> on_connection_changed_cb) |
| : client_id_(client_id), |
| observer_(observer), |
| send_to_client_(send_to_client_cb), |
| on_connection_changed_cb_(on_connection_changed_cb) {} |
| |
| ClientHandler::~ClientHandler() { |
| for (auto &data_channel : data_channels_) { |
| data_channel->UnregisterObserver(); |
| } |
| } |
| |
| bool ClientHandler::SetPeerConnection( |
| rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection) { |
| peer_connection_ = peer_connection; |
| |
| // libwebrtc configures the video encoder with a start bitrate of just 300kbs |
| // which causes it to drop the first 4 frames it receives. Any value over 2Mbs |
| // will be capped at 2Mbs when passed to the encoder by the peer_connection |
| // object, so we pass the maximum possible value here. |
| webrtc::BitrateSettings bitrate_settings; |
| bitrate_settings.start_bitrate_bps = 2000000; // 2Mbs |
| peer_connection_->SetBitrate(bitrate_settings); |
| // At least one data channel needs to be created on the side that makes the |
| // SDP offer (the device) for data channels to be enabled at all. |
| // This channel is meant to carry control commands from the client. |
| auto control_channel = peer_connection_->CreateDataChannel( |
| "device-control", nullptr /* config */); |
| if (!control_channel) { |
| LOG(ERROR) << "Failed to create control data channel"; |
| return false; |
| } |
| control_handler_.reset(new ControlChannelHandler(control_channel, observer_)); |
| return true; |
| } |
| |
| bool ClientHandler::AddDisplay( |
| rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track, |
| const std::string &label) { |
| // Send each track as part of a different stream with the label as id |
| auto err_or_sender = |
| peer_connection_->AddTrack(video_track, {label} /* stream_id */); |
| if (!err_or_sender.ok()) { |
| LOG(ERROR) << "Failed to add video track to the peer connection"; |
| return false; |
| } |
| // TODO (b/154138394): use the returned sender (err_or_sender.value()) to |
| // remove the display from the connection. |
| return true; |
| } |
| |
| bool ClientHandler::AddAudio( |
| rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track, |
| const std::string &label) { |
| // Send each track as part of a different stream with the label as id |
| auto err_or_sender = |
| peer_connection_->AddTrack(audio_track, {label} /* stream_id */); |
| if (!err_or_sender.ok()) { |
| LOG(ERROR) << "Failed to add video track to the peer connection"; |
| return false; |
| } |
| return true; |
| } |
| |
| webrtc::VideoTrackInterface *ClientHandler::GetCameraStream() const { |
| for (const auto &tranceiver : peer_connection_->GetTransceivers()) { |
| auto track = tranceiver->receiver()->track(); |
| if (track && |
| track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { |
| return static_cast<webrtc::VideoTrackInterface *>(track.get()); |
| } |
| } |
| return nullptr; |
| } |
| |
| void ClientHandler::LogAndReplyError(const std::string &error_msg) const { |
| LOG(ERROR) << error_msg; |
| Json::Value reply; |
| reply["type"] = "error"; |
| reply["error"] = error_msg; |
| send_to_client_(reply); |
| } |
| |
| void ClientHandler::OnCreateSDPSuccess( |
| webrtc::SessionDescriptionInterface *desc) { |
| std::string offer_str; |
| desc->ToString(&offer_str); |
| peer_connection_->SetLocalDescription( |
| // The peer connection wraps this raw pointer with a scoped_refptr, so |
| // it's guaranteed to be deleted at some point |
| new rtc::RefCountedObject<CvdSetSessionDescriptionObserver>( |
| weak_from_this()), |
| desc); |
| // The peer connection takes ownership of the description so it should not be |
| // used after this |
| desc = nullptr; |
| |
| Json::Value reply; |
| reply["type"] = "offer"; |
| reply["sdp"] = offer_str; |
| |
| state_ = State::kAwaitingAnswer; |
| send_to_client_(reply); |
| } |
| |
| void ClientHandler::OnCreateSDPFailure(webrtc::RTCError error) { |
| state_ = State::kFailed; |
| LogAndReplyError(error.message()); |
| Close(); |
| } |
| |
| void ClientHandler::OnSetSDPFailure(webrtc::RTCError error) { |
| state_ = State::kFailed; |
| LogAndReplyError(error.message()); |
| LOG(ERROR) << "Error setting local description: Either there is a bug in " |
| "libwebrtc or the local description was (incorrectly) modified " |
| "after creating it"; |
| Close(); |
| } |
| |
| void ClientHandler::HandleMessage(const Json::Value &message) { |
| { |
| auto result = ValidationResult::ValidateJsonObject(message, "", |
| {{"type", Json::ValueType::stringValue}}); |
| if (!result.ok()) { |
| LogAndReplyError(result.error()); |
| return; |
| } |
| } |
| auto type = message["type"].asString(); |
| if (type == "request-offer") { |
| // Can't check for state being different that kNew because renegotiation can |
| // start in any state after the answer is returned. |
| if (state_ == State::kCreatingOffer) { |
| // An offer has been requested already |
| LogAndReplyError("Multiple requests for offer received from single client"); |
| return; |
| } |
| state_ = State::kCreatingOffer; |
| peer_connection_->CreateOffer( |
| // No memory leak here because this is a ref counted objects and the |
| // peer connection immediately wraps it with a scoped_refptr |
| new rtc::RefCountedObject<CvdCreateSessionDescriptionObserver>( |
| weak_from_this()), |
| webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); |
| // The created offer wil be sent to the client on |
| // OnSuccess(webrtc::SessionDescriptionInterface* desc) |
| } else if (type == "answer") { |
| if (state_ != State::kAwaitingAnswer) { |
| LogAndReplyError("Received unexpected SDP answer"); |
| return; |
| } |
| auto result = ValidationResult::ValidateJsonObject(message, type, |
| {{"sdp", Json::ValueType::stringValue}}); |
| if (!result.ok()) { |
| LogAndReplyError(result.error()); |
| return; |
| } |
| auto remote_desc_str = message["sdp"].asString(); |
| auto remote_desc = webrtc::CreateSessionDescription( |
| webrtc::SdpType::kAnswer, remote_desc_str, nullptr /*error*/); |
| if (!remote_desc) { |
| LogAndReplyError("Failed to parse answer."); |
| return; |
| } |
| rtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface> observer( |
| new rtc::RefCountedObject<CvdOnSetRemoteDescription>( |
| [this](webrtc::RTCError error) { |
| if (!error.ok()) { |
| LogAndReplyError(error.message()); |
| // The remote description was rejected, this client can't be |
| // trusted anymore. |
| Close(); |
| } |
| })); |
| peer_connection_->SetRemoteDescription(std::move(remote_desc), observer); |
| state_ = State::kConnecting; |
| |
| } else if (type == "ice-candidate") { |
| { |
| auto result = ValidationResult::ValidateJsonObject( |
| message, type, {{"candidate", Json::ValueType::objectValue}}); |
| if (!result.ok()) { |
| LogAndReplyError(result.error()); |
| return; |
| } |
| } |
| auto candidate_json = message["candidate"]; |
| { |
| auto result = |
| ValidationResult::ValidateJsonObject(candidate_json, |
| "ice-candidate/candidate", |
| { |
| {"sdpMid", Json::ValueType::stringValue}, |
| {"candidate", Json::ValueType::stringValue}, |
| {"sdpMLineIndex", Json::ValueType::intValue}, |
| }); |
| if (!result.ok()) { |
| LogAndReplyError(result.error()); |
| return; |
| } |
| } |
| auto mid = candidate_json["sdpMid"].asString(); |
| auto candidate_sdp = candidate_json["candidate"].asString(); |
| auto line_index = candidate_json["sdpMLineIndex"].asInt(); |
| |
| std::unique_ptr<webrtc::IceCandidateInterface> candidate( |
| webrtc::CreateIceCandidate(mid, line_index, candidate_sdp, |
| nullptr /*error*/)); |
| if (!candidate) { |
| LogAndReplyError("Failed to parse ICE candidate"); |
| return; |
| } |
| peer_connection_->AddIceCandidate(std::move(candidate), |
| [this](webrtc::RTCError error) { |
| if (!error.ok()) { |
| LogAndReplyError(error.message()); |
| } |
| }); |
| } else { |
| LogAndReplyError("Unknown client message type: " + type); |
| return; |
| } |
| } |
| |
| void ClientHandler::Close() { |
| // We can't simply call peer_connection_->Close() here because this method |
| // could be called from one of the PeerConnectionObserver callbacks and that |
| // would lead to a deadlock (Close eventually tries to destroy an object that |
| // will then wait for the callback to return -> deadlock). Destroying the |
| // peer_connection_ has the same effect. The only alternative is to postpone |
| // that operation until after the callback returns. |
| on_connection_changed_cb_(false); |
| } |
| |
| void ClientHandler::OnConnectionChange( |
| webrtc::PeerConnectionInterface::PeerConnectionState new_state) { |
| switch (new_state) { |
| case webrtc::PeerConnectionInterface::PeerConnectionState::kNew: |
| break; |
| case webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting: |
| break; |
| case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected: |
| LOG(VERBOSE) << "Client " << client_id_ << ": WebRTC connected"; |
| state_ = State::kConnected; |
| observer_->OnConnected( |
| [this](const uint8_t *msg, size_t size, bool binary) { |
| control_handler_->Send(msg, size, binary); |
| return true; |
| }); |
| on_connection_changed_cb_(true); |
| break; |
| case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: |
| LOG(VERBOSE) << "Client " << client_id_ << ": Connection disconnected"; |
| Close(); |
| break; |
| case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed: |
| LOG(ERROR) << "Client " << client_id_ << ": Connection failed"; |
| Close(); |
| break; |
| case webrtc::PeerConnectionInterface::PeerConnectionState::kClosed: |
| LOG(VERBOSE) << "Client " << client_id_ << ": Connection closed"; |
| Close(); |
| break; |
| } |
| } |
| |
| void ClientHandler::OnIceCandidate( |
| const webrtc::IceCandidateInterface *candidate) { |
| std::string candidate_sdp; |
| candidate->ToString(&candidate_sdp); |
| auto sdp_mid = candidate->sdp_mid(); |
| auto line_index = candidate->sdp_mline_index(); |
| |
| Json::Value reply; |
| reply["type"] = "ice-candidate"; |
| reply["mid"] = sdp_mid; |
| reply["mLineIndex"] = static_cast<Json::UInt64>(line_index); |
| reply["candidate"] = candidate_sdp; |
| |
| send_to_client_(reply); |
| } |
| |
| void ClientHandler::OnDataChannel( |
| rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) { |
| auto label = data_channel->label(); |
| if (label == kInputChannelLabel) { |
| input_handler_.reset(new InputChannelHandler(data_channel, observer_)); |
| } else if (label == kAdbChannelLabel) { |
| adb_handler_.reset(new AdbChannelHandler(data_channel, observer_)); |
| } else if (label == kBluetoothChannelLabel) { |
| bluetooth_handler_.reset( |
| new BluetoothChannelHandler(data_channel, observer_)); |
| } else if (label == kCameraDataChannelLabel) { |
| camera_data_handler_.reset( |
| new CameraChannelHandler(data_channel, observer_)); |
| } else { |
| LOG(VERBOSE) << "Data channel connected: " << label; |
| data_channels_.push_back(data_channel); |
| } |
| } |
| |
| void ClientHandler::OnRenegotiationNeeded() { |
| state_ = State::kNew; |
| LOG(VERBOSE) << "Client " << client_id_ << " needs renegotiation"; |
| } |
| |
| void ClientHandler::OnIceGatheringChange( |
| webrtc::PeerConnectionInterface::IceGatheringState new_state) { |
| std::string state_str; |
| switch (new_state) { |
| case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringNew: |
| state_str = "NEW"; |
| break; |
| case webrtc::PeerConnectionInterface::IceGatheringState:: |
| kIceGatheringGathering: |
| state_str = "GATHERING"; |
| break; |
| case webrtc::PeerConnectionInterface::IceGatheringState:: |
| kIceGatheringComplete: |
| state_str = "COMPLETE"; |
| break; |
| default: |
| state_str = "UNKNOWN"; |
| } |
| LOG(VERBOSE) << "Client " << client_id_ |
| << ": ICE Gathering state set to: " << state_str; |
| } |
| |
| void ClientHandler::OnIceCandidateError(const std::string &host_candidate, |
| const std::string &url, int error_code, |
| const std::string &error_text) { |
| LOG(VERBOSE) << "Gathering of an ICE candidate (host candidate: " |
| << host_candidate << ", url: " << url |
| << ") failed: " << error_text; |
| } |
| |
| void ClientHandler::OnIceCandidateError(const std::string &address, int port, |
| const std::string &url, int error_code, |
| const std::string &error_text) { |
| LOG(VERBOSE) << "Gathering of an ICE candidate (address: " << address |
| << ", port: " << port << ", url: " << url |
| << ") failed: " << error_text; |
| } |
| |
| void ClientHandler::OnSignalingChange( |
| webrtc::PeerConnectionInterface::SignalingState new_state) { |
| // ignore |
| } |
| void ClientHandler::OnStandardizedIceConnectionChange( |
| webrtc::PeerConnectionInterface::IceConnectionState new_state) { |
| switch (new_state) { |
| case webrtc::PeerConnectionInterface::kIceConnectionNew: |
| LOG(DEBUG) << "ICE connection state: New"; |
| break; |
| case webrtc::PeerConnectionInterface::kIceConnectionChecking: |
| LOG(DEBUG) << "ICE connection state: Checking"; |
| break; |
| case webrtc::PeerConnectionInterface::kIceConnectionConnected: |
| LOG(DEBUG) << "ICE connection state: Connected"; |
| break; |
| case webrtc::PeerConnectionInterface::kIceConnectionCompleted: |
| LOG(DEBUG) << "ICE connection state: Completed"; |
| break; |
| case webrtc::PeerConnectionInterface::kIceConnectionFailed: |
| state_ = State::kFailed; |
| LOG(DEBUG) << "ICE connection state: Failed"; |
| break; |
| case webrtc::PeerConnectionInterface::kIceConnectionDisconnected: |
| LOG(DEBUG) << "ICE connection state: Disconnected"; |
| break; |
| case webrtc::PeerConnectionInterface::kIceConnectionClosed: |
| LOG(DEBUG) << "ICE connection state: Closed"; |
| break; |
| case webrtc::PeerConnectionInterface::kIceConnectionMax: |
| LOG(DEBUG) << "ICE connection state: Max"; |
| break; |
| } |
| } |
| void ClientHandler::OnIceCandidatesRemoved( |
| const std::vector<cricket::Candidate> &candidates) { |
| // ignore |
| } |
| void ClientHandler::OnTrack( |
| rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) { |
| // ignore |
| } |
| void ClientHandler::OnRemoveTrack( |
| rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) { |
| // ignore |
| } |
| |
| } // namespace webrtc_streaming |
| } // namespace cuttlefish |