Home | History | Annotate | Download | only in imageburner
      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 {
     43   ImageBurnStatus() : amount_burnt(0), total_size(0) {
     44   }
     45 
     46   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 
     97   bool empty() const { return config_struct_.empty(); }
     98 
     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 
    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 
    152   bool download_started() const { return download_started_; }
    153   void OnDownloadStarted() {
    154     download_started_ = true;
    155     state_ = DOWNLOADING;
    156     OnStateChanged();
    157   }
    158 
    159   bool download_finished() const { return download_finished_; }
    160   void OnDownloadFinished() { download_finished_ = true; }
    161 
    162   void OnBurnStarted() {
    163     state_ = BURNING;
    164     OnStateChanged();
    165   }
    166 
    167   bool new_burn_posible() const { return state_ == INITIAL; }
    168 
    169   void OnSuccess();
    170   void OnError(int error_message_id);
    171 
    172   void OnStateChanged() {
    173     FOR_EACH_OBSERVER(Observer, observers_, OnBurnStateChanged(state_));
    174   }
    175 
    176   void AddObserver(Observer* observer) {
    177     observers_.AddObserver(observer);
    178   }
    179 
    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 
    331   const base::FilePath& target_device_path() { return target_device_path_; }
    332   void set_target_device_path(const base::FilePath& path) {
    333     target_device_path_ = path;
    334   }
    335 
    336   const base::FilePath& target_file_path() { return target_file_path_; }
    337   void set_target_file_path(const base::FilePath& path) {
    338     target_file_path_ = path;
    339   }
    340 
    341   void ResetTargetPaths() {
    342     target_device_path_.clear();
    343     target_file_path_.clear();
    344   }
    345 
    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