chromium/chrome/browser/lacros/geolocation/system_geolocation_source_lacros.cc

// Copyright 2023 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/lacros/geolocation/system_geolocation_source_lacros.h"

#include "ash/constants/geolocation_access_level.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "chrome/grit/branded_strings.h"
#include "chromeos/crosapi/mojom/url_handler.mojom.h"
#include "chromeos/lacros/crosapi_pref_observer.h"
#include "chromeos/lacros/lacros_service.h"
#include "services/device/public/cpp/geolocation/geolocation_system_permission_manager.h"
#include "ui/base/l10n/l10n_util.h"

SystemGeolocationSourceLacros::SystemGeolocationSourceLacros()
    : permission_update_callback_(base::DoNothing()) {
  // Binding to remote for pref observation.
  crosapi_pref_observer_ = std::make_unique<CrosapiPrefObserver>(
      crosapi::mojom::PrefPath::kUserGeolocationAccessLevel,
      base::BindRepeating(&SystemGeolocationSourceLacros::OnPrefChanged,
                          weak_factory_.GetWeakPtr()));
}

SystemGeolocationSourceLacros::~SystemGeolocationSourceLacros() = default;

// static
std::unique_ptr<device::GeolocationSystemPermissionManager>
SystemGeolocationSourceLacros::
    CreateGeolocationSystemPermissionManagerOnLacros() {
  return std::make_unique<device::GeolocationSystemPermissionManager>(
      std::make_unique<SystemGeolocationSourceLacros>());
}

void SystemGeolocationSourceLacros::RegisterPermissionUpdateCallback(
    PermissionUpdateCallback callback) {
  permission_update_callback_ = std::move(callback);
  if (current_status_ ==
      device::LocationSystemPermissionStatus::kNotDetermined) {
    // This is here to support older versions of Ash that do not send the system
    // geolocation switch via crosapi.
    // The original behavior before the system wide switch was introduced was to
    // allow, so we keep this as the default behavior when the system doesn't
    // indicate othervise.
    // TODO(272426671): clean this up when we can safely assume that Ash
    // provides the value.
    permission_update_callback_.Run(
        device::LocationSystemPermissionStatus::kAllowed);
    return;
  }
  // If available, pass the (up-to-date) status into the new callback
  permission_update_callback_.Run(current_status_);
}

void SystemGeolocationSourceLacros::OpenSystemPermissionSetting() {
  auto* lacros_service = chromeos::LacrosService::Get();
  CHECK(lacros_service);

  if (!lacros_service->IsRegistered<crosapi::mojom::UrlHandler>()) {
    return;
  }
  if (!lacros_service->IsAvailable<crosapi::mojom::UrlHandler>()) {
    return;
  }
  mojo::Remote<crosapi::mojom::UrlHandler>& service =
      lacros_service->GetRemote<crosapi::mojom::UrlHandler>();
  if (service.is_connected()) {
    // Open the appropriate CrOS system settings page.
    service->OpenUrl(
        GURL("chrome://os-settings/osPrivacy/privacyHub/geolocation"));
  }
}

void SystemGeolocationSourceLacros::OnPrefChanged(base::Value value) {
  if (!value.is_int()) {
    LOG(ERROR) << "GeolocationSourceLacros received a non-integral value";
    return;
  }
  switch (static_cast<ash::GeolocationAccessLevel>(value.GetInt())) {
    case ash::GeolocationAccessLevel::kDisallowed:
      current_status_ = device::LocationSystemPermissionStatus::kDenied;
      break;
    case ash::GeolocationAccessLevel::kAllowed:
      current_status_ = device::LocationSystemPermissionStatus::kAllowed;
      break;
    case ash::GeolocationAccessLevel::kOnlyAllowedForSystem:
      current_status_ = device::LocationSystemPermissionStatus::kDenied;
      break;
    default:
      LOG(ERROR) << "Incorrect GeolocationAccessLevel: " << value.GetInt();
      return;
  }

  permission_update_callback_.Run(current_status_);
}