| Index: goopdate/worker.cc
|
| diff --git a/goopdate/worker.cc b/goopdate/worker.cc
|
| deleted file mode 100644
|
| index 69821a6d5d922d0ee0422a7d5c50a5f86829e609..0000000000000000000000000000000000000000
|
| --- a/goopdate/worker.cc
|
| +++ /dev/null
|
| @@ -1,977 +0,0 @@
|
| -// Copyright 2007-2010 Google Inc.
|
| -//
|
| -// Licensed under the Apache License, Version 2.0 (the "License");
|
| -// you may not use this file except in compliance with the License.
|
| -// You may obtain a copy of the License at
|
| -//
|
| -// http://www.apache.org/licenses/LICENSE-2.0
|
| -//
|
| -// Unless required by applicable law or agreed to in writing, software
|
| -// distributed under the License is distributed on an "AS IS" BASIS,
|
| -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -// See the License for the specific language governing permissions and
|
| -// limitations under the License.
|
| -// ========================================================================
|
| -
|
| -#include "omaha/goopdate/worker.h"
|
| -#include "omaha/goopdate/worker_internal.h"
|
| -#include <atlbase.h>
|
| -#include <atlstr.h>
|
| -#include "omaha/base/app_util.h"
|
| -#include "omaha/base/const_object_names.h"
|
| -#include "omaha/base/debug.h"
|
| -#include "omaha/base/error.h"
|
| -#include "omaha/base/file.h"
|
| -#include "omaha/base/firewall_product_detection.h"
|
| -#include "omaha/base/logging.h"
|
| -#include "omaha/base/path.h"
|
| -#include "omaha/base/reactor.h"
|
| -#include "omaha/base/safe_format.h"
|
| -#include "omaha/base/scoped_impersonation.h"
|
| -#include "omaha/base/system.h"
|
| -#include "omaha/base/utils.h"
|
| -#include "omaha/base/thread_pool_callback.h"
|
| -#include "omaha/base/vistautil.h"
|
| -#include "omaha/common/app_registry_utils.h"
|
| -#include "omaha/common/config_manager.h"
|
| -#include "omaha/common/event_logger.h"
|
| -#include "omaha/common/goopdate_utils.h"
|
| -#include "omaha/common/ping.h"
|
| -#include "omaha/common/update_request.h"
|
| -#include "omaha/common/update_response.h"
|
| -#include "omaha/common/web_services_client.h"
|
| -#include "omaha/goopdate/app_manager.h"
|
| -#include "omaha/goopdate/download_manager.h"
|
| -#include "omaha/goopdate/goopdate.h"
|
| -#include "omaha/goopdate/install_manager.h"
|
| -#include "omaha/goopdate/model.h"
|
| -#include "omaha/goopdate/offline_utils.h"
|
| -#include "omaha/goopdate/server_resource.h"
|
| -#include "omaha/goopdate/string_formatter.h"
|
| -#include "omaha/goopdate/update_request_utils.h"
|
| -#include "omaha/goopdate/update_response_utils.h"
|
| -#include "omaha/goopdate/worker_metrics.h"
|
| -#include "omaha/goopdate/worker_utils.h"
|
| -
|
| -namespace omaha {
|
| -
|
| -namespace internal {
|
| -
|
| -void RecordUpdateAvailableUsageStats() {
|
| - AppManager& app_manager = *AppManager::Instance();
|
| -
|
| - DWORD update_responses(0);
|
| - DWORD64 time_since_first_response_ms(0);
|
| - app_manager.ReadUpdateAvailableStats(kGoopdateGuid,
|
| - &update_responses,
|
| - &time_since_first_response_ms);
|
| - if (update_responses) {
|
| - metric_worker_self_update_responses = update_responses;
|
| - }
|
| - if (time_since_first_response_ms) {
|
| - metric_worker_self_update_response_time_since_first_ms =
|
| - time_since_first_response_ms;
|
| - }
|
| -
|
| - AppIdVector registered_app_ids;
|
| - HRESULT hr = app_manager.GetRegisteredApps(®istered_app_ids);
|
| - if (FAILED(hr)) {
|
| - ASSERT1(false);
|
| - return;
|
| - }
|
| -
|
| - // These store information about the app with the most update responses.
|
| - GUID max_responses_app(GUID_NULL);
|
| - DWORD max_responses(0);
|
| - DWORD64 max_responses_time_since_first_response_ms(0);
|
| -
|
| - for (size_t i = 0; i < registered_app_ids.size(); ++i) {
|
| - const CString& app_id = registered_app_ids[i];
|
| - GUID app_guid = GUID_NULL;
|
| -
|
| - if (FAILED(StringToGuidSafe(app_id, &app_guid))) {
|
| - ASSERT(false, (_T("Invalid App ID: %s"), app_id));
|
| - continue;
|
| - }
|
| -
|
| - if (::IsEqualGUID(kGoopdateGuid, app_guid)) {
|
| - continue;
|
| - }
|
| -
|
| - DWORD update_responses(0);
|
| - DWORD64 time_since_first_response_ms(0);
|
| - app_manager.ReadUpdateAvailableStats(app_guid,
|
| - &update_responses,
|
| - &time_since_first_response_ms);
|
| -
|
| - if (max_responses < update_responses) {
|
| - max_responses_app = app_guid;
|
| - max_responses = update_responses;
|
| - max_responses_time_since_first_response_ms = time_since_first_response_ms;
|
| - }
|
| - }
|
| -
|
| - if (max_responses) {
|
| - metric_worker_app_max_update_responses_app_high =
|
| - GetGuidMostSignificantUint64(max_responses_app);
|
| - metric_worker_app_max_update_responses = max_responses;
|
| - metric_worker_app_max_update_responses_ms_since_first =
|
| - max_responses_time_since_first_response_ms;
|
| - }
|
| -}
|
| -
|
| -// Will block if any apps are being installed.
|
| -HRESULT AddUninstalledAppsPings(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[AddUninstalledAppsPings]")));
|
| - ASSERT1(app_bundle);
|
| -
|
| - AppManager& app_manager = *AppManager::Instance();
|
| -
|
| - // Ensure that no installers are running while determining uninstalled apps
|
| - // and information about them.
|
| - __mutexScope(app_manager.GetRegistryStableStateLock());
|
| -
|
| - AppIdVector uninstalled_app_ids;
|
| - HRESULT hr = app_manager.GetUninstalledApps(&uninstalled_app_ids);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[GetUninstalledApps failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - if (!uninstalled_app_ids.size()) {
|
| - return S_OK;
|
| - }
|
| -
|
| - for (size_t i = 0; i < uninstalled_app_ids.size(); ++i) {
|
| - CString app_id = uninstalled_app_ids[i];
|
| - CORE_LOG(L3, (_T("[found uninstalled product][%s]"), app_id));
|
| -
|
| - // Omaha uninstall ping is sent by the Uninstall function in setup.
|
| - if (app_id == kGoogleUpdateAppId) {
|
| - CORE_LOG(L3, (_T("[skipping Omaha]")));
|
| - continue;
|
| - }
|
| -
|
| - App* app = NULL;
|
| - hr = app_bundle->CreateUninstalledApp(app_id, &app);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - PingEventPtr ping_event(
|
| - new PingEvent(PingEvent::EVENT_UNINSTALL,
|
| - PingEvent::EVENT_RESULT_SUCCESS,
|
| - 0, // error code
|
| - 0)); // extra code 1
|
| - app->AddPingEvent(ping_event);
|
| -
|
| - // TODO(omaha3) It would be nice to call RemoveClientState() only after the
|
| - // uninstall ping is sent so that the values are not deleted if the ping
|
| - // fails. However, this would allow race conditions between installers
|
| - // and the uninstall ping unless app_manager.GetRegistryStableStateLock() is
|
| - // held from the ping building through the send, which is not currently
|
| - // feasible since the ping is sent in the AppBundle destructor, and the
|
| - // AppBundle's lifetime is controlled by the client. Improving the ping
|
| - // architecture, such as having a ping queue managed by the Worker, may
|
| - // enable this.
|
| - VERIFY1(SUCCEEDED(app_manager.RemoveClientState(app->app_guid())));
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT SendOemInstalledPing(bool is_machine, const CString& session_id) {
|
| - CORE_LOG(L3, (_T("[SendOemInstalledPing]")));
|
| -
|
| - std::vector<CString> oem_installed_apps;
|
| - HRESULT hr = AppManager::Instance()->GetOemInstalledAndEulaAcceptedApps(
|
| - &oem_installed_apps);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - PingEventPtr oem_ping_event(
|
| - new PingEvent(PingEvent::EVENT_INSTALL_OEM_FIRST_CHECK,
|
| - PingEvent::EVENT_RESULT_SUCCESS,
|
| - 0,
|
| - 0));
|
| - Ping ping(is_machine, session_id, CString());
|
| - ping.LoadAppDataFromRegistry(oem_installed_apps);
|
| - ping.BuildAppsPing(oem_ping_event);
|
| - hr = ping.Send(false);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(L3, (_T("[SendOemInstalledPing failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - AppManager::Instance()->ClearOemInstalled(oem_installed_apps);
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -} // namespace internal
|
| -
|
| -Worker::Worker()
|
| - : is_machine_(false) {
|
| - CORE_LOG(L1, (_T("[Worker::Worker]")));
|
| -}
|
| -
|
| -Worker::~Worker() {
|
| - CORE_LOG(L1, (_T("[Worker::~Worker]")));
|
| -
|
| - // TODO(omaha3): Remove when Run() is used. See TODO in GoogleUpdate::Main().
|
| - Stop();
|
| -
|
| - AppManager::DeleteInstance();
|
| -}
|
| -
|
| -Worker* const Worker::kInvalidInstance = reinterpret_cast<Worker* const>(-1);
|
| -Worker* Worker::instance_ = NULL;
|
| -
|
| -Worker& Worker::Instance() {
|
| - // Getting the instance after the instance has been deleted is a bug in
|
| - // the logic of the program.
|
| - ASSERT1(instance_ != kInvalidInstance);
|
| - if (!instance_) {
|
| - instance_ = new Worker();
|
| - }
|
| - return *instance_;
|
| -}
|
| -
|
| -int Worker::Lock() {
|
| - return _pAtlModule->Lock();
|
| -}
|
| -
|
| -int Worker::Unlock() {
|
| - return _pAtlModule->Unlock();
|
| -}
|
| -
|
| -HRESULT Worker::Initialize(bool is_machine) {
|
| - CORE_LOG(L1, (_T("[Worker::Initialize][%d]"), is_machine));
|
| -
|
| - is_machine_ = is_machine;
|
| -
|
| - reactor_.reset(new Reactor);
|
| - shutdown_handler_.reset(new ShutdownHandler);
|
| - model_.reset(new Model(this));
|
| -
|
| - ASSERT1(!single_instance_.get());
|
| - NamedObjectAttributes attr;
|
| - GetNamedObjectAttributes(kGoogleUpdate3SingleInstance, is_machine, &attr);
|
| - single_instance_.reset(new ProgramInstance(attr.name));
|
| - if (!single_instance_.get()) {
|
| - CORE_LOG(LE, (_T("[Failed to create Worker Single Instance]")));
|
| - return E_OUTOFMEMORY;
|
| - }
|
| -
|
| - if (!single_instance_->EnsureSingleInstance()) {
|
| - CORE_LOG(LW, (_T("[Another Worker instance already running]")));
|
| - return GOOPDATE_E_INSTANCES_RUNNING;
|
| - }
|
| -
|
| - HRESULT hr = AppManager::CreateInstance(is_machine_);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - download_manager_.reset(new DownloadManager(is_machine_));
|
| -
|
| - hr = download_manager_->Initialize();
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - install_manager_.reset(new InstallManager(&model_->lock(), is_machine_));
|
| - hr = install_manager_->Initialize();
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -void Worker::Stop() {
|
| - // Stop the concurrent objects to avoid spurious events.
|
| - shutdown_handler_.reset();
|
| - reactor_.reset();
|
| -
|
| - // TODO(omaha3): Remove when Run() is used. See TODO in GoogleUpdate::Main().
|
| - // Until then this call is necessary to wait for threads to complete on
|
| - // destruction.
|
| - // TODO(omaha3): Is it correct that the thread pool does not wait for the
|
| - // threads if !shutdown_event_?
|
| - Goopdate::Instance().Stop();
|
| -}
|
| -
|
| -HRESULT Worker::Run() {
|
| - HRESULT hr = DoRun();
|
| - Stop();
|
| -
|
| - return hr;
|
| -}
|
| -
|
| -HRESULT Worker::DoRun() {
|
| - CORE_LOG(L1, (_T("[Worker::DoRun]")));
|
| -
|
| - HRESULT hr = InitializeShutDownHandler();
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - return hr;
|
| -}
|
| -
|
| -HRESULT Worker::InitializeShutDownHandler() {
|
| - CORE_LOG(L3, (_T("[InitializeShutDownHandler]")));
|
| - return shutdown_handler_->Initialize(reactor_.get(), this, is_machine_);
|
| -}
|
| -
|
| -HRESULT Worker::Shutdown() {
|
| - CORE_LOG(L2, (_T("[Worker::Shutdown]")));
|
| - return S_OK;
|
| -}
|
| -
|
| -void Worker::CollectAmbientUsageStats() {
|
| -#if 0
|
| - // The firewall detection code has been proved to block on some
|
| - // computers in the IWbemLocator::ConnectServer call even though a
|
| - // timeout was specified in the call.
|
| - CString name, version;
|
| - HRESULT hr = firewall_detection::Detect(&name, &version);
|
| - bool has_software_firewall = SUCCEEDED(hr) && !name.IsEmpty();
|
| - metric_worker_has_software_firewall.Set(has_software_firewall);
|
| -#endif
|
| -
|
| - if (System::IsRunningOnBatteries()) {
|
| - ++metric_worker_silent_update_running_on_batteries;
|
| - }
|
| -
|
| - metric_worker_shell_version = app_util::GetVersionFromModule(NULL);
|
| -
|
| - metric_worker_is_windows_installing.Set(IsWindowsInstalling());
|
| - metric_worker_is_uac_disabled.Set(vista_util::IsVistaOrLater() &&
|
| - !vista_util::IsUACMaybeOn());
|
| - metric_worker_is_clickonce_disabled.Set(IsClickOnceDisabled());
|
| -}
|
| -
|
| -HRESULT Worker::CheckForUpdateAsync(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::CheckForUpdateAsync][0x%p]"), app_bundle));
|
| -
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(model_->IsLockedByCaller());
|
| -
|
| - HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::CheckForUpdate);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - app->QueueUpdateCheck();
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -void Worker::CheckForUpdate(shared_ptr<AppBundle> app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::CheckForUpdate][0x%p]"), app_bundle.get()));
|
| - ASSERT1(app_bundle.get());
|
| -
|
| - bool is_check_successful = false;
|
| - CheckForUpdateHelper(app_bundle.get(), &is_check_successful);
|
| -
|
| - app_bundle->CompleteAsyncCall();
|
| -}
|
| -
|
| -// *is_check_successful is true if the network request was successful and the
|
| -// response was successfully parsed. The elements' status need not be success,
|
| -// but an invalid response, such as HTML from a proxy, should result in false.
|
| -// TODO(omaha): Unit test this by mocking update_check_client.
|
| -void Worker::CheckForUpdateHelper(AppBundle* app_bundle,
|
| - bool* is_check_successful) {
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(is_check_successful);
|
| - *is_check_successful = false;
|
| -
|
| - if (ConfigManager::Instance()->CanUseNetwork(is_machine_)) {
|
| - VERIFY1(SUCCEEDED(internal::SendOemInstalledPing(
|
| - is_machine_, app_bundle->session_id())));
|
| - }
|
| -
|
| - scoped_impersonation impersonate_user(app_bundle->impersonation_token());
|
| - HRESULT hr = impersonate_user.result();
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
|
| - return;
|
| - }
|
| -
|
| - scoped_ptr<xml::UpdateRequest> update_request(
|
| - xml::UpdateRequest::Create(is_machine_,
|
| - app_bundle->session_id(),
|
| - app_bundle->install_source(),
|
| - app_bundle->origin_url()));
|
| -
|
| - scoped_ptr<xml::UpdateResponse> update_response(
|
| - xml::UpdateResponse::Create());
|
| -
|
| - CallAsSelfAndImpersonate2(this,
|
| - &Worker::DoPreUpdateCheck,
|
| - app_bundle,
|
| - update_request.get());
|
| -
|
| - // This is a blocking call on the network.
|
| - hr = DoUpdateCheck(app_bundle,
|
| - update_request.get(),
|
| - update_response.get());
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LW, (_T("[DoUpdateCheck failed][0x%08x]"), hr));
|
| - }
|
| - *is_check_successful = SUCCEEDED(hr);
|
| -
|
| - CallAsSelfAndImpersonate3(this,
|
| - &Worker::DoPostUpdateCheck,
|
| - app_bundle,
|
| - hr,
|
| - update_response.get());
|
| -
|
| - CString event_description;
|
| - event_description.Format(_T("Update check. Status = 0x%08x"), hr);
|
| - CString event_text;
|
| - CString url;
|
| - VERIFY1(SUCCEEDED(ConfigManager::Instance()->GetUpdateCheckUrl(&url)));
|
| - SafeCStringFormat(&event_text, _T("url=%s\n%s"),
|
| - url,
|
| - app_bundle->FetchAndResetLogText());
|
| -
|
| - WriteEventLog(EVENTLOG_INFORMATION_TYPE,
|
| - kUpdateEventId,
|
| - event_description,
|
| - event_text);
|
| -}
|
| -
|
| -HRESULT Worker::DownloadAsync(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::DownloadAsync][0x%p]"), app_bundle));
|
| -
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(model_->IsLockedByCaller());
|
| -
|
| - HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::Download);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - app->QueueDownload();
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -void Worker::Download(shared_ptr<AppBundle> app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::Download][0x%p]"), app_bundle.get()));
|
| - ASSERT1(app_bundle.get());
|
| -
|
| - scoped_impersonation impersonate_user(app_bundle->impersonation_token());
|
| - HRESULT hr = impersonate_user.result();
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
|
| - return;
|
| - }
|
| -
|
| - const size_t num_apps = app_bundle->GetNumberOfApps();
|
| -
|
| - for (size_t i = 0; i != num_apps; ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| -
|
| - ASSERT1(app->state() == STATE_WAITING_TO_DOWNLOAD ||
|
| - app->state() == STATE_NO_UPDATE ||
|
| - app->state() == STATE_ERROR);
|
| -
|
| - // This is a blocking call on the network.
|
| - app->Download(download_manager_.get());
|
| -
|
| - ASSERT1(app->state() == STATE_READY_TO_INSTALL ||
|
| - app->state() == STATE_NO_UPDATE ||
|
| - app->state() == STATE_ERROR);
|
| - }
|
| -
|
| - WriteEventLog(EVENTLOG_INFORMATION_TYPE,
|
| - kDownloadEventId,
|
| - _T("Bundle download"),
|
| - app_bundle->FetchAndResetLogText());
|
| - app_bundle->CompleteAsyncCall();
|
| -}
|
| -
|
| -HRESULT Worker::DownloadAndInstallAsync(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::DownloadAndInstallAsync][0x%p]"), app_bundle));
|
| -
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(model_->IsLockedByCaller());
|
| -
|
| - HRESULT hr = QueueDeferredFunctionCall0(app_bundle,
|
| - &Worker::DownloadAndInstall);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - // Queue the download or install depending on the current state. The correct
|
| - // one must be queued so that all apps are moved into waiting now and remain
|
| - // there until the download or install starts for that app.
|
| - app->QueueDownloadOrInstall();
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -void Worker::DownloadAndInstall(shared_ptr<AppBundle> app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::DownloadAndInstall][0x%p]"), app_bundle.get()));
|
| - ASSERT1(app_bundle.get());
|
| -
|
| - DownloadAndInstallHelper(app_bundle.get());
|
| -
|
| - app_bundle->CompleteAsyncCall();
|
| -}
|
| -
|
| -void Worker::DownloadAndInstallHelper(AppBundle* app_bundle) {
|
| - ASSERT1(app_bundle);
|
| -
|
| - scoped_impersonation impersonate_user(app_bundle->impersonation_token());
|
| - HRESULT hr = impersonate_user.result();
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
|
| - return;
|
| - }
|
| -
|
| - const size_t num_apps = app_bundle->GetNumberOfApps();
|
| -
|
| - for (size_t i = 0; i != num_apps; ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| -
|
| - ASSERT1(app->state() == STATE_WAITING_TO_DOWNLOAD ||
|
| - app->state() == STATE_WAITING_TO_INSTALL ||
|
| - app->state() == STATE_NO_UPDATE ||
|
| - app->state() == STATE_ERROR);
|
| -
|
| - // Download the app if it has not already been downloaded.
|
| - // This is a blocking call on the network.
|
| - app->Download(download_manager_.get());
|
| -
|
| - ASSERT1(app->state() == STATE_READY_TO_INSTALL || // Downloaded above.
|
| - app->state() == STATE_WAITING_TO_INSTALL || // Downloaded earlier.
|
| - app->state() == STATE_NO_UPDATE ||
|
| - app->state() == STATE_ERROR);
|
| -
|
| - app->QueueInstall();
|
| -
|
| - // This is a blocking call on the app installer.
|
| - CallAsSelfAndImpersonate1(
|
| - app,
|
| - &App::Install,
|
| - install_manager_.get());
|
| -
|
| - ASSERT1(app->state() == STATE_INSTALL_COMPLETE ||
|
| - app->state() == STATE_NO_UPDATE ||
|
| - app->state() == STATE_ERROR);
|
| - }
|
| -
|
| - WriteEventLog(EVENTLOG_INFORMATION_TYPE,
|
| - kUpdateEventId,
|
| - _T("Application update/install"),
|
| - app_bundle->FetchAndResetLogText());
|
| -}
|
| -
|
| -
|
| -HRESULT Worker::UpdateAllAppsAsync(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::UpdateAllAppsAsync][0x%p]"), app_bundle));
|
| -
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(model_->IsLockedByCaller());
|
| -
|
| - HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::UpdateAllApps);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - app->QueueUpdateCheck();
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// Runs through all steps regardless of error. App state machine handles errors.
|
| -void Worker::UpdateAllApps(shared_ptr<AppBundle> app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::UpdateAllApps][0x%p]"), app_bundle.get()));
|
| - ASSERT1(app_bundle.get());
|
| -
|
| - bool is_check_successful = false;
|
| - CheckForUpdateHelper(app_bundle.get(), &is_check_successful);
|
| -
|
| - if (is_check_successful) {
|
| - HRESULT hr = goopdate_utils::UpdateLastChecked(is_machine_);
|
| - ASSERT(SUCCEEDED(hr), (_T("UpdateLastChecked failed with 0x%08x"), hr));
|
| - }
|
| -
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - app->QueueDownloadOrInstall();
|
| - }
|
| -
|
| - DownloadAndInstallHelper(app_bundle.get());
|
| -
|
| - internal::RecordUpdateAvailableUsageStats();
|
| - CollectAmbientUsageStats();
|
| -
|
| - HRESULT hr = internal::AddUninstalledAppsPings(app_bundle.get());
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LW, (_T("[AddUninstalledAppsPings failed][0x%08x]"), hr));
|
| - }
|
| -
|
| - app_bundle->CompleteAsyncCall();
|
| -}
|
| -
|
| -HRESULT Worker::DownloadPackageAsync(Package* package) {
|
| - ASSERT1(package);
|
| - AppBundle* app_bundle = package->app_version()->app()->app_bundle();
|
| - ASSERT1(app_bundle);
|
| -
|
| - ASSERT1(model_->IsLockedByCaller());
|
| -
|
| - CORE_LOG(L3, (_T("[Worker::DownloadPackageAsync][0x%p][0x%p]"),
|
| - app_bundle, package));
|
| -
|
| - HRESULT hr = QueueDeferredFunctionCall1<Package*>(app_bundle,
|
| - package,
|
| - &Worker::DownloadPackage);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - App* app = package->app_version()->app();
|
| - app->QueueDownload();
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -void Worker::DownloadPackage(shared_ptr<AppBundle> app_bundle,
|
| - Package* package) {
|
| - CORE_LOG(L3, (_T("[Worker::DownloadPackage][0x%p][0x%p]"),
|
| - app_bundle.get(), package));
|
| - ASSERT1(app_bundle.get());
|
| - ASSERT1(package);
|
| -
|
| - scoped_impersonation impersonate_user(app_bundle->impersonation_token());
|
| - HRESULT hr = impersonate_user.result();
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
|
| - return;
|
| - }
|
| -
|
| - UNREFERENCED_PARAMETER(package);
|
| -
|
| - // TODO(omaha): implement downloading a package.
|
| -
|
| - // TODO(omaha): The event log function should be modified depends on
|
| - // how the download is implemented.
|
| - // * whether HRESULT is needed.
|
| - // * whether the app_bundle has the log text.
|
| - // * additional information needed?
|
| - CString event_text;
|
| - SafeCStringFormat(&event_text, _T("Package: %s\n%s"),
|
| - package->filename(),
|
| - app_bundle->FetchAndResetLogText());
|
| -
|
| - WriteEventLog(EVENTLOG_INFORMATION_TYPE,
|
| - kDownloadEventId,
|
| - _T("Package download"),
|
| - event_text);
|
| - app_bundle->CompleteAsyncCall();
|
| -}
|
| -
|
| -// TODO(omaha3): Implement this and enforce the postcondition that the
|
| -// bundle object is not busy before the function returns.
|
| -HRESULT Worker::Stop(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::Stop][0x%p]"), app_bundle));
|
| -
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(model_->IsLockedByCaller());
|
| -
|
| - // Cancels update check client but not the ping client since we need to send
|
| - // cancellation ping.
|
| - WebServicesClientInterface* update_check_client(
|
| - app_bundle->update_check_client());
|
| - if (update_check_client) {
|
| - update_check_client->Cancel();
|
| - }
|
| -
|
| - // TODO(omaha3): What do we do with active installs? We can at least cancel
|
| - // the InstallManager/InstallerWrapper if it has not started.
|
| -
|
| - // Cancel any apps that might have pending operations.
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - download_manager_->Cancel(app);
|
| - app->Cancel();
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT Worker::Pause(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::Pause][0x%p]"), app_bundle));
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(model_->IsLockedByCaller());
|
| - UNREFERENCED_PARAMETER(app_bundle);
|
| -
|
| - return E_NOTIMPL;
|
| -}
|
| -
|
| -HRESULT Worker::Resume(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::Resume][0x%p]"), app_bundle));
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(model_->IsLockedByCaller());
|
| - UNREFERENCED_PARAMETER(app_bundle);
|
| -
|
| - return E_NOTIMPL;
|
| -}
|
| -
|
| -HRESULT Worker::GetPackage(const Package* package, const CString& dir) {
|
| - CORE_LOG(L3, (_T("[Worker::GetPackage]")));
|
| - ASSERT1(model_->IsLockedByCaller());
|
| - return download_manager_->GetPackage(package, dir);
|
| -}
|
| -
|
| -bool Worker::IsPackageAvailable(const Package* package) const {
|
| - CORE_LOG(L3, (_T("[Worker::IsPackageAvailable]")));
|
| - ASSERT1(model_->IsLockedByCaller());
|
| - return download_manager_->IsPackageAvailable(package);
|
| -}
|
| -
|
| -HRESULT Worker::CacheOfflinePackages(AppBundle* app_bundle) {
|
| - CORE_LOG(L3, (_T("[Worker::CacheOfflinePackages]")));
|
| - ASSERT1(app_bundle);
|
| -
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - AppVersion* app_version = app->working_version();
|
| - const size_t num_packages = app_version->GetNumberOfPackages();
|
| -
|
| - for (size_t i = 0; i < num_packages; ++i) {
|
| - Package* package(app_version->GetPackage(i));
|
| - if (download_manager_->IsPackageAvailable(package)) {
|
| - continue;
|
| - }
|
| -
|
| - CString offline_app_dir = ConcatenatePath(app_bundle->offline_dir(),
|
| - app->app_guid_string());
|
| - CString offline_package_path = ConcatenatePath(offline_app_dir,
|
| - package->filename());
|
| - if (!File::Exists(offline_package_path)) {
|
| - HRESULT hr = offline_utils::FindV2OfflinePackagePath(
|
| - offline_app_dir, &offline_package_path);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[FindOfflinePackagePath failed][0x%x]"), hr));
|
| - return hr;
|
| - }
|
| - }
|
| -
|
| - HRESULT hr = download_manager_->CachePackage(package,
|
| - &offline_package_path);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[CachePackage failed][%s][%s][0x%x][%Iu]"),
|
| - app->app_guid_string(), offline_package_path, hr, i));
|
| - return hr;
|
| - }
|
| - }
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT Worker::PurgeAppLowerVersions(const CString& app_id,
|
| - const CString& version) {
|
| - return download_manager_->PurgeAppLowerVersions(app_id, version);
|
| -}
|
| -
|
| -// metric_worker_apps_not_*ed_group_policy are integers, not a counter, so they
|
| -// should be set to a value, not incremented. Otherwise the same app could be
|
| -// counted twice if the same COM server instance was used for multiple bundles
|
| -// of the same app(s). For this reason and to avoid overwriting valid counts
|
| -// with 0, the number of disabled apps is accumulated then the appropriate
|
| -// metric is set only if the count is non-zero.
|
| -void Worker::DoPreUpdateCheck(AppBundle* app_bundle,
|
| - xml::UpdateRequest* update_request) {
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(update_request);
|
| -
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - app->PreUpdateCheck(update_request);
|
| - }
|
| -
|
| - size_t num_disabled_apps = 0;
|
| - const std::vector<xml::request::App>& apps = update_request->request().apps;
|
| -
|
| - // apps.size is 0 if update check was cancelled. Its size can also be less
|
| - // than the number of apps in app_bundle if EULA is not accepted for some
|
| - // apps.
|
| - ASSERT1(apps.size() <= app_bundle->GetNumberOfApps());
|
| -
|
| - for (size_t i = 0; i != apps.size(); ++i) {
|
| - ASSERT1(apps[i].update_check.is_valid);
|
| - if (apps[i].update_check.is_update_disabled) {
|
| - ++num_disabled_apps;
|
| - }
|
| - }
|
| -
|
| - if (num_disabled_apps) {
|
| - // Assumes that all apps are either updates or installs.
|
| - if (app_bundle->GetNumberOfApps() && app_bundle->GetApp(0)->is_update()) {
|
| - metric_worker_apps_not_updated_group_policy = num_disabled_apps;
|
| - } else {
|
| - metric_worker_apps_not_installed_group_policy = num_disabled_apps;
|
| - }
|
| - }
|
| -}
|
| -
|
| -HRESULT Worker::DoUpdateCheck(AppBundle* app_bundle,
|
| - const xml::UpdateRequest* update_request,
|
| - xml::UpdateResponse* update_response) {
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(update_request);
|
| - ASSERT1(update_response);
|
| -
|
| - ASSERT1(app_bundle->GetNumberOfApps() > 0);
|
| -
|
| - if (app_bundle->is_offline_install()) {
|
| - return offline_utils::ParseOfflineManifest(
|
| - app_bundle->GetApp(0)->app_guid_string(), app_bundle->offline_dir(),
|
| - update_response);
|
| - }
|
| -
|
| - if (!ConfigManager::Instance()->CanUseNetwork(is_machine_)) {
|
| - CORE_LOG(L1, (_T("[Update check failed because network use prohibited]")));
|
| - return GOOPDATE_E_CANNOT_USE_NETWORK;
|
| - }
|
| -
|
| - const bool is_update = app_bundle->is_auto_update();
|
| -
|
| - if (is_update) {
|
| - ++metric_worker_update_check_total;
|
| - }
|
| -
|
| - // This is a blocking call on the network.
|
| - HRESULT hr = app_bundle->update_check_client()->Send(update_request,
|
| - update_response);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[Send failed][0x%08x]"), hr));
|
| - worker_utils::AddHttpRequestDataToEventLog(
|
| - hr,
|
| - app_bundle->update_check_client()->http_status_code(),
|
| - app_bundle->update_check_client()->http_trace(),
|
| - is_machine_);
|
| -
|
| - // TODO(omaha3): Omaha 2 would launch a web browser here for installs by
|
| - // calling goopdate_utils::LaunchBrowser(). Browser launch needs to be in
|
| - // the client but it currently has no way of knowing that it was a network
|
| - // error. Even here, we don't know that the it wasn't a parsing, etc. error.
|
| - return hr;
|
| - }
|
| -
|
| - if (is_update) {
|
| - ++metric_worker_update_check_succeeded;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -void Worker::DoPostUpdateCheck(AppBundle* app_bundle,
|
| - HRESULT update_check_result,
|
| - xml::UpdateResponse* update_response) {
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(update_response);
|
| -
|
| - VERIFY1(SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas(
|
| - is_machine_,
|
| - update_response)));
|
| -
|
| - for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
|
| - App* app = app_bundle->GetApp(i);
|
| - app->PostUpdateCheck(update_check_result, update_response);
|
| -
|
| - ASSERT(app->state() == STATE_UPDATE_AVAILABLE ||
|
| - app->state() == STATE_NO_UPDATE ||
|
| - app->state() == STATE_ERROR,
|
| - (_T("App %Iu state is %u"), i, app->state()));
|
| - }
|
| -
|
| - if (app_bundle->is_offline_install()) {
|
| - VERIFY1(SUCCEEDED(CacheOfflinePackages(app_bundle)));
|
| - VERIFY1(SUCCEEDED(DeleteDirectory(app_bundle->offline_dir())));
|
| - }
|
| -}
|
| -
|
| -// Creates a thread pool work item for deferred execution of deferred_function.
|
| -// The thread pool owns this callback object.
|
| -HRESULT Worker::QueueDeferredFunctionCall0(
|
| - AppBundle* app_bundle,
|
| - void (Worker::*deferred_function)(shared_ptr<AppBundle>)) {
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(deferred_function);
|
| -
|
| - typedef ThreadPoolCallBack1<Worker, shared_ptr<AppBundle> > Callback;
|
| - scoped_ptr<Callback> callback(new Callback(this,
|
| - deferred_function,
|
| - app_bundle->controlling_ptr()));
|
| - HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(),
|
| - WT_EXECUTELONGFUNCTION);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - // Transfers the ownership of the callback from Worker to ThreadPool.
|
| - app_bundle->set_user_work_item(callback.release());
|
| - return S_OK;
|
| -}
|
| -
|
| -// Creates a thread pool work item for deferred execution of deferred_function.
|
| -// The thread pool owns this callback object.
|
| -template <typename P1>
|
| -HRESULT Worker::QueueDeferredFunctionCall1(
|
| - AppBundle* app_bundle,
|
| - P1 p1,
|
| - void (Worker::*deferred_function)(shared_ptr<AppBundle>, P1)) {
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(deferred_function);
|
| -
|
| - typedef ThreadPoolCallBack2<Worker, shared_ptr<AppBundle>, P1> Callback;
|
| - scoped_ptr<Callback> callback(new Callback(this,
|
| - deferred_function,
|
| - app_bundle->controlling_ptr(),
|
| - p1));
|
| - HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(),
|
| - WT_EXECUTELONGFUNCTION);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - // Transfers the ownership of the callback from Worker to ThreadPool.
|
| - app_bundle->set_user_work_item(callback.release());
|
| - return S_OK;
|
| -}
|
| -
|
| -void Worker::WriteEventLog(int event_type,
|
| - int event_id,
|
| - const CString& event_description,
|
| - const CString& event_text) {
|
| - GoogleUpdateLogEvent log_event(event_type, event_id, is_machine_);
|
| - log_event.set_event_desc(event_description);
|
| - log_event.set_event_text(event_text);
|
| - log_event.WriteEvent();
|
| -}
|
| -
|
| -} // namespace omaha
|
|
|