• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 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 #ifndef CHROME_BROWSER_CHROMEOS_IMAGEBURNER_BURN_MANAGER_H_
6 #define CHROME_BROWSER_CHROMEOS_IMAGEBURNER_BURN_MANAGER_H_
7 
8 #include <list>
9 #include <map>
10 #include <set>
11 #include <string>
12 #include <vector>
13 
14 #include "base/files/file_path.h"
15 #include "base/memory/ref_counted_memory.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/observer_list.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/chromeos/imageburner/burn_device_handler.h"
20 #include "chromeos/disks/disk_mount_manager.h"
21 #include "chromeos/network/network_state_handler_observer.h"
22 #include "net/url_request/url_fetcher_delegate.h"
23 #include "url/gurl.h"
24 
25 namespace net {
26 class URLFetcher;
27 class URLRequestContextGetter;
28 }  // namespace net
29 
30 namespace chromeos {
31 
32 enum BurnEvent {
33   UNZIP_STARTED,
34   UNZIP_COMPLETE,
35   UNZIP_FAIL,
36   BURN_UPDATE,
37   BURN_SUCCESS,
38   BURN_FAIL,
39   UNKNOWN
40 };
41 
42 struct ImageBurnStatus {
ImageBurnStatusImageBurnStatus43   ImageBurnStatus() : amount_burnt(0), total_size(0) {
44   }
45 
ImageBurnStatusImageBurnStatus46   ImageBurnStatus(int64 burnt, int64 total)
47       : amount_burnt(burnt), total_size(total) {
48   }
49 
50   int64 amount_burnt;
51   int64 total_size;
52 };
53 
54 namespace imageburner {
55 
56 // An enum used to describe what type of progress is being made.
57 // TODO(hidehiko): This should be merged into the StateMachine's state.
58 enum ProgressType {
59   DOWNLOADING,
60   UNZIPPING,
61   BURNING
62 };
63 
64 // Config file properties.
65 extern const char kName[];
66 extern const char kHwid[];
67 extern const char kFileName[];
68 extern const char kUrl[];
69 
70 // Config file is divided into blocks. Each block is associated with one image
71 // and containes information about that image in form of key-value pairs, one
72 // pair per line. Each block starts with name property.
73 // Also, Each image can be associated with multiple hardware classes, so we
74 // treat hwid property separately.
75 // Config file example:
76 //   name=image1
77 //   version=version1
78 //   hwid=hwid1
79 //   hwid=hwid2
80 //
81 //   name=name2
82 //   version=version2
83 //   hwid=hwid3
84 class ConfigFile {
85  public:
86   ConfigFile();
87 
88   explicit ConfigFile(const std::string& file_content);
89 
90   ~ConfigFile();
91 
92   // Builds config file data structure.
93   void reset(const std::string& file_content);
94 
95   void clear();
96 
empty()97   bool empty() const { return config_struct_.empty(); }
98 
size()99   size_t size() const { return config_struct_.size(); }
100 
101   // Returns property_name property of image for hardware class hwid.
102   const std::string& GetProperty(const std::string& property_name,
103                                  const std::string& hwid) const;
104 
105  private:
106   void DeleteLastBlockIfHasNoHwid();
107   void ProcessLine(const std::vector<std::string>& line);
108 
109   typedef std::map<std::string, std::string> PropertyMap;
110   typedef std::set<std::string> HwidsSet;
111 
112   // Struct that contains config file block info. We separate hwid from other
113   // properties for two reasons:
114   //   * there are multiple hwids defined for each block.
115   //   * we will retieve properties by hwid.
116   struct ConfigFileBlock {
117     ConfigFileBlock();
118     ~ConfigFileBlock();
119 
120     PropertyMap properties;
121     HwidsSet hwids;
122   };
123 
124   // At the moment we have only two entries in the config file, so we can live
125   // with linear search. Should consider changing data structure if number of
126   // entries gets bigger.
127   // Also, there is only one entry for each hwid, if that changes we should
128   // return vector of strings.
129   typedef std::list<ConfigFileBlock> BlockList;
130   BlockList config_struct_;
131 };
132 
133 class StateMachine {
134  public:
135   enum State {
136     INITIAL,
137     DOWNLOADING,
138     BURNING,
139   };
140 
state()141   State state() { return state_; }
142 
143   class Observer {
144    public:
145     virtual void OnBurnStateChanged(State new_state) = 0;
146     virtual void OnError(int error_message_id) = 0;
147   };
148 
149   StateMachine();
150   ~StateMachine();
151 
download_started()152   bool download_started() const { return download_started_; }
OnDownloadStarted()153   void OnDownloadStarted() {
154     download_started_ = true;
155     state_ = DOWNLOADING;
156     OnStateChanged();
157   }
158 
download_finished()159   bool download_finished() const { return download_finished_; }
OnDownloadFinished()160   void OnDownloadFinished() { download_finished_ = true; }
161 
OnBurnStarted()162   void OnBurnStarted() {
163     state_ = BURNING;
164     OnStateChanged();
165   }
166 
new_burn_posible()167   bool new_burn_posible() const { return state_ == INITIAL; }
168 
169   void OnSuccess();
170   void OnError(int error_message_id);
171 
OnStateChanged()172   void OnStateChanged() {
173     FOR_EACH_OBSERVER(Observer, observers_, OnBurnStateChanged(state_));
174   }
175 
AddObserver(Observer * observer)176   void AddObserver(Observer* observer) {
177     observers_.AddObserver(observer);
178   }
179 
RemoveObserver(Observer * observer)180   void RemoveObserver(Observer* observer) {
181     observers_.RemoveObserver(observer);
182   }
183 
184  private:
185   bool download_started_;
186   bool download_finished_;
187 
188   State state_;
189 
190   ObserverList<Observer> observers_;
191 
192   DISALLOW_COPY_AND_ASSIGN(StateMachine);
193 };
194 
195 // This is a system-wide singleton class to manage burning the recovery media.
196 // Here is how the burning image procedure works:
197 // 0) Choose the device the image to be burned (manually via web-ui).
198 // 1) Create ImageDir, which is a working directory for the procedure.
199 // 2) Download the config file.
200 //   2-1) Fetch the config file from the server.
201 //   2-2) Parse the config file content, and extract url and name of the image
202 //        file.
203 // 3) Fetch the image file.
204 // 4) Burn the image to the device.
205 //   4-1) Unzip the fetched image file.
206 //   4-2) Unmount the device from file system.
207 //   4-3) Copy the unzipped file to the device directly.
208 // Currently, this only provides some methods to start/cancel background tasks,
209 // and some accessors to obtain the current status. Other functions are
210 // in BurnController.
211 // TODO(hidehiko): Simplify the relationship among this class,
212 // BurnController and helper classes defined above.
213 class BurnManager : public net::URLFetcherDelegate,
214                     public NetworkStateHandlerObserver {
215  public:
216   // Interface for classes that need to observe events for the burning image
217   // tasks.
218   class Observer {
219    public:
220     // Triggered when a burnable device is added.
221     virtual void OnDeviceAdded(const disks::DiskMountManager::Disk& disk) = 0;
222 
223     // Triggered when a burnable device is removed.
224     virtual void OnDeviceRemoved(const disks::DiskMountManager::Disk& disk) = 0;
225 
226     // Triggered when a network is detected.
227     virtual void OnNetworkDetected() = 0;
228 
229     // Triggered when burning the image is successfully done.
230     virtual void OnSuccess() = 0;
231 
232     // Triggered during the image file downloading periodically.
233     // |estimated_remaining_time| is the remaining duration to download the
234     // remaining content estimated based on the elapsed time.
235     virtual void OnProgressWithRemainingTime(
236         ProgressType progress_type,
237         int64 received_bytes,
238         int64 total_bytes,
239         const base::TimeDelta& estimated_remaining_time) = 0;
240 
241     // Triggered when some progress is made, but estimated_remaining_time is
242     // not available.
243     // TODO(hidehiko): We should be able to merge this method with above one.
244     virtual void OnProgress(ProgressType progress_type,
245                             int64 received_bytes,
246                             int64 total_bytes) = 0;
247   };
248 
249   // Creates the global BurnManager instance.
250   static void Initialize(
251       const base::FilePath& downloads_directory,
252       scoped_refptr<net::URLRequestContextGetter> context_getter);
253 
254   // Destroys the global BurnManager instance if it exists.
255   static void Shutdown();
256 
257   // Returns the global BurnManager instance.
258   // Initialize() should already have been called.
259   static BurnManager* GetInstance();
260 
261   // Add an observer.
262   void AddObserver(Observer* observer);
263 
264   // Remove an observer.
265   void RemoveObserver(Observer* observer);
266 
267   // Returns devices on which we can burn recovery image.
268   std::vector<disks::DiskMountManager::Disk> GetBurnableDevices();
269 
270   // Cancels a currently running task of burning recovery image.
271   // Note: currently we only support Cancel method, which may look asymmetry
272   // because there is no method to start the task. It is just because that
273   // we are on the way of refactoring.
274   // TODO(hidehiko): Introduce Start method, which actually starts a whole
275   // image burning task, including config/image file fetching and unzipping.
276   void Cancel();
277 
278   // Error is usually detected by all existing Burn handlers, but only first
279   // one that calls this method should actually process it.
280   // The |message_id| is the id for human readable error message, although
281   // here is not the place to handle UI.
282   // TODO(hidehiko): Replace it with semantical enum value.
283   // Note: currently, due to some implementation reasons, the errors can be
284   // observed in outside classes, and this method is public to be accessed from
285   // them.
286   // TODO(hidehiko): Refactor the structure.
287   void OnError(int message_id);
288 
289   // Creates URL image should be fetched from.
290   // Must be called from UI thread.
291   void FetchConfigFile();
292 
293   // Fetch a zipped recovery image.
294   void FetchImage();
295 
296   // Burns the image of |zip_image_file_path_| and |image_file_name|
297   // to |target_device_path_| and |target_file_path_|.
298   // TODO(hidehiko): The name "Burn" sounds confusing because there are two
299   // meaning here.
300   // 1) In wider sense, Burn means a whole process, including config/image
301   //    file fetching, or file unzipping.
302   // 2) In narrower sense, Burn means just write the image onto a device.
303   // To avoid such a confusion, rename the method.
304   void DoBurn();
305 
306   // Cancels the image burning.
307   // TODO(hidehiko): Rename this method along with the renaming of DoBurn.
308   void CancelBurnImage();
309 
310   // Cancel fetching image.
311   void CancelImageFetch();
312 
313   // URLFetcherDelegate overrides:
314   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
315   virtual void OnURLFetchDownloadProgress(const net::URLFetcher* source,
316                                           int64 current,
317                                           int64 total) OVERRIDE;
318 
319   // NetworkStateHandlerObserver override.
320   virtual void DefaultNetworkChanged(const NetworkState* network) OVERRIDE;
321 
322   // Creates directory image will be downloaded to.
323   // Must be called from FILE thread.
324   void CreateImageDir();
325 
326   // Returns the directory to which the recovery image should be downloaded.
327   // If the directory hasn't been previously created, an empty path is returned
328   // (in which case |CreateImageDir()| should be called).
329   base::FilePath GetImageDir();
330 
target_device_path()331   const base::FilePath& target_device_path() { return target_device_path_; }
set_target_device_path(const base::FilePath & path)332   void set_target_device_path(const base::FilePath& path) {
333     target_device_path_ = path;
334   }
335 
target_file_path()336   const base::FilePath& target_file_path() { return target_file_path_; }
set_target_file_path(const base::FilePath & path)337   void set_target_file_path(const base::FilePath& path) {
338     target_file_path_ = path;
339   }
340 
ResetTargetPaths()341   void ResetTargetPaths() {
342     target_device_path_.clear();
343     target_file_path_.clear();
344   }
345 
state_machine()346   StateMachine* state_machine() const { return state_machine_.get(); }
347 
348  private:
349   BurnManager(const base::FilePath& downloads_directory,
350               scoped_refptr<net::URLRequestContextGetter> context_getter);
351   virtual ~BurnManager();
352 
353   void UpdateBurnStatus(BurnEvent evt, const ImageBurnStatus& status);
354 
355   void OnImageDirCreated(bool success);
356   void ConfigFileFetched(bool fetched, const std::string& content);
357 
358   void OnImageUnzipped(scoped_refptr<base::RefCountedString> source_image_file);
359   void OnDevicesUnmounted(bool success);
360   void OnBurnImageFail();
361   void OnBurnFinished(const std::string& target_path,
362                       bool success,
363                       const std::string& error);
364   void OnBurnProgressUpdate(const std::string& target_path,
365                             int64 num_bytes_burnt,
366                             int64 total_size);
367 
368   void NotifyDeviceAdded(const disks::DiskMountManager::Disk& disk);
369   void NotifyDeviceRemoved(const disks::DiskMountManager::Disk& disk);
370 
371   BurnDeviceHandler device_handler_;
372 
373   bool image_dir_created_;
374   bool unzipping_;
375   bool cancelled_;
376   bool burning_;
377   bool block_burn_signals_;
378 
379   base::FilePath image_dir_;
380   base::FilePath zip_image_file_path_;
381   base::FilePath source_image_path_;
382   base::FilePath target_device_path_;
383   base::FilePath target_file_path_;
384 
385   GURL config_file_url_;
386   bool config_file_fetched_;
387   std::string image_file_name_;
388   GURL image_download_url_;
389 
390   scoped_ptr<StateMachine> state_machine_;
391 
392   scoped_ptr<net::URLFetcher> config_fetcher_;
393   scoped_ptr<net::URLFetcher> image_fetcher_;
394 
395   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
396 
397   base::TimeTicks tick_image_download_start_;
398   int64 bytes_image_download_progress_last_reported_;
399 
400   ObserverList<Observer> observers_;
401 
402   // Note: This should remain the last member so it'll be destroyed and
403   // invalidate its weak pointers before any other members are destroyed.
404   base::WeakPtrFactory<BurnManager> weak_ptr_factory_;
405 
406   DISALLOW_COPY_AND_ASSIGN(BurnManager);
407 };
408 
409 }  // namespace imageburner
410 
411 }  // namespace chromeos
412 
413 #endif  // CHROME_BROWSER_CHROMEOS_IMAGEBURNER_BURN_MANAGER_H_
414