cast_message_util.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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.
  4. #include "components/cast_channel/cast_message_util.h"
  5. #include <memory>
  6. #include "base/json/json_writer.h"
  7. #include "base/logging.h"
  8. #include "base/strings/string_number_conversions.h"
  9. #include "base/strings/string_piece.h"
  10. #include "base/strings/string_util.h"
  11. #include "build/build_config.h"
  12. #include "build/chromeos_buildflags.h"
  13. #include "components/cast_channel/cast_auth_util.h"
  14. #include "components/cast_channel/enum_table.h"
  15. #include "third_party/openscreen/src/cast/common/channel/proto/cast_channel.pb.h"
  16. using base::Value;
  17. using cast_util::EnumToString;
  18. using cast_util::StringToEnum;
  19. namespace cast_util {
  20. using ::cast::channel::AuthChallenge;
  21. using ::cast::channel::CastMessage;
  22. using cast_channel::CastMessageType;
  23. using cast_channel::GetAppAvailabilityResult;
  24. template <>
  25. const EnumTable<CastMessageType>& EnumTable<CastMessageType>::GetInstance() {
  26. static const EnumTable<CastMessageType> kInstance(
  27. {
  28. {CastMessageType::kPing, "PING"},
  29. {CastMessageType::kPong, "PONG"},
  30. {CastMessageType::kRpc, "RPC"},
  31. {CastMessageType::kGetAppAvailability, "GET_APP_AVAILABILITY"},
  32. {CastMessageType::kGetStatus, "GET_STATUS"},
  33. {CastMessageType::kConnect, "CONNECT"},
  34. {CastMessageType::kCloseConnection, "CLOSE"},
  35. {CastMessageType::kBroadcast, "APPLICATION_BROADCAST"},
  36. {CastMessageType::kLaunch, "LAUNCH"},
  37. {CastMessageType::kStop, "STOP"},
  38. {CastMessageType::kReceiverStatus, "RECEIVER_STATUS"},
  39. {CastMessageType::kMediaStatus, "MEDIA_STATUS"},
  40. {CastMessageType::kLaunchError, "LAUNCH_ERROR"},
  41. {CastMessageType::kOffer, "OFFER"},
  42. {CastMessageType::kAnswer, "ANSWER"},
  43. {CastMessageType::kCapabilitiesResponse, "CAPABILITIES_RESPONSE"},
  44. {CastMessageType::kStatusResponse, "STATUS_RESPONSE"},
  45. {CastMessageType::kMultizoneStatus, "MULTIZONE_STATUS"},
  46. {CastMessageType::kInvalidPlayerState, "INVALID_PLAYER_STATE"},
  47. {CastMessageType::kLoadFailed, "LOAD_FAILED"},
  48. {CastMessageType::kLoadCancelled, "LOAD_CANCELLED"},
  49. {CastMessageType::kInvalidRequest, "INVALID_REQUEST"},
  50. {CastMessageType::kPresentation, "PRESENTATION"},
  51. {CastMessageType::kGetCapabilities, "GET_CAPABILITIES"},
  52. {CastMessageType::kOther},
  53. },
  54. CastMessageType::kMaxValue);
  55. return kInstance;
  56. }
  57. template <>
  58. const EnumTable<cast_channel::V2MessageType>&
  59. EnumTable<cast_channel::V2MessageType>::GetInstance() {
  60. static const EnumTable<cast_channel::V2MessageType> kInstance(
  61. {
  62. {cast_channel::V2MessageType::kEditTracksInfo, "EDIT_TRACKS_INFO"},
  63. {cast_channel::V2MessageType::kGetStatus, "GET_STATUS"},
  64. {cast_channel::V2MessageType::kLoad, "LOAD"},
  65. {cast_channel::V2MessageType::kMediaGetStatus, "MEDIA_GET_STATUS"},
  66. {cast_channel::V2MessageType::kMediaSetVolume, "MEDIA_SET_VOLUME"},
  67. {cast_channel::V2MessageType::kPause, "PAUSE"},
  68. {cast_channel::V2MessageType::kPlay, "PLAY"},
  69. {cast_channel::V2MessageType::kPrecache, "PRECACHE"},
  70. {cast_channel::V2MessageType::kQueueInsert, "QUEUE_INSERT"},
  71. {cast_channel::V2MessageType::kQueueLoad, "QUEUE_LOAD"},
  72. {cast_channel::V2MessageType::kQueueRemove, "QUEUE_REMOVE"},
  73. {cast_channel::V2MessageType::kQueueReorder, "QUEUE_REORDER"},
  74. {cast_channel::V2MessageType::kQueueUpdate, "QUEUE_UPDATE"},
  75. {cast_channel::V2MessageType::kQueueNext, "QUEUE_NEXT"},
  76. {cast_channel::V2MessageType::kQueuePrev, "QUEUE_PREV"},
  77. {cast_channel::V2MessageType::kSeek, "SEEK"},
  78. {cast_channel::V2MessageType::kSetVolume, "SET_VOLUME"},
  79. {cast_channel::V2MessageType::kStop, "STOP"},
  80. {cast_channel::V2MessageType::kStopMedia, "STOP_MEDIA"},
  81. {cast_channel::V2MessageType::kOther},
  82. },
  83. cast_channel::V2MessageType::kMaxValue);
  84. return kInstance;
  85. }
  86. template <>
  87. const EnumTable<GetAppAvailabilityResult>&
  88. EnumTable<GetAppAvailabilityResult>::GetInstance() {
  89. static const EnumTable<GetAppAvailabilityResult> kInstance(
  90. {
  91. {GetAppAvailabilityResult::kAvailable, "APP_AVAILABLE"},
  92. {GetAppAvailabilityResult::kUnavailable, "APP_UNAVAILABLE"},
  93. {GetAppAvailabilityResult::kUnknown},
  94. },
  95. GetAppAvailabilityResult::kMaxValue);
  96. return kInstance;
  97. }
  98. } // namespace cast_util
  99. namespace cast_channel {
  100. namespace {
  101. constexpr base::StringPiece kCastReservedNamespacePrefix =
  102. "urn:x-cast:com.google.cast.";
  103. constexpr const char* kReservedNamespaces[] = {
  104. kAuthNamespace,
  105. kHeartbeatNamespace,
  106. kConnectionNamespace,
  107. kReceiverNamespace,
  108. kBroadcastNamespace,
  109. kMediaNamespace,
  110. // mirroring::mojom::kRemotingNamespace
  111. "urn:x-cast:com.google.cast.remoting",
  112. // mirroring::mojom::kWebRtcNamespace
  113. "urn:x-cast:com.google.cast.webrtc",
  114. };
  115. // The value used for "sdkType" in a virtual connect request. Historically, this
  116. // value is used in the Media Router extension, but here it is reused in Chrome.
  117. constexpr int kVirtualConnectSdkType = 2;
  118. // The value used for "connectionType" in a virtual connect request. This value
  119. // stands for CONNECTION_TYPE_LOCAL, which is the only type used in Chrome.
  120. constexpr int kVirtualConnectTypeLocal = 1;
  121. // The reason code passed to the virtual connection CLOSE message indicating
  122. // that the connection has been gracefully closed by the sender.
  123. constexpr int kVirtualConnectionClosedByPeer = 5;
  124. void FillCommonCastMessageFields(CastMessage* message,
  125. const std::string& source_id,
  126. const std::string& destination_id,
  127. const std::string& message_namespace) {
  128. message->set_protocol_version(CastMessage::CASTV2_1_0);
  129. message->set_source_id(source_id);
  130. message->set_destination_id(destination_id);
  131. message->set_namespace_(message_namespace);
  132. }
  133. CastMessage CreateKeepAliveMessage(base::StringPiece keep_alive_type) {
  134. base::Value::Dict type_dict;
  135. type_dict.Set("type", keep_alive_type);
  136. return CreateCastMessage(kHeartbeatNamespace,
  137. base::Value(std::move(type_dict)), kPlatformSenderId,
  138. kPlatformReceiverId);
  139. }
  140. // Returns the value to be set as the "platform" value in a virtual connect
  141. // request. The value is platform-dependent and is taken from the Platform enum
  142. // defined in third_party/metrics_proto/cast_logs.proto.
  143. int GetVirtualConnectPlatformValue() {
  144. #if BUILDFLAG(IS_WIN)
  145. return 3;
  146. #elif BUILDFLAG(IS_APPLE)
  147. return 4;
  148. #elif BUILDFLAG(IS_CHROMEOS_ASH)
  149. return 5;
  150. #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
  151. return 6;
  152. #else
  153. return 0;
  154. #endif
  155. }
  156. // Maps from from API-internal message types to "real" message types from the
  157. // Cast V2 protocol. This is necessary because the protocol defines messages
  158. // with the same type in different namespaces, and the namespace is lost when
  159. // messages are passed using a CastInternalMessage object.
  160. base::StringPiece GetRemappedMediaRequestType(
  161. base::StringPiece v2_message_type) {
  162. absl::optional<V2MessageType> type =
  163. StringToEnum<V2MessageType>(v2_message_type);
  164. DCHECK(type && IsMediaRequestMessageType(*type));
  165. switch (*type) {
  166. case V2MessageType::kStopMedia:
  167. type = V2MessageType::kStop;
  168. break;
  169. case V2MessageType::kMediaSetVolume:
  170. type = V2MessageType::kSetVolume;
  171. break;
  172. case V2MessageType::kMediaGetStatus:
  173. type = V2MessageType::kGetStatus;
  174. break;
  175. default:
  176. return v2_message_type;
  177. }
  178. return *EnumToString(*type);
  179. }
  180. } // namespace
  181. std::ostream& operator<<(std::ostream& lhs, const CastMessage& rhs) {
  182. lhs << "{";
  183. if (rhs.has_source_id()) {
  184. lhs << "source_id: " << rhs.source_id() << ", ";
  185. }
  186. if (rhs.has_destination_id()) {
  187. lhs << "destination_id: " << rhs.destination_id() << ", ";
  188. }
  189. if (rhs.has_namespace_()) {
  190. lhs << "namespace: " << rhs.namespace_() << ", ";
  191. }
  192. if (rhs.has_payload_utf8()) {
  193. lhs << "payload_utf8: " << rhs.payload_utf8();
  194. }
  195. if (rhs.has_payload_binary()) {
  196. lhs << "payload_binary: (" << rhs.payload_binary().size() << " bytes)";
  197. }
  198. lhs << "}";
  199. return lhs;
  200. }
  201. bool IsCastMessageValid(const CastMessage& message_proto) {
  202. if (!message_proto.IsInitialized())
  203. return false;
  204. if (message_proto.namespace_().empty() || message_proto.source_id().empty() ||
  205. message_proto.destination_id().empty()) {
  206. return false;
  207. }
  208. return (message_proto.payload_type() ==
  209. cast::channel::CastMessage_PayloadType_STRING &&
  210. message_proto.has_payload_utf8()) ||
  211. (message_proto.payload_type() ==
  212. cast::channel::CastMessage_PayloadType_BINARY &&
  213. message_proto.has_payload_binary());
  214. }
  215. bool IsCastReservedNamespace(base::StringPiece message_namespace) {
  216. // Note: Any namespace with the prefix is theoretically reserved for internal
  217. // messages, but there is at least one namespace in widespread use that uses
  218. // the "reserved" prefix for app-level messages, so after matching the main
  219. // prefix, we look for longer prefixes that really need to be reserved.
  220. if (!base::StartsWith(message_namespace, kCastReservedNamespacePrefix))
  221. return false;
  222. const auto prefix_length = kCastReservedNamespacePrefix.length();
  223. for (base::StringPiece reserved_namespace : kReservedNamespaces) {
  224. DCHECK(base::StartsWith(reserved_namespace, kCastReservedNamespacePrefix));
  225. // This comparison skips the first |prefix_length| characters
  226. // because we already know they match.
  227. if (base::StartsWith(message_namespace.substr(prefix_length),
  228. reserved_namespace.substr(prefix_length)) &&
  229. // This condition allows |reserved_namespace| to be equal
  230. // |message_namespace| or be a prefix of it, but if it's a
  231. // prefix, it must be followed by a dot. The subscript is
  232. // never out of bounds because |message_namespace| must be
  233. // at least as long as |reserved_namespace|.
  234. (message_namespace.length() == reserved_namespace.length() ||
  235. message_namespace[reserved_namespace.length()] == '.'))
  236. return true;
  237. }
  238. return false;
  239. }
  240. CastMessageType ParseMessageTypeFromPayload(const base::Value::Dict& payload) {
  241. const std::string* type_string = payload.FindString("type");
  242. return type_string ? CastMessageTypeFromString(*type_string)
  243. : CastMessageType::kOther;
  244. }
  245. // TODO(crbug.com/1291730): Eliminate this function.
  246. const char* ToString(CastMessageType message_type) {
  247. return EnumToString(message_type).value_or("").data();
  248. }
  249. // TODO(crbug.com/1291730): Eliminate this function.
  250. const char* ToString(V2MessageType message_type) {
  251. return EnumToString(message_type).value_or("").data();
  252. }
  253. // TODO(crbug.com/1291730): Eliminate this function.
  254. CastMessageType CastMessageTypeFromString(const std::string& type) {
  255. auto result = StringToEnum<CastMessageType>(type);
  256. DVLOG_IF(1, !result) << "Unknown message type: " << type;
  257. return result.value_or(CastMessageType::kOther);
  258. }
  259. // TODO(crbug.com/1291730): Eliminate this function.
  260. V2MessageType V2MessageTypeFromString(const std::string& type) {
  261. return StringToEnum<V2MessageType>(type).value_or(V2MessageType::kOther);
  262. }
  263. std::string AuthMessageToString(const DeviceAuthMessage& message) {
  264. std::string out("{");
  265. if (message.has_challenge()) {
  266. out += "challenge: {}, ";
  267. }
  268. if (message.has_response()) {
  269. out += "response: {signature: (";
  270. out += base::NumberToString(message.response().signature().length());
  271. out += " bytes), certificate: (";
  272. out += base::NumberToString(
  273. message.response().client_auth_certificate().length());
  274. out += " bytes)}";
  275. }
  276. if (message.has_error()) {
  277. out += ", error: {";
  278. out += base::NumberToString(message.error().error_type());
  279. out += "}";
  280. }
  281. out += "}";
  282. return out;
  283. }
  284. void CreateAuthChallengeMessage(CastMessage* message_proto,
  285. const AuthContext& auth_context) {
  286. CHECK(message_proto);
  287. DeviceAuthMessage auth_message;
  288. cast::channel::AuthChallenge* challenge = auth_message.mutable_challenge();
  289. DCHECK(challenge);
  290. challenge->set_sender_nonce(auth_context.nonce());
  291. challenge->set_hash_algorithm(cast::channel::SHA256);
  292. std::string auth_message_string;
  293. auth_message.SerializeToString(&auth_message_string);
  294. FillCommonCastMessageFields(message_proto, kPlatformSenderId,
  295. kPlatformReceiverId, kAuthNamespace);
  296. message_proto->set_payload_type(
  297. cast::channel::CastMessage_PayloadType_BINARY);
  298. message_proto->set_payload_binary(auth_message_string);
  299. }
  300. bool IsAuthMessage(const CastMessage& message) {
  301. return message.namespace_() == kAuthNamespace;
  302. }
  303. bool IsReceiverMessage(const CastMessage& message) {
  304. return message.namespace_() == kReceiverNamespace;
  305. }
  306. bool IsPlatformSenderMessage(const CastMessage& message) {
  307. return message.destination_id() != cast_channel::kPlatformSenderId;
  308. }
  309. CastMessage CreateKeepAlivePingMessage() {
  310. return CreateKeepAliveMessage(
  311. EnumToString<CastMessageType, CastMessageType::kPing>());
  312. }
  313. CastMessage CreateKeepAlivePongMessage() {
  314. return CreateKeepAliveMessage(
  315. EnumToString<CastMessageType, CastMessageType::kPong>());
  316. }
  317. CastMessage CreateVirtualConnectionRequest(
  318. const std::string& source_id,
  319. const std::string& destination_id,
  320. VirtualConnectionType connection_type,
  321. const std::string& user_agent,
  322. const std::string& browser_version) {
  323. // Parse system_version from user agent string. It contains platform, OS and
  324. // CPU info and is contained in the first set of parentheses of the user
  325. // agent string (e.g., X11; Linux x86_64).
  326. std::string system_version;
  327. size_t start_index = user_agent.find('(');
  328. if (start_index != std::string::npos) {
  329. size_t end_index = user_agent.find(')', start_index + 1);
  330. if (end_index != std::string::npos) {
  331. system_version =
  332. user_agent.substr(start_index + 1, end_index - start_index - 1);
  333. }
  334. }
  335. Value::Dict dict;
  336. dict.Set("type", EnumToString<CastMessageType, CastMessageType::kConnect>());
  337. dict.Set("userAgent", user_agent);
  338. dict.Set("connType", connection_type);
  339. dict.Set("origin", base::Value::Dict());
  340. Value::Dict sender_info;
  341. sender_info.Set("sdkType", kVirtualConnectSdkType);
  342. sender_info.Set("version", browser_version);
  343. sender_info.Set("browserVersion", browser_version);
  344. sender_info.Set("platform", GetVirtualConnectPlatformValue());
  345. sender_info.Set("connectionType", kVirtualConnectTypeLocal);
  346. if (!system_version.empty())
  347. sender_info.Set("systemVersion", system_version);
  348. dict.Set("senderInfo", std::move(sender_info));
  349. return CreateCastMessage(kConnectionNamespace, base::Value(std::move(dict)),
  350. source_id, destination_id);
  351. }
  352. CastMessage CreateVirtualConnectionClose(const std::string& source_id,
  353. const std::string& destination_id) {
  354. Value::Dict dict;
  355. dict.Set("type",
  356. EnumToString<CastMessageType, CastMessageType::kCloseConnection>());
  357. dict.Set("reasonCode", kVirtualConnectionClosedByPeer);
  358. return CreateCastMessage(kConnectionNamespace, base::Value(std::move(dict)),
  359. source_id, destination_id);
  360. }
  361. CastMessage CreateGetAppAvailabilityRequest(const std::string& source_id,
  362. int request_id,
  363. const std::string& app_id) {
  364. Value::Dict dict;
  365. dict.Set(
  366. "type",
  367. EnumToString<CastMessageType, CastMessageType::kGetAppAvailability>());
  368. Value::List app_id_value;
  369. app_id_value.Append(app_id);
  370. dict.Set("appId", std::move(app_id_value));
  371. dict.Set("requestId", request_id);
  372. return CreateCastMessage(kReceiverNamespace, base::Value(std::move(dict)),
  373. source_id, kPlatformReceiverId);
  374. }
  375. CastMessage CreateReceiverStatusRequest(const std::string& source_id,
  376. int request_id) {
  377. Value::Dict dict;
  378. dict.Set("type",
  379. EnumToString<CastMessageType, CastMessageType::kGetStatus>());
  380. dict.Set("requestId", request_id);
  381. return CreateCastMessage(kReceiverNamespace, base::Value(std::move(dict)),
  382. source_id, kPlatformReceiverId);
  383. }
  384. BroadcastRequest::BroadcastRequest(const std::string& broadcast_namespace,
  385. const std::string& message)
  386. : broadcast_namespace(broadcast_namespace), message(message) {}
  387. BroadcastRequest::~BroadcastRequest() = default;
  388. bool BroadcastRequest::operator==(const BroadcastRequest& other) const {
  389. return broadcast_namespace == other.broadcast_namespace &&
  390. message == other.message;
  391. }
  392. CastMessage CreateBroadcastRequest(const std::string& source_id,
  393. int request_id,
  394. const std::vector<std::string>& app_ids,
  395. const BroadcastRequest& request) {
  396. Value::Dict dict;
  397. dict.Set("type",
  398. EnumToString<CastMessageType, CastMessageType::kBroadcast>());
  399. base::Value::List app_ids_value;
  400. for (const std::string& app_id : app_ids)
  401. app_ids_value.Append(app_id);
  402. dict.Set("appIds", std::move(app_ids_value));
  403. dict.Set("namespace", request.broadcast_namespace);
  404. dict.Set("message", request.message);
  405. return CreateCastMessage(kBroadcastNamespace, base::Value(std::move(dict)),
  406. source_id, kPlatformReceiverId);
  407. }
  408. CastMessage CreateLaunchRequest(
  409. const std::string& source_id,
  410. int request_id,
  411. const std::string& app_id,
  412. const std::string& locale,
  413. const std::vector<std::string>& supported_app_types,
  414. const absl::optional<base::Value>& app_params) {
  415. Value::Dict dict;
  416. dict.Set("type", EnumToString<CastMessageType, CastMessageType::kLaunch>());
  417. dict.Set("requestId", request_id);
  418. dict.Set("appId", app_id);
  419. dict.Set("language", locale);
  420. base::Value::List supported_app_types_value;
  421. for (const std::string& type : supported_app_types)
  422. supported_app_types_value.Append(type);
  423. dict.Set("supportedAppTypes", std::move(supported_app_types_value));
  424. if (app_params)
  425. dict.Set("appParams", app_params.value().Clone());
  426. return CreateCastMessage(kReceiverNamespace, base::Value(std::move(dict)),
  427. source_id, kPlatformReceiverId);
  428. }
  429. CastMessage CreateStopRequest(const std::string& source_id,
  430. int request_id,
  431. const std::string& session_id) {
  432. Value::Dict dict;
  433. dict.Set("type", EnumToString<CastMessageType, CastMessageType::kStop>());
  434. dict.Set("requestId", request_id);
  435. dict.Set("sessionId", session_id);
  436. return CreateCastMessage(kReceiverNamespace, base::Value(std::move(dict)),
  437. source_id, kPlatformReceiverId);
  438. }
  439. CastMessage CreateCastMessage(const std::string& message_namespace,
  440. const base::Value& message,
  441. const std::string& source_id,
  442. const std::string& destination_id) {
  443. CastMessage output;
  444. FillCommonCastMessageFields(&output, source_id, destination_id,
  445. message_namespace);
  446. output.set_payload_type(
  447. CastMessage::PayloadType::CastMessage_PayloadType_STRING);
  448. if (message.is_string()) {
  449. // NOTE(jrw): This case is needed to fix crbug.com/149843471, which affects
  450. // the ability to cast the Shaka player. Without it, the payload of the
  451. // first message sent with the urn:x-cast:com.google.shaka.v2 namespace is
  452. // given an extra level of quoting. It's not clear whether switching on the
  453. // JSON type of the message is the right thing to do here, or if this case
  454. // is simply compensating for some other problem that occurs between here
  455. // and PresentationConnection::send(string, ...), which receives a value
  456. // with the correct amount of quotation.
  457. output.set_payload_utf8(message.GetString());
  458. } else {
  459. CHECK(base::JSONWriter::Write(message, output.mutable_payload_utf8()));
  460. }
  461. return output;
  462. }
  463. CastMessage CreateMediaRequest(const base::Value& body,
  464. int request_id,
  465. const std::string& source_id,
  466. const std::string& destination_id) {
  467. Value dict = body.Clone();
  468. std::string* type = dict.GetDict().FindString("type");
  469. CHECK(type);
  470. dict.GetDict().Set("type", GetRemappedMediaRequestType(*type));
  471. dict.GetDict().Set("requestId", request_id);
  472. return CreateCastMessage(kMediaNamespace, dict, source_id, destination_id);
  473. }
  474. CastMessage CreateSetVolumeRequest(const base::Value& body,
  475. int request_id,
  476. const std::string& source_id) {
  477. DCHECK(body.GetDict().FindString("type") &&
  478. *body.GetDict().FindString("type") ==
  479. (EnumToString<V2MessageType, V2MessageType::kSetVolume>()));
  480. Value dict = body.Clone();
  481. dict.GetDict().Remove("sessionId");
  482. dict.GetDict().Set("requestId", request_id);
  483. return CreateCastMessage(kReceiverNamespace, dict, source_id,
  484. kPlatformReceiverId);
  485. }
  486. bool IsMediaRequestMessageType(V2MessageType type) {
  487. switch (type) {
  488. case V2MessageType::kEditTracksInfo:
  489. case V2MessageType::kLoad:
  490. case V2MessageType::kMediaGetStatus:
  491. case V2MessageType::kMediaSetVolume:
  492. case V2MessageType::kPause:
  493. case V2MessageType::kPlay:
  494. case V2MessageType::kPrecache:
  495. case V2MessageType::kQueueInsert:
  496. case V2MessageType::kQueueLoad:
  497. case V2MessageType::kQueueRemove:
  498. case V2MessageType::kQueueReorder:
  499. case V2MessageType::kQueueUpdate:
  500. case V2MessageType::kQueueNext:
  501. case V2MessageType::kQueuePrev:
  502. case V2MessageType::kSeek:
  503. case V2MessageType::kStopMedia:
  504. return true;
  505. default:
  506. return false;
  507. }
  508. }
  509. // TODO(crbug.com/1291730): Eliminate this function.
  510. const char* ToString(GetAppAvailabilityResult result) {
  511. return EnumToString(result).value_or("").data();
  512. }
  513. absl::optional<int> GetRequestIdFromResponse(const Value::Dict& payload) {
  514. return payload.FindInt("requestId");
  515. }
  516. GetAppAvailabilityResult GetAppAvailabilityResultFromResponse(
  517. const Value::Dict& payload,
  518. const std::string& app_id) {
  519. const Value::Dict* availability_dict = payload.FindDict("availability");
  520. if (!availability_dict)
  521. return GetAppAvailabilityResult::kUnknown;
  522. const std::string* availability = availability_dict->FindString(app_id);
  523. if (!availability)
  524. return GetAppAvailabilityResult::kUnknown;
  525. return StringToEnum<GetAppAvailabilityResult>(*availability)
  526. .value_or(GetAppAvailabilityResult::kUnknown);
  527. }
  528. LaunchSessionResponse::LaunchSessionResponse() = default;
  529. LaunchSessionResponse::LaunchSessionResponse(LaunchSessionResponse&& other) =
  530. default;
  531. LaunchSessionResponse& LaunchSessionResponse::operator=(
  532. LaunchSessionResponse&& other) = default;
  533. LaunchSessionResponse::~LaunchSessionResponse() = default;
  534. LaunchSessionResponse GetLaunchSessionResponseError(std::string error_msg) {
  535. LaunchSessionResponse response;
  536. response.result = LaunchSessionResponse::Result::kError;
  537. response.error_msg = std::move(error_msg);
  538. return response;
  539. }
  540. LaunchSessionResponse GetLaunchSessionResponse(
  541. const base::Value::Dict& payload) {
  542. const std::string* type_string = payload.FindString("type");
  543. if (!type_string)
  544. return LaunchSessionResponse();
  545. const auto type = CastMessageTypeFromString(*type_string);
  546. if (type != CastMessageType::kReceiverStatus &&
  547. type != CastMessageType::kLaunchError) {
  548. return LaunchSessionResponse();
  549. }
  550. LaunchSessionResponse response;
  551. if (type == CastMessageType::kLaunchError) {
  552. response.result = LaunchSessionResponse::Result::kError;
  553. return response;
  554. }
  555. const Value::Dict* receiver_status = payload.FindDict("status");
  556. if (!receiver_status)
  557. return LaunchSessionResponse();
  558. response.result = LaunchSessionResponse::Result::kOk;
  559. response.receiver_status = receiver_status->Clone();
  560. return response;
  561. }
  562. VirtualConnectionType GetConnectionType(const std::string& destination_id) {
  563. // VCs to recevier-0 are invisible to the receiver application by design.
  564. // We create a strong connection because some commands (e.g. LAUNCH) are
  565. // not accepted from invisible connections.
  566. return destination_id == kPlatformReceiverId
  567. ? VirtualConnectionType::kStrong
  568. : VirtualConnectionType::kInvisible;
  569. }
  570. } // namespace cast_channel