OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2012 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. | |
4 | |
5 #include "ios/chrome/browser/omaha/omaha_service.h" | |
6 | |
7 #include <Foundation/Foundation.h> | |
8 | |
9 #include <memory> | |
10 #include <utility> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/i18n/time_formatting.h" | |
14 #include "base/ios/device_util.h" | |
15 #include "base/logging.h" | |
16 #include "base/mac/scoped_nsobject.h" | |
17 #include "base/metrics/field_trial.h" | |
18 #include "base/rand_util.h" | |
19 #include "base/strings/stringprintf.h" | |
20 #include "base/strings/sys_string_conversions.h" | |
21 #include "base/strings/utf_string_conversions.h" | |
22 #include "base/sys_info.h" | |
23 #include "base/time/time.h" | |
24 #include "base/values.h" | |
25 #include "components/metrics/metrics_pref_names.h" | |
26 #include "components/prefs/pref_service.h" | |
27 #include "components/version_info/version_info.h" | |
28 #include "ios/chrome/browser/application_context.h" | |
29 #include "ios/chrome/browser/arch_util.h" | |
30 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h" | |
31 #include "ios/chrome/browser/browser_state_metrics/browser_state_metrics.h" | |
32 #include "ios/chrome/browser/install_time_util.h" | |
33 #include "ios/chrome/browser/ui/ui_util.h" | |
34 #include "ios/chrome/browser/upgrade/upgrade_recommended_details.h" | |
35 #include "ios/chrome/common/channel_info.h" | |
36 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h" | |
37 #include "ios/public/provider/chrome/browser/omaha/omaha_service_provider.h" | |
38 #include "ios/public/provider/chrome/browser/omaha/omaha_xml_writer.h" | |
39 #include "ios/web/public/web_thread.h" | |
40 #include "libxml/xmlwriter.h" | |
41 #include "net/base/backoff_entry.h" | |
42 #include "net/base/load_flags.h" | |
43 #include "net/url_request/url_fetcher.h" | |
44 #include "url/gurl.h" | |
45 | |
46 namespace { | |
47 // Number of hours to wait between successful requests. | |
48 const int kHoursBetweenRequests = 5; | |
49 // Minimal time to wait between retry requests. | |
50 const CFTimeInterval kPostRetryBaseSeconds = 3600; | |
51 // Maximal time to wait between retry requests. | |
52 const CFTimeInterval kPostRetryMaxSeconds = 6 * kPostRetryBaseSeconds; | |
53 | |
54 // Default last sent application version when none has been sent yet. | |
55 const char kDefaultLastSentVersion[] = "0.0.0.0"; | |
56 | |
57 // Key for saving states in the UserDefaults. | |
58 NSString* const kNextTriesTimesKey = @"ChromeOmahaServiceNextTries"; | |
59 NSString* const kCurrentPingKey = @"ChromeOmahaServiceCurrentPing"; | |
60 NSString* const kNumberTriesKey = @"ChromeOmahaServiceNumberTries"; | |
61 NSString* const kLastSentVersionKey = @"ChromeOmahaServiceLastSentVersion"; | |
62 NSString* const kLastSentTimeKey = @"ChromeOmahaServiceLastSentTime"; | |
63 NSString* const kRetryRequestIdKey = @"ChromeOmahaServiceRetryRequestId"; | |
64 | |
65 class XmlWrapper : public OmahaXmlWriter { | |
66 public: | |
67 XmlWrapper() | |
68 : buffer_(xmlBufferCreate()), | |
69 writer_(xmlNewTextWriterMemory(buffer_, /* compression */ 0)) { | |
70 DCHECK(buffer_); | |
71 DCHECK(writer_); | |
72 } | |
73 | |
74 ~XmlWrapper() override { | |
75 xmlFreeTextWriter(writer_); | |
76 xmlBufferFree(buffer_); | |
77 } | |
78 | |
79 void StartElement(const char* name) override { | |
80 DCHECK(name); | |
81 int result = xmlTextWriterStartElement( | |
82 writer_, reinterpret_cast<const xmlChar*>(name)); | |
83 DCHECK_GE(result, 0); | |
84 } | |
85 | |
86 void EndElement() override { | |
87 int result = xmlTextWriterEndElement(writer_); | |
88 DCHECK_GE(result, 0); | |
89 } | |
90 | |
91 void WriteAttribute(const char* name, const char* value) override { | |
92 DCHECK(name); | |
93 int result = xmlTextWriterWriteAttribute( | |
94 writer_, reinterpret_cast<const xmlChar*>(name), | |
95 reinterpret_cast<const xmlChar*>(value)); | |
96 DCHECK_GE(result, 0); | |
97 } | |
98 | |
99 void Finalize() override { | |
100 int result = xmlTextWriterEndDocument(writer_); | |
101 DCHECK_GE(result, 0); | |
102 } | |
103 | |
104 std::string GetContentAsString() override { | |
105 return std::string(reinterpret_cast<char*>(buffer_->content)); | |
106 } | |
107 | |
108 private: | |
109 xmlBufferPtr buffer_; | |
110 xmlTextWriterPtr writer_; | |
111 | |
112 DISALLOW_COPY_AND_ASSIGN(XmlWrapper); | |
113 }; | |
114 | |
115 } // namespace | |
116 | |
117 #pragma mark - | |
118 | |
119 // XML Parser for the server response. | |
sdefresne
2016/12/14 11:50:56
nit: "XML parser for the server response."
rohitrao (ping after 24h)
2016/12/14 13:26:32
Done.
| |
120 @interface ResponseParser : NSObject<NSXMLParserDelegate> { | |
121 @public | |
sdefresne
2016/12/14 11:50:56
This is the default, so you can remove.
rohitrao (ping after 24h)
2016/12/14 13:26:32
Done.
| |
122 BOOL hasError_; | |
123 BOOL responseIsParsed_; | |
124 BOOL appIsParsed_; | |
125 BOOL updateCheckIsParsed_; | |
126 BOOL urlIsParsed_; | |
127 BOOL manifestIsParsed_; | |
128 BOOL pingIsParsed_; | |
129 BOOL eventIsParsed_; | |
130 base::scoped_nsobject<NSString> appId_; | |
131 std::unique_ptr<UpgradeRecommendedDetails> updateInformation_; | |
132 } | |
133 | |
134 // Initialization method. |appId| is the application id one expects to find in | |
135 // the response message. | |
136 - (id)initWithAppId:(NSString*)appId; | |
sdefresne
2016/12/14 11:50:56
nit: id -> instancetype
rohitrao (ping after 24h)
2016/12/14 13:26:33
This is some old code =)
| |
137 | |
138 // Returns YES if the message has been correctly parsed. | |
139 - (BOOL)isCorrect; | |
140 | |
141 // If an upgrade is possible, returns the details of the notification to send. | |
142 // Otherwise, return NULL. | |
143 - (UpgradeRecommendedDetails*)upgradeRecommendedDetails; | |
144 | |
145 @end | |
146 | |
147 @implementation ResponseParser | |
148 | |
149 - (id)initWithAppId:(NSString*)appId { | |
sdefresne
2016/12/14 11:50:56
nit: id -> instancetype
rohitrao (ping after 24h)
2016/12/14 13:26:32
Done.
| |
150 if (self = [super init]) { | |
151 appId_.reset([appId retain]); | |
152 } | |
153 return self; | |
154 } | |
155 | |
156 - (BOOL)isCorrect { | |
157 // A response should have either a ping ACK or an event ACK, depending on the | |
158 // contents of the request. | |
159 return !hasError_ && (pingIsParsed_ || eventIsParsed_); | |
160 } | |
161 | |
162 - (UpgradeRecommendedDetails*)upgradeRecommendedDetails { | |
163 return updateInformation_.get(); | |
164 } | |
165 | |
166 // This method is parsing a message with the following type: | |
167 // <response...> | |
168 // <daystart.../> | |
169 // <app...> | |
170 // <updatecheck status="ok"> | |
171 // <urls> | |
172 // <url codebase="???"/> | |
173 // </urls> | |
174 // <manifest version="???"> | |
175 // <packages> | |
176 // <package hash="0" name="Chrome" required="true" size="0"/> | |
177 // </packages> | |
178 // <actions> | |
179 // <action event="update" run="Chrome"/> | |
180 // <action event="postinstall"/> | |
181 // </actions> | |
182 // </manifest> | |
183 // </updatecheck> | |
184 // <ping.../> | |
185 // </app> | |
186 // </response> | |
187 // --- OR --- | |
188 // <response...> | |
189 // <daystart.../> | |
190 // <app...> | |
191 // <event.../> | |
192 // </app> | |
193 // </response> | |
194 // See http://code.google.com/p/omaha/wiki/ServerProtocol for details. | |
195 - (void)parser:(NSXMLParser*)parser | |
196 didStartElement:(NSString*)elementName | |
197 namespaceURI:(NSString*)namespaceURI | |
198 qualifiedName:(NSString*)qualifiedName | |
199 attributes:(NSDictionary*)attributeDict { | |
200 if (hasError_) | |
201 return; | |
202 | |
203 // Array of uninteresting tags in the Omaha xml response. | |
204 NSArray* ignoredTagNames = | |
205 @[ @"action", @"actions", @"daystart", @"package", @"packages", @"urls" ]; | |
206 if ([ignoredTagNames containsObject:elementName]) | |
207 return; | |
208 | |
209 if (!responseIsParsed_) { | |
210 if ([elementName isEqualToString:@"response"] && | |
211 [[attributeDict valueForKey:@"protocol"] isEqualToString:@"3.0"] && | |
212 [[attributeDict valueForKey:@"server"] isEqualToString:@"prod"]) { | |
213 responseIsParsed_ = YES; | |
214 } else { | |
215 hasError_ = YES; | |
216 } | |
217 } else if (!appIsParsed_) { | |
218 if ([elementName isEqualToString:@"app"] && | |
219 [[attributeDict valueForKey:@"status"] isEqualToString:@"ok"] && | |
220 [[attributeDict valueForKey:@"appid"] isEqualToString:appId_]) { | |
221 appIsParsed_ = YES; | |
222 } else { | |
223 hasError_ = YES; | |
224 } | |
225 } else if (!eventIsParsed_ && !updateCheckIsParsed_) { | |
226 if ([elementName isEqualToString:@"updatecheck"]) { | |
227 updateCheckIsParsed_ = YES; | |
228 NSString* status = [attributeDict valueForKey:@"status"]; | |
229 if ([status isEqualToString:@"noupdate"]) { | |
230 // No update is available on the Market, so we won't get a <url> or | |
231 // <manifest> tag. | |
232 urlIsParsed_ = YES; | |
233 manifestIsParsed_ = YES; | |
234 } else if ([status isEqualToString:@"ok"]) { | |
235 updateInformation_.reset(new UpgradeRecommendedDetails); | |
sdefresne
2016/12/14 11:50:56
nit: can you change to use base::MakeUnique
upd
rohitrao (ping after 24h)
2016/12/14 13:26:33
Done.
| |
236 } else { | |
237 hasError_ = YES; | |
238 } | |
239 } else if ([elementName isEqualToString:@"event"]) { | |
240 if ([[attributeDict valueForKey:@"status"] isEqualToString:@"ok"]) { | |
241 eventIsParsed_ = YES; | |
242 } else { | |
243 hasError_ = YES; | |
244 } | |
245 } else { | |
246 hasError_ = YES; | |
247 } | |
248 } else if (!urlIsParsed_) { | |
249 if ([elementName isEqualToString:@"url"] && | |
250 [[attributeDict valueForKey:@"codebase"] length] > 0) { | |
251 urlIsParsed_ = YES; | |
252 DCHECK(updateInformation_); | |
253 NSString* url = [attributeDict valueForKey:@"codebase"]; | |
254 if ([[url substringFromIndex:([url length] - 1)] isEqualToString:@"/"]) | |
255 url = [url substringToIndex:([url length] - 1)]; | |
256 updateInformation_.get()->upgrade_url = | |
257 GURL(base::SysNSStringToUTF8(url)); | |
258 if (!updateInformation_.get()->upgrade_url.is_valid()) | |
259 hasError_ = YES; | |
260 } else { | |
261 hasError_ = YES; | |
262 } | |
263 } else if (!manifestIsParsed_) { | |
264 if ([elementName isEqualToString:@"manifest"] && | |
265 [attributeDict valueForKey:@"version"]) { | |
266 manifestIsParsed_ = YES; | |
267 DCHECK(updateInformation_); | |
268 updateInformation_.get()->next_version = | |
269 base::SysNSStringToUTF8([attributeDict valueForKey:@"version"]); | |
270 } else { | |
271 hasError_ = YES; | |
272 } | |
273 } else if (!pingIsParsed_) { | |
274 if ([elementName isEqualToString:@"ping"] && | |
275 [[attributeDict valueForKey:@"status"] isEqualToString:@"ok"]) { | |
276 pingIsParsed_ = YES; | |
277 } else { | |
278 hasError_ = YES; | |
279 } | |
280 } else { | |
281 hasError_ = YES; | |
282 } | |
283 } | |
284 | |
285 @end | |
286 | |
287 // static | |
288 OmahaService* OmahaService::GetInstance() { | |
289 return base::Singleton<OmahaService>::get(); | |
290 } | |
291 | |
292 // static | |
293 void OmahaService::Start(net::URLRequestContextGetter* request_context_getter, | |
294 const UpgradeRecommendedCallback& callback) { | |
295 DCHECK(request_context_getter); | |
296 DCHECK(!callback.is_null()); | |
297 OmahaService* result = GetInstance(); | |
298 result->set_upgrade_recommended_callback(callback); | |
299 // This should only be called once. | |
300 DCHECK(!result->request_context_getter_); | |
301 result->request_context_getter_ = request_context_getter; | |
302 result->locale_lang_ = GetApplicationContext()->GetApplicationLocale(); | |
303 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, | |
304 base::Bind(&OmahaService::SendOrScheduleNextPing, | |
305 base::Unretained(result))); | |
306 } | |
307 | |
308 OmahaService::OmahaService() | |
309 : request_context_getter_(NULL), | |
310 schedule_(true), | |
311 application_install_date_(0), | |
312 sending_install_event_(false) { | |
313 Initialize(); | |
314 } | |
315 | |
316 OmahaService::OmahaService(bool schedule) | |
317 : request_context_getter_(NULL), | |
318 schedule_(schedule), | |
319 application_install_date_(0), | |
320 sending_install_event_(false) { | |
321 Initialize(); | |
322 } | |
323 | |
324 OmahaService::~OmahaService() {} | |
325 | |
326 void OmahaService::Initialize() { | |
327 // Initialize the provider at the same time as the rest of the service. | |
328 ios::GetChromeBrowserProvider()->GetOmahaServiceProvider()->Initialize(); | |
329 | |
330 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; | |
331 next_tries_time_ = base::Time::FromCFAbsoluteTime( | |
332 [defaults doubleForKey:kNextTriesTimesKey]); | |
333 current_ping_time_ = | |
334 base::Time::FromCFAbsoluteTime([defaults doubleForKey:kCurrentPingKey]); | |
335 number_of_tries_ = [defaults integerForKey:kNumberTriesKey]; | |
336 last_sent_time_ = | |
337 base::Time::FromCFAbsoluteTime([defaults doubleForKey:kLastSentTimeKey]); | |
338 NSString* lastSentVersion = [defaults stringForKey:kLastSentVersionKey]; | |
339 if (lastSentVersion) { | |
340 last_sent_version_ = | |
341 base::Version(base::SysNSStringToUTF8(lastSentVersion)); | |
342 } else { | |
343 last_sent_version_ = base::Version(kDefaultLastSentVersion); | |
344 } | |
345 | |
346 application_install_date_ = | |
347 GetApplicationContext()->GetLocalState()->GetInt64( | |
348 metrics::prefs::kInstallDate); | |
349 DCHECK(application_install_date_); | |
350 | |
351 // Whether data should be persisted again to the user preferences. | |
352 bool persist_again = false; | |
353 | |
354 base::Time now = base::Time::Now(); | |
355 // If |last_sent_time_| is in the future, the clock has been tampered with. | |
356 // Reset |last_sent_time_| to now. | |
357 if (last_sent_time_ > now) { | |
358 last_sent_time_ = now; | |
359 persist_again = true; | |
360 } | |
361 | |
362 // If the |next_tries_time_| is more than kHoursBetweenRequests hours away, | |
363 // there is a possibility that the clock has been tampered with. Reschedule | |
364 // the ping to be the usual interval after the last successful one. | |
365 if (next_tries_time_ - now > | |
366 base::TimeDelta::FromHours(kHoursBetweenRequests)) { | |
367 next_tries_time_ = | |
368 last_sent_time_ + base::TimeDelta::FromHours(kHoursBetweenRequests); | |
369 persist_again = true; | |
370 } | |
371 | |
372 // Fire a ping as early as possible if the version changed. | |
373 base::Version current_version(version_info::GetVersionNumber()); | |
374 if (last_sent_version_ < current_version) { | |
375 next_tries_time_ = base::Time::Now() - base::TimeDelta::FromSeconds(1); | |
376 number_of_tries_ = 0; | |
377 persist_again = true; | |
378 } | |
379 | |
380 if (persist_again) | |
381 PersistStates(); | |
382 } | |
383 | |
384 // static | |
385 void OmahaService::GetDebugInformation( | |
386 const base::Callback<void(base::DictionaryValue*)> callback) { | |
387 web::WebThread::PostTask( | |
388 web::WebThread::IO, FROM_HERE, | |
389 base::Bind(&OmahaService::GetDebugInformationOnIOThread, | |
390 base::Unretained(GetInstance()), callback)); | |
391 } | |
392 | |
393 // static | |
394 base::TimeDelta OmahaService::GetBackOff(uint8_t number_of_tries) { | |
395 // Configuration for the service exponential backoff | |
396 static net::BackoffEntry::Policy kBackoffPolicy = { | |
397 0, // num_errors_to_ignore | |
398 kPostRetryBaseSeconds * 1000, // initial_delay_ms | |
399 2.0, // multiply_factor | |
400 0.1, // jitter_factor | |
401 kPostRetryMaxSeconds * 1000, // maximum_backoff_ms | |
402 -1, // entry_lifetime_ms | |
403 false // always_use_initial_delay | |
404 }; | |
405 | |
406 net::BackoffEntry backoff_entry(&kBackoffPolicy); | |
407 for (int i = 0; i < number_of_tries; ++i) { | |
408 backoff_entry.InformOfRequest(false); | |
409 } | |
410 | |
411 return backoff_entry.GetTimeUntilRelease(); | |
412 } | |
413 | |
414 std::string OmahaService::GetPingContent(const std::string& requestId, | |
415 const std::string& sessionId, | |
416 const std::string& versionName, | |
417 const std::string& channelName, | |
418 const base::Time& installationTime, | |
419 PingContent pingContent) { | |
420 OmahaServiceProvider* provider = | |
421 ios::GetChromeBrowserProvider()->GetOmahaServiceProvider(); | |
422 | |
423 XmlWrapper xml_wrapper; | |
424 xml_wrapper.StartElement("request"); | |
425 xml_wrapper.WriteAttribute("protocol", "3.0"); | |
426 xml_wrapper.WriteAttribute("version", "iOS-1.0.0.0"); | |
427 xml_wrapper.WriteAttribute("ismachine", "1"); | |
428 xml_wrapper.WriteAttribute("requestid", requestId.c_str()); | |
429 xml_wrapper.WriteAttribute("sessionid", sessionId.c_str()); | |
430 provider->AppendExtraAttributes("request", &xml_wrapper); | |
431 xml_wrapper.WriteAttribute("hardware_class", | |
432 ios::device_util::GetPlatform().c_str()); | |
433 // Set up <os platform="ios"... /> | |
434 xml_wrapper.StartElement("os"); | |
435 xml_wrapper.WriteAttribute("platform", "ios"); | |
436 xml_wrapper.WriteAttribute("version", | |
437 base::SysInfo::OperatingSystemVersion().c_str()); | |
438 xml_wrapper.WriteAttribute("arch", arch_util::kCurrentArch); | |
439 xml_wrapper.EndElement(); | |
440 | |
441 bool is_first_install = | |
442 pingContent == INSTALL_EVENT && | |
443 last_sent_version_ == base::Version(kDefaultLastSentVersion); | |
444 | |
445 // Set up <app version="" ...> | |
446 xml_wrapper.StartElement("app"); | |
447 if (pingContent == INSTALL_EVENT) { | |
448 std::string previous_version = | |
449 is_first_install ? "" : last_sent_version_.GetString(); | |
450 xml_wrapper.WriteAttribute("version", previous_version.c_str()); | |
451 xml_wrapper.WriteAttribute("nextversion", versionName.c_str()); | |
452 } else { | |
453 xml_wrapper.WriteAttribute("version", versionName.c_str()); | |
454 xml_wrapper.WriteAttribute("nextversion", ""); | |
455 } | |
456 xml_wrapper.WriteAttribute("lang", locale_lang_.c_str()); | |
457 xml_wrapper.WriteAttribute("brand", provider->GetBrandCode().c_str()); | |
458 xml_wrapper.WriteAttribute("client", ""); | |
459 std::string application_id = provider->GetApplicationID(); | |
460 xml_wrapper.WriteAttribute("appid", application_id.c_str()); | |
461 std::string install_age; | |
462 if (is_first_install) { | |
463 install_age = "-1"; | |
464 } else if (!installationTime.is_null() && | |
465 installationTime.ToTimeT() != | |
466 install_time_util::kUnknownInstallDate) { | |
467 install_age = base::StringPrintf( | |
468 "%d", (base::Time::Now() - installationTime).InDays()); | |
469 } | |
470 provider->AppendExtraAttributes("app", &xml_wrapper); | |
471 // If the install date is unknown, send nothing. | |
472 if (!install_age.empty()) | |
473 xml_wrapper.WriteAttribute("installage", install_age.c_str()); | |
474 | |
475 if (pingContent == INSTALL_EVENT) { | |
476 // Add an install complete event. | |
477 xml_wrapper.StartElement("event"); | |
478 if (is_first_install) { | |
479 xml_wrapper.WriteAttribute("eventtype", "2"); // install | |
480 } else { | |
481 xml_wrapper.WriteAttribute("eventtype", "3"); // update | |
482 } | |
483 xml_wrapper.WriteAttribute("eventresult", "1"); // succeeded | |
484 xml_wrapper.EndElement(); | |
485 } else { | |
486 // Set up <updatecheck/> | |
487 xml_wrapper.StartElement("updatecheck"); | |
488 xml_wrapper.WriteAttribute("tag", channelName.c_str()); | |
489 xml_wrapper.EndElement(); | |
490 | |
491 // Set up <ping active=1/> | |
492 xml_wrapper.StartElement("ping"); | |
493 xml_wrapper.WriteAttribute("active", "1"); | |
494 xml_wrapper.EndElement(); | |
495 } | |
496 | |
497 // End app. | |
498 xml_wrapper.EndElement(); | |
499 // End request. | |
500 xml_wrapper.EndElement(); | |
501 | |
502 xml_wrapper.Finalize(); | |
503 return xml_wrapper.GetContentAsString(); | |
504 } | |
505 | |
506 std::string OmahaService::GetCurrentPingContent() { | |
507 base::Version current_version(version_info::GetVersionNumber()); | |
508 sending_install_event_ = last_sent_version_ < current_version; | |
509 PingContent ping_content = | |
510 sending_install_event_ ? INSTALL_EVENT : USAGE_PING; | |
511 | |
512 // An install retry ping only makes sense if an install event must be send. | |
513 DCHECK(sending_install_event_ || !IsNextPingInstallRetry()); | |
514 std::string request_id = GetNextPingRequestId(ping_content); | |
515 return GetPingContent(request_id, ios::device_util::GetRandomId(), | |
516 version_info::GetVersionNumber(), GetChannelString(), | |
517 base::Time::FromTimeT(application_install_date_), | |
518 ping_content); | |
519 } | |
520 | |
521 void OmahaService::SendPing() { | |
522 // Check that no request is in progress. | |
523 DCHECK(!fetcher_); | |
524 | |
525 GURL url(ios::GetChromeBrowserProvider() | |
526 ->GetOmahaServiceProvider() | |
527 ->GetUpdateServerURL()); | |
528 if (!url.is_valid()) { | |
529 return; | |
530 } | |
531 | |
532 fetcher_ = net::URLFetcher::Create(0, url, net::URLFetcher::POST, this); | |
533 fetcher_->SetRequestContext(request_context_getter_); | |
534 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
535 net::LOAD_DO_NOT_SAVE_COOKIES); | |
536 fetcher_->SetUploadData("text/xml", GetCurrentPingContent()); | |
537 | |
538 // If this is not the first try, notify the omaha server. | |
539 if (number_of_tries_ && IsNextPingInstallRetry()) { | |
540 fetcher_->SetExtraRequestHeaders(base::StringPrintf( | |
541 "X-RequestAge: %lld", | |
542 (base::Time::Now() - current_ping_time_).InSeconds())); | |
543 } | |
544 | |
545 // Update last fail time and number of tries, so that if anything fails | |
546 // catastrophically, the fail is taken into account. | |
547 if (number_of_tries_ < 30) | |
548 ++number_of_tries_; | |
549 next_tries_time_ = base::Time::Now() + GetBackOff(number_of_tries_); | |
550 PersistStates(); | |
551 | |
552 fetcher_->Start(); | |
553 } | |
554 | |
555 void OmahaService::SendOrScheduleNextPing() { | |
556 base::Time now = base::Time::Now(); | |
557 if (next_tries_time_ <= now) { | |
558 SendPing(); | |
559 return; | |
560 } | |
561 if (schedule_) { | |
562 timer_.Start(FROM_HERE, next_tries_time_ - now, this, | |
563 &OmahaService::SendPing); | |
564 } | |
565 } | |
566 | |
567 void OmahaService::PersistStates() { | |
568 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; | |
569 | |
570 [defaults setDouble:next_tries_time_.ToCFAbsoluteTime() | |
571 forKey:kNextTriesTimesKey]; | |
572 [defaults setDouble:current_ping_time_.ToCFAbsoluteTime() | |
573 forKey:kCurrentPingKey]; | |
574 [defaults setDouble:last_sent_time_.ToCFAbsoluteTime() | |
575 forKey:kLastSentTimeKey]; | |
576 [defaults setInteger:number_of_tries_ forKey:kNumberTriesKey]; | |
577 [defaults setObject:base::SysUTF8ToNSString(last_sent_version_.GetString()) | |
578 forKey:kLastSentVersionKey]; | |
579 | |
580 // Save critical state information for usage reporting. | |
581 [defaults synchronize]; | |
582 } | |
583 | |
584 void OmahaService::OnURLFetchComplete(const net::URLFetcher* fetcher) { | |
585 DCHECK(fetcher_.get() == fetcher); | |
586 // Transfer the ownership of fetcher_ to this method. | |
587 std::unique_ptr<net::URLFetcher> local_fetcher = std::move(fetcher_); | |
588 | |
589 if (fetcher->GetResponseCode() != 200) { | |
590 DLOG(WARNING) << "Error contacting the Omaha server"; | |
591 SendOrScheduleNextPing(); | |
592 return; | |
593 } | |
594 | |
595 std::string response; | |
596 bool result = fetcher->GetResponseAsString(&response); | |
597 DCHECK(result); | |
598 NSData* xml = [NSData dataWithBytes:response.data() length:response.length()]; | |
599 base::scoped_nsobject<NSXMLParser> parser( | |
600 [[NSXMLParser alloc] initWithData:xml]); | |
601 const std::string application_id = ios::GetChromeBrowserProvider() | |
602 ->GetOmahaServiceProvider() | |
603 ->GetApplicationID(); | |
604 base::scoped_nsobject<ResponseParser> delegate([[ResponseParser alloc] | |
605 initWithAppId:base::SysUTF8ToNSString(application_id)]); | |
606 parser.get().delegate = delegate.get(); | |
607 | |
608 if (![parser parse] || ![delegate isCorrect]) { | |
609 DLOG(ERROR) << "Unable to parse XML response from Omaha server."; | |
610 SendOrScheduleNextPing(); | |
611 return; | |
612 } | |
613 // Handle success | |
sdefresne
2016/12/14 11:50:56
nit: can you add a dot at the end of comment? "Han
rohitrao (ping after 24h)
2016/12/14 13:26:32
Done.
| |
614 number_of_tries_ = 0; | |
615 // Schedule the next request. If requset that just finished was an install | |
616 // notification, send an active ping immediately. | |
617 next_tries_time_ = sending_install_event_ | |
618 ? base::Time::Now() | |
619 : base::Time::Now() + base::TimeDelta::FromHours( | |
620 kHoursBetweenRequests); | |
621 current_ping_time_ = next_tries_time_; | |
622 last_sent_time_ = base::Time::Now(); | |
623 last_sent_version_ = base::Version(version_info::GetVersionNumber()); | |
624 sending_install_event_ = false; | |
625 ClearInstallRetryRequestId(); | |
626 PersistStates(); | |
627 SendOrScheduleNextPing(); | |
628 | |
629 // Send notification for updates if needed. | |
630 UpgradeRecommendedDetails* details = | |
631 [delegate.get() upgradeRecommendedDetails]; | |
632 if (details) { | |
633 web::WebThread::PostTask( | |
634 web::WebThread::UI, FROM_HERE, | |
635 base::Bind(upgrade_recommended_callback_, *details)); | |
636 } | |
637 } | |
638 | |
639 void OmahaService::GetDebugInformationOnIOThread( | |
640 const base::Callback<void(base::DictionaryValue*)> callback) { | |
641 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); | |
sdefresne
2016/12/14 11:50:55
nit: can you change to use base::MakeUnique
aut
rohitrao (ping after 24h)
2016/12/14 13:26:33
Done.
| |
642 | |
643 result->SetString("message", GetCurrentPingContent()); | |
644 result->SetString("last_sent_time", | |
645 base::TimeFormatShortDateAndTime(last_sent_time_)); | |
646 result->SetString("next_tries_time", | |
647 base::TimeFormatShortDateAndTime(next_tries_time_)); | |
648 result->SetString("current_ping_time", | |
649 base::TimeFormatShortDateAndTime(current_ping_time_)); | |
650 result->SetString("last_sent_version", last_sent_version_.GetString()); | |
651 result->SetString("number_of_tries", | |
652 base::StringPrintf("%d", number_of_tries_)); | |
653 result->SetString("timer_running", | |
654 base::StringPrintf("%d", timer_.IsRunning())); | |
655 result->SetString( | |
656 "timer_current_delay", | |
657 base::StringPrintf("%llds", timer_.GetCurrentDelay().InSeconds())); | |
658 result->SetString("timer_desired_run_time", | |
659 base::TimeFormatShortDateAndTime( | |
660 base::Time::Now() + | |
661 (timer_.desired_run_time() - base::TimeTicks::Now()))); | |
662 | |
663 // Sending the value to the callback. | |
664 web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, | |
665 base::Bind(callback, base::Owned(result.release()))); | |
666 } | |
667 | |
668 bool OmahaService::IsNextPingInstallRetry() { | |
669 return [[NSUserDefaults standardUserDefaults] | |
670 stringForKey:kRetryRequestIdKey] != nil; | |
671 } | |
672 | |
673 std::string OmahaService::GetNextPingRequestId(PingContent ping_content) { | |
674 NSString* stored_id = | |
675 [[NSUserDefaults standardUserDefaults] stringForKey:kRetryRequestIdKey]; | |
676 if (stored_id) { | |
677 DCHECK(ping_content == INSTALL_EVENT); | |
678 return base::SysNSStringToUTF8(stored_id); | |
679 } else { | |
680 std::string identifier = ios::device_util::GetRandomId(); | |
681 if (ping_content == INSTALL_EVENT) | |
682 OmahaService::SetInstallRetryRequestId(identifier); | |
683 return identifier; | |
684 } | |
685 } | |
686 | |
687 void OmahaService::SetInstallRetryRequestId(const std::string& request_id) { | |
688 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; | |
689 [defaults setObject:base::SysUTF8ToNSString(request_id) | |
690 forKey:kRetryRequestIdKey]; | |
691 // Save critical state information for usage reporting. | |
692 [defaults synchronize]; | |
693 } | |
694 | |
695 void OmahaService::ClearInstallRetryRequestId() { | |
696 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; | |
697 [defaults removeObjectForKey:kRetryRequestIdKey]; | |
698 // Clear critical state information for usage reporting. | |
699 [defaults synchronize]; | |
700 } | |
701 | |
702 void OmahaService::ClearPersistentStateForTests() { | |
703 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; | |
704 [defaults removeObjectForKey:kNextTriesTimesKey]; | |
705 [defaults removeObjectForKey:kCurrentPingKey]; | |
706 [defaults removeObjectForKey:kNumberTriesKey]; | |
707 [defaults removeObjectForKey:kLastSentVersionKey]; | |
708 [defaults removeObjectForKey:kLastSentTimeKey]; | |
709 [defaults removeObjectForKey:kRetryRequestIdKey]; | |
710 } | |
OLD | NEW |