1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/signin/core/browser/refresh_token_annotation_request.h"
7 #include "base/location.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/rand_util.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/time/time.h"
15 #include "components/signin/core/browser/signin_client.h"
16 #include "components/signin/core/common/signin_pref_names.h"
17 #include "google_apis/gaia/gaia_constants.h"
18 #include "google_apis/gaia/gaia_urls.h"
19 #include "net/base/escape.h"
20 #include "net/url_request/url_request_context_getter.h"
24 void RecordRequestStatusHistogram(bool success
) {
25 UMA_HISTOGRAM_BOOLEAN("Signin.RefreshTokenAnnotationRequest", success
);
29 RefreshTokenAnnotationRequest::RefreshTokenAnnotationRequest(
30 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
31 const std::string
& product_version
,
32 const std::string
& device_id
,
33 const std::string
& client_id
,
34 const base::Closure
& request_callback
)
35 : OAuth2TokenService::Consumer("refresh_token_annotation"),
36 request_context_getter_(request_context_getter
),
37 product_version_(product_version
),
38 device_id_(device_id
),
39 client_id_(client_id
),
40 request_callback_(request_callback
) {
43 RefreshTokenAnnotationRequest::~RefreshTokenAnnotationRequest() {
44 DCHECK(CalledOnValidThread());
48 scoped_ptr
<RefreshTokenAnnotationRequest
>
49 RefreshTokenAnnotationRequest::SendIfNeeded(
50 PrefService
* pref_service
,
51 OAuth2TokenService
* token_service
,
52 SigninClient
* signin_client
,
53 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
54 const std::string
& account_id
,
55 const base::Closure
& request_callback
) {
56 scoped_ptr
<RefreshTokenAnnotationRequest
> request
;
58 if (!ShouldSendNow(pref_service
))
59 return request
.Pass();
61 // Don't send request if device_id is disabled.
62 std::string device_id
= signin_client
->GetSigninScopedDeviceId();
63 if (device_id
.empty())
64 return request
.Pass();
66 request
.reset(new RefreshTokenAnnotationRequest(
67 request_context_getter
, signin_client
->GetProductVersion(), device_id
,
68 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), request_callback
));
69 request
->RequestAccessToken(token_service
, account_id
);
70 return request
.Pass();
74 bool RefreshTokenAnnotationRequest::ShouldSendNow(PrefService
* pref_service
) {
75 bool should_send_now
= false;
76 // Read scheduled time from prefs.
77 base::Time scheduled_time
=
78 base::Time::FromInternalValue(pref_service
->GetInt64(
79 prefs::kGoogleServicesRefreshTokenAnnotateScheduledTime
));
81 if (!scheduled_time
.is_null() && scheduled_time
<= base::Time::Now()) {
82 DVLOG(2) << "Sending RefreshTokenAnnotationRequest";
83 should_send_now
= true;
84 // Reset scheduled_time so that it gets rescheduled later in the function.
85 scheduled_time
= base::Time();
88 if (scheduled_time
.is_null()) {
89 // Random delay in interval [0, 20] days.
90 double delay_in_sec
= base::RandDouble() * 20.0 * 24.0 * 3600.0;
92 base::Time::Now() + base::TimeDelta::FromSecondsD(delay_in_sec
);
93 DVLOG(2) << "RefreshTokenAnnotationRequest scheduled for "
95 pref_service
->SetInt64(
96 prefs::kGoogleServicesRefreshTokenAnnotateScheduledTime
,
97 scheduled_time
.ToInternalValue());
99 return should_send_now
;
102 void RefreshTokenAnnotationRequest::RequestAccessToken(
103 OAuth2TokenService
* token_service
,
104 const std::string
& account_id
) {
105 DCHECK(CalledOnValidThread());
106 OAuth2TokenService::ScopeSet scopes
;
107 scopes
.insert(GaiaConstants::kOAuth1LoginScope
);
108 access_token_request_
= token_service
->StartRequest(account_id
, scopes
, this);
111 void RefreshTokenAnnotationRequest::OnGetTokenSuccess(
112 const OAuth2TokenService::Request
* request
,
113 const std::string
& access_token
,
114 const base::Time
& expiration_time
) {
115 DCHECK(CalledOnValidThread());
116 DVLOG(2) << "Got access token";
117 Start(request_context_getter_
.get(), access_token
);
120 void RefreshTokenAnnotationRequest::OnGetTokenFailure(
121 const OAuth2TokenService::Request
* request
,
122 const GoogleServiceAuthError
& error
) {
123 DCHECK(CalledOnValidThread());
124 DVLOG(2) << "Failed to get access token";
125 RecordRequestStatusHistogram(false);
126 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, request_callback_
);
127 request_callback_
.Reset();
130 GURL
RefreshTokenAnnotationRequest::CreateApiCallUrl() {
131 return GaiaUrls::GetInstance()->oauth2_issue_token_url();
134 std::string
RefreshTokenAnnotationRequest::CreateApiCallBody() {
135 // response_type=none means we don't want any token back, just record that
136 // this request was sent.
137 const char kIssueTokenBodyFormat
[] =
139 "&response_type=none"
143 "&device_type=chrome"
146 // It doesn't matter which scope to use for IssueToken request, any common
147 // scope would do. In this case I'm using "userinfo.email".
148 return base::StringPrintf(
149 kIssueTokenBodyFormat
,
150 net::EscapeUrlEncodedData(GaiaConstants::kGoogleUserInfoEmail
, true)
152 net::EscapeUrlEncodedData(client_id_
, true).c_str(),
153 net::EscapeUrlEncodedData(device_id_
, true).c_str(),
154 net::EscapeUrlEncodedData(product_version_
, true).c_str());
157 void RefreshTokenAnnotationRequest::ProcessApiCallSuccess(
158 const net::URLFetcher
* source
) {
159 DCHECK(CalledOnValidThread());
160 DVLOG(2) << "Request succeeded";
161 RecordRequestStatusHistogram(true);
162 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, request_callback_
);
163 request_callback_
.Reset();
166 void RefreshTokenAnnotationRequest::ProcessApiCallFailure(
167 const net::URLFetcher
* source
) {
168 DCHECK(CalledOnValidThread());
169 DVLOG(2) << "Request failed";
170 RecordRequestStatusHistogram(false);
171 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, request_callback_
);
172 request_callback_
.Reset();