| 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 |