diff options
author | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-23 06:17:00 +0000 |
---|---|---|
committer | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-23 06:17:00 +0000 |
commit | 02d96b139b73c27ff3ceebbda132c59866de46f1 (patch) | |
tree | 1912ade2a73ddb933fb2aa66951a83c1dc98dcf2 /apps/app_host/binaries_installer.cc | |
parent | cbba20e8ddf92868c097a10ea31ddf4e18f3776d (diff) | |
download | chromium_src-02d96b139b73c27ff3ceebbda132c59866de46f1.zip chromium_src-02d96b139b73c27ff3ceebbda132c59866de46f1.tar.gz chromium_src-02d96b139b73c27ff3ceebbda132c59866de46f1.tar.bz2 |
Move app_host code from extensions into src/apps.
BUG=None
Review URL: https://chromiumcodereview.appspot.com/12310059
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@184295 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'apps/app_host/binaries_installer.cc')
-rw-r--r-- | apps/app_host/binaries_installer.cc | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/apps/app_host/binaries_installer.cc b/apps/app_host/binaries_installer.cc new file mode 100644 index 0000000..ce72888 --- /dev/null +++ b/apps/app_host/binaries_installer.cc @@ -0,0 +1,327 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "apps/app_host/binaries_installer.h" + +#include "base/logging.h" +#include "base/threading/platform_thread.h" +#include "base/win/scoped_bstr.h" +#include "base/win/scoped_com_initializer.h" +#include "base/win/scoped_comptr.h" +#include "google_update/google_update_idl.h" + + +namespace app_host { + +// Helpers -------------------------------------------------------------------- + +namespace { + +const wchar_t kAppHostAppId[] = L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}"; +const wchar_t kBinariesAppId[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; +const int kInstallationPollingIntervalMs = 50; + +HRESULT CreateInstalledApp(IAppBundle* app_bundle, + const wchar_t* app_guid, + IApp** app) { + base::win::ScopedComPtr<IDispatch> idispatch; + HRESULT hr = app_bundle->createInstalledApp(base::win::ScopedBstr(app_guid), + idispatch.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to configure App Bundle: " << hr; + return hr; + } + + base::win::ScopedComPtr<IApp> temp_app; + hr = temp_app.QueryFrom(idispatch); + if (FAILED(hr)) { + LOG(ERROR) << "Unexpected error querying IApp from " + << "IAppBundle->createInstalledApp return value: " << hr; + } else { + *app = temp_app.Detach(); + } + return hr; +} + +HRESULT GetAppHostApValue(IGoogleUpdate3* update3, + IAppBundle* app_bundle, + BSTR* ap_value) { + base::win::ScopedComPtr<IApp> app; + HRESULT hr = CreateInstalledApp(app_bundle, kAppHostAppId, app.Receive()); + if (FAILED(hr)) + return hr; + + hr = app->get_ap(ap_value); + if (FAILED(hr)) + LOG(ERROR) << "Failed to get the App Launcher AP value."; + return hr; +} + +HRESULT GetCurrentState(IApp* app, + ICurrentState** current_state, + CurrentState* state_value) { + base::win::ScopedComPtr<IDispatch> idispatch; + HRESULT hr = app->get_currentState(idispatch.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get App Bundle state: " << hr; + return hr; + } + + base::win::ScopedComPtr<ICurrentState> temp_current_state; + hr = temp_current_state.QueryFrom(idispatch); + if (FAILED(hr)) { + LOG(ERROR) << "Unexpected error querying ICurrentState from " + << "IApp::get_currentState return value: " << hr; + return hr; + } + + LONG long_state_value; + hr = temp_current_state->get_stateValue(&long_state_value); + if (SUCCEEDED(hr)) { + *state_value = static_cast<CurrentState>(long_state_value); + *current_state = temp_current_state.Detach(); + } else { + LOG(ERROR) << "Failed to get App Bundle state value: " << hr; + } + return hr; +} + +bool CheckIsBusy(IAppBundle* app_bundle, HRESULT* hr) { + VARIANT_BOOL variant_is_busy = VARIANT_TRUE; + *hr = app_bundle->isBusy(&variant_is_busy); + if (FAILED(*hr)) + LOG(ERROR) << "Failed to check app_bundle->isBusy: " << *hr; + return (variant_is_busy == VARIANT_TRUE); +} + +void OnUpdateAvailable(IAppBundle* app_bundle, HRESULT* hr) { + // If the app bundle is busy we will just wait some more. + if (CheckIsBusy(app_bundle, hr) || FAILED(*hr)) + return; + *hr = app_bundle->download(); + if (FAILED(*hr)) + LOG(ERROR) << "Failed to initiate bundle download: " << *hr; +} + +void OnReadyToInstall(IAppBundle* app_bundle, HRESULT* hr) { + // If the app bundle is busy we will just wait some more. + if (CheckIsBusy(app_bundle, hr) || FAILED(*hr)) + return; + *hr = app_bundle->install(); + if (FAILED(*hr)) + LOG(ERROR) << "Failed to initiate bundle install: " << *hr; +} + +HRESULT OnError(ICurrentState* current_state) { + LONG error_code; + HRESULT hr = current_state->get_errorCode(&error_code); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to retrieve bundle error code: " << hr; + return hr; + } + + base::win::ScopedBstr completion_message; + HRESULT completion_message_hr = + current_state->get_completionMessage(completion_message.Receive()); + if (FAILED(completion_message_hr)) { + LOG(ERROR) << "Bundle installation failed with error " << error_code + << ". Error message retrieval failed with error: " + << completion_message_hr; + } else { + LOG(ERROR) << "Bundle installation failed with error " << error_code << ": " + << completion_message; + } + return error_code; +} + +HRESULT CreateGoogleUpdate3(IGoogleUpdate3** update3) { + base::win::ScopedComPtr<IGoogleUpdate3> temp_update3; + HRESULT hr = temp_update3.CreateInstance(CLSID_GoogleUpdate3UserClass); + if (SUCCEEDED(hr)) { + *update3 = temp_update3.Detach(); + } else { + // TODO(erikwright): Try in-proc to support running elevated? According + // to update3_utils.cc (CreateGoogleUpdate3UserClass): + // The primary reason for the LocalServer activation failing on Vista/Win7 + // is that COM does not look at HKCU registration when the code is running + // elevated. We fall back to an in-proc mode. The in-proc mode is limited to + // one install at a time, so we use it only as a backup mechanism. + LOG(ERROR) << "Failed to instantiate GoogleUpdate3: " << hr; + } + return hr; +} + +HRESULT CreateAppBundle(IGoogleUpdate3* update3, IAppBundle** app_bundle) { + base::win::ScopedComPtr<IDispatch> idispatch; + HRESULT hr = update3->createAppBundle(idispatch.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to createAppBundle: " << hr; + return hr; + } + + base::win::ScopedComPtr<IAppBundle> temp_app_bundle; + hr = temp_app_bundle.QueryFrom(idispatch); + if (FAILED(hr)) { + LOG(ERROR) << "Unexpected error querying IAppBundle from " + << "IGoogleUpdate3->createAppBundle return value: " << hr; + return hr; + } + + hr = temp_app_bundle->initialize(); + if (FAILED(hr)) + LOG(ERROR) << "Failed to initialize App Bundle: " << hr; + else + *app_bundle = temp_app_bundle.Detach(); + return hr; +} + +HRESULT SelectBinariesApValue(IGoogleUpdate3* update3, + IAppBundle* app_bundle, + BSTR* ap_value) { + HRESULT hr = GetAppHostApValue(update3, app_bundle, ap_value); + if (SUCCEEDED(hr)) + return hr; + + // TODO(erikwright): distinguish between AppHost not installed and an + // error in GetAppHostApValue. + // TODO(erikwright): Use stable by default when App Host support is in + // stable. + base::win::ScopedBstr temp_ap_value; + if (temp_ap_value.Allocate(L"2.0-dev-multi-apphost") == NULL) { + LOG(ERROR) << "Unexpected error in ScopedBstr::Allocate."; + return E_FAIL; + } + *ap_value = temp_ap_value.Release(); + return S_OK; +} + +HRESULT CreateBinariesIApp(IAppBundle* app_bundle, BSTR ap, IApp** app) { + base::win::ScopedComPtr<IDispatch> idispatch; + HRESULT hr = app_bundle->createApp(base::win::ScopedBstr(kBinariesAppId), + idispatch.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to configure App Bundle: " << hr; + return hr; + } + + base::win::ScopedComPtr<IApp> temp_app; + hr = temp_app.QueryFrom(idispatch); + if (FAILED(hr)) { + LOG(ERROR) << "Unexpected error querying IApp from " + << "IAppBundle->createApp return value: " << hr; + return hr; + } + + hr = temp_app->put_isEulaAccepted(VARIANT_TRUE); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to set 'EULA Accepted': " << hr; + return hr; + } + + hr = temp_app->put_ap(ap); + if (FAILED(hr)) + LOG(ERROR) << "Failed to set AP value: " << hr; + else + *app = temp_app.Detach(); + return hr; +} + +bool CheckIfDone(IAppBundle* app_bundle, IApp* app, HRESULT* hr) { + base::win::ScopedComPtr<ICurrentState> current_state; + CurrentState state_value; + *hr = GetCurrentState(app, current_state.Receive(), &state_value); + if (FAILED(*hr)) + return true; + + switch (state_value) { + case STATE_WAITING_TO_CHECK_FOR_UPDATE: + case STATE_CHECKING_FOR_UPDATE: + case STATE_WAITING_TO_DOWNLOAD: + case STATE_RETRYING_DOWNLOAD: + case STATE_DOWNLOADING: + case STATE_WAITING_TO_INSTALL: + case STATE_INSTALLING: + case STATE_DOWNLOAD_COMPLETE: + case STATE_EXTRACTING: + case STATE_APPLYING_DIFFERENTIAL_PATCH: + // These states will all transition on their own. + return false; + + case STATE_UPDATE_AVAILABLE: + OnUpdateAvailable(app_bundle, hr); + return FAILED(*hr); + + case STATE_READY_TO_INSTALL: + OnReadyToInstall(app_bundle, hr); + return FAILED(*hr); + + case STATE_NO_UPDATE: + LOG(INFO) << "Google Update reports that the binaries are already " + << "installed and up-to-date."; + return true; + + case STATE_INSTALL_COMPLETE: + return true; + + case STATE_ERROR: + *hr = OnError(current_state); + return FAILED(*hr); + + case STATE_INIT: + case STATE_PAUSED: + default: + LOG(ERROR) << "Unexpected bundle state: " << state_value << "."; + *hr = E_FAIL; + return true; + } +} + +} // namespace + + +// Globals -------------------------------------------------------------------- + +HRESULT InstallBinaries() { + base::win::ScopedCOMInitializer initialize_com; + if (!initialize_com.succeeded()) { + LOG(ERROR) << "COM initialization failed"; + return E_FAIL; + } + + base::win::ScopedComPtr<IGoogleUpdate3> update3; + HRESULT hr = CreateGoogleUpdate3(update3.Receive()); + if (FAILED(hr)) + return hr; + + base::win::ScopedComPtr<IAppBundle> app_bundle; + hr = CreateAppBundle(update3, app_bundle.Receive()); + if (FAILED(hr)) + return hr; + + base::win::ScopedBstr ap_value; + hr = SelectBinariesApValue(update3, app_bundle, ap_value.Receive()); + if (FAILED(hr)) + return hr; + + base::win::ScopedComPtr<IApp> app; + hr = CreateBinariesIApp(app_bundle, ap_value, app.Receive()); + if (FAILED(hr)) + return hr; + + hr = app_bundle->checkForUpdate(); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to initiate update check: " << hr; + return hr; + } + + // We rely upon Omaha to eventually time out and transition to a failure + // state. + while (!CheckIfDone(app_bundle, app, &hr)) { + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( + kInstallationPollingIntervalMs)); + } + return hr; +} + +} // namespace app_host |