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.
4
5#ifndef CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_OPERATION_H_
6#define CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_OPERATION_H_
7
8#include "base/callback.h"
9#include "base/files/file.h"
10#include "base/files/scoped_temp_dir.h"
11#include "base/md5.h"
12#include "base/memory/ref_counted_memory.h"
13#include "base/memory/weak_ptr.h"
14#include "base/task/cancelable_task_tracker.h"
15#include "base/timer/timer.h"
16#include "chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h"
17#include "chrome/common/extensions/api/image_writer_private.h"
18#include "third_party/zlib/google/zip_reader.h"
19
20namespace image_writer_api = extensions::api::image_writer_private;
21
22namespace base {
23class FilePath;
24}  // namespace base
25
26namespace extensions {
27namespace image_writer {
28
29const int kProgressComplete = 100;
30
31class OperationManager;
32
33// Encapsulates an operation being run on behalf of the
34// OperationManager.  Construction of the operation does not start
35// anything.  The operation's Start method should be called to start it, and
36// then the Cancel method will stop it.  The operation will call back to the
37// OperationManager periodically or on any significant event.
38//
39// Each stage of the operation is generally divided into three phases: Start,
40// Run, Complete.  Start and Complete run on the UI thread and are responsible
41// for advancing to the next stage and other UI interaction.  The Run phase does
42// the work on the FILE thread and calls SendProgress or Error as appropriate.
43//
44// TODO(haven): This class is current refcounted because it is owned by the
45// OperationManager on the UI thread but needs to do work on the FILE thread.
46// There is probably a better way to organize this so that it can be represented
47// by a WeakPtr, but those are not thread-safe.  Additionally, if destruction is
48// done on the UI thread then that causes problems if any of the fields were
49// allocated/accessed on the FILE thread.  http://crbug.com/344713
50class Operation : public base::RefCountedThreadSafe<Operation> {
51 public:
52  typedef base::Callback<void(bool, const std::string&)> StartWriteCallback;
53  typedef base::Callback<void(bool, const std::string&)> CancelWriteCallback;
54  typedef std::string ExtensionId;
55
56  Operation(base::WeakPtr<OperationManager> manager,
57            const ExtensionId& extension_id,
58            const std::string& device_path);
59
60  // Starts the operation.
61  void Start();
62
63  // Cancel the operation. This must be called to clean up internal state and
64  // cause the the operation to actually stop.  It will not be destroyed until
65  // all callbacks have completed.
66  void Cancel();
67
68  // Aborts the operation, cancelling it and generating an error.
69  void Abort();
70
71  // Informational getters.
72  int GetProgress();
73  image_writer_api::Stage GetStage();
74
75#if !defined(OS_CHROMEOS)
76  // Set an ImageWriterClient to use.  Should be called only when testing.  This
77  // does not set up automatic shutdown of the client and it must be shutdown
78  // manually.
79  static void SetUtilityClientForTesting(
80      scoped_refptr<ImageWriterUtilityClient> client);
81#endif
82
83 protected:
84  virtual ~Operation();
85
86  // This function should be overriden by subclasses to set up the work of the
87  // operation.  It will be called from Start().
88  virtual void StartImpl() = 0;
89
90  // Unzips the current file if it ends in ".zip".  The current_file will be set
91  // to the unzipped file.
92  void Unzip(const base::Closure& continuation);
93
94  // Writes the current file to device_path.
95  void Write(const base::Closure& continuation);
96
97  // Verifies that the current file and device_path contents match.
98  void VerifyWrite(const base::Closure& continuation);
99
100  // Completes the operation.
101  void Finish();
102
103  // Generates an error.
104  // |error_message| is used to create an OnWriteError event which is
105  // sent to the extension
106  virtual void Error(const std::string& error_message);
107
108  // Set |progress_| and send an event.  Progress should be in the interval
109  // [0,100]
110  void SetProgress(int progress);
111  // Change to a new |stage_| and set |progress_| to zero.  Triggers a progress
112  // event.
113  void SetStage(image_writer_api::Stage stage);
114
115  // Can be queried to safely determine if the operation has been cancelled.
116  bool IsCancelled();
117
118  // Adds a callback that will be called during clean-up, whether the operation
119  // is aborted, encounters and error, or finishes successfully.  These
120  // functions will be run on the FILE thread.
121  void AddCleanUpFunction(const base::Closure& callback);
122
123  // Completes the current operation (progress set to 100) and runs the
124  // continuation.
125  void CompleteAndContinue(const base::Closure& continuation);
126
127  // If |file_size| is non-zero, only |file_size| bytes will be read from file,
128  // otherwise the entire file will be read.
129  // |progress_scale| is a percentage to which the progress will be scale, e.g.
130  // a scale of 50 means it will increment from 0 to 50 over the course of the
131  // sum.  |progress_offset| is an percentage that will be added to the progress
132  // of the MD5 sum before updating |progress_| but after scaling.
133  void GetMD5SumOfFile(
134      const base::FilePath& file,
135      int64 file_size,
136      int progress_offset,
137      int progress_scale,
138      const base::Callback<void(const std::string&)>& callback);
139
140  base::WeakPtr<OperationManager> manager_;
141  const ExtensionId extension_id_;
142
143  base::FilePath image_path_;
144  base::FilePath device_path_;
145
146  // Temporary directory to store files as we go.
147  base::ScopedTempDir temp_dir_;
148
149 private:
150  friend class base::RefCountedThreadSafe<Operation>;
151
152#if !defined(OS_CHROMEOS)
153  // Ensures the client is started.  This may be called many times but will only
154  // instantiate one client which should exist for the lifetime of the
155  // Operation.
156  void StartUtilityClient();
157
158  // Stops the client.  This must be called to ensure the utility process can
159  // shutdown.
160  void StopUtilityClient();
161
162  // Reports progress from the client, transforming from bytes to percentage.
163  virtual void WriteImageProgress(int64 total_bytes, int64 curr_bytes);
164
165  scoped_refptr<ImageWriterUtilityClient> image_writer_client_;
166#endif
167
168#if defined(OS_CHROMEOS)
169  // Unmounts all volumes on |device_path_|.
170  void UnmountVolumes(const base::Closure& continuation);
171  // Starts the write after unmounting.
172  void UnmountVolumesCallback(const base::Closure& continuation, bool success);
173  // Starts the ImageBurner write.  Note that target_path is the file path of
174  // the device where device_path has been a system device path.
175  void StartWriteOnUIThread(const std::string& target_path,
176                            const base::Closure& continuation);
177  void OnBurnFinished(const base::Closure& continuation,
178                      const std::string& target_path,
179                      bool success,
180                      const std::string& error);
181  void OnBurnProgress(const std::string& target_path,
182                      int64 num_bytes_burnt,
183                      int64 total_size);
184  void OnBurnError();
185#endif
186
187  // Incrementally calculates the MD5 sum of a file.
188  void MD5Chunk(base::File file,
189                int64 bytes_processed,
190                int64 bytes_total,
191                int progress_offset,
192                int progress_scale,
193                const base::Callback<void(const std::string&)>& callback);
194
195  // Callbacks for zip::ZipReader.
196  void OnUnzipFailure();
197  void OnUnzipProgress(int64 total_bytes, int64 progress_bytes);
198
199  // Runs all cleanup functions.
200  void CleanUp();
201
202  // |stage_| and |progress_| are owned by the FILE thread, use |SetStage| and
203  // |SetProgress| to update.  Progress should be in the interval [0,100]
204  image_writer_api::Stage stage_;
205  int progress_;
206
207  // MD5 contexts don't play well with smart pointers.  Just going to allocate
208  // memory here.  This requires that we only do one MD5 sum at a time.
209  base::MD5Context md5_context_;
210
211  // Zip reader for unzip operations.
212  zip::ZipReader zip_reader_;
213
214  // CleanUp operations that must be run.  All these functions are run on the
215  // FILE thread.
216  std::vector<base::Closure> cleanup_functions_;
217};
218
219}  // namespace image_writer
220}  // namespace extensions
221
222#endif  // CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_OPERATION_H_
223