Chromium Code Reviews| Index: ios/chrome/browser/omaha/omaha_service.mm |
| diff --git a/ios/chrome/browser/omaha/omaha_service.mm b/ios/chrome/browser/omaha/omaha_service.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..de753504b71850fa20cf5bf926f8b18a8c4804b8 |
| --- /dev/null |
| +++ b/ios/chrome/browser/omaha/omaha_service.mm |
| @@ -0,0 +1,710 @@ |
| +// Copyright 2012 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 "ios/chrome/browser/omaha/omaha_service.h" |
| + |
| +#include <Foundation/Foundation.h> |
| + |
| +#include <memory> |
| +#include <utility> |
| + |
| +#include "base/bind.h" |
| +#include "base/i18n/time_formatting.h" |
| +#include "base/ios/device_util.h" |
| +#include "base/logging.h" |
| +#include "base/mac/scoped_nsobject.h" |
| +#include "base/metrics/field_trial.h" |
| +#include "base/rand_util.h" |
| +#include "base/strings/stringprintf.h" |
| +#include "base/strings/sys_string_conversions.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/sys_info.h" |
| +#include "base/time/time.h" |
| +#include "base/values.h" |
| +#include "components/metrics/metrics_pref_names.h" |
| +#include "components/prefs/pref_service.h" |
| +#include "components/version_info/version_info.h" |
| +#include "ios/chrome/browser/application_context.h" |
| +#include "ios/chrome/browser/arch_util.h" |
| +#include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h" |
| +#include "ios/chrome/browser/browser_state_metrics/browser_state_metrics.h" |
| +#include "ios/chrome/browser/install_time_util.h" |
| +#include "ios/chrome/browser/ui/ui_util.h" |
| +#include "ios/chrome/browser/upgrade/upgrade_recommended_details.h" |
| +#include "ios/chrome/common/channel_info.h" |
| +#include "ios/public/provider/chrome/browser/chrome_browser_provider.h" |
| +#include "ios/public/provider/chrome/browser/omaha/omaha_service_provider.h" |
| +#include "ios/public/provider/chrome/browser/omaha/omaha_xml_writer.h" |
| +#include "ios/web/public/web_thread.h" |
| +#include "libxml/xmlwriter.h" |
| +#include "net/base/backoff_entry.h" |
| +#include "net/base/load_flags.h" |
| +#include "net/url_request/url_fetcher.h" |
| +#include "url/gurl.h" |
| + |
| +namespace { |
| +// Number of hours to wait between successful requests. |
| +const int kHoursBetweenRequests = 5; |
| +// Minimal time to wait between retry requests. |
| +const CFTimeInterval kPostRetryBaseSeconds = 3600; |
| +// Maximal time to wait between retry requests. |
| +const CFTimeInterval kPostRetryMaxSeconds = 6 * kPostRetryBaseSeconds; |
| + |
| +// Default last sent application version when none has been sent yet. |
| +const char kDefaultLastSentVersion[] = "0.0.0.0"; |
| + |
| +// Key for saving states in the UserDefaults. |
| +NSString* const kNextTriesTimesKey = @"ChromeOmahaServiceNextTries"; |
| +NSString* const kCurrentPingKey = @"ChromeOmahaServiceCurrentPing"; |
| +NSString* const kNumberTriesKey = @"ChromeOmahaServiceNumberTries"; |
| +NSString* const kLastSentVersionKey = @"ChromeOmahaServiceLastSentVersion"; |
| +NSString* const kLastSentTimeKey = @"ChromeOmahaServiceLastSentTime"; |
| +NSString* const kRetryRequestIdKey = @"ChromeOmahaServiceRetryRequestId"; |
| + |
| +class XmlWrapper : public OmahaXmlWriter { |
| + public: |
| + XmlWrapper() |
| + : buffer_(xmlBufferCreate()), |
| + writer_(xmlNewTextWriterMemory(buffer_, /* compression */ 0)) { |
| + DCHECK(buffer_); |
| + DCHECK(writer_); |
| + } |
| + |
| + ~XmlWrapper() override { |
| + xmlFreeTextWriter(writer_); |
| + xmlBufferFree(buffer_); |
| + } |
| + |
| + void StartElement(const char* name) override { |
| + DCHECK(name); |
| + int result = xmlTextWriterStartElement( |
| + writer_, reinterpret_cast<const xmlChar*>(name)); |
| + DCHECK_GE(result, 0); |
| + } |
| + |
| + void EndElement() override { |
| + int result = xmlTextWriterEndElement(writer_); |
| + DCHECK_GE(result, 0); |
| + } |
| + |
| + void WriteAttribute(const char* name, const char* value) override { |
| + DCHECK(name); |
| + int result = xmlTextWriterWriteAttribute( |
| + writer_, reinterpret_cast<const xmlChar*>(name), |
| + reinterpret_cast<const xmlChar*>(value)); |
| + DCHECK_GE(result, 0); |
| + } |
| + |
| + void Finalize() override { |
| + int result = xmlTextWriterEndDocument(writer_); |
| + DCHECK_GE(result, 0); |
| + } |
| + |
| + std::string GetContentAsString() override { |
| + return std::string(reinterpret_cast<char*>(buffer_->content)); |
| + } |
| + |
| + private: |
| + xmlBufferPtr buffer_; |
| + xmlTextWriterPtr writer_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(XmlWrapper); |
| +}; |
| + |
| +} // namespace |
| + |
| +#pragma mark - |
| + |
| +// 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.
|
| +@interface ResponseParser : NSObject<NSXMLParserDelegate> { |
| + @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.
|
| + BOOL hasError_; |
| + BOOL responseIsParsed_; |
| + BOOL appIsParsed_; |
| + BOOL updateCheckIsParsed_; |
| + BOOL urlIsParsed_; |
| + BOOL manifestIsParsed_; |
| + BOOL pingIsParsed_; |
| + BOOL eventIsParsed_; |
| + base::scoped_nsobject<NSString> appId_; |
| + std::unique_ptr<UpgradeRecommendedDetails> updateInformation_; |
| +} |
| + |
| +// Initialization method. |appId| is the application id one expects to find in |
| +// the response message. |
| +- (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 =)
|
| + |
| +// Returns YES if the message has been correctly parsed. |
| +- (BOOL)isCorrect; |
| + |
| +// If an upgrade is possible, returns the details of the notification to send. |
| +// Otherwise, return NULL. |
| +- (UpgradeRecommendedDetails*)upgradeRecommendedDetails; |
| + |
| +@end |
| + |
| +@implementation ResponseParser |
| + |
| +- (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.
|
| + if (self = [super init]) { |
| + appId_.reset([appId retain]); |
| + } |
| + return self; |
| +} |
| + |
| +- (BOOL)isCorrect { |
| + // A response should have either a ping ACK or an event ACK, depending on the |
| + // contents of the request. |
| + return !hasError_ && (pingIsParsed_ || eventIsParsed_); |
| +} |
| + |
| +- (UpgradeRecommendedDetails*)upgradeRecommendedDetails { |
| + return updateInformation_.get(); |
| +} |
| + |
| +// This method is parsing a message with the following type: |
| +// <response...> |
| +// <daystart.../> |
| +// <app...> |
| +// <updatecheck status="ok"> |
| +// <urls> |
| +// <url codebase="???"/> |
| +// </urls> |
| +// <manifest version="???"> |
| +// <packages> |
| +// <package hash="0" name="Chrome" required="true" size="0"/> |
| +// </packages> |
| +// <actions> |
| +// <action event="update" run="Chrome"/> |
| +// <action event="postinstall"/> |
| +// </actions> |
| +// </manifest> |
| +// </updatecheck> |
| +// <ping.../> |
| +// </app> |
| +// </response> |
| +// --- OR --- |
| +// <response...> |
| +// <daystart.../> |
| +// <app...> |
| +// <event.../> |
| +// </app> |
| +// </response> |
| +// See http://code.google.com/p/omaha/wiki/ServerProtocol for details. |
| +- (void)parser:(NSXMLParser*)parser |
| + didStartElement:(NSString*)elementName |
| + namespaceURI:(NSString*)namespaceURI |
| + qualifiedName:(NSString*)qualifiedName |
| + attributes:(NSDictionary*)attributeDict { |
| + if (hasError_) |
| + return; |
| + |
| + // Array of uninteresting tags in the Omaha xml response. |
| + NSArray* ignoredTagNames = |
| + @[ @"action", @"actions", @"daystart", @"package", @"packages", @"urls" ]; |
| + if ([ignoredTagNames containsObject:elementName]) |
| + return; |
| + |
| + if (!responseIsParsed_) { |
| + if ([elementName isEqualToString:@"response"] && |
| + [[attributeDict valueForKey:@"protocol"] isEqualToString:@"3.0"] && |
| + [[attributeDict valueForKey:@"server"] isEqualToString:@"prod"]) { |
| + responseIsParsed_ = YES; |
| + } else { |
| + hasError_ = YES; |
| + } |
| + } else if (!appIsParsed_) { |
| + if ([elementName isEqualToString:@"app"] && |
| + [[attributeDict valueForKey:@"status"] isEqualToString:@"ok"] && |
| + [[attributeDict valueForKey:@"appid"] isEqualToString:appId_]) { |
| + appIsParsed_ = YES; |
| + } else { |
| + hasError_ = YES; |
| + } |
| + } else if (!eventIsParsed_ && !updateCheckIsParsed_) { |
| + if ([elementName isEqualToString:@"updatecheck"]) { |
| + updateCheckIsParsed_ = YES; |
| + NSString* status = [attributeDict valueForKey:@"status"]; |
| + if ([status isEqualToString:@"noupdate"]) { |
| + // No update is available on the Market, so we won't get a <url> or |
| + // <manifest> tag. |
| + urlIsParsed_ = YES; |
| + manifestIsParsed_ = YES; |
| + } else if ([status isEqualToString:@"ok"]) { |
| + 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.
|
| + } else { |
| + hasError_ = YES; |
| + } |
| + } else if ([elementName isEqualToString:@"event"]) { |
| + if ([[attributeDict valueForKey:@"status"] isEqualToString:@"ok"]) { |
| + eventIsParsed_ = YES; |
| + } else { |
| + hasError_ = YES; |
| + } |
| + } else { |
| + hasError_ = YES; |
| + } |
| + } else if (!urlIsParsed_) { |
| + if ([elementName isEqualToString:@"url"] && |
| + [[attributeDict valueForKey:@"codebase"] length] > 0) { |
| + urlIsParsed_ = YES; |
| + DCHECK(updateInformation_); |
| + NSString* url = [attributeDict valueForKey:@"codebase"]; |
| + if ([[url substringFromIndex:([url length] - 1)] isEqualToString:@"/"]) |
| + url = [url substringToIndex:([url length] - 1)]; |
| + updateInformation_.get()->upgrade_url = |
| + GURL(base::SysNSStringToUTF8(url)); |
| + if (!updateInformation_.get()->upgrade_url.is_valid()) |
| + hasError_ = YES; |
| + } else { |
| + hasError_ = YES; |
| + } |
| + } else if (!manifestIsParsed_) { |
| + if ([elementName isEqualToString:@"manifest"] && |
| + [attributeDict valueForKey:@"version"]) { |
| + manifestIsParsed_ = YES; |
| + DCHECK(updateInformation_); |
| + updateInformation_.get()->next_version = |
| + base::SysNSStringToUTF8([attributeDict valueForKey:@"version"]); |
| + } else { |
| + hasError_ = YES; |
| + } |
| + } else if (!pingIsParsed_) { |
| + if ([elementName isEqualToString:@"ping"] && |
| + [[attributeDict valueForKey:@"status"] isEqualToString:@"ok"]) { |
| + pingIsParsed_ = YES; |
| + } else { |
| + hasError_ = YES; |
| + } |
| + } else { |
| + hasError_ = YES; |
| + } |
| +} |
| + |
| +@end |
| + |
| +// static |
| +OmahaService* OmahaService::GetInstance() { |
| + return base::Singleton<OmahaService>::get(); |
| +} |
| + |
| +// static |
| +void OmahaService::Start(net::URLRequestContextGetter* request_context_getter, |
| + const UpgradeRecommendedCallback& callback) { |
| + DCHECK(request_context_getter); |
| + DCHECK(!callback.is_null()); |
| + OmahaService* result = GetInstance(); |
| + result->set_upgrade_recommended_callback(callback); |
| + // This should only be called once. |
| + DCHECK(!result->request_context_getter_); |
| + result->request_context_getter_ = request_context_getter; |
| + result->locale_lang_ = GetApplicationContext()->GetApplicationLocale(); |
| + web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, |
| + base::Bind(&OmahaService::SendOrScheduleNextPing, |
| + base::Unretained(result))); |
| +} |
| + |
| +OmahaService::OmahaService() |
| + : request_context_getter_(NULL), |
| + schedule_(true), |
| + application_install_date_(0), |
| + sending_install_event_(false) { |
| + Initialize(); |
| +} |
| + |
| +OmahaService::OmahaService(bool schedule) |
| + : request_context_getter_(NULL), |
| + schedule_(schedule), |
| + application_install_date_(0), |
| + sending_install_event_(false) { |
| + Initialize(); |
| +} |
| + |
| +OmahaService::~OmahaService() {} |
| + |
| +void OmahaService::Initialize() { |
| + // Initialize the provider at the same time as the rest of the service. |
| + ios::GetChromeBrowserProvider()->GetOmahaServiceProvider()->Initialize(); |
| + |
| + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| + next_tries_time_ = base::Time::FromCFAbsoluteTime( |
| + [defaults doubleForKey:kNextTriesTimesKey]); |
| + current_ping_time_ = |
| + base::Time::FromCFAbsoluteTime([defaults doubleForKey:kCurrentPingKey]); |
| + number_of_tries_ = [defaults integerForKey:kNumberTriesKey]; |
| + last_sent_time_ = |
| + base::Time::FromCFAbsoluteTime([defaults doubleForKey:kLastSentTimeKey]); |
| + NSString* lastSentVersion = [defaults stringForKey:kLastSentVersionKey]; |
| + if (lastSentVersion) { |
| + last_sent_version_ = |
| + base::Version(base::SysNSStringToUTF8(lastSentVersion)); |
| + } else { |
| + last_sent_version_ = base::Version(kDefaultLastSentVersion); |
| + } |
| + |
| + application_install_date_ = |
| + GetApplicationContext()->GetLocalState()->GetInt64( |
| + metrics::prefs::kInstallDate); |
| + DCHECK(application_install_date_); |
| + |
| + // Whether data should be persisted again to the user preferences. |
| + bool persist_again = false; |
| + |
| + base::Time now = base::Time::Now(); |
| + // If |last_sent_time_| is in the future, the clock has been tampered with. |
| + // Reset |last_sent_time_| to now. |
| + if (last_sent_time_ > now) { |
| + last_sent_time_ = now; |
| + persist_again = true; |
| + } |
| + |
| + // If the |next_tries_time_| is more than kHoursBetweenRequests hours away, |
| + // there is a possibility that the clock has been tampered with. Reschedule |
| + // the ping to be the usual interval after the last successful one. |
| + if (next_tries_time_ - now > |
| + base::TimeDelta::FromHours(kHoursBetweenRequests)) { |
| + next_tries_time_ = |
| + last_sent_time_ + base::TimeDelta::FromHours(kHoursBetweenRequests); |
| + persist_again = true; |
| + } |
| + |
| + // Fire a ping as early as possible if the version changed. |
| + base::Version current_version(version_info::GetVersionNumber()); |
| + if (last_sent_version_ < current_version) { |
| + next_tries_time_ = base::Time::Now() - base::TimeDelta::FromSeconds(1); |
| + number_of_tries_ = 0; |
| + persist_again = true; |
| + } |
| + |
| + if (persist_again) |
| + PersistStates(); |
| +} |
| + |
| +// static |
| +void OmahaService::GetDebugInformation( |
| + const base::Callback<void(base::DictionaryValue*)> callback) { |
| + web::WebThread::PostTask( |
| + web::WebThread::IO, FROM_HERE, |
| + base::Bind(&OmahaService::GetDebugInformationOnIOThread, |
| + base::Unretained(GetInstance()), callback)); |
| +} |
| + |
| +// static |
| +base::TimeDelta OmahaService::GetBackOff(uint8_t number_of_tries) { |
| + // Configuration for the service exponential backoff |
| + static net::BackoffEntry::Policy kBackoffPolicy = { |
| + 0, // num_errors_to_ignore |
| + kPostRetryBaseSeconds * 1000, // initial_delay_ms |
| + 2.0, // multiply_factor |
| + 0.1, // jitter_factor |
| + kPostRetryMaxSeconds * 1000, // maximum_backoff_ms |
| + -1, // entry_lifetime_ms |
| + false // always_use_initial_delay |
| + }; |
| + |
| + net::BackoffEntry backoff_entry(&kBackoffPolicy); |
| + for (int i = 0; i < number_of_tries; ++i) { |
| + backoff_entry.InformOfRequest(false); |
| + } |
| + |
| + return backoff_entry.GetTimeUntilRelease(); |
| +} |
| + |
| +std::string OmahaService::GetPingContent(const std::string& requestId, |
| + const std::string& sessionId, |
| + const std::string& versionName, |
| + const std::string& channelName, |
| + const base::Time& installationTime, |
| + PingContent pingContent) { |
| + OmahaServiceProvider* provider = |
| + ios::GetChromeBrowserProvider()->GetOmahaServiceProvider(); |
| + |
| + XmlWrapper xml_wrapper; |
| + xml_wrapper.StartElement("request"); |
| + xml_wrapper.WriteAttribute("protocol", "3.0"); |
| + xml_wrapper.WriteAttribute("version", "iOS-1.0.0.0"); |
| + xml_wrapper.WriteAttribute("ismachine", "1"); |
| + xml_wrapper.WriteAttribute("requestid", requestId.c_str()); |
| + xml_wrapper.WriteAttribute("sessionid", sessionId.c_str()); |
| + provider->AppendExtraAttributes("request", &xml_wrapper); |
| + xml_wrapper.WriteAttribute("hardware_class", |
| + ios::device_util::GetPlatform().c_str()); |
| + // Set up <os platform="ios"... /> |
| + xml_wrapper.StartElement("os"); |
| + xml_wrapper.WriteAttribute("platform", "ios"); |
| + xml_wrapper.WriteAttribute("version", |
| + base::SysInfo::OperatingSystemVersion().c_str()); |
| + xml_wrapper.WriteAttribute("arch", arch_util::kCurrentArch); |
| + xml_wrapper.EndElement(); |
| + |
| + bool is_first_install = |
| + pingContent == INSTALL_EVENT && |
| + last_sent_version_ == base::Version(kDefaultLastSentVersion); |
| + |
| + // Set up <app version="" ...> |
| + xml_wrapper.StartElement("app"); |
| + if (pingContent == INSTALL_EVENT) { |
| + std::string previous_version = |
| + is_first_install ? "" : last_sent_version_.GetString(); |
| + xml_wrapper.WriteAttribute("version", previous_version.c_str()); |
| + xml_wrapper.WriteAttribute("nextversion", versionName.c_str()); |
| + } else { |
| + xml_wrapper.WriteAttribute("version", versionName.c_str()); |
| + xml_wrapper.WriteAttribute("nextversion", ""); |
| + } |
| + xml_wrapper.WriteAttribute("lang", locale_lang_.c_str()); |
| + xml_wrapper.WriteAttribute("brand", provider->GetBrandCode().c_str()); |
| + xml_wrapper.WriteAttribute("client", ""); |
| + std::string application_id = provider->GetApplicationID(); |
| + xml_wrapper.WriteAttribute("appid", application_id.c_str()); |
| + std::string install_age; |
| + if (is_first_install) { |
| + install_age = "-1"; |
| + } else if (!installationTime.is_null() && |
| + installationTime.ToTimeT() != |
| + install_time_util::kUnknownInstallDate) { |
| + install_age = base::StringPrintf( |
| + "%d", (base::Time::Now() - installationTime).InDays()); |
| + } |
| + provider->AppendExtraAttributes("app", &xml_wrapper); |
| + // If the install date is unknown, send nothing. |
| + if (!install_age.empty()) |
| + xml_wrapper.WriteAttribute("installage", install_age.c_str()); |
| + |
| + if (pingContent == INSTALL_EVENT) { |
| + // Add an install complete event. |
| + xml_wrapper.StartElement("event"); |
| + if (is_first_install) { |
| + xml_wrapper.WriteAttribute("eventtype", "2"); // install |
| + } else { |
| + xml_wrapper.WriteAttribute("eventtype", "3"); // update |
| + } |
| + xml_wrapper.WriteAttribute("eventresult", "1"); // succeeded |
| + xml_wrapper.EndElement(); |
| + } else { |
| + // Set up <updatecheck/> |
| + xml_wrapper.StartElement("updatecheck"); |
| + xml_wrapper.WriteAttribute("tag", channelName.c_str()); |
| + xml_wrapper.EndElement(); |
| + |
| + // Set up <ping active=1/> |
| + xml_wrapper.StartElement("ping"); |
| + xml_wrapper.WriteAttribute("active", "1"); |
| + xml_wrapper.EndElement(); |
| + } |
| + |
| + // End app. |
| + xml_wrapper.EndElement(); |
| + // End request. |
| + xml_wrapper.EndElement(); |
| + |
| + xml_wrapper.Finalize(); |
| + return xml_wrapper.GetContentAsString(); |
| +} |
| + |
| +std::string OmahaService::GetCurrentPingContent() { |
| + base::Version current_version(version_info::GetVersionNumber()); |
| + sending_install_event_ = last_sent_version_ < current_version; |
| + PingContent ping_content = |
| + sending_install_event_ ? INSTALL_EVENT : USAGE_PING; |
| + |
| + // An install retry ping only makes sense if an install event must be send. |
| + DCHECK(sending_install_event_ || !IsNextPingInstallRetry()); |
| + std::string request_id = GetNextPingRequestId(ping_content); |
| + return GetPingContent(request_id, ios::device_util::GetRandomId(), |
| + version_info::GetVersionNumber(), GetChannelString(), |
| + base::Time::FromTimeT(application_install_date_), |
| + ping_content); |
| +} |
| + |
| +void OmahaService::SendPing() { |
| + // Check that no request is in progress. |
| + DCHECK(!fetcher_); |
| + |
| + GURL url(ios::GetChromeBrowserProvider() |
| + ->GetOmahaServiceProvider() |
| + ->GetUpdateServerURL()); |
| + if (!url.is_valid()) { |
| + return; |
| + } |
| + |
| + fetcher_ = net::URLFetcher::Create(0, url, net::URLFetcher::POST, this); |
| + fetcher_->SetRequestContext(request_context_getter_); |
| + fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | |
| + net::LOAD_DO_NOT_SAVE_COOKIES); |
| + fetcher_->SetUploadData("text/xml", GetCurrentPingContent()); |
| + |
| + // If this is not the first try, notify the omaha server. |
| + if (number_of_tries_ && IsNextPingInstallRetry()) { |
| + fetcher_->SetExtraRequestHeaders(base::StringPrintf( |
| + "X-RequestAge: %lld", |
| + (base::Time::Now() - current_ping_time_).InSeconds())); |
| + } |
| + |
| + // Update last fail time and number of tries, so that if anything fails |
| + // catastrophically, the fail is taken into account. |
| + if (number_of_tries_ < 30) |
| + ++number_of_tries_; |
| + next_tries_time_ = base::Time::Now() + GetBackOff(number_of_tries_); |
| + PersistStates(); |
| + |
| + fetcher_->Start(); |
| +} |
| + |
| +void OmahaService::SendOrScheduleNextPing() { |
| + base::Time now = base::Time::Now(); |
| + if (next_tries_time_ <= now) { |
| + SendPing(); |
| + return; |
| + } |
| + if (schedule_) { |
| + timer_.Start(FROM_HERE, next_tries_time_ - now, this, |
| + &OmahaService::SendPing); |
| + } |
| +} |
| + |
| +void OmahaService::PersistStates() { |
| + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| + |
| + [defaults setDouble:next_tries_time_.ToCFAbsoluteTime() |
| + forKey:kNextTriesTimesKey]; |
| + [defaults setDouble:current_ping_time_.ToCFAbsoluteTime() |
| + forKey:kCurrentPingKey]; |
| + [defaults setDouble:last_sent_time_.ToCFAbsoluteTime() |
| + forKey:kLastSentTimeKey]; |
| + [defaults setInteger:number_of_tries_ forKey:kNumberTriesKey]; |
| + [defaults setObject:base::SysUTF8ToNSString(last_sent_version_.GetString()) |
| + forKey:kLastSentVersionKey]; |
| + |
| + // Save critical state information for usage reporting. |
| + [defaults synchronize]; |
| +} |
| + |
| +void OmahaService::OnURLFetchComplete(const net::URLFetcher* fetcher) { |
| + DCHECK(fetcher_.get() == fetcher); |
| + // Transfer the ownership of fetcher_ to this method. |
| + std::unique_ptr<net::URLFetcher> local_fetcher = std::move(fetcher_); |
| + |
| + if (fetcher->GetResponseCode() != 200) { |
| + DLOG(WARNING) << "Error contacting the Omaha server"; |
| + SendOrScheduleNextPing(); |
| + return; |
| + } |
| + |
| + std::string response; |
| + bool result = fetcher->GetResponseAsString(&response); |
| + DCHECK(result); |
| + NSData* xml = [NSData dataWithBytes:response.data() length:response.length()]; |
| + base::scoped_nsobject<NSXMLParser> parser( |
| + [[NSXMLParser alloc] initWithData:xml]); |
| + const std::string application_id = ios::GetChromeBrowserProvider() |
| + ->GetOmahaServiceProvider() |
| + ->GetApplicationID(); |
| + base::scoped_nsobject<ResponseParser> delegate([[ResponseParser alloc] |
| + initWithAppId:base::SysUTF8ToNSString(application_id)]); |
| + parser.get().delegate = delegate.get(); |
| + |
| + if (![parser parse] || ![delegate isCorrect]) { |
| + DLOG(ERROR) << "Unable to parse XML response from Omaha server."; |
| + SendOrScheduleNextPing(); |
| + return; |
| + } |
| + // 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.
|
| + number_of_tries_ = 0; |
| + // Schedule the next request. If requset that just finished was an install |
| + // notification, send an active ping immediately. |
| + next_tries_time_ = sending_install_event_ |
| + ? base::Time::Now() |
| + : base::Time::Now() + base::TimeDelta::FromHours( |
| + kHoursBetweenRequests); |
| + current_ping_time_ = next_tries_time_; |
| + last_sent_time_ = base::Time::Now(); |
| + last_sent_version_ = base::Version(version_info::GetVersionNumber()); |
| + sending_install_event_ = false; |
| + ClearInstallRetryRequestId(); |
| + PersistStates(); |
| + SendOrScheduleNextPing(); |
| + |
| + // Send notification for updates if needed. |
| + UpgradeRecommendedDetails* details = |
| + [delegate.get() upgradeRecommendedDetails]; |
| + if (details) { |
| + web::WebThread::PostTask( |
| + web::WebThread::UI, FROM_HERE, |
| + base::Bind(upgrade_recommended_callback_, *details)); |
| + } |
| +} |
| + |
| +void OmahaService::GetDebugInformationOnIOThread( |
| + const base::Callback<void(base::DictionaryValue*)> callback) { |
| + 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.
|
| + |
| + result->SetString("message", GetCurrentPingContent()); |
| + result->SetString("last_sent_time", |
| + base::TimeFormatShortDateAndTime(last_sent_time_)); |
| + result->SetString("next_tries_time", |
| + base::TimeFormatShortDateAndTime(next_tries_time_)); |
| + result->SetString("current_ping_time", |
| + base::TimeFormatShortDateAndTime(current_ping_time_)); |
| + result->SetString("last_sent_version", last_sent_version_.GetString()); |
| + result->SetString("number_of_tries", |
| + base::StringPrintf("%d", number_of_tries_)); |
| + result->SetString("timer_running", |
| + base::StringPrintf("%d", timer_.IsRunning())); |
| + result->SetString( |
| + "timer_current_delay", |
| + base::StringPrintf("%llds", timer_.GetCurrentDelay().InSeconds())); |
| + result->SetString("timer_desired_run_time", |
| + base::TimeFormatShortDateAndTime( |
| + base::Time::Now() + |
| + (timer_.desired_run_time() - base::TimeTicks::Now()))); |
| + |
| + // Sending the value to the callback. |
| + web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, |
| + base::Bind(callback, base::Owned(result.release()))); |
| +} |
| + |
| +bool OmahaService::IsNextPingInstallRetry() { |
| + return [[NSUserDefaults standardUserDefaults] |
| + stringForKey:kRetryRequestIdKey] != nil; |
| +} |
| + |
| +std::string OmahaService::GetNextPingRequestId(PingContent ping_content) { |
| + NSString* stored_id = |
| + [[NSUserDefaults standardUserDefaults] stringForKey:kRetryRequestIdKey]; |
| + if (stored_id) { |
| + DCHECK(ping_content == INSTALL_EVENT); |
| + return base::SysNSStringToUTF8(stored_id); |
| + } else { |
| + std::string identifier = ios::device_util::GetRandomId(); |
| + if (ping_content == INSTALL_EVENT) |
| + OmahaService::SetInstallRetryRequestId(identifier); |
| + return identifier; |
| + } |
| +} |
| + |
| +void OmahaService::SetInstallRetryRequestId(const std::string& request_id) { |
| + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| + [defaults setObject:base::SysUTF8ToNSString(request_id) |
| + forKey:kRetryRequestIdKey]; |
| + // Save critical state information for usage reporting. |
| + [defaults synchronize]; |
| +} |
| + |
| +void OmahaService::ClearInstallRetryRequestId() { |
| + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| + [defaults removeObjectForKey:kRetryRequestIdKey]; |
| + // Clear critical state information for usage reporting. |
| + [defaults synchronize]; |
| +} |
| + |
| +void OmahaService::ClearPersistentStateForTests() { |
| + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| + [defaults removeObjectForKey:kNextTriesTimesKey]; |
| + [defaults removeObjectForKey:kCurrentPingKey]; |
| + [defaults removeObjectForKey:kNumberTriesKey]; |
| + [defaults removeObjectForKey:kLastSentVersionKey]; |
| + [defaults removeObjectForKey:kLastSentTimeKey]; |
| + [defaults removeObjectForKey:kRetryRequestIdKey]; |
| +} |