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