chromium/chromeos/ash/components/phonehub/message_receiver_impl.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/phonehub/message_receiver_impl.h"

#include <netinet/in.h>
#include <stdint.h>
#include <string>

#include "ash/constants/ash_features.h"
#include "base/logging.h"
#include "chromeos/ash/components/phonehub/proto/phonehub_api.pb.h"
#include "chromeos/ash/components/phonehub/util/histogram_util.h"

namespace ash::phonehub {

namespace {

std::string GetMessageTypeName(proto::MessageType message_type) {
  switch (message_type) {
    case proto::MessageType::PHONE_STATUS_SNAPSHOT:
      return "PHONE_STATUS_SNAPSHOT";
    case proto::MessageType::PHONE_STATUS_UPDATE:
      return "PHONE_STATUS_UPDATE";
    case proto::MessageType::UPDATE_NOTIFICATION_MODE_RESPONSE:
      return "UPDATE_NOTIFICATION_MODE_RESPONSE";
    case proto::MessageType::RING_DEVICE_RESPONSE:
      return "RING_DEVICE_RESPONSE";
    case proto::MessageType::UPDATE_BATTERY_MODE_RESPONSE:
      return "UPDATE_BATTERY_MODE_RESPONSE";
    case proto::MessageType::DISMISS_NOTIFICATION_RESPONSE:
      return "DISMISS_NOTIFICATION_RESPONSE";
    case proto::MessageType::NOTIFICATION_INLINE_REPLY_RESPONSE:
      return "NOTIFICATION_INLINE_REPLY_RESPONSE";
    case proto::MessageType::SHOW_NOTIFICATION_ACCESS_SETUP_RESPONSE:
      return "SHOW_NOTIFICATION_ACCESS_SETUP_RESPONSE";
    case proto::MessageType::FETCH_CAMERA_ROLL_ITEMS_RESPONSE:
      return "FETCH_CAMERA_ROLL_ITEMS_RESPONSE";
    case proto::MessageType::FETCH_CAMERA_ROLL_ITEM_DATA_RESPONSE:
      return "FETCH_CAMERA_ROLL_ITEM_DATA_RESPONSE";
    case proto::MessageType::FEATURE_SETUP_RESPONSE:
      return "FEATURE_SETUP_RESPONSE";
    case proto::MessageType::PING_RESPONSE:
      return "PING_RESPONSE";
    case proto::MessageType::APP_STREAM_UPDATE:
      return "APP_STREAM_UPDATE";
    case proto::MessageType::APP_LIST_UPDATE:
      return "APP_LIST_UPDATE";
    case proto::MessageType::APP_LIST_INCREMENTAL_UPDATE:
      return "APP_LIST_INCREMENTAL_UPDATE";
    default:
      return "UNKOWN_MESSAGE";
  }
}

}  // namespace

MessageReceiverImpl::MessageReceiverImpl(
    secure_channel::ConnectionManager* connection_manager,
    PhoneHubStructuredMetricsLogger* phone_hub_structured_metrics_logger)
    : connection_manager_(connection_manager),
      phone_hub_structured_metrics_logger_(
          phone_hub_structured_metrics_logger) {
  DCHECK(connection_manager_);

  connection_manager_->AddObserver(this);
}

MessageReceiverImpl::~MessageReceiverImpl() {
  connection_manager_->RemoveObserver(this);
}

void MessageReceiverImpl::OnMessageReceived(const std::string& payload) {
  // The first two bytes of |payload| is reserved for the header
  // proto::MessageType.
  uint16_t* ptr =
      reinterpret_cast<uint16_t*>(const_cast<char*>(payload.data()));
  proto::MessageType message_type =
      static_cast<proto::MessageType>(ntohs(*ptr));

  PA_LOG(INFO) << "MessageReceiver received a "
               << GetMessageTypeName(message_type) << " message.";
  util::LogMessageResult(message_type,
                         util::PhoneHubMessageResult::kResponseReceived);
  phone_hub_structured_metrics_logger_->LogPhoneHubMessageEvent(
      message_type, PhoneHubMessageDirection::kPhoneToChromebook);

  // Decode the proto message if the message is something we want to notify to
  // clients.
  if (message_type == proto::MessageType::PHONE_STATUS_SNAPSHOT) {
    proto::PhoneStatusSnapshot snapshot_proto;
    // Serialized proto is after the first two bytes of |payload|.
    if (!snapshot_proto.ParseFromString(payload.substr(2))) {
      PA_LOG(ERROR) << "OnMessageReceived() could not deserialize the "
                    << "PhoneStatusSnapshot proto message.";
      return;
    }
    NotifyPhoneStatusSnapshotReceived(snapshot_proto);
    return;
  }

  if (message_type == proto::MessageType::PHONE_STATUS_UPDATE) {
    proto::PhoneStatusUpdate update_proto;
    // Serialized proto is after the first two bytes of |payload|.
    if (!update_proto.ParseFromString(payload.substr(2))) {
      PA_LOG(ERROR) << "OnMessageReceived() could not deserialize the "
                    << "PhoneStatusUpdate proto message.";
      return;
    }
    NotifyPhoneStatusUpdateReceived(update_proto);
    return;
  }

  if (features::IsPhoneHubFeatureSetupErrorHandlingEnabled() &&
      message_type == proto::MessageType::FEATURE_SETUP_RESPONSE) {
    proto::FeatureSetupResponse response;
    // Serialized proto is after the first two bytes of |payload|.
    if (!response.ParseFromString(payload.substr(2))) {
      PA_LOG(ERROR) << "OnMessageReceived() could not deserialize the "
                    << "FeatureSetupResponse proto message.";
      return;
    }
    NotifyFeatureSetupResponseReceived(response);
  }

  if (features::IsPhoneHubCameraRollEnabled() &&
      message_type == proto::MessageType::FETCH_CAMERA_ROLL_ITEMS_RESPONSE) {
    proto::FetchCameraRollItemsResponse response;
    // Serialized proto is after the first two bytes of |payload|.
    if (!response.ParseFromString(payload.substr(2))) {
      PA_LOG(ERROR) << "OnMessageReceived() could not deserialize the "
                    << "FetchCameraRollItemsResponse proto message.";
      return;
    }
    NotifyFetchCameraRollItemsResponseReceived(response);
    return;
  }

  if (features::IsPhoneHubCameraRollEnabled() &&
      message_type ==
          proto::MessageType::FETCH_CAMERA_ROLL_ITEM_DATA_RESPONSE) {
    proto::FetchCameraRollItemDataResponse response;
    // Serialized proto is after the first two bytes of |payload|.
    if (!response.ParseFromString(payload.substr(2))) {
      PA_LOG(ERROR) << "OnMessageReceived() could not deserialize the "
                    << "FetchCameraRollItemDataResponse proto message.";
      return;
    }
    NotifyFetchCameraRollItemDataResponseReceived(response);
    return;
  }

  if (features::IsPhoneHubPingOnBubbleOpenEnabled() &&
      message_type == proto::MessageType::PING_RESPONSE) {
    // We don't need to send the content of the ping response, we only care
    // that we got a response.
    NotifyPingResponseReceived();
    return;
  }

  if (features::IsEcheSWAEnabled() &&
      message_type == proto::MessageType::APP_STREAM_UPDATE) {
    proto::AppStreamUpdate app_stream_update;
    // Serialized proto is after the first two bytes of |payload|.
    if (!app_stream_update.ParseFromString(payload.substr(2))) {
      PA_LOG(ERROR) << "OnMessageReceived() could not deserialize the "
                    << "AppStreamUpdate proto message.";
      return;
    }
    NotifyAppStreamUpdateReceived(app_stream_update);
    return;
  }

  if (features::IsEcheSWAEnabled() &&
      message_type == proto::MessageType::APP_LIST_UPDATE) {
    proto::AppListUpdate app_list_update;
    // Serialized proto is after the first two bytes of |payload|.
    if (!app_list_update.ParseFromString(payload.substr(2))) {
      PA_LOG(ERROR) << "OnMessageReceived() could not deserialize the "
                    << "AppListUpdate proto message.";
      return;
    }
    NotifyAppListUpdateReceived(app_list_update);
    return;
  }

  if (features::IsEcheSWAEnabled() &&
      message_type == proto::MessageType::APP_LIST_INCREMENTAL_UPDATE) {
    proto::AppListIncrementalUpdate app_list_incrementalUpdate;
    if (!app_list_incrementalUpdate.ParseFromString(payload.substr(2))) {
      PA_LOG(ERROR) << "OnMessageReceived() could not deserialize the "
                    << "AppListIncrementalUpdate proto message.";
      return;
    }
    NotifyAppListIncrementalUpdateReceived(app_list_incrementalUpdate);
    return;
  }
}

}  // namespace ash::phonehub