chromium/ash/webui/eche_app_ui/system_info_provider.cc

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

#include "ash/webui/eche_app_ui/system_info_provider.h"

#include "ash/constants/ash_features.h"
#include "ash/webui/eche_app_ui/mojom/types_mojom_traits.h"
#include "ash/webui/eche_app_ui/system_info.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
#include "crypto/sha2.h"
#include "ui/display/screen.h"
#include "ui/display/tablet_state.h"

namespace ash::eche_app {

namespace network_config = ::chromeos::network_config;
using network_config::mojom::ConnectionStateType;

const char kJsonDeviceNameKey[] = "device_name";
const char kJsonBoardNameKey[] = "board_name";
const char kJsonTabletModeKey[] = "tablet_mode";
const char kJsonWifiConnectionStateKey[] = "wifi_connection_state";
const char kJsonDebugModeKey[] = "debug_mode";
const char kJsonGaiaIdKey[] = "gaia_id";
const char kJsonDeviceTypeKey[] = "device_type";
const char kJsonOsVersionKey[] = "os_version";
const char kJsonChannelKey[] = "channel";
const char kJsonMeasureLatencyKey[] = "measure_latency";
const char kJsonSendStartSignalingKey[] = "send_start_signaling";
const char kJsonDisableStunServerKey[] = "disable_stun_server";
const char kJsonCheckAndroidNetworkInfoKey[] = "check_android_network_info";
const char kJsonProcessAndroidAccessibilityTreeKey[] = "process_android_accessibility_tree";

const std::map<ConnectionStateType, const char*> CONNECTION_STATE_TYPE{
    {ConnectionStateType::kOnline, "online"},
    {ConnectionStateType::kConnected, "connected"},
    {ConnectionStateType::kPortal, "portal"},
    {ConnectionStateType::kConnecting, "connecting"},
    {ConnectionStateType::kNotConnected, "not_connected"}};

SystemInfoProvider::SystemInfoProvider(
    std::unique_ptr<SystemInfo> system_info,
    network_config::mojom::CrosNetworkConfig* cros_network_config)
    : system_info_(std::move(system_info)),
      cros_network_config_(cros_network_config),
      wifi_connection_state_(ConnectionStateType::kNotConnected) {
  // TODO(samchiu): The intention of null check was for unit test. Add a fake
  // ScreenBacklight object to remove null check.
  if (ScreenBacklight::Get()) {
    ScreenBacklight::Get()->AddObserver(this);
  }
  cros_network_config_->AddObserver(
      cros_network_config_receiver_.BindNewPipeAndPassRemote());
  FetchWifiNetworkList();
}

SystemInfoProvider::SystemInfoProvider()
    : system_info_(nullptr),
      cros_network_config_(nullptr),
      wifi_connection_state_(ConnectionStateType::kNotConnected) {
  PA_LOG(INFO) << "echeapi SystemInfoProvider SystemInfoProvider";
}

SystemInfoProvider::~SystemInfoProvider() {
  // Ash may be released before us.
  if (ScreenBacklight::Get()) {
    ScreenBacklight::Get()->RemoveObserver(this);
  }
}

std::string SystemInfoProvider::GetHashedWiFiSsid() {
  return hashed_wifi_ssid_;
}

void SystemInfoProvider::GetSystemInfo(
    base::OnceCallback<void(const std::string&)> callback) {
  PA_LOG(INFO) << "echeapi SystemInfoProvider GetSystemInfo";
  base::Value::Dict json_dictionary;
  json_dictionary.Set(kJsonDeviceNameKey, system_info_->GetDeviceName());
  json_dictionary.Set(kJsonBoardNameKey, system_info_->GetBoardName());
  json_dictionary.Set(kJsonTabletModeKey,
                      display::Screen::GetScreen()->InTabletMode());
  json_dictionary.Set(kJsonGaiaIdKey, system_info_->GetGaiaId());
  json_dictionary.Set(kJsonDeviceTypeKey, system_info_->GetDeviceType());
  if (features::IsEcheMetricsRevampEnabled()) {
    json_dictionary.Set(kJsonOsVersionKey, system_info_->GetOsVersion());
    json_dictionary.Set(kJsonChannelKey, system_info_->GetChannel());
  }
  auto found_type = CONNECTION_STATE_TYPE.find(wifi_connection_state_);
  std::string connecton_state_string =
      found_type == CONNECTION_STATE_TYPE.end() ? "" : found_type->second;
  json_dictionary.Set(kJsonWifiConnectionStateKey, connecton_state_string);
  json_dictionary.Set(kJsonDebugModeKey, base::FeatureList::IsEnabled(
                                             features::kEcheSWADebugMode));
  json_dictionary.Set(
      kJsonMeasureLatencyKey,
      base::FeatureList::IsEnabled(features::kEcheSWAMeasureLatency));
  json_dictionary.Set(
      kJsonSendStartSignalingKey,
      base::FeatureList::IsEnabled(features::kEcheSWASendStartSignaling));
  json_dictionary.Set(
      kJsonDisableStunServerKey,
      base::FeatureList::IsEnabled(features::kEcheSWADisableStunServer));
  json_dictionary.Set(
      kJsonCheckAndroidNetworkInfoKey,
      base::FeatureList::IsEnabled(features::kEcheSWACheckAndroidNetworkInfo));
  json_dictionary.Set(
      kJsonProcessAndroidAccessibilityTreeKey,
      base::FeatureList::IsEnabled(features::kEcheSWAProcessAndroidAccessibilityTree));

  std::string json_message;
  base::JSONWriter::Write(json_dictionary, &json_message);
  std::move(callback).Run(json_message);
}

void SystemInfoProvider::SetSystemInfoObserver(
    mojo::PendingRemote<mojom::SystemInfoObserver> observer) {
  PA_LOG(INFO) << "echeapi SystemInfoProvider SetSystemInfoObserver";
  observer_remote_.reset();
  observer_remote_.Bind(std::move(observer));
}

void SystemInfoProvider::Bind(
    mojo::PendingReceiver<mojom::SystemInfoProvider> receiver) {
  info_receiver_.reset();
  info_receiver_.Bind(std::move(receiver));
}

void SystemInfoProvider::OnScreenBacklightStateChanged(
    ash::ScreenBacklightState screen_state) {
  PA_LOG(INFO) << "echeapi SystemInfoProvider OnScreenBacklightStateChanged";
  if (!observer_remote_.is_bound()) {
    return;
  }

  observer_remote_->OnScreenBacklightStateChanged(screen_state);
}

void SystemInfoProvider::OnDisplayTabletStateChanged(
    display::TabletState state) {
  switch (state) {
    case display::TabletState::kEnteringTabletMode:
    case display::TabletState::kExitingTabletMode:
      break;
    case display::TabletState::kInTabletMode:
      SetTabletModeChanged(true);
      break;
    case display::TabletState::kInClamshellMode:
      SetTabletModeChanged(false);
      break;
  }
}

void SystemInfoProvider::SetTabletModeChanged(bool enabled) {
  PA_LOG(INFO) << "echeapi SystemInfoProvider SetTabletModeChanged";
  if (!observer_remote_.is_bound()) {
    return;
  }
  PA_LOG(VERBOSE) << "OnReceivedTabletModeChanged:" << enabled;
  observer_remote_->OnReceivedTabletModeChanged(enabled);
}

void SystemInfoProvider::SetAndroidDeviceNetworkInfoChanged(
    bool is_different_network,
    bool android_device_on_cellular) {
  PA_LOG(INFO) << "echeapi SystemInfoProvider "
                  "SetAndroidDeviceNetworkInfoChanged is_different_network:"
               << is_different_network;
  PA_LOG(INFO)
      << "echeapi SystemInfoProvider "
         "SetAndroidDeviceNetworkInfoChanged android_device_on_cellular:"
      << android_device_on_cellular;

  is_different_network_ = is_different_network;
  android_device_on_cellular_ = android_device_on_cellular;

  if (!observer_remote_.is_bound()) {
    return;
  }

  PA_LOG(VERBOSE) << "OnAndroidDeviceNetworkInfoChanged";
  observer_remote_->OnAndroidDeviceNetworkInfoChanged(
      is_different_network, android_device_on_cellular);
}

// network_config::mojom::CrosNetworkConfigObserver implementation:
void SystemInfoProvider::OnActiveNetworksChanged(
    std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) {
  FetchWifiNetworkList();
}

void SystemInfoProvider::FetchWifiNetworkSsidHash() {
  PA_LOG(INFO) << "echeapi SystemInfoProvider FetchWifiNetworkSsidHash";
  cros_network_config_->GetNetworkStateList(
      network_config::mojom::NetworkFilter::New(
          network_config::mojom::FilterType::kActive,
          network_config::mojom::NetworkType::kWiFi,
          network_config::mojom::kNoLimit),
      base::BindOnce(&SystemInfoProvider::OnActiveWifiNetworkListFetched,
                     base::Unretained(this)));
}

void SystemInfoProvider::FetchWifiNetworkList() {
  cros_network_config_->GetNetworkStateList(
      network_config::mojom::NetworkFilter::New(
          network_config::mojom::FilterType::kActive,
          network_config::mojom::NetworkType::kWiFi,
          network_config::mojom::kNoLimit),
      base::BindOnce(&SystemInfoProvider::OnActiveWifiNetworkListFetched,
                     base::Unretained(this)));
}

void SystemInfoProvider::OnActiveWifiNetworkListFetched(
    std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) {
  for (const auto& network : networks) {
    if (network->type == chromeos::network_config::mojom::NetworkType::kWiFi) {
      hashed_wifi_ssid_ =
          crypto::SHA256HashString(network->type_state->get_wifi()->ssid);
      wifi_connection_state_ = network->connection_state;
      return;
    }
  }

  // Reset connection state and SSID hash if there is no active WiFi network.
  wifi_connection_state_ = ConnectionStateType::kNotConnected;
  hashed_wifi_ssid_ = std::string();
}

}  // namespace ash::eche_app