text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/feedback/feedback_uploader_chrome.h"
  
  #include "base/bind.h"
  #include "base/strings/stringprintf.h"
  #include "build/chromeos_buildflags.h"
  #include "chrome/browser/profiles/profile.h"
  #include "chrome/browser/signin/identity_manager_factory.h"
  #include "components/feedback/feedback_report.h"
  #include "components/signin/public/identity_manager/consent_level.h"
  #include "components/signin/public/identity_manager/identity_manager.h"
  #include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
  #include "components/signin/public/identity_manager/scope_set.h"
  #include "content/public/browser/browser_context.h"
  #include "content/public/browser/browser_task_traits.h"
  #include "content/public/browser/browser_thread.h"
  #include "content/public/browser/storage_partition.h"
  
  #if BUILDFLAG(IS_CHROMEOS_ASH)
  #include "chromeos/components/chromebox_for_meetings/buildflags/buildflags.h"
  #if BUILDFLAG(PLATFORM_CFM)
  #include "chrome/browser/chromeos/policy/enrollment_requisition_manager.h"
  #include "chrome/browser/device_identity/device_identity_provider.h"
  #include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
  #endif  // BUILDFLAG(PLATFORM_CFM)
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  
  namespace feedback {
  
  namespace {
  
  constexpr char kAuthenticationErrorLogMessage[] =
      "Feedback report will be sent without authentication.";
  
  constexpr char kConsumer[] = "feedback_uploader_chrome";
  
  constexpr char kScope[] = "https://www.googleapis.com/auth/supportcontent";
  
  void QueueSingleReport(base::WeakPtr<feedback::FeedbackUploader> uploader,
                         scoped_refptr<FeedbackReport> report) {
    content::GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE, base::BindOnce(&FeedbackUploaderChrome::RequeueReport,
                                  std::move(uploader), std::move(report)));
  }
  
  // Helper function to create an URLLoaderFactory for the FeedbackUploader from
  // the BrowserContext storage partition. As creating the storage partition can
  // be expensive, this is delayed so that it does not happen during startup.
  scoped_refptr<network::SharedURLLoaderFactory>
  CreateURLLoaderFactoryForBrowserContext(content::BrowserContext* context) {
    return content::BrowserContext::GetDefaultStoragePartition(context)
        ->GetURLLoaderFactoryForBrowserProcess();
  }
  
  }  // namespace
  
  FeedbackUploaderChrome::FeedbackUploaderChrome(content::BrowserContext* context)
      // The FeedbackUploaderChrome lifetime is bound to that of BrowserContext
      // by the KeyedServiceFactory infrastructure. The FeedbackUploaderChrome
      // will be destroyed before the BrowserContext, thus base::Unretained()
      // usage is safe.
      : FeedbackUploader(/*is_off_the_record=*/false,
                         context->GetPath(),
                         base::BindOnce(&CreateURLLoaderFactoryForBrowserContext,
                                        base::Unretained(context))),
        context_(context) {
    DCHECK(!context_->IsOffTheRecord());
  
    task_runner()->PostTask(
        FROM_HERE,
        base::BindOnce(&FeedbackReport::LoadReportsAndQueue,
                       feedback_reports_path(),
                       base::BindRepeating(&QueueSingleReport, AsWeakPtr())));
  }
  
  FeedbackUploaderChrome::~FeedbackUploaderChrome() = default;
  
  void FeedbackUploaderChrome::PrimaryAccountAccessTokenAvailable(
      GoogleServiceAuthError error,
      signin::AccessTokenInfo access_token_info) {
    DCHECK(primary_account_token_fetcher_);
    primary_account_token_fetcher_.reset();
    AccessTokenAvailable(error, access_token_info.token);
  }
  
  #if BUILDFLAG(IS_CHROMEOS_ASH)
  #if BUILDFLAG(PLATFORM_CFM)
  void FeedbackUploaderChrome::ActiveAccountAccessTokenAvailable(
      GoogleServiceAuthError error,
      std::string token) {
    DCHECK(active_account_token_fetcher_);
    active_account_token_fetcher_.reset();
    AccessTokenAvailable(error, token);
  }
  #endif  // BUILDFLAG(PLATFORM_CFM)
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  
  void FeedbackUploaderChrome::AccessTokenAvailable(GoogleServiceAuthError error,
                                                    std::string token) {
    if (error.state() == GoogleServiceAuthError::NONE) {
      DCHECK(!token.empty());
      access_token_ = token;
    } else {
      LOG(ERROR) << "Failed to get the access token. "
                 << kAuthenticationErrorLogMessage;
    }
  
    FeedbackUploader::StartDispatchingReport();
  }
  
  void FeedbackUploaderChrome::StartDispatchingReport() {
    if (delegate_)
      delegate_->OnStartDispatchingReport();
  
    access_token_.clear();
  
    // TODO(crbug.com/849591): Instead of getting the IdentityManager from the
    // profile, we should pass the IdentityManager to FeedbackUploaderChrome's
    // ctor.
    Profile* profile = Profile::FromBrowserContext(context_);
    DCHECK(profile);
    signin::IdentityManager* identity_manager =
        IdentityManagerFactory::GetForProfile(profile);
  
    // Sync consent is not required to send feedback because the feedback dialog
    // has its own privacy notice.
    if (identity_manager &&
        identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
      signin::ScopeSet scopes;
      scopes.insert(kScope);
      primary_account_token_fetcher_ =
          std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
              kConsumer, identity_manager, scopes,
              base::BindOnce(
                  &FeedbackUploaderChrome::PrimaryAccountAccessTokenAvailable,
                  base::Unretained(this)),
              signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate,
              signin::ConsentLevel::kSignin);
      return;
    }
  
  #if BUILDFLAG(IS_CHROMEOS_ASH)
  #if BUILDFLAG(PLATFORM_CFM)
    // CFM Devices may need to acquire the auth token for their robot account
    // before they submit feedback.
    DeviceOAuth2TokenService* deviceTokenService =
        DeviceOAuth2TokenServiceFactory::Get();
    DCHECK(deviceTokenService);
    auto device_identity_provider =
        std::make_unique<DeviceIdentityProvider>(deviceTokenService);
  
    // Flag indicating that a device was intended to be used as a CFM.
    bool isMeetDevice =
        policy::EnrollmentRequisitionManager::IsRemoraRequisition();
    if (isMeetDevice && !device_identity_provider->GetActiveAccountId().empty()) {
      OAuth2AccessTokenManager::ScopeSet scopes;
      scopes.insert(kScope);
      active_account_token_fetcher_ = device_identity_provider->FetchAccessToken(
          kConsumer, scopes,
          base::BindOnce(
              &FeedbackUploaderChrome::ActiveAccountAccessTokenAvailable,
              base::Unretained(this)));
      return;
    }
  #endif  // BUILDFLAG(PLATFORM_CFM)
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  
    LOG(ERROR) << "Failed to request oauth access token. "
               << kAuthenticationErrorLogMessage;
    FeedbackUploader::StartDispatchingReport();
  }
  
  void FeedbackUploaderChrome::AppendExtraHeadersToUploadRequest(
      network::ResourceRequest* resource_request) {
    if (access_token_.empty())
      return;
  
    resource_request->headers.SetHeader(
        net::HttpRequestHeaders::kAuthorization,
        base::StringPrintf("Bearer %s", access_token_.c_str()));
  }
  
  }  // namespace feedback
  