Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / ios / chrome / browser / updatable_config / updatable_config_base.mm
blob80c3ddb83fb36b810043a6a200e63428e672769d
1 // Copyright 2013 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 #import "ios/chrome/browser/updatable_config/updatable_config_base.h"
7 #include "base/logging.h"
8 #import "base/mac/bind_objc_block.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
13 #import "ios/public/provider/chrome/browser/updatable_resource_provider.h"
14 #include "ios/web/public/web_thread.h"
15 #import "net/base/mac/url_conversions.h"
16 #include "net/http/http_status_code.h"
17 #include "net/url_request/url_fetcher.h"
18 #include "net/url_request/url_fetcher_delegate.h"
19 #include "net/url_request/url_request_context.h"
20 #include "net/url_request/url_request_context_getter.h"
21 #include "url/gurl.h"
23 @interface UpdatableConfigBase ()
24 // Returns the application ID to use for fetching updatable configuration.
25 + (NSString*)defaultAppId;
26 // Fetches server-side updatable configuration plist.
27 - (void)checkUpdate;
28 #if !defined(NDEBUG)
29 // A method that will be executed on a delay if -startUpdate: was NOT called.
30 - (void)startUpdateNotCalled:(id)config;
31 // Schedules a call to -startUpdateNotCalled: for later to make sure that
32 // -startUpdate: will be called.
33 - (void)scheduleConsistencyCheck;
34 // Cancels the delayed call to -startUpdateNotCalled:.
35 - (void)cancelConsistencyCheck;
36 #endif  // !defined(NDEBUG)
37 @end
39 namespace {
41 #if !defined(NDEBUG)
42 // Global flag to enable or disable debug check that -startUpdate:
43 // has been called.
44 BOOL g_consistency_check_enabled = NO;
45 #endif
47 // Periodically fetch configuration updates from server.
48 const int64_t kPeriodicCheckInNanoseconds = 60 * 60 * 24 * NSEC_PER_SEC;
50 // Class to fetch config update |url| and also act as the delegate to
51 // handle callbacks from URLFetcher.
52 class ConfigFetcher : public net::URLFetcherDelegate {
53  public:
54   ConfigFetcher(UpdatableConfigBase* owner,
55                 id<UpdatableResourceDescriptorBridge> descriptor)
56       : owner_(owner), descriptor_(descriptor) {}
58   // Starts fetching |url| for updated configuration.
59   void Fetch(const GURL& url, net::URLRequestContextGetter* context) {
60     fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this);
61     fetcher_->SetRequestContext(context);
62     fetcher_->Start();
63   }
65   void OnURLFetchComplete(const net::URLFetcher* fetcher) override {
66     DCHECK_EQ(fetcher_, fetcher);
67     NSData* responseData = nil;
68     if (fetcher_->GetResponseCode() == net::HTTP_OK) {
69       std::string response;
70       fetcher_->GetResponseAsString(&response);
71       responseData =
72           [NSData dataWithBytes:response.c_str() length:response.length()];
73     }
74     fetcher_.reset();
75     // If data was fetched, write the fetched data to local store in a
76     // separate thread. Then update the resource descriptor that configuration
77     // update is completed. Finally, schedule the next update check.
78     web::WebThread::PostBlockingPoolTask(FROM_HERE, base::BindBlock(^{
79       BOOL updateSuccess = NO;
80       if (responseData) {
81         NSString* path = [descriptor_ updateResourcePath];
82         updateSuccess = [responseData writeToFile:path atomically:YES];
83       }
84       dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^() {
85         [descriptor_ updateCheckDidFinishWithSuccess:updateSuccess];
86       });
87       dispatch_after(
88           dispatch_time(DISPATCH_TIME_NOW, kPeriodicCheckInNanoseconds),
89           dispatch_get_main_queue(), ^{
90             [owner_ checkUpdate];
91           });
92     }));
93   };
95  private:
96   UpdatableConfigBase* owner_;
97   id<UpdatableResourceDescriptorBridge> descriptor_;
98   scoped_ptr<net::URLFetcher> fetcher_;
101 }  // namespace
103 @implementation UpdatableConfigBase {
104   base::scoped_nsprotocol<id<UpdatableResourceBridge>> _updatableResource;
105   scoped_ptr<ConfigFetcher> _configFetcher;
106   scoped_refptr<net::URLRequestContextGetter> _requestContextGetter;
109 + (void)enableConsistencyCheck {
110 #if !defined(NDEBUG)
111   g_consistency_check_enabled = YES;
112 #endif
115 // Overrides default designated initializer.
116 - (instancetype)init {
117   NOTREACHED();
118   return nil;
121 - (instancetype)initWithAppId:(NSString*)appId
122                       version:(NSString*)appVersion
123                         plist:(NSString*)plistName {
124   self = [super init];
125   if (self) {
126     _updatableResource.reset([self newResource:plistName]);
127     // UpdatableResourceBridge initializes the appId to what is in the
128     // application bundle. The following overrides that with either the |appId|
129     // passed in or a default based on experimental settings if |appId| is nil.
130     if (!appId)
131       appId = [UpdatableConfigBase defaultAppId];
132     [[_updatableResource descriptor] setApplicationIdentifier:appId];
133     // Overrides the default application version if necessary.
134     if (appVersion)
135       [[_updatableResource descriptor] setApplicationVersion:appVersion];
136     // [UpdatableResourceBridge -loadDefaults] updates the resource in
137     // two phases and is probably not MT safe. However,
138     // initWithAppId:version:plist: is called from a singleton's
139     // initialization loop and thus will not be called more than once.
140     // TODO(pkl): -loadDefaults accesses the file system to load in the
141     // plist. This should be done via PostBlockingPoolTask.
142     [_updatableResource loadDefaults];
144     NSString* notificationName = ios::GetChromeBrowserProvider()
145                                      ->GetUpdatableResourceProvider()
146                                      ->GetUpdateNotificationName();
147     [[NSNotificationCenter defaultCenter]
148         addObserver:self
149            selector:@selector(resourceDidUpdate:)
150                name:notificationName
151              object:[_updatableResource descriptor]];
153 #if !defined(NDEBUG)
154     [self scheduleConsistencyCheck];
155 #endif
156   }
157   return self;
160 - (void)dealloc {
161   [[NSNotificationCenter defaultCenter] removeObserver:self];
162 #if !defined(NDEBUG)
163   [self cancelConsistencyCheck];
164 #endif
165   [super dealloc];
168 - (void)startUpdate:(net::URLRequestContextGetter*)requestContextGetter {
169 #if !defined(NDEBUG)
170   [self cancelConsistencyCheck];
171 #endif
172   _requestContextGetter = requestContextGetter;
173   [self checkUpdate];
176 - (void)stopUpdateChecks {
177   _requestContextGetter = nullptr;
180 - (void)resourceDidUpdate:(NSNotification*)notification {
181   id sender = [notification object];
182   DCHECK([_updatableResource descriptor] == sender);
185 #pragma mark -
186 #pragma mark For Subclasses
188 - (id<UpdatableResourceBridge>)newResource:(NSString*)resourceName {
189   // Subclasses must override this factory method.
190   NOTREACHED();
191   return nil;
194 - (id<UpdatableResourceBridge>)updatableResource {
195   return _updatableResource.get();
198 #pragma mark -
199 #pragma mark For Debug Compilations
201 #if !defined(NDEBUG)
202 - (void)scheduleConsistencyCheck {
203   // Sets a delayed call that will cause a DCHECK if -startUpdate:
204   // was not called.
205   [self performSelector:@selector(startUpdateNotCalled:)
206              withObject:self
207              afterDelay:60.0];
210 - (void)cancelConsistencyCheck {
211   // Cancels the delayed error check since -startUpdate: has been called.
212   // Added for completeness since singletons should never be deallocated.
213   [NSObject
214       cancelPreviousPerformRequestsWithTarget:self
215                                      selector:@selector(startUpdateNotCalled:)
216                                        object:self];
219 - (void)startUpdateNotCalled:(id)config {
220   DCHECK(self == config);
221   if (!g_consistency_check_enabled)
222     return;
223   // Make sure that |startUpdate:| was called for this config.
224   NOTREACHED() << "startUpdate: was not called for "
225                << [[self description] UTF8String];
227 #endif  // !defined(NDEBUG)
229 #pragma mark -
230 #pragma mark For Unit Testing
232 - (void)setUpdatableResource:(id<UpdatableResourceBridge>)resource {
233   _updatableResource.reset([resource retain]);
236 #pragma mark -
237 #pragma mark Private
239 + (NSString*)defaultAppId {
240   // During development and dogfooding, allow a different configuration
241   // update file to be used for testing.
242   NSString* flag = [[NSUserDefaults standardUserDefaults]
243       stringForKey:@"UpdatableConfigLocation"];
244   if ([flag length]) {
245     if ([flag isEqualToString:@"Stable"])
246       return @"com.google.chrome.ios";
247     else if ([flag isEqualToString:@"Dogfood"])
248       return @"com.google.chrome.ios.beta";
249     else if ([flag isEqualToString:@"None"])
250       return @"this.does.not.update";
251   }
252   return [[NSBundle mainBundle] bundleIdentifier];
255 - (void)checkUpdate {
256   if (!_requestContextGetter.get())
257     return;
258   if (!_configFetcher) {
259     _configFetcher.reset(
260         new ConfigFetcher(self, [_updatableResource descriptor]));
261   }
262   GURL url = net::GURLWithNSURL([[_updatableResource descriptor] updateURL]);
263   _configFetcher->Fetch(url, _requestContextGetter.get());
266 @end