// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/pairing/proto_decoder.h" #include "components/pairing/pairing_api.pb.h" #include "net/base/io_buffer.h" namespace { enum { MESSAGE_NONE, MESSAGE_HOST_STATUS, MESSAGE_CONFIGURE_HOST, MESSAGE_PAIR_DEVICES, MESSAGE_COMPLETE_SETUP, MESSAGE_ERROR, MESSAGE_ADD_NETWORK, NUM_MESSAGES, }; } namespace pairing_chromeos { ProtoDecoder::ProtoDecoder(Observer* observer) : observer_(observer), next_message_type_(MESSAGE_NONE), next_message_size_(0) { DCHECK(observer_); } ProtoDecoder::~ProtoDecoder() {} bool ProtoDecoder::DecodeIOBuffer(int size, ProtoDecoder::IOBufferRefPtr io_buffer) { // Update the message buffer. message_buffer_.AddIOBuffer(io_buffer, size); // If there is no current message, the next byte is the message type. if (next_message_type_ == MESSAGE_NONE) { if (message_buffer_.AvailableBytes() < static_cast(sizeof(uint8_t))) return true; uint8_t message_type = MESSAGE_NONE; message_buffer_.ReadBytes(reinterpret_cast(&message_type), sizeof(message_type)); if (message_type == MESSAGE_NONE || message_type >= NUM_MESSAGES) { LOG(ERROR) << "Unknown message type received: " << message_type; return false; } next_message_type_ = message_type; } // If the message size isn't set, the next two bytes are the message size. if (next_message_size_ == 0) { if (message_buffer_.AvailableBytes() < static_cast(sizeof(uint16_t))) return true; // The size is sent in network byte order. uint8_t high_byte = 0; message_buffer_.ReadBytes(reinterpret_cast(&high_byte), sizeof(high_byte)); uint8_t low_byte = 0; message_buffer_.ReadBytes(reinterpret_cast(&low_byte), sizeof(low_byte)); next_message_size_ = (high_byte << 8) + low_byte; } // If the whole proto buffer is not yet available, return early. if (message_buffer_.AvailableBytes() < next_message_size_) return true; std::vector buffer(next_message_size_); message_buffer_.ReadBytes(&buffer[0], next_message_size_); switch (next_message_type_) { case MESSAGE_HOST_STATUS: { pairing_api::HostStatus message; message.ParseFromArray(&buffer[0], buffer.size()); observer_->OnHostStatusMessage(message); } break; case MESSAGE_CONFIGURE_HOST: { pairing_api::ConfigureHost message; message.ParseFromArray(&buffer[0], buffer.size()); observer_->OnConfigureHostMessage(message); } break; case MESSAGE_PAIR_DEVICES: { pairing_api::PairDevices message; message.ParseFromArray(&buffer[0], buffer.size()); observer_->OnPairDevicesMessage(message); } break; case MESSAGE_COMPLETE_SETUP: { pairing_api::CompleteSetup message; message.ParseFromArray(&buffer[0], buffer.size()); observer_->OnCompleteSetupMessage(message); } break; case MESSAGE_ERROR: { pairing_api::Error message; message.ParseFromArray(&buffer[0], buffer.size()); observer_->OnErrorMessage(message); } break; case MESSAGE_ADD_NETWORK: { pairing_api::AddNetwork message; message.ParseFromArray(&buffer[0], buffer.size()); observer_->OnAddNetworkMessage(message); } break; default: LOG(WARNING) << "Skipping unknown message type: " << next_message_type_; break; } // Reset the message data. next_message_type_ = MESSAGE_NONE; next_message_size_ = 0; return true; } ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendHostStatus( const pairing_api::HostStatus& message, int* size) { std::string serialized_proto; if (!message.SerializeToString(&serialized_proto)) { NOTREACHED(); } return SendMessage(MESSAGE_HOST_STATUS, serialized_proto, size); } ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendConfigureHost( const pairing_api::ConfigureHost& message, int* size) { std::string serialized_proto; if (!message.SerializeToString(&serialized_proto)) { NOTREACHED(); } return SendMessage(MESSAGE_CONFIGURE_HOST, serialized_proto, size); } ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendPairDevices( const pairing_api::PairDevices& message, int* size) { std::string serialized_proto; if (!message.SerializeToString(&serialized_proto)) { NOTREACHED(); } return SendMessage(MESSAGE_PAIR_DEVICES, serialized_proto, size); } ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendCompleteSetup( const pairing_api::CompleteSetup& message, int* size) { std::string serialized_proto; if (!message.SerializeToString(&serialized_proto)) { NOTREACHED(); } return SendMessage(MESSAGE_COMPLETE_SETUP, serialized_proto, size); } ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendError( const pairing_api::Error& message, int* size) { std::string serialized_proto; if (!message.SerializeToString(&serialized_proto)) { NOTREACHED(); } return SendMessage(MESSAGE_ERROR, serialized_proto, size); } ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendMessage( uint8_t message_type, const std::string& message, int* size) { uint16_t message_size = message.size(); *size = sizeof(message_type) + sizeof(message_size) + message.size(); IOBufferRefPtr io_buffer(new net::IOBuffer(*size)); // Write the message type. int offset = 0; memcpy(&io_buffer->data()[offset], &message_type, sizeof(message_type)); offset += sizeof(message_type); // Network byte order. // Write the high byte of the size. uint8_t data = (message_size >> 8) & 0xFF; memcpy(&io_buffer->data()[offset], &data, sizeof(data)); offset += sizeof(data); // Write the low byte of the size. data = message_size & 0xFF; memcpy(&io_buffer->data()[offset], &data, sizeof(data)); offset += sizeof(data); // Write the actual message. memcpy(&io_buffer->data()[offset], message.data(), message.size()); return io_buffer; } } // namespace pairing_chromeos