1// Copyright (c) 2011 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#include "chrome/browser/chromeos/cros/burn_library.h"
6
7#include <cstring>
8#include "base/memory/linked_ptr.h"
9#include "chrome/browser/chromeos/cros/cros_library.h"
10#include "content/browser/browser_thread.h"
11
12namespace chromeos {
13
14class BurnLibraryImpl : public BurnLibrary,
15                        public base::SupportsWeakPtr<BurnLibraryImpl> {
16 public:
17
18  BurnLibraryImpl();
19  virtual ~BurnLibraryImpl();
20
21  // BurnLibrary implementation.
22  virtual void AddObserver(Observer* observer);
23  virtual void RemoveObserver(Observer* observer);
24  virtual bool DoBurn(const FilePath& from_path, const FilePath& to_path);
25
26  bool BurnImage(const FilePath& from_path, const FilePath& to_path);
27  void UpdateBurnStatus(const ImageBurnStatus& status, BurnEventType evt);
28
29 private:
30  void Init();
31  static void BurnStatusChangedHandler(void* object,
32                                       const BurnStatus& status,
33                                       BurnEventType evt);
34
35 private:
36  ObserverList<BurnLibrary::Observer> observers_;
37  BurnStatusConnection burn_status_connection_;
38
39  // Holds a path that is currently being burnt to.
40  std::string target_path_;
41
42  DISALLOW_COPY_AND_ASSIGN(BurnLibraryImpl);
43};
44
45class BurnLibraryTaskProxy
46    : public base::RefCountedThreadSafe<BurnLibraryTaskProxy> {
47 public:
48  explicit BurnLibraryTaskProxy(const base::WeakPtr<BurnLibraryImpl>& library);
49
50  void BurnImage(const FilePath& from_path, const FilePath& to_path);
51
52  void UpdateBurnStatus(ImageBurnStatus* status, BurnEventType evt);
53
54 private:
55  base::WeakPtr<BurnLibraryImpl> library_;
56
57  friend class base::RefCountedThreadSafe<BurnLibraryTaskProxy>;
58
59  DISALLOW_COPY_AND_ASSIGN(BurnLibraryTaskProxy);
60};
61
62BurnLibraryImpl::BurnLibraryImpl() {
63  if (CrosLibrary::Get()->EnsureLoaded()) {
64      Init();
65    } else {
66      LOG(ERROR) << "Cros Library has not been loaded";
67    }
68}
69
70BurnLibraryImpl::~BurnLibraryImpl() {
71  if (burn_status_connection_) {
72    DisconnectBurnStatus(burn_status_connection_);
73  }
74}
75
76void BurnLibraryImpl::AddObserver(Observer* observer) {
77  observers_.AddObserver(observer);
78}
79
80void BurnLibraryImpl::RemoveObserver(Observer* observer) {
81  observers_.RemoveObserver(observer);
82}
83
84bool BurnLibraryImpl::DoBurn(const FilePath& from_path,
85                             const FilePath& to_path) {
86  BurnLibraryTaskProxy* task = new BurnLibraryTaskProxy(AsWeakPtr());
87  task->AddRef();
88  task->BurnImage(from_path, to_path);
89  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
90      NewRunnableMethod(task, &BurnLibraryTaskProxy::BurnImage,
91                        from_path, to_path));
92  return true;
93}
94
95bool BurnLibraryImpl::BurnImage(const FilePath& from_path,
96                                const FilePath& to_path) {
97  // Make sure we run on file thread.
98  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
99
100  // Check if there is a target path already being burnt to.
101  if (target_path_ == "") {
102    target_path_ = to_path.value();
103  } else {
104    return false;
105  }
106
107  StartBurn(from_path.value().c_str(), to_path.value().c_str(),
108            burn_status_connection_);
109  return true;
110}
111
112void BurnLibraryImpl::BurnStatusChangedHandler(void* object,
113                                               const BurnStatus& status,
114                                               BurnEventType evt) {
115  BurnLibraryImpl* burn = static_cast<BurnLibraryImpl*>(object);
116
117  // Copy burn status because it will be freed after returning from this method.
118  ImageBurnStatus* status_copy = new ImageBurnStatus(status);
119
120  BurnLibraryTaskProxy* task = new BurnLibraryTaskProxy(burn->AsWeakPtr());
121  task->AddRef();
122  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
123      NewRunnableMethod(task, &BurnLibraryTaskProxy::UpdateBurnStatus,
124                        status_copy, evt));
125}
126
127void BurnLibraryImpl::Init() {
128  burn_status_connection_ = MonitorBurnStatus(&BurnStatusChangedHandler, this);
129}
130
131void BurnLibraryImpl::UpdateBurnStatus(const ImageBurnStatus& status,
132                                       BurnEventType evt) {
133  // Make sure we run on UI thread.
134  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
135
136  // If burn is finished, remove target paths from paths being burnt to.
137  // This has to be done in thread-safe way, hence using task proxy class.
138  if ((evt == BURN_CANCELED || evt == BURN_COMPLETE) &&
139      target_path_ == status.target_path)
140    target_path_ = "";
141
142  FOR_EACH_OBSERVER(Observer, observers_, ProgressUpdated(this, evt, status));
143}
144
145BurnLibraryTaskProxy::BurnLibraryTaskProxy(
146                        const base::WeakPtr<BurnLibraryImpl>& library)
147                            : library_(library) {
148}
149
150void BurnLibraryTaskProxy::BurnImage(const FilePath& from_path,
151                                     const FilePath& to_path) {
152  library_->BurnImage(from_path, to_path);
153}
154
155void BurnLibraryTaskProxy::UpdateBurnStatus(ImageBurnStatus* status,
156                                            BurnEventType evt) {
157  library_->UpdateBurnStatus(*status, evt);
158  delete status;
159}
160
161
162class BurnLibraryStubImpl : public BurnLibrary {
163 public:
164  BurnLibraryStubImpl() {}
165  virtual ~BurnLibraryStubImpl() {}
166
167  // BurnLibrary overrides.
168  virtual void AddObserver(Observer* observer) {}
169  virtual void RemoveObserver(Observer* observer) {}
170  virtual bool DoBurn(const FilePath& from_path, const FilePath& to_path) {
171    return false;
172  }
173
174  DISALLOW_COPY_AND_ASSIGN(BurnLibraryStubImpl);
175};
176
177// static
178BurnLibrary* BurnLibrary::GetImpl(bool stub) {
179  if (stub)
180    return new BurnLibraryStubImpl();
181  else
182    return new BurnLibraryImpl();
183}
184
185}  // namespace chromeos
186
187// Allows InvokeLater without adding refcounting. This class is a Singleton and
188// won't be deleted until it's last InvokeLater is run.
189DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::BurnLibraryImpl);
190
191