1 // Copyright 2014 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.
5 #include "components/cast_channel/cast_message_util.h"
9 #include "base/json/json_writer.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "build/build_config.h"
14 #include "components/cast_channel/cast_auth_util.h"
15 #include "components/cast_channel/proto/cast_channel.pb.h"
19 namespace cast_channel {
23 // String values for CastMessageType enum.
24 constexpr char kCastMessageTypePingString[] = "PING";
25 constexpr char kCastMessageTypePongString[] = "PONG";
26 constexpr char kCastMessageTypeGetAppAvailabilityString[] =
27 "GET_APP_AVAILABILITY";
28 constexpr char kCastMessageTypeReceiverStatusRequestString[] = "GET_STATUS";
29 constexpr char kCastMessageTypeConnectString[] = "CONNECT";
30 constexpr char kCastMessageTypeCloseConnectionString[] = "CLOSE";
31 constexpr char kCastMessageTypeBroadcastString[] = "APPLICATION_BROADCAST";
32 constexpr char kCastMessageTypeLaunchString[] = "LAUNCH";
33 constexpr char kCastMessageTypeStopString[] = "STOP";
34 constexpr char kCastMessageTypeReceiverStatusString[] = "RECEIVER_STATUS";
35 constexpr char kCastMessageTypeMediaStatusString[] = "MEDIA_STATUS";
36 constexpr char kCastMessageTypeLaunchErrorString[] = "LAUNCH_ERROR";
38 // String values for V2MessageType enum.
39 constexpr char kV2MessageTypeEditTracksInfoString[] = "EDIT_TRACKS_INFO";
40 constexpr char kV2MessageTypeGetStatusString[] = "GET_STATUS";
41 constexpr char kV2MessageTypeLoadString[] = "LOAD";
42 constexpr char kV2MessageTypeMediaGetStatusString[] = "MEDIA_GET_STATUS";
43 constexpr char kV2MessageTypeMediaSetVolumeString[] = "MEDIA_SET_VOLUME";
44 constexpr char kV2MessageTypePauseString[] = "PAUSE";
45 constexpr char kV2MessageTypePlayString[] = "PLAY";
46 constexpr char kV2MessageTypePrecacheString[] = "PRECACHE";
47 constexpr char kV2MessageTypeQueueInsertString[] = "QUEUE_INSERT";
48 constexpr char kV2MessageTypeQueueLoadString[] = "QUEUE_LOAD";
49 constexpr char kV2MessageTypeQueueRemoveString[] = "QUEUE_REMOVE";
50 constexpr char kV2MessageTypeQueueReorderString[] = "QUEUE_REORDER";
51 constexpr char kV2MessageTypeQueueUpdateString[] = "QUEUE_UPDATE";
52 constexpr char kV2MessageTypeSeekString[] = "SEEK";
53 constexpr char kV2MessageTypeSetVolumeString[] = "SET_VOLUME";
54 constexpr char kV2MessageTypeStopString[] = "STOP";
55 constexpr char kV2MessageTypeStopMediaString[] = "STOP_MEDIA";
57 // String values for GetAppAvailabilityResult enum.
58 constexpr char kGetAppAvailabilityResultAvailableString[] = "APP_AVAILABLE";
59 constexpr char kGetAppAvailabilityResultUnavailableString[] = "APP_UNAVAILABLE";
61 // The value used for "sdkType" in a virtual connect request. Historically, this
62 // value is used in the Media Router extension, but here it is reused in Chrome.
63 constexpr int kVirtualConnectSdkType = 2;
65 // The value used for "connectionType" in a virtual connect request. This value
66 // stands for CONNECTION_TYPE_LOCAL, which is the only type used in Chrome.
67 constexpr int kVirtualConnectTypeLocal = 1;
69 void FillCommonCastMessageFields(CastMessage* message,
70 const std::string& source_id,
71 const std::string& destination_id,
72 const std::string& message_namespace) {
73 message->set_protocol_version(CastMessage::CASTV2_1_0);
74 message->set_source_id(source_id);
75 message->set_destination_id(destination_id);
76 message->set_namespace_(message_namespace);
79 CastMessage CreateKeepAliveMessage(base::StringPiece keep_alive_type) {
80 base::Value type_dict(base::Value::Type::DICTIONARY);
81 type_dict.SetKey("type", base::Value(keep_alive_type));
82 return CreateCastMessage(kHeartbeatNamespace, type_dict, kPlatformSenderId,
86 // Returns the value to be set as the "platform" value in a virtual connect
87 // request. The value is platform-dependent and is taken from the Platform enum
88 // defined in third_party/metrics_proto/cast_logs.proto.
89 int GetVirtualConnectPlatformValue() {
92 #elif defined(OS_MACOSX)
94 #elif defined(OS_CHROMEOS)
96 #elif defined(OS_LINUX)
103 // Maps from from API-internal message types to "real" message types from the
104 // Cast V2 protocol. This is necessary because the protocol defines messages
105 // with the same type in different namespaces, and the namespace is lost when
106 // messages are passed using a CastInternalMessage object.
107 std::string GetRemappedMediaRequestType(const std::string& v2_message_type) {
108 V2MessageType type = V2MessageTypeFromString(v2_message_type);
109 DCHECK(IsMediaRequestMessageType(type));
112 case V2MessageType::kStopMedia:
113 result = ToString(V2MessageType::kStop);
115 case V2MessageType::kMediaSetVolume:
116 result = ToString(V2MessageType::kSetVolume);
118 case V2MessageType::kMediaGetStatus:
119 result = ToString(V2MessageType::kGetStatus);
122 return v2_message_type;
130 bool IsCastMessageValid(const CastMessage& message_proto) {
131 if (!message_proto.IsInitialized())
134 if (message_proto.namespace_().empty() || message_proto.source_id().empty() ||
135 message_proto.destination_id().empty()) {
138 return (message_proto.payload_type() == CastMessage_PayloadType_STRING &&
139 message_proto.has_payload_utf8()) ||
140 (message_proto.payload_type() == CastMessage_PayloadType_BINARY &&
141 message_proto.has_payload_binary());
144 bool IsCastInternalNamespace(const std::string& message_namespace) {
145 // Note: any namespace with the prefix is assumed to be reserved for internal
147 return base::StartsWith(message_namespace, kCastInternalNamespacePrefix,
148 base::CompareCase::SENSITIVE);
151 CastMessageType ParseMessageTypeFromPayload(const base::Value& payload) {
152 const Value* type_string = payload.FindKeyOfType("type", Value::Type::STRING);
153 return type_string ? CastMessageTypeFromString(type_string->GetString())
154 : CastMessageType::kOther;
157 const char* ToString(CastMessageType message_type) {
158 switch (message_type) {
159 case CastMessageType::kPing:
160 return kCastMessageTypePingString;
161 case CastMessageType::kPong:
162 return kCastMessageTypePongString;
163 case CastMessageType::kGetAppAvailability:
164 return kCastMessageTypeGetAppAvailabilityString;
165 case CastMessageType::kReceiverStatusRequest:
166 return kCastMessageTypeReceiverStatusRequestString;
167 case CastMessageType::kConnect:
168 return kCastMessageTypeConnectString;
169 case CastMessageType::kCloseConnection:
170 return kCastMessageTypeCloseConnectionString;
171 case CastMessageType::kBroadcast:
172 return kCastMessageTypeBroadcastString;
173 case CastMessageType::kLaunch:
174 return kCastMessageTypeLaunchString;
175 case CastMessageType::kStop:
176 return kCastMessageTypeStopString;
177 case CastMessageType::kReceiverStatus:
178 return kCastMessageTypeReceiverStatusString;
179 case CastMessageType::kMediaStatus:
180 return kCastMessageTypeMediaStatusString;
181 case CastMessageType::kLaunchError:
182 return kCastMessageTypeLaunchErrorString;
188 const char* ToString(V2MessageType message_type) {
189 switch (message_type) {
190 case V2MessageType::kEditTracksInfo:
191 return kV2MessageTypeEditTracksInfoString;
192 case V2MessageType::kGetStatus:
193 return kV2MessageTypeGetStatusString;
194 case V2MessageType::kLoad:
195 return kV2MessageTypeLoadString;
196 case V2MessageType::kMediaGetStatus:
197 return kV2MessageTypeMediaGetStatusString;
198 case V2MessageType::kMediaSetVolume:
199 return kV2MessageTypeMediaSetVolumeString;
200 case V2MessageType::kPause:
201 return kV2MessageTypePauseString;
202 case V2MessageType::kPlay:
203 return kV2MessageTypePlayString;
204 case V2MessageType::kPrecache:
205 return kV2MessageTypePrecacheString;
206 case V2MessageType::kQueueInsert:
207 return kV2MessageTypeQueueInsertString;
208 case V2MessageType::kQueueLoad:
209 return kV2MessageTypeQueueLoadString;
210 case V2MessageType::kQueueRemove:
211 return kV2MessageTypeQueueRemoveString;
212 case V2MessageType::kQueueReorder:
213 return kV2MessageTypeQueueReorderString;
214 case V2MessageType::kQueueUpdate:
215 return kV2MessageTypeQueueUpdateString;
216 case V2MessageType::kSeek:
217 return kV2MessageTypeSeekString;
218 case V2MessageType::kSetVolume:
219 return kV2MessageTypeSetVolumeString;
220 case V2MessageType::kStop:
221 return kV2MessageTypeStopString;
222 case V2MessageType::kStopMedia:
223 return kV2MessageTypeStopMediaString;
229 CastMessageType CastMessageTypeFromString(const std::string& type) {
230 if (type == kCastMessageTypePingString)
231 return CastMessageType::kPing;
232 if (type == kCastMessageTypePongString)
233 return CastMessageType::kPong;
234 if (type == kCastMessageTypeGetAppAvailabilityString)
235 return CastMessageType::kGetAppAvailability;
236 if (type == kCastMessageTypeReceiverStatusRequestString)
237 return CastMessageType::kReceiverStatusRequest;
238 if (type == kCastMessageTypeConnectString)
239 return CastMessageType::kConnect;
240 if (type == kCastMessageTypeCloseConnectionString)
241 return CastMessageType::kCloseConnection;
242 if (type == kCastMessageTypeBroadcastString)
243 return CastMessageType::kBroadcast;
244 if (type == kCastMessageTypeLaunchString)
245 return CastMessageType::kLaunch;
246 if (type == kCastMessageTypeStopString)
247 return CastMessageType::kStop;
248 if (type == kCastMessageTypeReceiverStatusString)
249 return CastMessageType::kReceiverStatus;
250 if (type == kCastMessageTypeMediaStatusString)
251 return CastMessageType::kMediaStatus;
252 if (type == kCastMessageTypeLaunchErrorString)
253 return CastMessageType::kLaunchError;
254 DVLOG(1) << "Unknown message type: " << type;
255 return CastMessageType::kOther;
258 V2MessageType V2MessageTypeFromString(const std::string& type) {
259 if (type == kV2MessageTypeGetStatusString)
260 return V2MessageType::kGetStatus;
261 if (type == kV2MessageTypeLoadString)
262 return V2MessageType::kLoad;
263 if (type == kV2MessageTypeMediaGetStatusString)
264 return V2MessageType::kMediaGetStatus;
265 if (type == kV2MessageTypeMediaSetVolumeString)
266 return V2MessageType::kMediaSetVolume;
267 if (type == kV2MessageTypePauseString)
268 return V2MessageType::kPause;
269 if (type == kV2MessageTypePlayString)
270 return V2MessageType::kPlay;
271 if (type == kV2MessageTypePrecacheString)
272 return V2MessageType::kPrecache;
273 if (type == kV2MessageTypeQueueInsertString)
274 return V2MessageType::kQueueInsert;
275 if (type == kV2MessageTypeQueueLoadString)
276 return V2MessageType::kQueueLoad;
277 if (type == kV2MessageTypeQueueRemoveString)
278 return V2MessageType::kQueueRemove;
279 if (type == kV2MessageTypeQueueReorderString)
280 return V2MessageType::kQueueReorder;
281 if (type == kV2MessageTypeQueueUpdateString)
282 return V2MessageType::kQueueUpdate;
283 if (type == kV2MessageTypeSeekString)
284 return V2MessageType::kSeek;
285 if (type == kV2MessageTypeSetVolumeString)
286 return V2MessageType::kSetVolume;
287 if (type == kV2MessageTypeStopString)
288 return V2MessageType::kStop;
289 if (type == kV2MessageTypeStopMediaString)
290 return V2MessageType::kStopMedia;
292 return V2MessageType::kOther;
295 std::string CastMessageToString(const CastMessage& message_proto) {
296 std::string out("{");
297 out += "namespace = " + message_proto.namespace_();
298 out += ", sourceId = " + message_proto.source_id();
299 out += ", destId = " + message_proto.destination_id();
300 out += ", type = " + base::IntToString(message_proto.payload_type());
301 out += ", str = \"" + message_proto.payload_utf8() + "\"}";
305 std::string AuthMessageToString(const DeviceAuthMessage& message) {
306 std::string out("{");
307 if (message.has_challenge()) {
308 out += "challenge: {}, ";
310 if (message.has_response()) {
311 out += "response: {signature: (";
312 out += base::NumberToString(message.response().signature().length());
313 out += " bytes), certificate: (";
314 out += base::NumberToString(
315 message.response().client_auth_certificate().length());
318 if (message.has_error()) {
320 out += base::IntToString(message.error().error_type());
327 void CreateAuthChallengeMessage(CastMessage* message_proto,
328 const AuthContext& auth_context) {
329 CHECK(message_proto);
330 DeviceAuthMessage auth_message;
332 AuthChallenge* challenge = auth_message.mutable_challenge();
334 challenge->set_sender_nonce(auth_context.nonce());
335 challenge->set_hash_algorithm(SHA256);
337 std::string auth_message_string;
338 auth_message.SerializeToString(&auth_message_string);
340 FillCommonCastMessageFields(message_proto, kPlatformSenderId,
341 kPlatformReceiverId, kAuthNamespace);
342 message_proto->set_payload_type(CastMessage_PayloadType_BINARY);
343 message_proto->set_payload_binary(auth_message_string);
346 bool IsAuthMessage(const CastMessage& message) {
347 return message.namespace_() == kAuthNamespace;
350 bool IsReceiverMessage(const CastMessage& message) {
351 return message.namespace_() == kReceiverNamespace;
354 bool IsPlatformSenderMessage(const CastMessage& message) {
355 return message.destination_id() != cast_channel::kPlatformSenderId;
358 CastMessage CreateKeepAlivePingMessage() {
359 return CreateKeepAliveMessage(kCastMessageTypePingString);
362 CastMessage CreateKeepAlivePongMessage() {
363 return CreateKeepAliveMessage(kCastMessageTypePongString);
366 CastMessage CreateVirtualConnectionRequest(
367 const std::string& source_id,
368 const std::string& destination_id,
369 VirtualConnectionType connection_type,
370 const std::string& user_agent,
371 const std::string& browser_version) {
372 DCHECK(destination_id != kPlatformReceiverId || connection_type == kStrong);
374 // Parse system_version from user agent string. It contains platform, OS and
375 // CPU info and is contained in the first set of parentheses of the user
376 // agent string (e.g., X11; Linux x86_64).
377 std::string system_version;
378 size_t start_index = user_agent.find('(');
379 if (start_index != std::string::npos) {
380 size_t end_index = user_agent.find(')', start_index + 1);
381 if (end_index != std::string::npos) {
383 user_agent.substr(start_index + 1, end_index - start_index - 1);
387 Value dict(Value::Type::DICTIONARY);
388 dict.SetKey("type", Value(kCastMessageTypeConnectString));
389 dict.SetKey("userAgent", Value(user_agent));
390 dict.SetKey("connType", Value(connection_type));
391 dict.SetKey("origin", Value(Value::Type::DICTIONARY));
393 Value sender_info(Value::Type::DICTIONARY);
394 sender_info.SetKey("sdkType", Value(kVirtualConnectSdkType));
395 sender_info.SetKey("version", Value(browser_version));
396 sender_info.SetKey("browserVersion", Value(browser_version));
397 sender_info.SetKey("platform", Value(GetVirtualConnectPlatformValue()));
398 sender_info.SetKey("connectionType", Value(kVirtualConnectTypeLocal));
399 if (!system_version.empty())
400 sender_info.SetKey("systemVersion", Value(system_version));
402 dict.SetKey("senderInfo", std::move(sender_info));
404 return CreateCastMessage(kConnectionNamespace, dict, source_id,
408 CastMessage CreateGetAppAvailabilityRequest(const std::string& source_id,
410 const std::string& app_id) {
411 Value dict(Value::Type::DICTIONARY);
412 dict.SetKey("type", Value(kCastMessageTypeGetAppAvailabilityString));
413 Value app_id_value(Value::Type::LIST);
414 app_id_value.GetList().push_back(Value(app_id));
415 dict.SetKey("appId", std::move(app_id_value));
416 dict.SetKey("requestId", Value(request_id));
418 return CreateCastMessage(kReceiverNamespace, dict, source_id,
419 kPlatformReceiverId);
422 CastMessage CreateReceiverStatusRequest(const std::string& source_id,
424 Value dict(Value::Type::DICTIONARY);
425 dict.SetKey("type", Value(kCastMessageTypeReceiverStatusRequestString));
426 dict.SetKey("requestId", Value(request_id));
427 return CreateCastMessage(kReceiverNamespace, dict, source_id,
428 kPlatformReceiverId);
431 BroadcastRequest::BroadcastRequest(const std::string& broadcast_namespace,
432 const std::string& message)
433 : broadcast_namespace(broadcast_namespace), message(message) {}
434 BroadcastRequest::~BroadcastRequest() = default;
436 bool BroadcastRequest::operator==(const BroadcastRequest& other) const {
437 return broadcast_namespace == other.broadcast_namespace &&
438 message == other.message;
441 CastMessage CreateBroadcastRequest(const std::string& source_id,
443 const std::vector<std::string>& app_ids,
444 const BroadcastRequest& request) {
445 Value dict(Value::Type::DICTIONARY);
446 dict.SetKey("type", Value(kCastMessageTypeBroadcastString));
447 std::vector<Value> app_ids_value;
448 for (const std::string& app_id : app_ids)
449 app_ids_value.push_back(Value(app_id));
451 dict.SetKey("appIds", Value(std::move(app_ids_value)));
452 dict.SetKey("namespace", Value(request.broadcast_namespace));
453 dict.SetKey("message", Value(request.message));
454 return CreateCastMessage(kBroadcastNamespace, dict, source_id,
455 kPlatformReceiverId);
458 CastMessage CreateLaunchRequest(const std::string& source_id,
460 const std::string& app_id,
461 const std::string& locale) {
462 Value dict(Value::Type::DICTIONARY);
463 dict.SetKey("type", Value(kCastMessageTypeLaunchString));
464 dict.SetKey("requestId", Value(request_id));
465 dict.SetKey("appId", Value(app_id));
466 dict.SetKey("language", Value(locale));
468 return CreateCastMessage(kReceiverNamespace, dict, source_id,
469 kPlatformReceiverId);
472 CastMessage CreateStopRequest(const std::string& source_id,
474 const std::string& session_id) {
475 Value dict(Value::Type::DICTIONARY);
476 dict.SetKey("type", Value(kCastMessageTypeStopString));
477 dict.SetKey("requestId", Value(request_id));
478 dict.SetKey("sessionId", Value(session_id));
479 return CreateCastMessage(kReceiverNamespace, dict, source_id,
480 kPlatformReceiverId);
483 CastMessage CreateCastMessage(const std::string& message_namespace,
484 const base::Value& message,
485 const std::string& source_id,
486 const std::string& destination_id) {
488 FillCommonCastMessageFields(&output, source_id, destination_id,
490 output.set_payload_type(
491 CastMessage::PayloadType::CastMessage_PayloadType_STRING);
492 CHECK(base::JSONWriter::Write(message, output.mutable_payload_utf8()));
496 CastMessage CreateMediaRequest(const base::Value& body,
498 const std::string& source_id,
499 const std::string& destination_id) {
500 Value dict = body.Clone();
501 Value* type = dict.FindKeyOfType("type", Value::Type::STRING);
503 dict.SetKey("type", Value(GetRemappedMediaRequestType(type->GetString())));
504 dict.SetKey("requestId", Value(request_id));
505 return CreateCastMessage(kMediaNamespace, dict, source_id, destination_id);
508 CastMessage CreateSetVolumeRequest(const base::Value& body,
510 const std::string& source_id) {
511 DCHECK(body.FindKeyOfType("type", Value::Type::STRING) &&
512 body.FindKeyOfType("type", Value::Type::STRING)->GetString() ==
513 kV2MessageTypeSetVolumeString);
514 Value dict = body.Clone();
515 dict.RemoveKey("sessionId");
516 dict.SetKey("requestId", Value(request_id));
517 return CreateCastMessage(kReceiverNamespace, dict, source_id,
518 kPlatformReceiverId);
521 bool IsMediaRequestMessageType(V2MessageType type) {
523 case V2MessageType::kEditTracksInfo:
524 case V2MessageType::kLoad:
525 case V2MessageType::kMediaGetStatus:
526 case V2MessageType::kMediaSetVolume:
527 case V2MessageType::kPause:
528 case V2MessageType::kPlay:
529 case V2MessageType::kPrecache:
530 case V2MessageType::kQueueInsert:
531 case V2MessageType::kQueueLoad:
532 case V2MessageType::kQueueRemove:
533 case V2MessageType::kQueueReorder:
534 case V2MessageType::kQueueUpdate:
535 case V2MessageType::kSeek:
536 case V2MessageType::kStopMedia:
543 const char* ToString(GetAppAvailabilityResult result) {
545 case GetAppAvailabilityResult::kAvailable:
546 return kGetAppAvailabilityResultAvailableString;
547 case GetAppAvailabilityResult::kUnavailable:
548 return kGetAppAvailabilityResultUnavailableString;
554 base::Optional<int> GetRequestIdFromResponse(const Value& payload) {
555 DCHECK(payload.is_dict());
557 const Value* request_id_value =
558 payload.FindKeyOfType("requestId", Value::Type::INTEGER);
559 if (!request_id_value)
560 return base::nullopt;
561 return request_id_value->GetInt();
564 GetAppAvailabilityResult GetAppAvailabilityResultFromResponse(
565 const Value& payload,
566 const std::string& app_id) {
567 DCHECK(payload.is_dict());
568 const Value* availability_value =
569 payload.FindPathOfType({"availability", app_id}, Value::Type::STRING);
570 if (availability_value) {
571 const std::string& result_str = availability_value->GetString();
572 if (result_str == kGetAppAvailabilityResultAvailableString)
573 return GetAppAvailabilityResult::kAvailable;
574 if (result_str == kGetAppAvailabilityResultUnavailableString)
575 return GetAppAvailabilityResult::kUnavailable;
577 return GetAppAvailabilityResult::kUnknown;
580 LaunchSessionResponse::LaunchSessionResponse() {}
581 LaunchSessionResponse::LaunchSessionResponse(LaunchSessionResponse&& other) =
583 LaunchSessionResponse::~LaunchSessionResponse() = default;
585 LaunchSessionResponse GetLaunchSessionResponse(const base::Value& payload) {
586 const Value* type_value = payload.FindKeyOfType("type", Value::Type::STRING);
588 return LaunchSessionResponse();
590 CastMessageType type = CastMessageTypeFromString(type_value->GetString());
591 if (type != CastMessageType::kReceiverStatus &&
592 type != CastMessageType::kLaunchError)
593 return LaunchSessionResponse();
595 LaunchSessionResponse response;
596 if (type == CastMessageType::kLaunchError) {
597 response.result = LaunchSessionResponse::Result::kError;
601 const Value* receiver_status =
602 payload.FindKeyOfType("status", Value::Type::DICTIONARY);
603 if (!receiver_status)
604 return LaunchSessionResponse();
606 response.result = LaunchSessionResponse::Result::kOk;
607 response.receiver_status = receiver_status->Clone();
611 } // namespace cast_channel