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/message_loop/message_loop.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/rand_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/time/time.h"
13 #include "components/signin/core/browser/signin_client.h"
14 #include "components/signin/core/common/signin_pref_names.h"
15 #include "google_apis/gaia/gaia_constants.h"
16 #include "google_apis/gaia/gaia_urls.h"
17 #include "net/base/escape.h"
18 #include "net/url_request/url_request_context_getter.h"
22 void RecordRequestStatusHistogram(bool success
) {
23 UMA_HISTOGRAM_BOOLEAN("Signin.RefreshTokenAnnotationRequest", success
);
27 RefreshTokenAnnotationRequest::RefreshTokenAnnotationRequest(
28 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
29 const std::string
& product_version
,
30 const std::string
& device_id
,
31 const std::string
& client_id
,
32 const base::Closure
& request_callback
)
33 : OAuth2TokenService::Consumer("refresh_token_annotation"),
34 request_context_getter_(request_context_getter
),
35 product_version_(product_version
),
36 device_id_(device_id
),
37 client_id_(client_id
),
38 request_callback_(request_callback
) {
41 RefreshTokenAnnotationRequest::~RefreshTokenAnnotationRequest() {
42 DCHECK(CalledOnValidThread());
46 scoped_ptr
<RefreshTokenAnnotationRequest
>
47 RefreshTokenAnnotationRequest::SendIfNeeded(
48 PrefService
* pref_service
,
49 OAuth2TokenService
* token_service
,
50 SigninClient
* signin_client
,
51 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
52 const std::string
& account_id
,
53 const base::Closure
& request_callback
) {
54 scoped_ptr
<RefreshTokenAnnotationRequest
> request
;
56 if (!ShouldSendNow(pref_service
))
57 return request
.Pass();
59 // Don't send request if device_id is disabled.
60 std::string device_id
= signin_client
->GetSigninScopedDeviceId();
61 if (device_id
.empty())
62 return request
.Pass();
64 request
.reset(new RefreshTokenAnnotationRequest(
65 request_context_getter
, signin_client
->GetProductVersion(), device_id
,
66 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), request_callback
));
67 request
->RequestAccessToken(token_service
, account_id
);
68 return request
.Pass();
72 bool RefreshTokenAnnotationRequest::ShouldSendNow(PrefService
* pref_service
) {
73 bool should_send_now
= false;
74 // Read scheduled time from prefs.
75 base::Time scheduled_time
=
76 base::Time::FromInternalValue(pref_service
->GetInt64(
77 prefs::kGoogleServicesRefreshTokenAnnotateScheduledTime
));
79 if (!scheduled_time
.is_null() && scheduled_time
<= base::Time::Now()) {
80 DVLOG(2) << "Sending RefreshTokenAnnotationRequest";
81 should_send_now
= true;
82 // Reset scheduled_time so that it gets rescheduled later in the function.
83 scheduled_time
= base::Time();
86 if (scheduled_time
.is_null()) {
87 // Random delay in interval [0, 20] days.
88 double delay_in_sec
= base::RandDouble() * 20.0 * 24.0 * 3600.0;
90 base::Time::Now() + base::TimeDelta::FromSecondsD(delay_in_sec
);
91 DVLOG(2) << "RefreshTokenAnnotationRequest scheduled for "
93 pref_service
->SetInt64(
94 prefs::kGoogleServicesRefreshTokenAnnotateScheduledTime
,
95 scheduled_time
.ToInternalValue());
97 return should_send_now
;
100 void RefreshTokenAnnotationRequest::RequestAccessToken(
101 OAuth2TokenService
* token_service
,
102 const std::string
& account_id
) {
103 DCHECK(CalledOnValidThread());
104 OAuth2TokenService::ScopeSet scopes
;
105 scopes
.insert(GaiaConstants::kOAuth1LoginScope
);
106 access_token_request_
= token_service
->StartRequest(account_id
, scopes
, this);
109 void RefreshTokenAnnotationRequest::OnGetTokenSuccess(
110 const OAuth2TokenService::Request
* request
,
111 const std::string
& access_token
,
112 const base::Time
& expiration_time
) {
113 DCHECK(CalledOnValidThread());
114 DVLOG(2) << "Got access token";
115 Start(request_context_getter_
.get(), access_token
);
118 void RefreshTokenAnnotationRequest::OnGetTokenFailure(
119 const OAuth2TokenService::Request
* request
,
120 const GoogleServiceAuthError
& error
) {
121 DCHECK(CalledOnValidThread());
122 DVLOG(2) << "Failed to get access token";
123 RecordRequestStatusHistogram(false);
124 base::MessageLoop::current()->PostTask(FROM_HERE
, request_callback_
);
125 request_callback_
.Reset();
128 GURL
RefreshTokenAnnotationRequest::CreateApiCallUrl() {
129 return GaiaUrls::GetInstance()->oauth2_issue_token_url();
132 std::string
RefreshTokenAnnotationRequest::CreateApiCallBody() {
133 // response_type=none means we don't want any token back, just record that
134 // this request was sent.
135 const char kIssueTokenBodyFormat
[] =
137 "&response_type=none"
141 "&device_type=chrome"
144 // It doesn't matter which scope to use for IssueToken request, any common
145 // scope would do. In this case I'm using "userinfo.email".
146 return base::StringPrintf(
147 kIssueTokenBodyFormat
,
148 net::EscapeUrlEncodedData(GaiaConstants::kGoogleUserInfoEmail
, true)
150 net::EscapeUrlEncodedData(client_id_
, true).c_str(),
151 net::EscapeUrlEncodedData(device_id_
, true).c_str(),
152 net::EscapeUrlEncodedData(product_version_
, true).c_str());
155 void RefreshTokenAnnotationRequest::ProcessApiCallSuccess(
156 const net::URLFetcher
* source
) {
157 DCHECK(CalledOnValidThread());
158 DVLOG(2) << "Request succeeded";
159 RecordRequestStatusHistogram(true);
160 base::MessageLoop::current()->PostTask(FROM_HERE
, request_callback_
);
161 request_callback_
.Reset();
164 void RefreshTokenAnnotationRequest::ProcessApiCallFailure(
165 const net::URLFetcher
* source
) {
166 DCHECK(CalledOnValidThread());
167 DVLOG(2) << "Request failed";
168 RecordRequestStatusHistogram(false);
169 base::MessageLoop::current()->PostTask(FROM_HERE
, request_callback_
);
170 request_callback_
.Reset();