1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/frontend/webrtc/libcommon/utils.h"
18
19 #include <functional>
20 #include <map>
21
22 #include <json/json.h>
23
24 namespace cuttlefish {
25 namespace webrtc_streaming {
26
27 namespace {
28
ValidateField(const Json::Value & obj,const std::string & type,const std::string & field_name,const Json::ValueType & field_type,bool required)29 Result<void> ValidateField(const Json::Value& obj, const std::string& type,
30 const std::string& field_name,
31 const Json::ValueType& field_type, bool required) {
32 CF_EXPECT(obj.isObject(), "Expected object with name-value pairs");
33 if (!obj.isMember(field_name) && !required) {
34 return {};
35 }
36 if (!(obj.isMember(field_name) &&
37 obj[field_name].isConvertibleTo(field_type))) {
38 std::string error_msg = "Expected a field named '";
39 error_msg += field_name + "' of type '";
40 error_msg += std::to_string(field_type);
41 error_msg += "'";
42 if (!type.empty()) {
43 error_msg += " in message of type '" + type + "'";
44 }
45 error_msg += ".";
46 return CF_ERR(error_msg);
47 }
48 return {};
49 }
50
51 template <typename T>
ToArray(const std::vector<T> & vec,std::function<Json::Value (const T &)> to_json)52 Json::Value ToArray(const std::vector<T>& vec,
53 std::function<Json::Value(const T&)> to_json) {
54 Json::Value arr(Json::ValueType::arrayValue);
55 for (const auto& t : vec) {
56 arr.append(to_json(t));
57 }
58 return arr;
59 }
60
61 } // namespace
62
ValidateJsonObject(const Json::Value & obj,const std::string & type,const std::map<std::string,Json::ValueType> & required_fields,const std::map<std::string,Json::ValueType> & optional_fields)63 Result<void> ValidateJsonObject(
64 const Json::Value& obj, const std::string& type,
65 const std::map<std::string, Json::ValueType>& required_fields,
66 const std::map<std::string, Json::ValueType>& optional_fields) {
67 for (const auto& field_spec : required_fields) {
68 CF_EXPECT(
69 ValidateField(obj, type, field_spec.first, field_spec.second, true));
70 }
71 for (const auto& field_spec : optional_fields) {
72 CF_EXPECT(
73 ValidateField(obj, type, field_spec.first, field_spec.second, false));
74 }
75 return {};
76 }
77
78 Result<std::unique_ptr<webrtc::SessionDescriptionInterface>>
ParseSessionDescription(const std::string & type,const Json::Value & message,webrtc::SdpType sdp_type)79 ParseSessionDescription(const std::string& type, const Json::Value& message,
80 webrtc::SdpType sdp_type) {
81 CF_EXPECT(ValidateJsonObject(message, type,
82 {{"sdp", Json::ValueType::stringValue}}));
83 auto remote_desc_str = message["sdp"].asString();
84 auto remote_desc =
85 webrtc::CreateSessionDescription(sdp_type, remote_desc_str);
86 CF_EXPECT(remote_desc.get(), "Failed to parse sdp.");
87 return remote_desc;
88 }
89
ParseIceCandidate(const std::string & type,const Json::Value & message)90 Result<std::unique_ptr<webrtc::IceCandidateInterface>> ParseIceCandidate(
91 const std::string& type, const Json::Value& message) {
92 CF_EXPECT(ValidateJsonObject(message, type,
93 {{"candidate", Json::ValueType::objectValue}}));
94 auto candidate_json = message["candidate"];
95 CF_EXPECT(ValidateJsonObject(candidate_json, "ice-candidate/candidate",
96 {
97 {"sdpMid", Json::ValueType::stringValue},
98 {"candidate", Json::ValueType::stringValue},
99 {"sdpMLineIndex", Json::ValueType::intValue},
100 }));
101 auto mid = candidate_json["sdpMid"].asString();
102 auto candidate_sdp = candidate_json["candidate"].asString();
103 auto line_index = candidate_json["sdpMLineIndex"].asInt();
104
105 auto candidate =
106 std::unique_ptr<webrtc::IceCandidateInterface>(webrtc::CreateIceCandidate(
107 mid, line_index, candidate_sdp, nullptr /*error*/));
108 CF_EXPECT(candidate.get(), "Failed to parse ICE candidate");
109 return candidate;
110 }
111
ParseError(const std::string & type,const Json::Value & message)112 Result<std::string> ParseError(const std::string& type,
113 const Json::Value& message) {
114 CF_EXPECT(ValidateJsonObject(message, type,
115 {{"error", Json::ValueType::stringValue}}));
116 return message["error"].asString();
117 }
118
119 Result<std::vector<webrtc::PeerConnectionInterface::IceServer>>
ParseIceServersMessage(const Json::Value & message)120 ParseIceServersMessage(const Json::Value& message) {
121 std::vector<webrtc::PeerConnectionInterface::IceServer> ret;
122 if (!message.isMember("ice_servers") || !message["ice_servers"].isArray()) {
123 // The ice_servers field is optional in some messages
124 LOG(VERBOSE)
125 << "ice_servers field not present in json object or not an array";
126 return ret;
127 }
128 auto& servers = message["ice_servers"];
129 for (const auto& server : servers) {
130 webrtc::PeerConnectionInterface::IceServer ice_server;
131 CF_EXPECT(server.isMember("urls") && server["urls"].isArray(),
132 "ICE server specification missing urls field or not an array: "
133 << server.toStyledString());
134 auto urls = server["urls"];
135 for (int url_idx = 0; url_idx < urls.size(); url_idx++) {
136 auto url = urls[url_idx];
137 CF_EXPECT(url.isString(), "Non string 'urls' field in ice server: "
138 << url.toStyledString());
139 ice_server.urls.push_back(url.asString());
140 }
141 if (server.isMember("credential") && server["credential"].isString()) {
142 ice_server.password = server["credential"].asString();
143 }
144 if (server.isMember("username") && server["username"].isString()) {
145 ice_server.username = server["username"].asString();
146 }
147 ret.push_back(ice_server);
148 }
149 return ret;
150 }
151
GenerateIceServersMessage(const std::vector<webrtc::PeerConnectionInterface::IceServer> & ice_servers)152 Json::Value GenerateIceServersMessage(
153 const std::vector<webrtc::PeerConnectionInterface::IceServer>&
154 ice_servers) {
155 return ToArray<webrtc::PeerConnectionInterface::IceServer>(
156 ice_servers,
157 [](const webrtc::PeerConnectionInterface::IceServer& ice_server) {
158 Json::Value server;
159 server["urls"] = ToArray<std::string>(
160 ice_server.urls,
161 [](const std::string& url) { return Json::Value(url); });
162 server["credential"] = ice_server.password;
163 server["username"] = ice_server.username;
164 return server;
165 });
166 }
167
168 } // namespace webrtc_streaming
169 } // namespace cuttlefish
170