chromium/chrome/browser/ash/calendar/calendar_keyed_service.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/ash/calendar/calendar_keyed_service.h"

#include <string>
#include <vector>

#include "ash/calendar/calendar_controller.h"
#include "base/functional/callback_helpers.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "google_apis/common/auth_service.h"
#include "google_apis/gaia/gaia_constants.h"
#include "net/traffic_annotation/network_traffic_annotation.h"

using google_apis::RequestSender;
using google_apis::calendar::CalendarApiCalendarListRequest;
using google_apis::calendar::CalendarApiEventsRequest;
using google_apis::calendar::CalendarEventListCallback;
using google_apis::calendar::CalendarListCallback;

namespace ash {
namespace {

constexpr net::NetworkTrafficAnnotationTag kCalendarTrafficAnnotation =
    net::DefineNetworkTrafficAnnotation("calendar_get_events", R"(
       semantics {
         sender: "Calendar Keyed Service"
         description:
            "Fetch a Chrome OS user's Google Calendar calendar list or event "
            "list in order to display their events in the Quick Settings "
            "Calendar."
          trigger:
              "Chrome OS system tray calendar view is opened by the user."
          data:
            "The request is authenticated with an OAuth2 access token "
            "identifying the Google account."
          destination: GOOGLE_OWNED_SERVICE
          internal {
            contacts {
              email: "[email protected]"
            }
            contacts {
              email: "[email protected]"
            }
          }
          user_data {
            type: ACCESS_TOKEN
          }
          last_reviewed: "2023-08-01"
        }
        policy {
          cookies_allowed: NO
          setting: "This feature cannot be disabled in settings."
          chrome_policy {
            CalendarIntegrationEnabled {
              CalendarIntegrationEnabled: false
            }
            ContextualGoogleIntegrationsEnabled {
              ContextualGoogleIntegrationsEnabled: false
            }
          }
        })");

}  // namespace

CalendarKeyedService::CalendarKeyedService(Profile* profile,
                                           const AccountId& account_id)
    : profile_(profile), account_id_(account_id), calendar_client_(profile) {
  identity_manager_ = IdentityManagerFactory::GetForProfile(profile_);
  // Instance check for tests.
  if (Shell::HasInstance()) {
    Shell::Get()->calendar_controller()->RegisterClientForUser(
        account_id_, &calendar_client_);
  }
  Initialize();
}

CalendarKeyedService::~CalendarKeyedService() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}

void CalendarKeyedService::Initialize() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  std::vector<std::string> scopes;
  scopes.push_back(GaiaConstants::kCalendarReadOnlyOAuth2Scope);
  url_loader_factory_ = profile_->GetURLLoaderFactory();
  sender_ = std::make_unique<RequestSender>(
      std::make_unique<google_apis::AuthService>(
          identity_manager_,
          identity_manager_->GetPrimaryAccountId(signin::ConsentLevel::kSignin),
          url_loader_factory_, scopes),
      url_loader_factory_,
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})
          .get(),
      /*custom_user_agent=*/std::string(), kCalendarTrafficAnnotation);
}

void CalendarKeyedService::SetUrlForTesting(const std::string& url) {
  url_generator_.SetBaseUrlForTesting(url);  // IN-TEST
}

void CalendarKeyedService::Shutdown() {
  if (Shell::HasInstance()) {
    Shell::Get()->calendar_controller()->RegisterClientForUser(
        account_id_,
        /*client=*/nullptr);
  }
  // Reset `sender_` early to prevent a crash during destruction of
  // CalendarKeyedService. See https://crbug.com/1319563.
  sender_.reset();
}

base::OnceClosure CalendarKeyedService::GetCalendarList(
    CalendarListCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  CHECK(callback);

  if (!sender_) {
    std::move(callback).Run(google_apis::OTHER_ERROR, /*calendars=*/nullptr);
    return base::DoNothing();
  }

  return sender_->StartRequestWithAuthRetry(
      std::make_unique<CalendarApiCalendarListRequest>(
          sender_.get(), url_generator_, std::move(callback)));
}

base::OnceClosure CalendarKeyedService::GetEventList(
    CalendarEventListCallback callback,
    const base::Time start_time,
    const base::Time end_time) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  CHECK(callback);
  if (start_time > end_time || !sender_) {
    std::move(callback).Run(google_apis::OTHER_ERROR, /*events=*/nullptr);
    return base::DoNothing();
  }

  return sender_->StartRequestWithAuthRetry(
      std::make_unique<CalendarApiEventsRequest>(sender_.get(), url_generator_,
                                                 std::move(callback),
                                                 start_time, end_time));
}

base::OnceClosure CalendarKeyedService::GetEventList(
    CalendarEventListCallback callback,
    const base::Time start_time,
    const base::Time end_time,
    const std::string& calendar_id,
    const std::string& calendar_color_id) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  CHECK(callback);
  if (start_time > end_time || !sender_) {
    std::move(callback).Run(google_apis::OTHER_ERROR, /*events=*/nullptr);
    return base::DoNothing();
  }

  return sender_->StartRequestWithAuthRetry(
      std::make_unique<CalendarApiEventsRequest>(
          sender_.get(), url_generator_, std::move(callback), start_time,
          end_time, calendar_id, calendar_color_id));
}

}  // namespace ash