OLD | NEW |
| (Empty) |
1 // Copyright 2007-2010 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "omaha/goopdate/worker.h" | |
17 #include "omaha/goopdate/worker_internal.h" | |
18 #include <atlbase.h> | |
19 #include <atlstr.h> | |
20 #include "omaha/base/app_util.h" | |
21 #include "omaha/base/const_object_names.h" | |
22 #include "omaha/base/debug.h" | |
23 #include "omaha/base/error.h" | |
24 #include "omaha/base/file.h" | |
25 #include "omaha/base/firewall_product_detection.h" | |
26 #include "omaha/base/logging.h" | |
27 #include "omaha/base/path.h" | |
28 #include "omaha/base/reactor.h" | |
29 #include "omaha/base/safe_format.h" | |
30 #include "omaha/base/scoped_impersonation.h" | |
31 #include "omaha/base/system.h" | |
32 #include "omaha/base/utils.h" | |
33 #include "omaha/base/thread_pool_callback.h" | |
34 #include "omaha/base/vistautil.h" | |
35 #include "omaha/common/app_registry_utils.h" | |
36 #include "omaha/common/config_manager.h" | |
37 #include "omaha/common/event_logger.h" | |
38 #include "omaha/common/goopdate_utils.h" | |
39 #include "omaha/common/ping.h" | |
40 #include "omaha/common/update_request.h" | |
41 #include "omaha/common/update_response.h" | |
42 #include "omaha/common/web_services_client.h" | |
43 #include "omaha/goopdate/app_manager.h" | |
44 #include "omaha/goopdate/download_manager.h" | |
45 #include "omaha/goopdate/goopdate.h" | |
46 #include "omaha/goopdate/install_manager.h" | |
47 #include "omaha/goopdate/model.h" | |
48 #include "omaha/goopdate/offline_utils.h" | |
49 #include "omaha/goopdate/server_resource.h" | |
50 #include "omaha/goopdate/string_formatter.h" | |
51 #include "omaha/goopdate/update_request_utils.h" | |
52 #include "omaha/goopdate/update_response_utils.h" | |
53 #include "omaha/goopdate/worker_metrics.h" | |
54 #include "omaha/goopdate/worker_utils.h" | |
55 | |
56 namespace omaha { | |
57 | |
58 namespace internal { | |
59 | |
60 void RecordUpdateAvailableUsageStats() { | |
61 AppManager& app_manager = *AppManager::Instance(); | |
62 | |
63 DWORD update_responses(0); | |
64 DWORD64 time_since_first_response_ms(0); | |
65 app_manager.ReadUpdateAvailableStats(kGoopdateGuid, | |
66 &update_responses, | |
67 &time_since_first_response_ms); | |
68 if (update_responses) { | |
69 metric_worker_self_update_responses = update_responses; | |
70 } | |
71 if (time_since_first_response_ms) { | |
72 metric_worker_self_update_response_time_since_first_ms = | |
73 time_since_first_response_ms; | |
74 } | |
75 | |
76 AppIdVector registered_app_ids; | |
77 HRESULT hr = app_manager.GetRegisteredApps(®istered_app_ids); | |
78 if (FAILED(hr)) { | |
79 ASSERT1(false); | |
80 return; | |
81 } | |
82 | |
83 // These store information about the app with the most update responses. | |
84 GUID max_responses_app(GUID_NULL); | |
85 DWORD max_responses(0); | |
86 DWORD64 max_responses_time_since_first_response_ms(0); | |
87 | |
88 for (size_t i = 0; i < registered_app_ids.size(); ++i) { | |
89 const CString& app_id = registered_app_ids[i]; | |
90 GUID app_guid = GUID_NULL; | |
91 | |
92 if (FAILED(StringToGuidSafe(app_id, &app_guid))) { | |
93 ASSERT(false, (_T("Invalid App ID: %s"), app_id)); | |
94 continue; | |
95 } | |
96 | |
97 if (::IsEqualGUID(kGoopdateGuid, app_guid)) { | |
98 continue; | |
99 } | |
100 | |
101 DWORD update_responses(0); | |
102 DWORD64 time_since_first_response_ms(0); | |
103 app_manager.ReadUpdateAvailableStats(app_guid, | |
104 &update_responses, | |
105 &time_since_first_response_ms); | |
106 | |
107 if (max_responses < update_responses) { | |
108 max_responses_app = app_guid; | |
109 max_responses = update_responses; | |
110 max_responses_time_since_first_response_ms = time_since_first_response_ms; | |
111 } | |
112 } | |
113 | |
114 if (max_responses) { | |
115 metric_worker_app_max_update_responses_app_high = | |
116 GetGuidMostSignificantUint64(max_responses_app); | |
117 metric_worker_app_max_update_responses = max_responses; | |
118 metric_worker_app_max_update_responses_ms_since_first = | |
119 max_responses_time_since_first_response_ms; | |
120 } | |
121 } | |
122 | |
123 // Will block if any apps are being installed. | |
124 HRESULT AddUninstalledAppsPings(AppBundle* app_bundle) { | |
125 CORE_LOG(L3, (_T("[AddUninstalledAppsPings]"))); | |
126 ASSERT1(app_bundle); | |
127 | |
128 AppManager& app_manager = *AppManager::Instance(); | |
129 | |
130 // Ensure that no installers are running while determining uninstalled apps | |
131 // and information about them. | |
132 __mutexScope(app_manager.GetRegistryStableStateLock()); | |
133 | |
134 AppIdVector uninstalled_app_ids; | |
135 HRESULT hr = app_manager.GetUninstalledApps(&uninstalled_app_ids); | |
136 if (FAILED(hr)) { | |
137 CORE_LOG(LE, (_T("[GetUninstalledApps failed][0x%08x]"), hr)); | |
138 return hr; | |
139 } | |
140 | |
141 if (!uninstalled_app_ids.size()) { | |
142 return S_OK; | |
143 } | |
144 | |
145 for (size_t i = 0; i < uninstalled_app_ids.size(); ++i) { | |
146 CString app_id = uninstalled_app_ids[i]; | |
147 CORE_LOG(L3, (_T("[found uninstalled product][%s]"), app_id)); | |
148 | |
149 // Omaha uninstall ping is sent by the Uninstall function in setup. | |
150 if (app_id == kGoogleUpdateAppId) { | |
151 CORE_LOG(L3, (_T("[skipping Omaha]"))); | |
152 continue; | |
153 } | |
154 | |
155 App* app = NULL; | |
156 hr = app_bundle->CreateUninstalledApp(app_id, &app); | |
157 if (FAILED(hr)) { | |
158 return hr; | |
159 } | |
160 | |
161 PingEventPtr ping_event( | |
162 new PingEvent(PingEvent::EVENT_UNINSTALL, | |
163 PingEvent::EVENT_RESULT_SUCCESS, | |
164 0, // error code | |
165 0)); // extra code 1 | |
166 app->AddPingEvent(ping_event); | |
167 | |
168 // TODO(omaha3) It would be nice to call RemoveClientState() only after the | |
169 // uninstall ping is sent so that the values are not deleted if the ping | |
170 // fails. However, this would allow race conditions between installers | |
171 // and the uninstall ping unless app_manager.GetRegistryStableStateLock() is | |
172 // held from the ping building through the send, which is not currently | |
173 // feasible since the ping is sent in the AppBundle destructor, and the | |
174 // AppBundle's lifetime is controlled by the client. Improving the ping | |
175 // architecture, such as having a ping queue managed by the Worker, may | |
176 // enable this. | |
177 VERIFY1(SUCCEEDED(app_manager.RemoveClientState(app->app_guid()))); | |
178 } | |
179 | |
180 return S_OK; | |
181 } | |
182 | |
183 HRESULT SendOemInstalledPing(bool is_machine, const CString& session_id) { | |
184 CORE_LOG(L3, (_T("[SendOemInstalledPing]"))); | |
185 | |
186 std::vector<CString> oem_installed_apps; | |
187 HRESULT hr = AppManager::Instance()->GetOemInstalledAndEulaAcceptedApps( | |
188 &oem_installed_apps); | |
189 if (FAILED(hr)) { | |
190 return hr; | |
191 } | |
192 | |
193 PingEventPtr oem_ping_event( | |
194 new PingEvent(PingEvent::EVENT_INSTALL_OEM_FIRST_CHECK, | |
195 PingEvent::EVENT_RESULT_SUCCESS, | |
196 0, | |
197 0)); | |
198 Ping ping(is_machine, session_id, CString()); | |
199 ping.LoadAppDataFromRegistry(oem_installed_apps); | |
200 ping.BuildAppsPing(oem_ping_event); | |
201 hr = ping.Send(false); | |
202 if (FAILED(hr)) { | |
203 CORE_LOG(L3, (_T("[SendOemInstalledPing failed][0x%08x]"), hr)); | |
204 return hr; | |
205 } | |
206 | |
207 AppManager::Instance()->ClearOemInstalled(oem_installed_apps); | |
208 | |
209 return S_OK; | |
210 } | |
211 | |
212 } // namespace internal | |
213 | |
214 Worker::Worker() | |
215 : is_machine_(false) { | |
216 CORE_LOG(L1, (_T("[Worker::Worker]"))); | |
217 } | |
218 | |
219 Worker::~Worker() { | |
220 CORE_LOG(L1, (_T("[Worker::~Worker]"))); | |
221 | |
222 // TODO(omaha3): Remove when Run() is used. See TODO in GoogleUpdate::Main(). | |
223 Stop(); | |
224 | |
225 AppManager::DeleteInstance(); | |
226 } | |
227 | |
228 Worker* const Worker::kInvalidInstance = reinterpret_cast<Worker* const>(-1); | |
229 Worker* Worker::instance_ = NULL; | |
230 | |
231 Worker& Worker::Instance() { | |
232 // Getting the instance after the instance has been deleted is a bug in | |
233 // the logic of the program. | |
234 ASSERT1(instance_ != kInvalidInstance); | |
235 if (!instance_) { | |
236 instance_ = new Worker(); | |
237 } | |
238 return *instance_; | |
239 } | |
240 | |
241 int Worker::Lock() { | |
242 return _pAtlModule->Lock(); | |
243 } | |
244 | |
245 int Worker::Unlock() { | |
246 return _pAtlModule->Unlock(); | |
247 } | |
248 | |
249 HRESULT Worker::Initialize(bool is_machine) { | |
250 CORE_LOG(L1, (_T("[Worker::Initialize][%d]"), is_machine)); | |
251 | |
252 is_machine_ = is_machine; | |
253 | |
254 reactor_.reset(new Reactor); | |
255 shutdown_handler_.reset(new ShutdownHandler); | |
256 model_.reset(new Model(this)); | |
257 | |
258 ASSERT1(!single_instance_.get()); | |
259 NamedObjectAttributes attr; | |
260 GetNamedObjectAttributes(kGoogleUpdate3SingleInstance, is_machine, &attr); | |
261 single_instance_.reset(new ProgramInstance(attr.name)); | |
262 if (!single_instance_.get()) { | |
263 CORE_LOG(LE, (_T("[Failed to create Worker Single Instance]"))); | |
264 return E_OUTOFMEMORY; | |
265 } | |
266 | |
267 if (!single_instance_->EnsureSingleInstance()) { | |
268 CORE_LOG(LW, (_T("[Another Worker instance already running]"))); | |
269 return GOOPDATE_E_INSTANCES_RUNNING; | |
270 } | |
271 | |
272 HRESULT hr = AppManager::CreateInstance(is_machine_); | |
273 if (FAILED(hr)) { | |
274 return hr; | |
275 } | |
276 | |
277 download_manager_.reset(new DownloadManager(is_machine_)); | |
278 | |
279 hr = download_manager_->Initialize(); | |
280 if (FAILED(hr)) { | |
281 return hr; | |
282 } | |
283 | |
284 install_manager_.reset(new InstallManager(&model_->lock(), is_machine_)); | |
285 hr = install_manager_->Initialize(); | |
286 if (FAILED(hr)) { | |
287 return hr; | |
288 } | |
289 | |
290 return S_OK; | |
291 } | |
292 | |
293 void Worker::Stop() { | |
294 // Stop the concurrent objects to avoid spurious events. | |
295 shutdown_handler_.reset(); | |
296 reactor_.reset(); | |
297 | |
298 // TODO(omaha3): Remove when Run() is used. See TODO in GoogleUpdate::Main(). | |
299 // Until then this call is necessary to wait for threads to complete on | |
300 // destruction. | |
301 // TODO(omaha3): Is it correct that the thread pool does not wait for the | |
302 // threads if !shutdown_event_? | |
303 Goopdate::Instance().Stop(); | |
304 } | |
305 | |
306 HRESULT Worker::Run() { | |
307 HRESULT hr = DoRun(); | |
308 Stop(); | |
309 | |
310 return hr; | |
311 } | |
312 | |
313 HRESULT Worker::DoRun() { | |
314 CORE_LOG(L1, (_T("[Worker::DoRun]"))); | |
315 | |
316 HRESULT hr = InitializeShutDownHandler(); | |
317 if (FAILED(hr)) { | |
318 return hr; | |
319 } | |
320 | |
321 return hr; | |
322 } | |
323 | |
324 HRESULT Worker::InitializeShutDownHandler() { | |
325 CORE_LOG(L3, (_T("[InitializeShutDownHandler]"))); | |
326 return shutdown_handler_->Initialize(reactor_.get(), this, is_machine_); | |
327 } | |
328 | |
329 HRESULT Worker::Shutdown() { | |
330 CORE_LOG(L2, (_T("[Worker::Shutdown]"))); | |
331 return S_OK; | |
332 } | |
333 | |
334 void Worker::CollectAmbientUsageStats() { | |
335 #if 0 | |
336 // The firewall detection code has been proved to block on some | |
337 // computers in the IWbemLocator::ConnectServer call even though a | |
338 // timeout was specified in the call. | |
339 CString name, version; | |
340 HRESULT hr = firewall_detection::Detect(&name, &version); | |
341 bool has_software_firewall = SUCCEEDED(hr) && !name.IsEmpty(); | |
342 metric_worker_has_software_firewall.Set(has_software_firewall); | |
343 #endif | |
344 | |
345 if (System::IsRunningOnBatteries()) { | |
346 ++metric_worker_silent_update_running_on_batteries; | |
347 } | |
348 | |
349 metric_worker_shell_version = app_util::GetVersionFromModule(NULL); | |
350 | |
351 metric_worker_is_windows_installing.Set(IsWindowsInstalling()); | |
352 metric_worker_is_uac_disabled.Set(vista_util::IsVistaOrLater() && | |
353 !vista_util::IsUACMaybeOn()); | |
354 metric_worker_is_clickonce_disabled.Set(IsClickOnceDisabled()); | |
355 } | |
356 | |
357 HRESULT Worker::CheckForUpdateAsync(AppBundle* app_bundle) { | |
358 CORE_LOG(L3, (_T("[Worker::CheckForUpdateAsync][0x%p]"), app_bundle)); | |
359 | |
360 ASSERT1(app_bundle); | |
361 ASSERT1(model_->IsLockedByCaller()); | |
362 | |
363 HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::CheckForUpdate); | |
364 if (FAILED(hr)) { | |
365 return hr; | |
366 } | |
367 | |
368 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
369 App* app = app_bundle->GetApp(i); | |
370 app->QueueUpdateCheck(); | |
371 } | |
372 | |
373 return S_OK; | |
374 } | |
375 | |
376 void Worker::CheckForUpdate(shared_ptr<AppBundle> app_bundle) { | |
377 CORE_LOG(L3, (_T("[Worker::CheckForUpdate][0x%p]"), app_bundle.get())); | |
378 ASSERT1(app_bundle.get()); | |
379 | |
380 bool is_check_successful = false; | |
381 CheckForUpdateHelper(app_bundle.get(), &is_check_successful); | |
382 | |
383 app_bundle->CompleteAsyncCall(); | |
384 } | |
385 | |
386 // *is_check_successful is true if the network request was successful and the | |
387 // response was successfully parsed. The elements' status need not be success, | |
388 // but an invalid response, such as HTML from a proxy, should result in false. | |
389 // TODO(omaha): Unit test this by mocking update_check_client. | |
390 void Worker::CheckForUpdateHelper(AppBundle* app_bundle, | |
391 bool* is_check_successful) { | |
392 ASSERT1(app_bundle); | |
393 ASSERT1(is_check_successful); | |
394 *is_check_successful = false; | |
395 | |
396 if (ConfigManager::Instance()->CanUseNetwork(is_machine_)) { | |
397 VERIFY1(SUCCEEDED(internal::SendOemInstalledPing( | |
398 is_machine_, app_bundle->session_id()))); | |
399 } | |
400 | |
401 scoped_impersonation impersonate_user(app_bundle->impersonation_token()); | |
402 HRESULT hr = impersonate_user.result(); | |
403 if (FAILED(hr)) { | |
404 CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr)); | |
405 return; | |
406 } | |
407 | |
408 scoped_ptr<xml::UpdateRequest> update_request( | |
409 xml::UpdateRequest::Create(is_machine_, | |
410 app_bundle->session_id(), | |
411 app_bundle->install_source(), | |
412 app_bundle->origin_url())); | |
413 | |
414 scoped_ptr<xml::UpdateResponse> update_response( | |
415 xml::UpdateResponse::Create()); | |
416 | |
417 CallAsSelfAndImpersonate2(this, | |
418 &Worker::DoPreUpdateCheck, | |
419 app_bundle, | |
420 update_request.get()); | |
421 | |
422 // This is a blocking call on the network. | |
423 hr = DoUpdateCheck(app_bundle, | |
424 update_request.get(), | |
425 update_response.get()); | |
426 if (FAILED(hr)) { | |
427 CORE_LOG(LW, (_T("[DoUpdateCheck failed][0x%08x]"), hr)); | |
428 } | |
429 *is_check_successful = SUCCEEDED(hr); | |
430 | |
431 CallAsSelfAndImpersonate3(this, | |
432 &Worker::DoPostUpdateCheck, | |
433 app_bundle, | |
434 hr, | |
435 update_response.get()); | |
436 | |
437 CString event_description; | |
438 event_description.Format(_T("Update check. Status = 0x%08x"), hr); | |
439 CString event_text; | |
440 CString url; | |
441 VERIFY1(SUCCEEDED(ConfigManager::Instance()->GetUpdateCheckUrl(&url))); | |
442 SafeCStringFormat(&event_text, _T("url=%s\n%s"), | |
443 url, | |
444 app_bundle->FetchAndResetLogText()); | |
445 | |
446 WriteEventLog(EVENTLOG_INFORMATION_TYPE, | |
447 kUpdateEventId, | |
448 event_description, | |
449 event_text); | |
450 } | |
451 | |
452 HRESULT Worker::DownloadAsync(AppBundle* app_bundle) { | |
453 CORE_LOG(L3, (_T("[Worker::DownloadAsync][0x%p]"), app_bundle)); | |
454 | |
455 ASSERT1(app_bundle); | |
456 ASSERT1(model_->IsLockedByCaller()); | |
457 | |
458 HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::Download); | |
459 if (FAILED(hr)) { | |
460 return hr; | |
461 } | |
462 | |
463 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
464 App* app = app_bundle->GetApp(i); | |
465 app->QueueDownload(); | |
466 } | |
467 | |
468 return S_OK; | |
469 } | |
470 | |
471 void Worker::Download(shared_ptr<AppBundle> app_bundle) { | |
472 CORE_LOG(L3, (_T("[Worker::Download][0x%p]"), app_bundle.get())); | |
473 ASSERT1(app_bundle.get()); | |
474 | |
475 scoped_impersonation impersonate_user(app_bundle->impersonation_token()); | |
476 HRESULT hr = impersonate_user.result(); | |
477 if (FAILED(hr)) { | |
478 CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr)); | |
479 return; | |
480 } | |
481 | |
482 const size_t num_apps = app_bundle->GetNumberOfApps(); | |
483 | |
484 for (size_t i = 0; i != num_apps; ++i) { | |
485 App* app = app_bundle->GetApp(i); | |
486 | |
487 ASSERT1(app->state() == STATE_WAITING_TO_DOWNLOAD || | |
488 app->state() == STATE_NO_UPDATE || | |
489 app->state() == STATE_ERROR); | |
490 | |
491 // This is a blocking call on the network. | |
492 app->Download(download_manager_.get()); | |
493 | |
494 ASSERT1(app->state() == STATE_READY_TO_INSTALL || | |
495 app->state() == STATE_NO_UPDATE || | |
496 app->state() == STATE_ERROR); | |
497 } | |
498 | |
499 WriteEventLog(EVENTLOG_INFORMATION_TYPE, | |
500 kDownloadEventId, | |
501 _T("Bundle download"), | |
502 app_bundle->FetchAndResetLogText()); | |
503 app_bundle->CompleteAsyncCall(); | |
504 } | |
505 | |
506 HRESULT Worker::DownloadAndInstallAsync(AppBundle* app_bundle) { | |
507 CORE_LOG(L3, (_T("[Worker::DownloadAndInstallAsync][0x%p]"), app_bundle)); | |
508 | |
509 ASSERT1(app_bundle); | |
510 ASSERT1(model_->IsLockedByCaller()); | |
511 | |
512 HRESULT hr = QueueDeferredFunctionCall0(app_bundle, | |
513 &Worker::DownloadAndInstall); | |
514 if (FAILED(hr)) { | |
515 return hr; | |
516 } | |
517 | |
518 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
519 App* app = app_bundle->GetApp(i); | |
520 // Queue the download or install depending on the current state. The correct | |
521 // one must be queued so that all apps are moved into waiting now and remain | |
522 // there until the download or install starts for that app. | |
523 app->QueueDownloadOrInstall(); | |
524 } | |
525 | |
526 return S_OK; | |
527 } | |
528 | |
529 void Worker::DownloadAndInstall(shared_ptr<AppBundle> app_bundle) { | |
530 CORE_LOG(L3, (_T("[Worker::DownloadAndInstall][0x%p]"), app_bundle.get())); | |
531 ASSERT1(app_bundle.get()); | |
532 | |
533 DownloadAndInstallHelper(app_bundle.get()); | |
534 | |
535 app_bundle->CompleteAsyncCall(); | |
536 } | |
537 | |
538 void Worker::DownloadAndInstallHelper(AppBundle* app_bundle) { | |
539 ASSERT1(app_bundle); | |
540 | |
541 scoped_impersonation impersonate_user(app_bundle->impersonation_token()); | |
542 HRESULT hr = impersonate_user.result(); | |
543 if (FAILED(hr)) { | |
544 CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr)); | |
545 return; | |
546 } | |
547 | |
548 const size_t num_apps = app_bundle->GetNumberOfApps(); | |
549 | |
550 for (size_t i = 0; i != num_apps; ++i) { | |
551 App* app = app_bundle->GetApp(i); | |
552 | |
553 ASSERT1(app->state() == STATE_WAITING_TO_DOWNLOAD || | |
554 app->state() == STATE_WAITING_TO_INSTALL || | |
555 app->state() == STATE_NO_UPDATE || | |
556 app->state() == STATE_ERROR); | |
557 | |
558 // Download the app if it has not already been downloaded. | |
559 // This is a blocking call on the network. | |
560 app->Download(download_manager_.get()); | |
561 | |
562 ASSERT1(app->state() == STATE_READY_TO_INSTALL || // Downloaded above. | |
563 app->state() == STATE_WAITING_TO_INSTALL || // Downloaded earlier. | |
564 app->state() == STATE_NO_UPDATE || | |
565 app->state() == STATE_ERROR); | |
566 | |
567 app->QueueInstall(); | |
568 | |
569 // This is a blocking call on the app installer. | |
570 CallAsSelfAndImpersonate1( | |
571 app, | |
572 &App::Install, | |
573 install_manager_.get()); | |
574 | |
575 ASSERT1(app->state() == STATE_INSTALL_COMPLETE || | |
576 app->state() == STATE_NO_UPDATE || | |
577 app->state() == STATE_ERROR); | |
578 } | |
579 | |
580 WriteEventLog(EVENTLOG_INFORMATION_TYPE, | |
581 kUpdateEventId, | |
582 _T("Application update/install"), | |
583 app_bundle->FetchAndResetLogText()); | |
584 } | |
585 | |
586 | |
587 HRESULT Worker::UpdateAllAppsAsync(AppBundle* app_bundle) { | |
588 CORE_LOG(L3, (_T("[Worker::UpdateAllAppsAsync][0x%p]"), app_bundle)); | |
589 | |
590 ASSERT1(app_bundle); | |
591 ASSERT1(model_->IsLockedByCaller()); | |
592 | |
593 HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::UpdateAllApps); | |
594 if (FAILED(hr)) { | |
595 return hr; | |
596 } | |
597 | |
598 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
599 App* app = app_bundle->GetApp(i); | |
600 app->QueueUpdateCheck(); | |
601 } | |
602 | |
603 return S_OK; | |
604 } | |
605 | |
606 // Runs through all steps regardless of error. App state machine handles errors. | |
607 void Worker::UpdateAllApps(shared_ptr<AppBundle> app_bundle) { | |
608 CORE_LOG(L3, (_T("[Worker::UpdateAllApps][0x%p]"), app_bundle.get())); | |
609 ASSERT1(app_bundle.get()); | |
610 | |
611 bool is_check_successful = false; | |
612 CheckForUpdateHelper(app_bundle.get(), &is_check_successful); | |
613 | |
614 if (is_check_successful) { | |
615 HRESULT hr = goopdate_utils::UpdateLastChecked(is_machine_); | |
616 ASSERT(SUCCEEDED(hr), (_T("UpdateLastChecked failed with 0x%08x"), hr)); | |
617 } | |
618 | |
619 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
620 App* app = app_bundle->GetApp(i); | |
621 app->QueueDownloadOrInstall(); | |
622 } | |
623 | |
624 DownloadAndInstallHelper(app_bundle.get()); | |
625 | |
626 internal::RecordUpdateAvailableUsageStats(); | |
627 CollectAmbientUsageStats(); | |
628 | |
629 HRESULT hr = internal::AddUninstalledAppsPings(app_bundle.get()); | |
630 if (FAILED(hr)) { | |
631 CORE_LOG(LW, (_T("[AddUninstalledAppsPings failed][0x%08x]"), hr)); | |
632 } | |
633 | |
634 app_bundle->CompleteAsyncCall(); | |
635 } | |
636 | |
637 HRESULT Worker::DownloadPackageAsync(Package* package) { | |
638 ASSERT1(package); | |
639 AppBundle* app_bundle = package->app_version()->app()->app_bundle(); | |
640 ASSERT1(app_bundle); | |
641 | |
642 ASSERT1(model_->IsLockedByCaller()); | |
643 | |
644 CORE_LOG(L3, (_T("[Worker::DownloadPackageAsync][0x%p][0x%p]"), | |
645 app_bundle, package)); | |
646 | |
647 HRESULT hr = QueueDeferredFunctionCall1<Package*>(app_bundle, | |
648 package, | |
649 &Worker::DownloadPackage); | |
650 if (FAILED(hr)) { | |
651 return hr; | |
652 } | |
653 | |
654 App* app = package->app_version()->app(); | |
655 app->QueueDownload(); | |
656 | |
657 return S_OK; | |
658 } | |
659 | |
660 void Worker::DownloadPackage(shared_ptr<AppBundle> app_bundle, | |
661 Package* package) { | |
662 CORE_LOG(L3, (_T("[Worker::DownloadPackage][0x%p][0x%p]"), | |
663 app_bundle.get(), package)); | |
664 ASSERT1(app_bundle.get()); | |
665 ASSERT1(package); | |
666 | |
667 scoped_impersonation impersonate_user(app_bundle->impersonation_token()); | |
668 HRESULT hr = impersonate_user.result(); | |
669 if (FAILED(hr)) { | |
670 CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr)); | |
671 return; | |
672 } | |
673 | |
674 UNREFERENCED_PARAMETER(package); | |
675 | |
676 // TODO(omaha): implement downloading a package. | |
677 | |
678 // TODO(omaha): The event log function should be modified depends on | |
679 // how the download is implemented. | |
680 // * whether HRESULT is needed. | |
681 // * whether the app_bundle has the log text. | |
682 // * additional information needed? | |
683 CString event_text; | |
684 SafeCStringFormat(&event_text, _T("Package: %s\n%s"), | |
685 package->filename(), | |
686 app_bundle->FetchAndResetLogText()); | |
687 | |
688 WriteEventLog(EVENTLOG_INFORMATION_TYPE, | |
689 kDownloadEventId, | |
690 _T("Package download"), | |
691 event_text); | |
692 app_bundle->CompleteAsyncCall(); | |
693 } | |
694 | |
695 // TODO(omaha3): Implement this and enforce the postcondition that the | |
696 // bundle object is not busy before the function returns. | |
697 HRESULT Worker::Stop(AppBundle* app_bundle) { | |
698 CORE_LOG(L3, (_T("[Worker::Stop][0x%p]"), app_bundle)); | |
699 | |
700 ASSERT1(app_bundle); | |
701 ASSERT1(model_->IsLockedByCaller()); | |
702 | |
703 // Cancels update check client but not the ping client since we need to send | |
704 // cancellation ping. | |
705 WebServicesClientInterface* update_check_client( | |
706 app_bundle->update_check_client()); | |
707 if (update_check_client) { | |
708 update_check_client->Cancel(); | |
709 } | |
710 | |
711 // TODO(omaha3): What do we do with active installs? We can at least cancel | |
712 // the InstallManager/InstallerWrapper if it has not started. | |
713 | |
714 // Cancel any apps that might have pending operations. | |
715 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
716 App* app = app_bundle->GetApp(i); | |
717 download_manager_->Cancel(app); | |
718 app->Cancel(); | |
719 } | |
720 | |
721 return S_OK; | |
722 } | |
723 | |
724 HRESULT Worker::Pause(AppBundle* app_bundle) { | |
725 CORE_LOG(L3, (_T("[Worker::Pause][0x%p]"), app_bundle)); | |
726 ASSERT1(app_bundle); | |
727 ASSERT1(model_->IsLockedByCaller()); | |
728 UNREFERENCED_PARAMETER(app_bundle); | |
729 | |
730 return E_NOTIMPL; | |
731 } | |
732 | |
733 HRESULT Worker::Resume(AppBundle* app_bundle) { | |
734 CORE_LOG(L3, (_T("[Worker::Resume][0x%p]"), app_bundle)); | |
735 ASSERT1(app_bundle); | |
736 ASSERT1(model_->IsLockedByCaller()); | |
737 UNREFERENCED_PARAMETER(app_bundle); | |
738 | |
739 return E_NOTIMPL; | |
740 } | |
741 | |
742 HRESULT Worker::GetPackage(const Package* package, const CString& dir) { | |
743 CORE_LOG(L3, (_T("[Worker::GetPackage]"))); | |
744 ASSERT1(model_->IsLockedByCaller()); | |
745 return download_manager_->GetPackage(package, dir); | |
746 } | |
747 | |
748 bool Worker::IsPackageAvailable(const Package* package) const { | |
749 CORE_LOG(L3, (_T("[Worker::IsPackageAvailable]"))); | |
750 ASSERT1(model_->IsLockedByCaller()); | |
751 return download_manager_->IsPackageAvailable(package); | |
752 } | |
753 | |
754 HRESULT Worker::CacheOfflinePackages(AppBundle* app_bundle) { | |
755 CORE_LOG(L3, (_T("[Worker::CacheOfflinePackages]"))); | |
756 ASSERT1(app_bundle); | |
757 | |
758 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
759 App* app = app_bundle->GetApp(i); | |
760 AppVersion* app_version = app->working_version(); | |
761 const size_t num_packages = app_version->GetNumberOfPackages(); | |
762 | |
763 for (size_t i = 0; i < num_packages; ++i) { | |
764 Package* package(app_version->GetPackage(i)); | |
765 if (download_manager_->IsPackageAvailable(package)) { | |
766 continue; | |
767 } | |
768 | |
769 CString offline_app_dir = ConcatenatePath(app_bundle->offline_dir(), | |
770 app->app_guid_string()); | |
771 CString offline_package_path = ConcatenatePath(offline_app_dir, | |
772 package->filename()); | |
773 if (!File::Exists(offline_package_path)) { | |
774 HRESULT hr = offline_utils::FindV2OfflinePackagePath( | |
775 offline_app_dir, &offline_package_path); | |
776 if (FAILED(hr)) { | |
777 CORE_LOG(LE, (_T("[FindOfflinePackagePath failed][0x%x]"), hr)); | |
778 return hr; | |
779 } | |
780 } | |
781 | |
782 HRESULT hr = download_manager_->CachePackage(package, | |
783 &offline_package_path); | |
784 if (FAILED(hr)) { | |
785 CORE_LOG(LE, (_T("[CachePackage failed][%s][%s][0x%x][%Iu]"), | |
786 app->app_guid_string(), offline_package_path, hr, i)); | |
787 return hr; | |
788 } | |
789 } | |
790 } | |
791 | |
792 return S_OK; | |
793 } | |
794 | |
795 HRESULT Worker::PurgeAppLowerVersions(const CString& app_id, | |
796 const CString& version) { | |
797 return download_manager_->PurgeAppLowerVersions(app_id, version); | |
798 } | |
799 | |
800 // metric_worker_apps_not_*ed_group_policy are integers, not a counter, so they | |
801 // should be set to a value, not incremented. Otherwise the same app could be | |
802 // counted twice if the same COM server instance was used for multiple bundles | |
803 // of the same app(s). For this reason and to avoid overwriting valid counts | |
804 // with 0, the number of disabled apps is accumulated then the appropriate | |
805 // metric is set only if the count is non-zero. | |
806 void Worker::DoPreUpdateCheck(AppBundle* app_bundle, | |
807 xml::UpdateRequest* update_request) { | |
808 ASSERT1(app_bundle); | |
809 ASSERT1(update_request); | |
810 | |
811 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
812 App* app = app_bundle->GetApp(i); | |
813 app->PreUpdateCheck(update_request); | |
814 } | |
815 | |
816 size_t num_disabled_apps = 0; | |
817 const std::vector<xml::request::App>& apps = update_request->request().apps; | |
818 | |
819 // apps.size is 0 if update check was cancelled. Its size can also be less | |
820 // than the number of apps in app_bundle if EULA is not accepted for some | |
821 // apps. | |
822 ASSERT1(apps.size() <= app_bundle->GetNumberOfApps()); | |
823 | |
824 for (size_t i = 0; i != apps.size(); ++i) { | |
825 ASSERT1(apps[i].update_check.is_valid); | |
826 if (apps[i].update_check.is_update_disabled) { | |
827 ++num_disabled_apps; | |
828 } | |
829 } | |
830 | |
831 if (num_disabled_apps) { | |
832 // Assumes that all apps are either updates or installs. | |
833 if (app_bundle->GetNumberOfApps() && app_bundle->GetApp(0)->is_update()) { | |
834 metric_worker_apps_not_updated_group_policy = num_disabled_apps; | |
835 } else { | |
836 metric_worker_apps_not_installed_group_policy = num_disabled_apps; | |
837 } | |
838 } | |
839 } | |
840 | |
841 HRESULT Worker::DoUpdateCheck(AppBundle* app_bundle, | |
842 const xml::UpdateRequest* update_request, | |
843 xml::UpdateResponse* update_response) { | |
844 ASSERT1(app_bundle); | |
845 ASSERT1(update_request); | |
846 ASSERT1(update_response); | |
847 | |
848 ASSERT1(app_bundle->GetNumberOfApps() > 0); | |
849 | |
850 if (app_bundle->is_offline_install()) { | |
851 return offline_utils::ParseOfflineManifest( | |
852 app_bundle->GetApp(0)->app_guid_string(), app_bundle->offline_dir(), | |
853 update_response); | |
854 } | |
855 | |
856 if (!ConfigManager::Instance()->CanUseNetwork(is_machine_)) { | |
857 CORE_LOG(L1, (_T("[Update check failed because network use prohibited]"))); | |
858 return GOOPDATE_E_CANNOT_USE_NETWORK; | |
859 } | |
860 | |
861 const bool is_update = app_bundle->is_auto_update(); | |
862 | |
863 if (is_update) { | |
864 ++metric_worker_update_check_total; | |
865 } | |
866 | |
867 // This is a blocking call on the network. | |
868 HRESULT hr = app_bundle->update_check_client()->Send(update_request, | |
869 update_response); | |
870 if (FAILED(hr)) { | |
871 CORE_LOG(LE, (_T("[Send failed][0x%08x]"), hr)); | |
872 worker_utils::AddHttpRequestDataToEventLog( | |
873 hr, | |
874 app_bundle->update_check_client()->http_status_code(), | |
875 app_bundle->update_check_client()->http_trace(), | |
876 is_machine_); | |
877 | |
878 // TODO(omaha3): Omaha 2 would launch a web browser here for installs by | |
879 // calling goopdate_utils::LaunchBrowser(). Browser launch needs to be in | |
880 // the client but it currently has no way of knowing that it was a network | |
881 // error. Even here, we don't know that the it wasn't a parsing, etc. error. | |
882 return hr; | |
883 } | |
884 | |
885 if (is_update) { | |
886 ++metric_worker_update_check_succeeded; | |
887 } | |
888 | |
889 return S_OK; | |
890 } | |
891 | |
892 void Worker::DoPostUpdateCheck(AppBundle* app_bundle, | |
893 HRESULT update_check_result, | |
894 xml::UpdateResponse* update_response) { | |
895 ASSERT1(app_bundle); | |
896 ASSERT1(update_response); | |
897 | |
898 VERIFY1(SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas( | |
899 is_machine_, | |
900 update_response))); | |
901 | |
902 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
903 App* app = app_bundle->GetApp(i); | |
904 app->PostUpdateCheck(update_check_result, update_response); | |
905 | |
906 ASSERT(app->state() == STATE_UPDATE_AVAILABLE || | |
907 app->state() == STATE_NO_UPDATE || | |
908 app->state() == STATE_ERROR, | |
909 (_T("App %Iu state is %u"), i, app->state())); | |
910 } | |
911 | |
912 if (app_bundle->is_offline_install()) { | |
913 VERIFY1(SUCCEEDED(CacheOfflinePackages(app_bundle))); | |
914 VERIFY1(SUCCEEDED(DeleteDirectory(app_bundle->offline_dir()))); | |
915 } | |
916 } | |
917 | |
918 // Creates a thread pool work item for deferred execution of deferred_function. | |
919 // The thread pool owns this callback object. | |
920 HRESULT Worker::QueueDeferredFunctionCall0( | |
921 AppBundle* app_bundle, | |
922 void (Worker::*deferred_function)(shared_ptr<AppBundle>)) { | |
923 ASSERT1(app_bundle); | |
924 ASSERT1(deferred_function); | |
925 | |
926 typedef ThreadPoolCallBack1<Worker, shared_ptr<AppBundle> > Callback; | |
927 scoped_ptr<Callback> callback(new Callback(this, | |
928 deferred_function, | |
929 app_bundle->controlling_ptr())); | |
930 HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(), | |
931 WT_EXECUTELONGFUNCTION); | |
932 if (FAILED(hr)) { | |
933 return hr; | |
934 } | |
935 | |
936 // Transfers the ownership of the callback from Worker to ThreadPool. | |
937 app_bundle->set_user_work_item(callback.release()); | |
938 return S_OK; | |
939 } | |
940 | |
941 // Creates a thread pool work item for deferred execution of deferred_function. | |
942 // The thread pool owns this callback object. | |
943 template <typename P1> | |
944 HRESULT Worker::QueueDeferredFunctionCall1( | |
945 AppBundle* app_bundle, | |
946 P1 p1, | |
947 void (Worker::*deferred_function)(shared_ptr<AppBundle>, P1)) { | |
948 ASSERT1(app_bundle); | |
949 ASSERT1(deferred_function); | |
950 | |
951 typedef ThreadPoolCallBack2<Worker, shared_ptr<AppBundle>, P1> Callback; | |
952 scoped_ptr<Callback> callback(new Callback(this, | |
953 deferred_function, | |
954 app_bundle->controlling_ptr(), | |
955 p1)); | |
956 HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(), | |
957 WT_EXECUTELONGFUNCTION); | |
958 if (FAILED(hr)) { | |
959 return hr; | |
960 } | |
961 | |
962 // Transfers the ownership of the callback from Worker to ThreadPool. | |
963 app_bundle->set_user_work_item(callback.release()); | |
964 return S_OK; | |
965 } | |
966 | |
967 void Worker::WriteEventLog(int event_type, | |
968 int event_id, | |
969 const CString& event_description, | |
970 const CString& event_text) { | |
971 GoogleUpdateLogEvent log_event(event_type, event_id, is_machine_); | |
972 log_event.set_event_desc(event_description); | |
973 log_event.set_event_text(event_text); | |
974 log_event.WriteEvent(); | |
975 } | |
976 | |
977 } // namespace omaha | |
OLD | NEW |