1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "cast/common/channel/message_util.h"
6
7 #include <sstream>
8 #include <utility>
9
10 #include "cast/common/channel/virtual_connection.h"
11 #include "util/json/json_serialization.h"
12 #include "util/json/json_value.h"
13 #include "util/osp_logging.h"
14
15 #if defined(__APPLE__) || defined(__MACH__)
16 #include <TargetConditionals.h>
17 #endif
18
19 namespace openscreen {
20 namespace cast {
21 namespace {
22
23 using ::cast::channel::CastMessage;
24
25 // The value used for "sdkType" in a virtual CONNECT request. Historically, this
26 // value was used in Chrome's C++ impl even though "2" refers to the Media
27 // Router Extension.
28 constexpr int kVirtualConnectSdkType = 2;
29
30 // The value used for "connectionType" in a virtual CONNECT request. This value
31 // stands for CONNECTION_TYPE_LOCAL.
32 constexpr int kVirtualConnectTypeLocal = 1;
33
34 // The value to be set as the "platform" value in a virtual CONNECT request.
35 // Source (in Chromium source tree):
36 // src/third_party/metrics_proto/cast_logs.proto
37 enum VirtualConnectPlatformValue {
38 kOtherPlatform = 0,
39 kAndroid = 1,
40 kIOS = 2,
41 kWindows = 3,
42 kMacOSX = 4,
43 kChromeOS = 5,
44 kLinux = 6,
45 kCastDevice = 7,
46 };
47
48 #if defined(__APPLE__) || defined(__MACH__)
GetVirtualConnectPlatformMacFlavor()49 constexpr VirtualConnectPlatformValue GetVirtualConnectPlatformMacFlavor() {
50 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
51 return kIOS;
52 #else
53 return kMacOSX;
54 #endif
55 }
56 #endif
57
GetVirtualConnectPlatform()58 constexpr VirtualConnectPlatformValue GetVirtualConnectPlatform() {
59 // Based on //build/build_config.h in the Chromium project. The order of these
60 // matters!
61 #if defined(__ANDROID__)
62 return kAndroid;
63 #elif defined(__APPLE__) || defined(__MACH__)
64 return GetVirtualConnectPlatformMacFlavor();
65 #elif defined(_WIN32) || defined(_WIN64)
66 return kWindows;
67 #elif defined(OS_CHROMEOS)
68 // Note: OS_CHROMEOS is defined via the compiler's command line in Chromium
69 // embedder builds by Chromium's //build/config/linux:runtime_library config.
70 return kChromeOS;
71 #elif defined(__linux__)
72 return kLinux;
73 #else
74 return kOtherPlatform;
75 #endif
76 }
77
MakeConnectionMessage(const std::string & source_id,const std::string & destination_id)78 CastMessage MakeConnectionMessage(const std::string& source_id,
79 const std::string& destination_id) {
80 CastMessage connect_message;
81 connect_message.set_protocol_version(kDefaultOutgoingMessageVersion);
82 connect_message.set_source_id(source_id);
83 connect_message.set_destination_id(destination_id);
84 connect_message.set_namespace_(kConnectionNamespace);
85 return connect_message;
86 }
87
88 } // namespace
89
ToString(AppAvailabilityResult availability)90 std::string ToString(AppAvailabilityResult availability) {
91 switch (availability) {
92 case AppAvailabilityResult::kAvailable:
93 return "Available";
94 case AppAvailabilityResult::kUnavailable:
95 return "Unavailable";
96 case AppAvailabilityResult::kUnknown:
97 return "Unknown";
98 default:
99 OSP_NOTREACHED();
100 }
101 }
102
MakeSimpleUTF8Message(const std::string & namespace_,std::string payload)103 CastMessage MakeSimpleUTF8Message(const std::string& namespace_,
104 std::string payload) {
105 CastMessage message;
106 message.set_protocol_version(kDefaultOutgoingMessageVersion);
107 message.set_namespace_(namespace_);
108 message.set_payload_type(::cast::channel::CastMessage_PayloadType_STRING);
109 message.set_payload_utf8(std::move(payload));
110 return message;
111 }
112
MakeConnectMessage(const std::string & source_id,const std::string & destination_id)113 CastMessage MakeConnectMessage(const std::string& source_id,
114 const std::string& destination_id) {
115 CastMessage connect_message =
116 MakeConnectionMessage(source_id, destination_id);
117 connect_message.set_payload_type(
118 ::cast::channel::CastMessage_PayloadType_STRING);
119
120 // Historically, the CONNECT message was meant to come from a Chrome browser.
121 // However, this library could be embedded in any app. So, properties like
122 // user agent, application version, etc. are not known here.
123 static constexpr char kUnknownVersion[] = "Unknown (Open Screen)";
124
125 Json::Value message(Json::objectValue);
126 message[kMessageKeyType] = CastMessageTypeToString(CastMessageType::kConnect);
127 for (int i = 0; i <= 3; ++i) {
128 message[kMessageKeyProtocolVersionList][i] =
129 ::cast::channel::CastMessage_ProtocolVersion_CASTV2_1_0 + i;
130 }
131 message[kMessageKeyUserAgent] = kUnknownVersion;
132 message[kMessageKeyConnType] =
133 static_cast<int>(VirtualConnection::Type::kStrong);
134 message[kMessageKeyOrigin] = Json::Value(Json::objectValue);
135
136 Json::Value sender_info(Json::objectValue);
137 sender_info[kMessageKeySdkType] = kVirtualConnectSdkType;
138 sender_info[kMessageKeyVersion] = kUnknownVersion;
139 sender_info[kMessageKeyBrowserVersion] = kUnknownVersion;
140 sender_info[kMessageKeyPlatform] = GetVirtualConnectPlatform();
141 sender_info[kMessageKeyConnectionType] = kVirtualConnectTypeLocal;
142 message[kMessageKeySenderInfo] = std::move(sender_info);
143
144 connect_message.set_payload_utf8(json::Stringify(std::move(message)).value());
145 return connect_message;
146 }
147
MakeCloseMessage(const std::string & source_id,const std::string & destination_id)148 CastMessage MakeCloseMessage(const std::string& source_id,
149 const std::string& destination_id) {
150 CastMessage close_message = MakeConnectionMessage(source_id, destination_id);
151 close_message.set_payload_type(
152 ::cast::channel::CastMessage_PayloadType_STRING);
153 close_message.set_payload_utf8(R"!({"type": "CLOSE"})!");
154 return close_message;
155 }
156
MakeUniqueSessionId(const char * prefix)157 std::string MakeUniqueSessionId(const char* prefix) {
158 static int next_id = 10000;
159
160 std::ostringstream oss;
161 oss << prefix << '-' << (next_id++);
162 return oss.str();
163 }
164
HasType(const Json::Value & object,CastMessageType type)165 bool HasType(const Json::Value& object, CastMessageType type) {
166 OSP_DCHECK(object.isObject());
167 const Json::Value& value =
168 object.get(kMessageKeyType, Json::Value::nullSingleton());
169 return value.isString() && value.asString() == CastMessageTypeToString(type);
170 }
171
172 } // namespace cast
173 } // namespace openscreen
174