OLD | NEW |
| (Empty) |
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 #include "chrome/browser/chromeos/imageburner/burn_manager.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/files/file_util.h" | |
9 #include "base/strings/string_util.h" | |
10 #include "base/threading/worker_pool.h" | |
11 #include "chrome/grit/generated_resources.h" | |
12 #include "chromeos/dbus/dbus_thread_manager.h" | |
13 #include "chromeos/dbus/image_burner_client.h" | |
14 #include "chromeos/network/network_state.h" | |
15 #include "chromeos/network/network_state_handler.h" | |
16 #include "chromeos/system/statistics_provider.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "net/url_request/url_fetcher.h" | |
19 #include "net/url_request/url_request_context_getter.h" | |
20 #include "net/url_request/url_request_status.h" | |
21 #include "third_party/zlib/google/zip.h" | |
22 | |
23 using content::BrowserThread; | |
24 | |
25 namespace chromeos { | |
26 namespace imageburner { | |
27 | |
28 namespace { | |
29 | |
30 const char kConfigFileUrl[] = | |
31 "https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.conf"; | |
32 const char kTempImageFolderName[] = "chromeos_image"; | |
33 | |
34 const char kImageZipFileName[] = "chromeos_image.bin.zip"; | |
35 | |
36 const int64 kBytesImageDownloadProgressReportInterval = 10240; | |
37 | |
38 BurnManager* g_burn_manager = NULL; | |
39 | |
40 // Cretes a directory and calls |callback| with the result on UI thread. | |
41 void CreateDirectory(const base::FilePath& path, | |
42 base::Callback<void(bool success)> callback) { | |
43 const bool success = base::CreateDirectory(path); | |
44 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
45 base::Bind(callback, success)); | |
46 } | |
47 | |
48 // Unzips |source_zip_file| and sets the filename of the unzipped image to | |
49 // |source_image_file|. | |
50 void UnzipImage(const base::FilePath& source_zip_file, | |
51 const std::string& image_name, | |
52 scoped_refptr<base::RefCountedString> source_image_file) { | |
53 if (zip::Unzip(source_zip_file, source_zip_file.DirName())) { | |
54 source_image_file->data() = | |
55 source_zip_file.DirName().Append(image_name).value(); | |
56 } | |
57 } | |
58 | |
59 } // namespace | |
60 | |
61 const char kName[] = "name"; | |
62 const char kHwid[] = "hwid"; | |
63 const char kFileName[] = "file"; | |
64 const char kUrl[] = "url"; | |
65 | |
66 //////////////////////////////////////////////////////////////////////////////// | |
67 // | |
68 // ConfigFile | |
69 // | |
70 //////////////////////////////////////////////////////////////////////////////// | |
71 ConfigFile::ConfigFile() { | |
72 } | |
73 | |
74 ConfigFile::ConfigFile(const std::string& file_content) { | |
75 reset(file_content); | |
76 } | |
77 | |
78 ConfigFile::~ConfigFile() { | |
79 } | |
80 | |
81 void ConfigFile::reset(const std::string& file_content) { | |
82 clear(); | |
83 | |
84 std::vector<std::string> lines; | |
85 Tokenize(file_content, "\n", &lines); | |
86 | |
87 std::vector<std::string> key_value_pair; | |
88 for (size_t i = 0; i < lines.size(); ++i) { | |
89 if (lines[i].empty()) | |
90 continue; | |
91 | |
92 key_value_pair.clear(); | |
93 Tokenize(lines[i], "=", &key_value_pair); | |
94 // Skip lines that don't contain key-value pair and lines without a key. | |
95 if (key_value_pair.size() != 2 || key_value_pair[0].empty()) | |
96 continue; | |
97 | |
98 ProcessLine(key_value_pair); | |
99 } | |
100 | |
101 // Make sure last block has at least one hwid associated with it. | |
102 DeleteLastBlockIfHasNoHwid(); | |
103 } | |
104 | |
105 void ConfigFile::clear() { | |
106 config_struct_.clear(); | |
107 } | |
108 | |
109 const std::string& ConfigFile::GetProperty( | |
110 const std::string& property_name, | |
111 const std::string& hwid) const { | |
112 // We search for block that has desired hwid property, and if we find it, we | |
113 // return its property_name property. | |
114 for (BlockList::const_iterator block_it = config_struct_.begin(); | |
115 block_it != config_struct_.end(); | |
116 ++block_it) { | |
117 if (block_it->hwids.find(hwid) != block_it->hwids.end()) { | |
118 PropertyMap::const_iterator property = | |
119 block_it->properties.find(property_name); | |
120 if (property != block_it->properties.end()) { | |
121 return property->second; | |
122 } else { | |
123 return base::EmptyString(); | |
124 } | |
125 } | |
126 } | |
127 | |
128 return base::EmptyString(); | |
129 } | |
130 | |
131 // Check if last block has a hwid associated with it, and erase it if it | |
132 // doesn't, | |
133 void ConfigFile::DeleteLastBlockIfHasNoHwid() { | |
134 if (!config_struct_.empty() && config_struct_.back().hwids.empty()) { | |
135 config_struct_.pop_back(); | |
136 } | |
137 } | |
138 | |
139 void ConfigFile::ProcessLine(const std::vector<std::string>& line) { | |
140 // If line contains name key, new image block is starting, so we have to add | |
141 // new entry to our data structure. | |
142 if (line[0] == kName) { | |
143 // If there was no hardware class defined for previous block, we can | |
144 // disregard is since we won't be abble to access any of its properties | |
145 // anyway. This should not happen, but let's be defensive. | |
146 DeleteLastBlockIfHasNoHwid(); | |
147 config_struct_.resize(config_struct_.size() + 1); | |
148 } | |
149 | |
150 // If we still haven't added any blocks to data struct, we disregard this | |
151 // line. Again, this should never happen. | |
152 if (config_struct_.empty()) | |
153 return; | |
154 | |
155 ConfigFileBlock& last_block = config_struct_.back(); | |
156 | |
157 if (line[0] == kHwid) { | |
158 // Check if line contains hwid property. If so, add it to set of hwids | |
159 // associated with current block. | |
160 last_block.hwids.insert(line[1]); | |
161 } else { | |
162 // Add new block property. | |
163 last_block.properties.insert(std::make_pair(line[0], line[1])); | |
164 } | |
165 } | |
166 | |
167 ConfigFile::ConfigFileBlock::ConfigFileBlock() { | |
168 } | |
169 | |
170 ConfigFile::ConfigFileBlock::~ConfigFileBlock() { | |
171 } | |
172 | |
173 //////////////////////////////////////////////////////////////////////////////// | |
174 // | |
175 // StateMachine | |
176 // | |
177 //////////////////////////////////////////////////////////////////////////////// | |
178 StateMachine::StateMachine() | |
179 : download_started_(false), | |
180 download_finished_(false), | |
181 state_(INITIAL) { | |
182 } | |
183 | |
184 StateMachine::~StateMachine() { | |
185 } | |
186 | |
187 void StateMachine::OnError(int error_message_id) { | |
188 if (state_ == INITIAL) | |
189 return; | |
190 if (!download_finished_) | |
191 download_started_ = false; | |
192 | |
193 state_ = INITIAL; | |
194 FOR_EACH_OBSERVER(Observer, observers_, OnError(error_message_id)); | |
195 } | |
196 | |
197 void StateMachine::OnSuccess() { | |
198 if (state_ == INITIAL) | |
199 return; | |
200 state_ = INITIAL; | |
201 OnStateChanged(); | |
202 } | |
203 | |
204 //////////////////////////////////////////////////////////////////////////////// | |
205 // | |
206 // BurnManager | |
207 // | |
208 //////////////////////////////////////////////////////////////////////////////// | |
209 | |
210 BurnManager::BurnManager( | |
211 const base::FilePath& downloads_directory, | |
212 scoped_refptr<net::URLRequestContextGetter> context_getter) | |
213 : device_handler_(disks::DiskMountManager::GetInstance()), | |
214 image_dir_created_(false), | |
215 unzipping_(false), | |
216 cancelled_(false), | |
217 burning_(false), | |
218 block_burn_signals_(false), | |
219 image_dir_(downloads_directory.Append(kTempImageFolderName)), | |
220 config_file_url_(kConfigFileUrl), | |
221 config_file_fetched_(false), | |
222 state_machine_(new StateMachine()), | |
223 url_request_context_getter_(context_getter), | |
224 bytes_image_download_progress_last_reported_(0), | |
225 weak_ptr_factory_(this) { | |
226 NetworkHandler::Get()->network_state_handler()->AddObserver( | |
227 this, FROM_HERE); | |
228 base::WeakPtr<BurnManager> weak_ptr(weak_ptr_factory_.GetWeakPtr()); | |
229 device_handler_.SetCallbacks( | |
230 base::Bind(&BurnManager::NotifyDeviceAdded, weak_ptr), | |
231 base::Bind(&BurnManager::NotifyDeviceRemoved, weak_ptr)); | |
232 DBusThreadManager::Get()->GetImageBurnerClient()->SetEventHandlers( | |
233 base::Bind(&BurnManager::OnBurnFinished, | |
234 weak_ptr_factory_.GetWeakPtr()), | |
235 base::Bind(&BurnManager::OnBurnProgressUpdate, | |
236 weak_ptr_factory_.GetWeakPtr())); | |
237 } | |
238 | |
239 BurnManager::~BurnManager() { | |
240 if (image_dir_created_) { | |
241 base::DeleteFile(image_dir_, true); | |
242 } | |
243 if (NetworkHandler::IsInitialized()) { | |
244 NetworkHandler::Get()->network_state_handler()->RemoveObserver( | |
245 this, FROM_HERE); | |
246 } | |
247 DBusThreadManager::Get()->GetImageBurnerClient()->ResetEventHandlers(); | |
248 } | |
249 | |
250 // static | |
251 void BurnManager::Initialize( | |
252 const base::FilePath& downloads_directory, | |
253 scoped_refptr<net::URLRequestContextGetter> context_getter) { | |
254 if (g_burn_manager) { | |
255 LOG(WARNING) << "BurnManager was already initialized"; | |
256 return; | |
257 } | |
258 g_burn_manager = new BurnManager(downloads_directory, context_getter); | |
259 VLOG(1) << "BurnManager initialized"; | |
260 } | |
261 | |
262 // static | |
263 void BurnManager::Shutdown() { | |
264 if (!g_burn_manager) { | |
265 LOG(WARNING) << "BurnManager::Shutdown() called with NULL manager"; | |
266 return; | |
267 } | |
268 delete g_burn_manager; | |
269 g_burn_manager = NULL; | |
270 VLOG(1) << "BurnManager Shutdown completed"; | |
271 } | |
272 | |
273 // static | |
274 BurnManager* BurnManager::GetInstance() { | |
275 return g_burn_manager; | |
276 } | |
277 | |
278 void BurnManager::AddObserver(Observer* observer) { | |
279 observers_.AddObserver(observer); | |
280 } | |
281 | |
282 void BurnManager::RemoveObserver(Observer* observer) { | |
283 observers_.RemoveObserver(observer); | |
284 } | |
285 | |
286 std::vector<disks::DiskMountManager::Disk> BurnManager::GetBurnableDevices() { | |
287 return device_handler_.GetBurnableDevices(); | |
288 } | |
289 | |
290 void BurnManager::Cancel() { | |
291 OnError(IDS_IMAGEBURN_USER_ERROR); | |
292 } | |
293 | |
294 void BurnManager::OnError(int message_id) { | |
295 // If we are in intial state, error has already been dispached. | |
296 if (state_machine_->state() == StateMachine::INITIAL) { | |
297 return; | |
298 } | |
299 | |
300 // Remember burner state, since it will be reset after OnError call. | |
301 StateMachine::State state = state_machine_->state(); | |
302 | |
303 // Dispach error. All hadlers' OnError event will be called before returning | |
304 // from this. This includes us, too. | |
305 state_machine_->OnError(message_id); | |
306 | |
307 // Cancel and clean up the current task. | |
308 // Note: the cancellation of this class looks not handled correctly. | |
309 // In particular, there seems no clean-up code for creating a temporary | |
310 // directory, or fetching config files. Also, there seems an issue | |
311 // about the cancellation of burning. | |
312 // TODO(hidehiko): Fix the issue. | |
313 if (state == StateMachine::DOWNLOADING) { | |
314 CancelImageFetch(); | |
315 } else if (state == StateMachine::BURNING) { | |
316 // Burn library doesn't send cancelled signal upon CancelBurnImage | |
317 // invokation. | |
318 CancelBurnImage(); | |
319 } | |
320 ResetTargetPaths(); | |
321 } | |
322 | |
323 void BurnManager::CreateImageDir() { | |
324 if (!image_dir_created_) { | |
325 BrowserThread::PostBlockingPoolTask( | |
326 FROM_HERE, | |
327 base::Bind(CreateDirectory, | |
328 image_dir_, | |
329 base::Bind(&BurnManager::OnImageDirCreated, | |
330 weak_ptr_factory_.GetWeakPtr()))); | |
331 } else { | |
332 const bool success = true; | |
333 OnImageDirCreated(success); | |
334 } | |
335 } | |
336 | |
337 void BurnManager::OnImageDirCreated(bool success) { | |
338 if (!success) { | |
339 // Failed to create the directory. Finish the burning process | |
340 // with failure state. | |
341 OnError(IDS_IMAGEBURN_DOWNLOAD_ERROR); | |
342 return; | |
343 } | |
344 | |
345 image_dir_created_ = true; | |
346 zip_image_file_path_ = image_dir_.Append(kImageZipFileName); | |
347 FetchConfigFile(); | |
348 } | |
349 | |
350 base::FilePath BurnManager::GetImageDir() { | |
351 if (!image_dir_created_) | |
352 return base::FilePath(); | |
353 return image_dir_; | |
354 } | |
355 | |
356 void BurnManager::FetchConfigFile() { | |
357 if (config_file_fetched_) { | |
358 // The config file is already fetched. So start to fetch the image. | |
359 FetchImage(); | |
360 return; | |
361 } | |
362 | |
363 if (config_fetcher_.get()) | |
364 return; | |
365 | |
366 config_fetcher_.reset(net::URLFetcher::Create( | |
367 config_file_url_, net::URLFetcher::GET, this)); | |
368 config_fetcher_->SetRequestContext(url_request_context_getter_.get()); | |
369 config_fetcher_->Start(); | |
370 } | |
371 | |
372 void BurnManager::FetchImage() { | |
373 if (state_machine_->download_finished()) { | |
374 DoBurn(); | |
375 return; | |
376 } | |
377 | |
378 if (state_machine_->download_started()) { | |
379 // The image downloading is already started. Do nothing. | |
380 return; | |
381 } | |
382 | |
383 tick_image_download_start_ = base::TimeTicks::Now(); | |
384 bytes_image_download_progress_last_reported_ = 0; | |
385 image_fetcher_.reset(net::URLFetcher::Create(image_download_url_, | |
386 net::URLFetcher::GET, | |
387 this)); | |
388 image_fetcher_->SetRequestContext(url_request_context_getter_.get()); | |
389 image_fetcher_->SaveResponseToFileAtPath( | |
390 zip_image_file_path_, | |
391 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); | |
392 image_fetcher_->Start(); | |
393 | |
394 state_machine_->OnDownloadStarted(); | |
395 } | |
396 | |
397 void BurnManager::CancelImageFetch() { | |
398 image_fetcher_.reset(); | |
399 } | |
400 | |
401 void BurnManager::DoBurn() { | |
402 if (state_machine_->state() == StateMachine::BURNING) | |
403 return; | |
404 | |
405 if (unzipping_) { | |
406 // We have unzip in progress, maybe it was "cancelled" before and did not | |
407 // finish yet. In that case, let's pretend cancel did not happen. | |
408 cancelled_ = false; | |
409 UpdateBurnStatus(UNZIP_STARTED, ImageBurnStatus()); | |
410 return; | |
411 } | |
412 | |
413 source_image_path_.clear(); | |
414 | |
415 unzipping_ = true; | |
416 cancelled_ = false; | |
417 UpdateBurnStatus(UNZIP_STARTED, ImageBurnStatus()); | |
418 | |
419 const bool task_is_slow = true; | |
420 scoped_refptr<base::RefCountedString> result(new base::RefCountedString); | |
421 base::WorkerPool::PostTaskAndReply( | |
422 FROM_HERE, | |
423 base::Bind(UnzipImage, zip_image_file_path_, image_file_name_, result), | |
424 base::Bind(&BurnManager::OnImageUnzipped, | |
425 weak_ptr_factory_.GetWeakPtr(), | |
426 result), | |
427 task_is_slow); | |
428 state_machine_->OnBurnStarted(); | |
429 } | |
430 | |
431 void BurnManager::CancelBurnImage() { | |
432 // At the moment, we cannot really stop uzipping or burning. Instead we | |
433 // prevent events from being sent to listeners. | |
434 if (burning_) | |
435 block_burn_signals_ = true; | |
436 cancelled_ = true; | |
437 } | |
438 | |
439 void BurnManager::OnURLFetchComplete(const net::URLFetcher* source) { | |
440 // TODO(hidehiko): Split the handler implementation into two, for | |
441 // the config file fetcher and the image file fetcher. | |
442 const bool success = | |
443 source->GetStatus().status() == net::URLRequestStatus::SUCCESS; | |
444 | |
445 if (source == config_fetcher_.get()) { | |
446 // Handler for the config file fetcher. | |
447 std::string data; | |
448 if (success) | |
449 config_fetcher_->GetResponseAsString(&data); | |
450 config_fetcher_.reset(); | |
451 ConfigFileFetched(success, data); | |
452 return; | |
453 } | |
454 | |
455 if (source == image_fetcher_.get()) { | |
456 // Handler for the image file fetcher. | |
457 state_machine_->OnDownloadFinished(); | |
458 if (!success) { | |
459 OnError(IDS_IMAGEBURN_DOWNLOAD_ERROR); | |
460 return; | |
461 } | |
462 DoBurn(); | |
463 return; | |
464 } | |
465 | |
466 NOTREACHED(); | |
467 } | |
468 | |
469 void BurnManager::OnURLFetchDownloadProgress(const net::URLFetcher* source, | |
470 int64 current, | |
471 int64 total) { | |
472 if (source == image_fetcher_.get()) { | |
473 if (current >= bytes_image_download_progress_last_reported_ + | |
474 kBytesImageDownloadProgressReportInterval) { | |
475 bytes_image_download_progress_last_reported_ = current; | |
476 base::TimeDelta estimated_remaining_time; | |
477 if (current > 0) { | |
478 // Extrapolate from the elapsed time. | |
479 const base::TimeDelta elapsed_time = | |
480 base::TimeTicks::Now() - tick_image_download_start_; | |
481 estimated_remaining_time = elapsed_time * (total - current) / current; | |
482 } | |
483 | |
484 // TODO(hidehiko): We should be able to clean the state check here. | |
485 if (state_machine_->state() == StateMachine::DOWNLOADING) { | |
486 FOR_EACH_OBSERVER( | |
487 Observer, observers_, | |
488 OnProgressWithRemainingTime( | |
489 DOWNLOADING, current, total, estimated_remaining_time)); | |
490 } | |
491 } | |
492 } | |
493 } | |
494 | |
495 void BurnManager::DefaultNetworkChanged(const NetworkState* network) { | |
496 // TODO(hidehiko): Split this into a class to write tests. | |
497 if (state_machine_->state() == StateMachine::INITIAL && network) | |
498 FOR_EACH_OBSERVER(Observer, observers_, OnNetworkDetected()); | |
499 | |
500 if (state_machine_->state() == StateMachine::DOWNLOADING && !network) | |
501 OnError(IDS_IMAGEBURN_NETWORK_ERROR); | |
502 } | |
503 | |
504 void BurnManager::UpdateBurnStatus(BurnEvent event, | |
505 const ImageBurnStatus& status) { | |
506 if (cancelled_) | |
507 return; | |
508 | |
509 if (event == BURN_FAIL || event == BURN_SUCCESS) { | |
510 burning_ = false; | |
511 if (block_burn_signals_) { | |
512 block_burn_signals_ = false; | |
513 return; | |
514 } | |
515 } | |
516 | |
517 if (block_burn_signals_ && event == BURN_UPDATE) | |
518 return; | |
519 | |
520 // Notify observers. | |
521 switch (event) { | |
522 case BURN_SUCCESS: | |
523 // The burning task is successfully done. | |
524 // Update the state. | |
525 ResetTargetPaths(); | |
526 state_machine_->OnSuccess(); | |
527 FOR_EACH_OBSERVER(Observer, observers_, OnSuccess()); | |
528 break; | |
529 case BURN_FAIL: | |
530 OnError(IDS_IMAGEBURN_BURN_ERROR); | |
531 break; | |
532 case BURN_UPDATE: | |
533 FOR_EACH_OBSERVER( | |
534 Observer, observers_, | |
535 OnProgress(BURNING, status.amount_burnt, status.total_size)); | |
536 break; | |
537 case(UNZIP_STARTED): | |
538 FOR_EACH_OBSERVER(Observer, observers_, OnProgress(UNZIPPING, 0, 0)); | |
539 break; | |
540 case UNZIP_FAIL: | |
541 OnError(IDS_IMAGEBURN_EXTRACTING_ERROR); | |
542 break; | |
543 case UNZIP_COMPLETE: | |
544 // We ignore this. | |
545 break; | |
546 default: | |
547 NOTREACHED(); | |
548 break; | |
549 } | |
550 } | |
551 | |
552 void BurnManager::ConfigFileFetched(bool fetched, const std::string& content) { | |
553 if (config_file_fetched_) | |
554 return; | |
555 | |
556 // Get image file name and image download URL. | |
557 std::string hwid; | |
558 if (fetched && system::StatisticsProvider::GetInstance()-> | |
559 GetMachineStatistic(system::kHardwareClassKey, &hwid)) { | |
560 ConfigFile config_file(content); | |
561 image_file_name_ = config_file.GetProperty(kFileName, hwid); | |
562 image_download_url_ = GURL(config_file.GetProperty(kUrl, hwid)); | |
563 } | |
564 | |
565 // Error check. | |
566 if (fetched && !image_file_name_.empty() && !image_download_url_.is_empty()) { | |
567 config_file_fetched_ = true; | |
568 } else { | |
569 fetched = false; | |
570 image_file_name_.clear(); | |
571 image_download_url_ = GURL(); | |
572 } | |
573 | |
574 if (!fetched) { | |
575 OnError(IDS_IMAGEBURN_DOWNLOAD_ERROR); | |
576 return; | |
577 } | |
578 | |
579 FetchImage(); | |
580 } | |
581 | |
582 void BurnManager::OnImageUnzipped( | |
583 scoped_refptr<base::RefCountedString> source_image_file) { | |
584 source_image_path_ = base::FilePath(source_image_file->data()); | |
585 | |
586 bool success = !source_image_path_.empty(); | |
587 UpdateBurnStatus(success ? UNZIP_COMPLETE : UNZIP_FAIL, ImageBurnStatus()); | |
588 | |
589 unzipping_ = false; | |
590 if (cancelled_) { | |
591 cancelled_ = false; | |
592 return; | |
593 } | |
594 | |
595 if (!success) | |
596 return; | |
597 | |
598 burning_ = true; | |
599 | |
600 chromeos::disks::DiskMountManager::GetInstance()->UnmountDeviceRecursively( | |
601 target_device_path_.value(), | |
602 base::Bind(&BurnManager::OnDevicesUnmounted, | |
603 weak_ptr_factory_.GetWeakPtr())); | |
604 } | |
605 | |
606 void BurnManager::OnDevicesUnmounted(bool success) { | |
607 if (!success) { | |
608 UpdateBurnStatus(BURN_FAIL, ImageBurnStatus(0, 0)); | |
609 return; | |
610 } | |
611 | |
612 DBusThreadManager::Get()->GetImageBurnerClient()->BurnImage( | |
613 source_image_path_.value(), | |
614 target_file_path_.value(), | |
615 base::Bind(&BurnManager::OnBurnImageFail, | |
616 weak_ptr_factory_.GetWeakPtr())); | |
617 } | |
618 | |
619 void BurnManager::OnBurnImageFail() { | |
620 UpdateBurnStatus(BURN_FAIL, ImageBurnStatus(0, 0)); | |
621 } | |
622 | |
623 void BurnManager::OnBurnFinished(const std::string& target_path, | |
624 bool success, | |
625 const std::string& error) { | |
626 UpdateBurnStatus(success ? BURN_SUCCESS : BURN_FAIL, ImageBurnStatus(0, 0)); | |
627 } | |
628 | |
629 void BurnManager::OnBurnProgressUpdate(const std::string& target_path, | |
630 int64 amount_burnt, | |
631 int64 total_size) { | |
632 UpdateBurnStatus(BURN_UPDATE, ImageBurnStatus(amount_burnt, total_size)); | |
633 } | |
634 | |
635 void BurnManager::NotifyDeviceAdded( | |
636 const disks::DiskMountManager::Disk& disk) { | |
637 FOR_EACH_OBSERVER(Observer, observers_, OnDeviceAdded(disk)); | |
638 } | |
639 | |
640 void BurnManager::NotifyDeviceRemoved( | |
641 const disks::DiskMountManager::Disk& disk) { | |
642 FOR_EACH_OBSERVER(Observer, observers_, OnDeviceRemoved(disk)); | |
643 | |
644 if (target_device_path_.value() == disk.device_path()) { | |
645 // The device is removed during the burning process. | |
646 // Note: in theory, this is not a part of notification, but cancelling | |
647 // the running burning task. However, there is no good place to be in the | |
648 // current code. | |
649 // TODO(hidehiko): Clean this up after refactoring. | |
650 OnError(IDS_IMAGEBURN_DEVICE_NOT_FOUND_ERROR); | |
651 } | |
652 } | |
653 | |
654 } // namespace imageburner | |
655 } // namespace chromeos | |
OLD | NEW |