chromium/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator.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 "chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator.h"

#include "ash/constants/ash_pref_names.h"
#include "base/check.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/ash/crosapi/crosapi_ash.h"
#include "chrome/browser/ash/crosapi/crosapi_manager.h"
#include "chrome/browser/ash/crosapi/crosapi_util.h"
#include "chrome/browser/ash/crosapi/networking_attributes_ash.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/core/device_attributes_impl.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/metrics_utils.h"
#include "chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/signals_decorator.h"
#include "chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/signals_utils.h"
#include "chrome/browser/enterprise/signals/signals_common.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/network/device_state.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/device_signals/core/browser/signals_types.h"
#include "components/device_signals/core/common/signals_constants.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_service.h"

namespace enterprise_connectors {

namespace {

using policy::BrowserPolicyConnectorAsh;

constexpr char kLatencyHistogramVariant[] = "Ash";

device_signals::Trigger DetermineTrigger(Profile* profile) {
  if (!profile) {
    return device_signals::Trigger::kUnspecified;
  }
  if (ash::ProfileHelper::IsUserProfile(profile)) {
    return device_signals::Trigger::kBrowserNavigation;
  }
  if (ash::ProfileHelper::IsSigninProfile(profile)) {
    return device_signals::Trigger::kLoginScreen;
  }

  return device_signals::Trigger::kUnspecified;
}

void GetNetworkDeviceStates(Profile* profile,
                            ash::NetworkStateHandler::DeviceStateList* list) {
  if (!crosapi::browser_util::IsSigninProfileOrBelongsToAffiliatedUser(
          profile)) {
    return;
  }

  ash::NetworkStateHandler* network_state_handler =
      ash::NetworkHandler::Get()->network_state_handler();

  network_state_handler->GetDeviceList(list);
}

}  // namespace

AshSignalsDecorator::AshSignalsDecorator(
    policy::BrowserPolicyConnectorAsh* browser_policy_connector,
    Profile* profile)
    : browser_policy_connector_(browser_policy_connector),
      profile_(profile),
      attributes_(std::make_unique<policy::DeviceAttributesImpl>()) {
  DCHECK(browser_policy_connector_);
  DCHECK(profile_);
}

AshSignalsDecorator::~AshSignalsDecorator() = default;

void AshSignalsDecorator::Decorate(base::Value::Dict& signals,
                                   base::OnceClosure done_closure) {
  auto start_time = base::TimeTicks::Now();

  signals.Set(device_signals::names::kDeviceEnrollmentDomain,
              browser_policy_connector_->GetEnterpriseDomainManager());
  signals.Set(device_signals::names::kAllowScreenLock,
              profile_->GetPrefs()->GetBoolean(ash::prefs::kAllowScreenLock));
  signals.Set(device_signals::names::kSerialNumber,
              attributes_->GetDeviceSerialNumber());
  signals.Set(device_signals::names::kDeviceHostName,
              ash::NetworkHandler::Get()->network_state_handler()->hostname());
  signals.Set(device_signals::names::kTrigger,
              static_cast<int32_t>(DetermineTrigger(profile_)));
  // On ChromeOS the disk is always encrypted. See (b/249756773) for more
  // information.
  signals.Set(device_signals::names::kDiskEncrypted,
              static_cast<int32_t>(enterprise_signals::SettingValue::ENABLED));

  // Also, there is no way to remove the need for a password when logging into a
  // device, including when the screen is locked. A password or pin is always
  // required.
  signals.Set(device_signals::names::kScreenLockSecured,
              static_cast<int32_t>(enterprise_signals::SettingValue::ENABLED));

  base::Value::List imei_list;
  base::Value::List meid_list;
  ash::NetworkStateHandler::DeviceStateList device_list;
  GetNetworkDeviceStates(profile_, &device_list);
  for (auto* device_state : device_list) {
    if (!device_state)
      continue;

    if (!device_state->imei().empty())
      imei_list.Append(device_state->imei());

    if (!device_state->meid().empty())
      meid_list.Append(device_state->meid());
  }
  signals.Set(device_signals::names::kImei, std::move(imei_list));
  signals.Set(device_signals::names::kMeid, std::move(meid_list));

  if (!crosapi::CrosapiManager::Get() ||
      !crosapi::CrosapiManager::Get()->crosapi_ash() ||
      !crosapi::CrosapiManager::Get()
           ->crosapi_ash()
           ->networking_attributes_ash()) {
    LogSignalsCollectionLatency(kLatencyHistogramVariant, start_time);

    std::move(done_closure).Run();
    return;
  }

  auto callback =
      base::BindOnce(&AshSignalsDecorator::OnNetworkInfoRetrieved,
                     weak_ptr_factory_.GetWeakPtr(), std::ref(signals),
                     start_time, std::move(done_closure));

  crosapi::CrosapiManager::Get()
      ->crosapi_ash()
      ->networking_attributes_ash()
      ->GetNetworkDetails(std::move(callback));
}

void AshSignalsDecorator::OnNetworkInfoRetrieved(
    base::Value::Dict& signals,
    base::TimeTicks start_time,
    base::OnceClosure done_closure,
    crosapi::mojom::GetNetworkDetailsResultPtr result) {
  std::vector<std::string> mac_addresses;
  using Result = crosapi::mojom::GetNetworkDetailsResult;
  switch (result->which()) {
    case Result::Tag::kErrorMessage:
      break;
    case Result::Tag::kNetworkDetails:
      std::optional<std::string> mac_address =
          result->get_network_details()->mac_address;

      // `get_network_details()->mac_address` returns a std::string. On other
      // platforms (Windows, Linux and Mac) there can be multiple mac
      // addresses.
      if (mac_address) {
        mac_addresses.push_back(mac_address.value());
      }
  }

  // The mac addresses signal must always have a value, which can be an empty
  // array.
  signals.Set(device_signals::names::kMacAddresses, ToListValue(mac_addresses));

  LogSignalsCollectionLatency(kLatencyHistogramVariant, start_time);

  std::move(done_closure).Run();
}

}  // namespace enterprise_connectors