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.
5 #include "chrome/browser/chromeos/imageburner/burn_manager.h"
8 #include "base/file_util.h"
9 #include "base/path_service.h"
10 #include "base/string_util.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/chromeos/system/statistics_provider.h"
13 #include "chrome/common/chrome_paths.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "net/url_request/url_fetcher.h"
16 #include "net/url_request/url_request_status.h"
18 using content::BrowserThread
;
21 namespace imageburner
{
25 // Name for hwid in machine statistics.
26 const char kHwidStatistic
[] = "hardware_class";
28 const char kConfigFileUrl
[] =
29 "https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.conf";
30 const char kTempImageFolderName
[] = "chromeos_image";
32 const int64 kBytesImageDownloadProgressReportInterval
= 10240;
34 BurnManager
* g_burn_manager
= NULL
;
36 // Cretes a directory and calls |callback| with the result on UI thread.
37 void CreateDirectory(const FilePath
& path
,
38 base::Callback
<void(bool success
)> callback
) {
39 const bool success
= file_util::CreateDirectory(path
);
40 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
41 base::Bind(callback
, success
));
46 const char kName
[] = "name";
47 const char kHwid
[] = "hwid";
48 const char kFileName
[] = "file";
49 const char kUrl
[] = "url";
51 ////////////////////////////////////////////////////////////////////////////////
55 ////////////////////////////////////////////////////////////////////////////////
56 ConfigFile::ConfigFile() {
59 ConfigFile::ConfigFile(const std::string
& file_content
) {
63 ConfigFile::~ConfigFile() {
66 void ConfigFile::reset(const std::string
& file_content
) {
69 std::vector
<std::string
> lines
;
70 Tokenize(file_content
, "\n", &lines
);
72 std::vector
<std::string
> key_value_pair
;
73 for (size_t i
= 0; i
< lines
.size(); ++i
) {
77 key_value_pair
.clear();
78 Tokenize(lines
[i
], "=", &key_value_pair
);
79 // Skip lines that don't contain key-value pair and lines without a key.
80 if (key_value_pair
.size() != 2 || key_value_pair
[0].empty())
83 ProcessLine(key_value_pair
);
86 // Make sure last block has at least one hwid associated with it.
87 DeleteLastBlockIfHasNoHwid();
90 void ConfigFile::clear() {
91 config_struct_
.clear();
94 const std::string
& ConfigFile::GetProperty(
95 const std::string
& property_name
,
96 const std::string
& hwid
) const {
97 // We search for block that has desired hwid property, and if we find it, we
98 // return its property_name property.
99 for (BlockList::const_iterator block_it
= config_struct_
.begin();
100 block_it
!= config_struct_
.end();
102 if (block_it
->hwids
.find(hwid
) != block_it
->hwids
.end()) {
103 PropertyMap::const_iterator property
=
104 block_it
->properties
.find(property_name
);
105 if (property
!= block_it
->properties
.end()) {
106 return property
->second
;
108 return EmptyString();
113 return EmptyString();
116 // Check if last block has a hwid associated with it, and erase it if it
118 void ConfigFile::DeleteLastBlockIfHasNoHwid() {
119 if (!config_struct_
.empty() && config_struct_
.back().hwids
.empty()) {
120 config_struct_
.pop_back();
124 void ConfigFile::ProcessLine(const std::vector
<std::string
>& line
) {
125 // If line contains name key, new image block is starting, so we have to add
126 // new entry to our data structure.
127 if (line
[0] == kName
) {
128 // If there was no hardware class defined for previous block, we can
129 // disregard is since we won't be abble to access any of its properties
130 // anyway. This should not happen, but let's be defensive.
131 DeleteLastBlockIfHasNoHwid();
132 config_struct_
.resize(config_struct_
.size() + 1);
135 // If we still haven't added any blocks to data struct, we disregard this
136 // line. Again, this should never happen.
137 if (config_struct_
.empty())
140 ConfigFileBlock
& last_block
= config_struct_
.back();
142 if (line
[0] == kHwid
) {
143 // Check if line contains hwid property. If so, add it to set of hwids
144 // associated with current block.
145 last_block
.hwids
.insert(line
[1]);
147 // Add new block property.
148 last_block
.properties
.insert(std::make_pair(line
[0], line
[1]));
152 ConfigFile::ConfigFileBlock::ConfigFileBlock() {
155 ConfigFile::ConfigFileBlock::~ConfigFileBlock() {
158 ////////////////////////////////////////////////////////////////////////////////
162 ////////////////////////////////////////////////////////////////////////////////
163 StateMachine::StateMachine()
164 : download_started_(false),
165 download_finished_(false),
169 StateMachine::~StateMachine() {
172 void StateMachine::OnError(int error_message_id
) {
173 if (state_
== INITIAL
)
175 if (!download_finished_
)
176 download_started_
= false;
179 FOR_EACH_OBSERVER(Observer
, observers_
, OnError(error_message_id
));
182 void StateMachine::OnSuccess() {
183 if (state_
== INITIAL
)
189 void StateMachine::OnCancelation() {
190 // We use state CANCELLED only to let observers know that they have to
191 // process cancelation. We don't actually change the state.
192 FOR_EACH_OBSERVER(Observer
, observers_
, OnBurnStateChanged(CANCELLED
));
195 ////////////////////////////////////////////////////////////////////////////////
199 ////////////////////////////////////////////////////////////////////////////////
201 BurnManager::BurnManager()
202 : weak_ptr_factory_(this),
203 config_file_url_(kConfigFileUrl
),
204 config_file_fetched_(false),
205 state_machine_(new StateMachine()),
206 bytes_image_download_progress_last_reported_(0) {
209 BurnManager::~BurnManager() {
210 if (!image_dir_
.empty()) {
211 file_util::Delete(image_dir_
, true);
216 void BurnManager::Initialize() {
217 if (g_burn_manager
) {
218 LOG(WARNING
) << "BurnManager was already initialized";
221 g_burn_manager
= new BurnManager();
222 VLOG(1) << "BurnManager initialized";
226 void BurnManager::Shutdown() {
227 if (!g_burn_manager
) {
228 LOG(WARNING
) << "BurnManager::Shutdown() called with NULL manager";
231 delete g_burn_manager
;
232 g_burn_manager
= NULL
;
233 VLOG(1) << "BurnManager Shutdown completed";
237 BurnManager
* BurnManager::GetInstance() {
238 return g_burn_manager
;
241 void BurnManager::AddObserver(Observer
* observer
) {
242 observers_
.AddObserver(observer
);
245 void BurnManager::RemoveObserver(Observer
* observer
) {
246 observers_
.RemoveObserver(observer
);
249 void BurnManager::CreateImageDir(Delegate
* delegate
) {
250 if (image_dir_
.empty()) {
251 CHECK(PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS
, &image_dir_
));
252 image_dir_
= image_dir_
.Append(kTempImageFolderName
);
253 BrowserThread::PostBlockingPoolTask(
255 base::Bind(CreateDirectory
,
257 base::Bind(&BurnManager::OnImageDirCreated
,
258 weak_ptr_factory_
.GetWeakPtr(),
261 const bool success
= true;
262 OnImageDirCreated(delegate
, success
);
266 void BurnManager::OnImageDirCreated(Delegate
* delegate
, bool success
) {
267 delegate
->OnImageDirCreated(success
);
270 const FilePath
& BurnManager::GetImageDir() {
274 void BurnManager::FetchConfigFile(Delegate
* delegate
) {
275 if (config_file_fetched_
) {
276 delegate
->OnConfigFileFetched(true, image_file_name_
, image_download_url_
);
279 downloaders_
.push_back(delegate
->AsWeakPtr());
281 if (config_fetcher_
.get())
284 config_fetcher_
.reset(net::URLFetcher::Create(
285 config_file_url_
, net::URLFetcher::GET
, this));
286 config_fetcher_
->SetRequestContext(
287 g_browser_process
->system_request_context());
288 config_fetcher_
->Start();
291 void BurnManager::FetchImage(const GURL
& image_url
, const FilePath
& file_path
) {
292 tick_image_download_start_
= base::TimeTicks::Now();
293 bytes_image_download_progress_last_reported_
= 0;
294 image_fetcher_
.reset(net::URLFetcher::Create(image_url
,
295 net::URLFetcher::GET
,
297 image_fetcher_
->SetRequestContext(
298 g_browser_process
->system_request_context());
299 image_fetcher_
->SaveResponseToFileAtPath(
301 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
302 image_fetcher_
->Start();
305 void BurnManager::CancelImageFetch() {
306 image_fetcher_
.reset();
309 void BurnManager::OnURLFetchComplete(const net::URLFetcher
* source
) {
311 source
->GetStatus().status() == net::URLRequestStatus::SUCCESS
;
312 if (source
== config_fetcher_
.get()) {
315 config_fetcher_
->GetResponseAsString(&data
);
316 config_fetcher_
.reset();
317 ConfigFileFetched(success
, data
);
318 } else if (source
== image_fetcher_
.get()) {
320 FOR_EACH_OBSERVER(Observer
, observers_
, OnDownloadCompleted());
322 FOR_EACH_OBSERVER(Observer
, observers_
, OnDownloadCancelled());
326 void BurnManager::OnURLFetchDownloadProgress(const net::URLFetcher
* source
,
329 if (source
== image_fetcher_
.get()) {
330 if (current
>= bytes_image_download_progress_last_reported_
+
331 kBytesImageDownloadProgressReportInterval
) {
332 bytes_image_download_progress_last_reported_
= current
;
333 base::TimeDelta time_remaining
;
335 const base::TimeDelta diff
=
336 base::TimeTicks::Now() - tick_image_download_start_
;
337 time_remaining
= diff
*(total
- current
)/current
;
339 FOR_EACH_OBSERVER(Observer
, observers_
,
340 OnDownloadUpdated(current
, total
, time_remaining
));
345 void BurnManager::ConfigFileFetched(bool fetched
, const std::string
& content
) {
346 if (config_file_fetched_
)
349 // Get image file name and image download URL.
351 if (fetched
&& system::StatisticsProvider::GetInstance()->
352 GetMachineStatistic(kHwidStatistic
, &hwid
)) {
353 ConfigFile
config_file(content
);
354 image_file_name_
= config_file
.GetProperty(kFileName
, hwid
);
355 image_download_url_
= GURL(config_file
.GetProperty(kUrl
, hwid
));
359 if (fetched
&& !image_file_name_
.empty() && !image_download_url_
.is_empty()) {
360 config_file_fetched_
= true;
363 image_file_name_
.clear();
364 image_download_url_
= GURL();
367 for (size_t i
= 0; i
< downloaders_
.size(); ++i
) {
368 if (downloaders_
[i
]) {
369 downloaders_
[i
]->OnConfigFileFetched(fetched
,
371 image_download_url_
);
374 downloaders_
.clear();
377 } // namespace imageburner
378 } // namespace chromeos