blob: 6b6a3c8e390b352959d46458ef79309600357626 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/ipc/update_service_proxy_win.h"
#include <windows.h>
#include <wrl/client.h>
#include <wrl/implements.h>
#include <ios>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/bind_post_task.h"
#include "base/time/time.h"
#include "base/version.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_safearray.h"
#include "chrome/updater/app/server/win/updater_idl.h"
#include "chrome/updater/ipc/proxy_impl_base_win.h"
#include "chrome/updater/ipc/update_service_proxy.h"
#include "chrome/updater/registration_data.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/win_constants.h"
#include "components/policy/core/common/policy_types.h"
namespace updater {
namespace {
class UpdaterObserver : public DYNAMICIIDSIMPL(IUpdaterObserver) {
public:
UpdaterObserver(
UpdaterScope scope,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update_callback,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback)
: DYNAMICIIDSIMPL(IUpdaterObserver)(scope),
state_update_callback_(state_update_callback),
callback_(std::move(callback)) {}
UpdaterObserver(const UpdaterObserver&) = delete;
UpdaterObserver& operator=(const UpdaterObserver&) = delete;
// Overrides for IUpdaterObserver. Called on a system thread by COM RPC.
// Retries querying the update state two times, since runtime RPC errors when
// calling IUpdateState members have been observed in production.
IFACEMETHODIMP OnStateChange(IUpdateState* update_state) override {
CHECK(update_state);
if (!state_update_callback_) {
VLOG(2) << "Skipping posting: no update state callback.";
return S_OK;
}
for (int try_count = 0; try_count < 2; ++try_count) {
HResultOr<UpdateService::UpdateState> service_state =
QueryUpdateState(update_state);
if (service_state.has_value()) {
state_update_callback_.Run(*service_state);
break;
}
VLOG(2) << "QueryUpdateState returned " << service_state.error();
}
return S_OK;
}
IFACEMETHODIMP OnComplete(ICompleteStatus* complete_status) override {
CHECK(complete_status);
result_ = QueryResult(complete_status);
return S_OK;
}
// Disconnects this observer from its subject and ensures the callbacks are
// not posted after this function is called. Returns the completion callback
// so that the owner of this object can take back the callback ownership.
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
Disconnect() {
VLOG(2) << __func__;
state_update_callback_.Reset();
return std::move(callback_);
}
private:
~UpdaterObserver() override {
if (callback_) {
std::move(callback_).Run(result_);
}
}
static HResultOr<UpdateService::UpdateState> QueryUpdateState(
IUpdateState* update_state) {
CHECK(update_state);
UpdateService::UpdateState update_service_state;
{
LONG val_state = 0;
if (HRESULT hr = update_state->get_state(&val_state); FAILED(hr)) {
return base::unexpected(hr);
}
using State = UpdateService::UpdateState::State;
std::optional<State> state = CheckedCastToEnum<State>(val_state);
if (!state) {
return base::unexpected(E_INVALIDARG);
}
update_service_state.state = *state;
}
{
base::win::ScopedBstr app_id;
if (HRESULT hr = update_state->get_appId(app_id.Receive()); FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.app_id = base::WideToUTF8(app_id.Get());
}
{
base::win::ScopedBstr next_version;
if (HRESULT hr = update_state->get_nextVersion(next_version.Receive());
FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.next_version =
base::Version(base::WideToUTF8(next_version.Get()));
}
{
LONGLONG downloaded_bytes = -1;
if (HRESULT hr = update_state->get_downloadedBytes(&downloaded_bytes);
FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.downloaded_bytes = downloaded_bytes;
}
{
LONGLONG total_bytes = -1;
if (HRESULT hr = update_state->get_totalBytes(&total_bytes); FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.total_bytes = total_bytes;
}
{
LONG install_progress = -1;
if (HRESULT hr = update_state->get_installProgress(&install_progress);
FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.install_progress = install_progress;
}
{
LONG val_error_category = 0;
if (HRESULT hr = update_state->get_errorCategory(&val_error_category);
FAILED(hr)) {
return base::unexpected(hr);
}
using ErrorCategory = UpdateService::ErrorCategory;
std::optional<ErrorCategory> error_category =
CheckedCastToEnum<ErrorCategory>(val_error_category);
if (!error_category) {
return base::unexpected(E_INVALIDARG);
}
update_service_state.error_category = *error_category;
}
{
LONG error_code = -1;
if (HRESULT hr = update_state->get_errorCode(&error_code); FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.error_code = error_code;
}
{
LONG extra_code1 = -1;
if (HRESULT hr = update_state->get_extraCode1(&extra_code1); FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.extra_code1 = extra_code1;
}
{
base::win::ScopedBstr installer_text;
if (HRESULT hr =
update_state->get_installerText(installer_text.Receive());
FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.installer_text =
base::WideToUTF8(installer_text.Get());
}
{
base::win::ScopedBstr installer_cmd_line;
if (HRESULT hr = update_state->get_installerCommandLine(
installer_cmd_line.Receive());
FAILED(hr)) {
return base::unexpected(hr);
}
update_service_state.installer_cmd_line =
base::WideToUTF8(installer_cmd_line.Get());
}
CHECK_NE(update_service_state.state,
UpdateService::UpdateState::State::kUnknown);
return update_service_state;
}
static UpdateService::Result QueryResult(ICompleteStatus* complete_status) {
CHECK(complete_status);
LONG code = 0;
base::win::ScopedBstr message;
CHECK(SUCCEEDED(complete_status->get_statusCode(&code)));
VLOG(2) << "ICompleteStatus::OnComplete(" << code << ")";
return static_cast<UpdateService::Result>(code);
}
// Called by IUpdaterObserver::OnStateChange when update state changes occur.
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update_callback_;
// Called by IUpdaterObserver::OnComplete when the COM RPC call is done.
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback_;
UpdateService::Result result_ = UpdateService::Result::kSuccess;
};
class UpdaterCallback : public DYNAMICIIDSIMPL(IUpdaterCallback) {
public:
explicit UpdaterCallback(
UpdaterScope scope,
base::OnceCallback<void(base::expected<LONG, RpcError>)> callback)
: DYNAMICIIDSIMPL(IUpdaterCallback)(scope),
callback_(std::move(callback)) {}
explicit UpdaterCallback(
UpdaterScope scope,
base::OnceCallback<void(base::expected<int, RpcError>)> callback)
: DYNAMICIIDSIMPL(IUpdaterCallback)(scope),
callback_(base::BindOnce(
[](base::OnceCallback<void(base::expected<int, RpcError>)> callback,
base::expected<LONG, RpcError> result) {
std::move(callback).Run(
result.transform([](LONG x) { return static_cast<int>(x); }));
},
std::move(callback))) {}
UpdaterCallback(const UpdaterCallback&) = delete;
UpdaterCallback& operator=(const UpdaterCallback&) = delete;
// Overrides for IUpdaterCallback. Called on a system thread by COM RPC.
IFACEMETHODIMP Run(LONG status_code) override {
VLOG(2) << __func__;
status_code_ = status_code;
return S_OK;
}
// Disconnects this observer from its subject and ensures the callbacks are
// not posted after this function is called. Returns the completion callback
// so that the owner of this object can take back the callback ownership.
base::OnceCallback<void(base::expected<LONG, RpcError>)> Disconnect() {
VLOG(2) << __func__;
return std::move(callback_);
}
private:
~UpdaterCallback() override {
if (callback_) {
std::move(callback_).Run(base::ok(status_code_));
}
}
base::OnceCallback<void(base::expected<LONG, RpcError>)> callback_;
LONG status_code_ = 0;
};
class UpdaterAppStatesCallback
: public DYNAMICIIDSIMPL(IUpdaterAppStatesCallback) {
public:
explicit UpdaterAppStatesCallback(
UpdaterScope scope,
base::OnceCallback<
void(base::expected<std::vector<UpdateService::AppState>, RpcError>)>
callback)
: DYNAMICIIDSIMPL(IUpdaterAppStatesCallback)(scope),
callback_(std::move(callback)) {}
UpdaterAppStatesCallback(const UpdaterAppStatesCallback&) = delete;
UpdaterAppStatesCallback& operator=(const UpdaterAppStatesCallback&) = delete;
// Overrides for IUpdaterAppStatesCallback. Called on a system thread by COM
// RPC.
IFACEMETHODIMP Run(VARIANT updater_app_states) override {
VLOG(2) << __func__;
if (V_VT(&updater_app_states) != (VT_ARRAY | VT_DISPATCH)) {
return E_INVALIDARG;
}
// The safearray is owned by the caller of `Run`, so ownership is released
// here after acquiring the `LockScope`.
base::win::ScopedSafearray safearray(V_ARRAY(&updater_app_states));
std::optional<base::win::ScopedSafearray::LockScope<VT_DISPATCH>>
lock_scope = safearray.CreateLockScope<VT_DISPATCH>();
safearray.Release();
if (!lock_scope.has_value() || !lock_scope->size()) {
return E_INVALIDARG;
}
for (size_t i = 0; i < lock_scope->size(); ++i) {
Microsoft::WRL::ComPtr<IDispatch> dispatch(lock_scope->at(i));
if (!dispatch) {
return E_INVALIDARG;
}
Microsoft::WRL::ComPtr<IUpdaterAppState> app_state;
const HRESULT hr = dispatch.CopyTo(IsSystemInstall(scope())
? __uuidof(IUpdaterAppStateSystem)
: __uuidof(IUpdaterAppStateUser),
IID_PPV_ARGS_Helper(&app_state));
if (FAILED(hr)) {
return hr;
}
static constexpr int kMaxTries = 2;
for (int try_count = 0; try_count < kMaxTries; ++try_count) {
HResultOr<UpdateService::AppState> service_app_states =
IUpdaterAppStateToAppState(app_state);
if (service_app_states.has_value()) {
app_states_.push_back(*service_app_states);
break;
}
VLOG(2) << "IUpdaterAppStateToAppState returned "
<< service_app_states.error();
if (try_count == kMaxTries - 1) {
return service_app_states.error();
}
}
}
return S_OK;
}
// Disconnects this observer from its subject and ensures the callbacks are
// not posted after this function is called. Returns the completion callback
// so that the owner of this object can take back the callback ownership.
base::OnceCallback<
void(base::expected<std::vector<UpdateService::AppState>, RpcError>)>
Disconnect() {
VLOG(2) << __func__;
return std::move(callback_);
}
private:
~UpdaterAppStatesCallback() override {
if (callback_) {
std::move(callback_).Run(app_states_);
}
}
static HResultOr<UpdateService::AppState> IUpdaterAppStateToAppState(
Microsoft::WRL::ComPtr<IUpdaterAppState> updater_app_state) {
CHECK(updater_app_state);
UpdateService::AppState app_state;
{
base::win::ScopedBstr app_id;
if (HRESULT hr = updater_app_state->get_appId(app_id.Receive());
FAILED(hr)) {
return base::unexpected(hr);
}
app_state.app_id = base::WideToUTF8(app_id.Get());
}
{
base::win::ScopedBstr version;
if (HRESULT hr = updater_app_state->get_version(version.Receive());
HRESULT(hr)) {
return base::unexpected(hr);
}
app_state.version = base::Version(base::WideToUTF8(version.Get()));
}
{
base::win::ScopedBstr ap;
if (HRESULT hr = updater_app_state->get_ap(ap.Receive()); FAILED(hr)) {
return base::unexpected(hr);
}
app_state.ap = base::WideToUTF8(ap.Get());
}
{
base::win::ScopedBstr brand_code;
if (HRESULT hr = updater_app_state->get_brandCode(brand_code.Receive());
FAILED(hr)) {
return base::unexpected(hr);
}
app_state.brand_code = base::WideToUTF8(brand_code.Get());
}
{
base::win::ScopedBstr brand_path;
if (HRESULT hr = updater_app_state->get_brandPath(brand_path.Receive());
FAILED(hr)) {
return base::unexpected(hr);
}
app_state.brand_path = base::FilePath(brand_path.Get());
}
{
base::win::ScopedBstr ecp;
if (HRESULT hr = updater_app_state->get_ecp(ecp.Receive()); FAILED(hr)) {
return base::unexpected(hr);
}
app_state.ecp = base::FilePath(ecp.Get());
}
return app_state;
}
base::OnceCallback<void(
base::expected<std::vector<UpdateService::AppState>, RpcError>)>
callback_;
std::vector<UpdateService::AppState> app_states_;
};
} // namespace
class UpdateServiceProxyImplImpl
: public base::RefCountedThreadSafe<UpdateServiceProxyImplImpl>,
public ProxyImplBase<UpdateServiceProxyImplImpl,
IUpdater,
__uuidof(IUpdaterUser),
__uuidof(IUpdaterSystem)> {
public:
explicit UpdateServiceProxyImplImpl(UpdaterScope scope)
: ProxyImplBase(scope) {}
static auto GetClassGuid(UpdaterScope scope) {
return IsSystemInstall(scope) ? __uuidof(UpdaterSystemClass)
: __uuidof(UpdaterUserClass);
}
void GetVersion(
base::OnceCallback<void(base::expected<base::Version, RpcError>)>
callback) {
PostRPCTask(
base::BindOnce(&UpdateServiceProxyImplImpl::GetVersionOnTaskRunner,
this, std::move(callback)));
}
void FetchPolicies(
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
PostRPCTask(
base::BindOnce(&UpdateServiceProxyImplImpl::FetchPoliciesOnTaskRunner,
this, std::move(callback)));
}
void RegisterApp(
const RegistrationRequest& request,
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
PostRPCTask(
base::BindOnce(&UpdateServiceProxyImplImpl::RegisterAppOnTaskRunner,
this, request, std::move(callback)));
}
void GetAppStates(
base::OnceCallback<
void(base::expected<std::vector<UpdateService::AppState>, RpcError>)>
callback) {
PostRPCTask(
base::BindOnce(&UpdateServiceProxyImplImpl::GetAppStatesOnTaskRunner,
this, std::move(callback)));
}
void RunPeriodicTasks(
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
PostRPCTask(base::BindOnce(
&UpdateServiceProxyImplImpl::RunPeriodicTasksOnTaskRunner, this,
std::move(callback)));
}
void CheckForUpdate(
const std::string& app_id,
UpdateService::Priority priority,
UpdateService::PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
PostRPCTask(
base::BindOnce(&UpdateServiceProxyImplImpl::CheckForUpdateOnTaskRunner,
this, app_id, priority, policy_same_version_update,
language, state_update, std::move(callback)));
}
void Update(
const std::string& app_id,
const std::string& install_data_index,
UpdateService::Priority priority,
UpdateService::PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
PostRPCTask(base::BindOnce(&UpdateServiceProxyImplImpl::UpdateOnTaskRunner,
this, app_id, install_data_index, priority,
policy_same_version_update, language,
state_update, std::move(callback)));
}
void UpdateAll(
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
PostRPCTask(
base::BindOnce(&UpdateServiceProxyImplImpl::UpdateAllOnTaskRunner, this,
state_update, std::move(callback)));
}
void Install(
const RegistrationRequest& registration,
const std::string& client_install_data,
const std::string& install_data_index,
UpdateService::Priority priority,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
PostRPCTask(base::BindOnce(&UpdateServiceProxyImplImpl::InstallOnTaskRunner,
this, registration, client_install_data,
install_data_index, priority, language,
state_update, std::move(callback)));
}
void CancelInstalls(const std::string& app_id) {
PostRPCTask(base::BindOnce(
&UpdateServiceProxyImplImpl::CancelInstallsOnTaskRunner, this, app_id));
}
void RunInstaller(
const std::string& app_id,
const base::FilePath& installer_path,
const std::string& install_args,
const std::string& install_data,
const std::string& install_settings,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
PostRPCTask(base::BindOnce(
&UpdateServiceProxyImplImpl::RunInstallerOnTaskRunner, this, app_id,
installer_path, install_args, install_data, install_settings, language,
state_update, std::move(callback)));
}
private:
friend class base::RefCountedThreadSafe<UpdateServiceProxyImplImpl>;
virtual ~UpdateServiceProxyImplImpl() = default;
Microsoft::WRL::ComPtr<IUpdater2> interface2_;
HRESULT ConnectToServer() {
HRESULT hr = ProxyImplBase::ConnectToServer();
if (FAILED(hr)) {
return hr;
}
hr = get_interface().CopyTo(IsSystemInstall(scope())
? __uuidof(IUpdater2System)
: __uuidof(IUpdater2User),
IID_PPV_ARGS_Helper(&interface2_));
VLOG_IF(1, FAILED(hr)) << "Failed to query IUpdater2: " << std::hex << hr;
// If CopyTo fails, interface2_ will be unset and but we can still use
// IUpdater from get_interface().
return S_OK;
}
void GetVersionOnTaskRunner(
base::OnceCallback<void(base::expected<base::Version, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
base::win::ScopedBstr version;
if (HRESULT hr = get_interface()->GetVersion(version.Receive());
FAILED(hr)) {
VLOG(2) << "IUpdater::GetVersion failed: " << std::hex << hr;
std::move(callback).Run(base::unexpected(hr));
return;
}
std::move(callback).Run(base::Version(base::WideToUTF8(version.Get())));
}
void FetchPoliciesOnTaskRunner(
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
auto callback_wrapper =
MakeComObjectOrCrash<UpdaterCallback>(scope(), std::move(callback));
if (HRESULT hr = get_interface()->FetchPolicies(callback_wrapper.Get());
FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::FetchPolicies: " << std::hex << hr;
callback_wrapper->Disconnect().Run(base::unexpected(hr));
return;
}
}
void RegisterAppOnTaskRunner(
const RegistrationRequest& request,
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
std::wstring app_id_w;
std::wstring brand_code_w;
std::wstring brand_path_w;
std::wstring ap_w;
std::wstring version_w;
std::wstring existence_checker_path_w;
std::wstring install_id_w;
if (![&] {
if (!base::UTF8ToWide(request.app_id.c_str(), request.app_id.size(),
&app_id_w)) {
return false;
}
if (!base::UTF8ToWide(request.brand_code.c_str(),
request.brand_code.size(), &brand_code_w)) {
return false;
}
brand_path_w = request.brand_path.value();
if (!base::UTF8ToWide(request.ap.c_str(), request.ap.size(), &ap_w)) {
return false;
}
std::string version_str = request.version.GetString();
if (!base::UTF8ToWide(version_str.c_str(), version_str.size(),
&version_w)) {
return false;
}
existence_checker_path_w = request.existence_checker_path.value();
if (!base::UTF8ToWide(request.install_id.c_str(),
request.install_id.size(), &install_id_w)) {
return false;
}
return true;
}()) {
std::move(callback).Run(base::ok(E_INVALIDARG));
return;
}
auto callback_wrapper =
MakeComObjectOrCrash<UpdaterCallback>(scope(), std::move(callback));
if (interface2_) {
if (HRESULT hr = interface2_->RegisterApp2(
app_id_w.c_str(), brand_code_w.c_str(), brand_path_w.c_str(),
ap_w.c_str(), version_w.c_str(), existence_checker_path_w.c_str(),
install_id_w.c_str(), callback_wrapper.Get());
FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater2::RegisterApp2: " << std::hex << hr;
callback_wrapper->Disconnect().Run(base::unexpected(hr));
return;
}
} else {
if (HRESULT hr = get_interface()->RegisterApp(
app_id_w.c_str(), brand_code_w.c_str(), brand_path_w.c_str(),
ap_w.c_str(), version_w.c_str(), existence_checker_path_w.c_str(),
callback_wrapper.Get());
FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::RegisterApp: " << std::hex << hr;
callback_wrapper->Disconnect().Run(base::unexpected(hr));
return;
}
}
}
void GetAppStatesOnTaskRunner(
base::OnceCallback<
void(base::expected<std::vector<UpdateService::AppState>, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
auto callback_wrapper = MakeComObjectOrCrash<UpdaterAppStatesCallback>(
scope(), std::move(callback));
if (HRESULT hr = get_interface()->GetAppStates(callback_wrapper.Get());
FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::GetAppStates: " << std::hex << hr;
callback_wrapper->Disconnect().Run(base::unexpected(hr));
return;
}
}
void RunPeriodicTasksOnTaskRunner(
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
auto callback_wrapper =
MakeComObjectOrCrash<UpdaterCallback>(scope(), std::move(callback));
if (HRESULT hr = get_interface()->RunPeriodicTasks(callback_wrapper.Get());
FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::RunPeriodicTasks " << std::hex << hr;
callback_wrapper->Disconnect().Run(base::unexpected(hr));
return;
}
}
void CheckForUpdateOnTaskRunner(
const std::string& app_id,
UpdateService::Priority priority,
UpdateService::PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
std::wstring app_id_w;
if (!base::UTF8ToWide(app_id.c_str(), app_id.size(), &app_id_w)) {
std::move(callback).Run(UpdateService::Result::kServiceFailed);
return;
}
std::wstring language_w;
if (!base::UTF8ToWide(language.c_str(), language.size(), &language_w)) {
std::move(callback).Run(UpdateService::Result::kServiceFailed);
return;
}
auto observer = MakeComObjectOrCrash<UpdaterObserver>(scope(), state_update,
std::move(callback));
if (interface2_) {
HRESULT hr = interface2_->CheckForUpdate2(
app_id_w.c_str(), static_cast<int>(priority),
policy_same_version_update ==
UpdateService::PolicySameVersionUpdate::kAllowed,
language_w.c_str(), observer.Get());
if (FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater2::CheckForUpdate2: " << std::hex
<< hr;
observer->Disconnect().Run(base::unexpected(hr));
return;
}
} else {
HRESULT hr = get_interface()->CheckForUpdate(
app_id_w.c_str(), static_cast<int>(priority),
policy_same_version_update ==
UpdateService::PolicySameVersionUpdate::kAllowed,
observer.Get());
if (FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::CheckForUpdate: " << std::hex
<< hr;
observer->Disconnect().Run(base::unexpected(hr));
return;
}
}
}
void UpdateOnTaskRunner(
const std::string& app_id,
const std::string& install_data_index,
UpdateService::Priority priority,
UpdateService::PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
std::wstring app_id_w;
std::wstring install_data_index_w;
std::wstring language_w;
if (![&] {
if (!base::UTF8ToWide(app_id.c_str(), app_id.size(), &app_id_w)) {
return false;
}
if (!base::UTF8ToWide(install_data_index.c_str(),
install_data_index.size(),
&install_data_index_w)) {
return false;
}
if (!base::UTF8ToWide(language.c_str(), language.size(),
&language_w)) {
return false;
}
return true;
}()) {
std::move(callback).Run(UpdateService::Result::kServiceFailed);
return;
}
auto observer = MakeComObjectOrCrash<UpdaterObserver>(scope(), state_update,
std::move(callback));
if (interface2_) {
HRESULT hr = interface2_->Update2(
app_id_w.c_str(), install_data_index_w.c_str(),
static_cast<int>(priority),
policy_same_version_update ==
UpdateService::PolicySameVersionUpdate::kAllowed,
language_w.c_str(), observer.Get());
if (FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater2::Update2: " << std::hex << hr;
observer->Disconnect().Run(base::unexpected(hr));
return;
}
} else {
HRESULT hr = get_interface()->Update(
app_id_w.c_str(), install_data_index_w.c_str(),
static_cast<int>(priority),
policy_same_version_update ==
UpdateService::PolicySameVersionUpdate::kAllowed,
observer.Get());
if (FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::Update: " << std::hex << hr;
observer->Disconnect().Run(base::unexpected(hr));
return;
}
}
}
void UpdateAllOnTaskRunner(
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
auto observer = MakeComObjectOrCrash<UpdaterObserver>(scope(), state_update,
std::move(callback));
if (HRESULT hr = get_interface()->UpdateAll(observer.Get()); FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::UpdateAll: " << std::hex << hr;
observer->Disconnect().Run(base::unexpected(hr));
return;
}
}
void InstallOnTaskRunner(
const RegistrationRequest& request,
const std::string& client_install_data,
const std::string& install_data_index,
UpdateService::Priority priority,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
std::wstring app_id_w;
std::wstring brand_code_w;
std::wstring brand_path_w;
std::wstring ap_w;
std::wstring version_w;
std::wstring existence_checker_path_w;
std::wstring client_install_data_w;
std::wstring install_data_index_w;
std::wstring install_id_w;
std::wstring language_w;
if (![&] {
if (!base::UTF8ToWide(request.app_id.c_str(), request.app_id.size(),
&app_id_w)) {
return false;
}
if (!base::UTF8ToWide(request.brand_code.c_str(),
request.brand_code.size(), &brand_code_w)) {
return false;
}
brand_path_w = request.brand_path.value();
if (!base::UTF8ToWide(request.ap.c_str(), request.ap.size(), &ap_w)) {
return false;
}
std::string version_str = request.version.GetString();
if (!base::UTF8ToWide(version_str.c_str(), version_str.size(),
&version_w)) {
return false;
}
existence_checker_path_w = request.existence_checker_path.value();
if (!base::UTF8ToWide(client_install_data.c_str(),
client_install_data.size(),
&client_install_data_w)) {
return false;
}
if (!base::UTF8ToWide(install_data_index.c_str(),
install_data_index.size(),
&install_data_index_w)) {
return false;
}
if (!base::UTF8ToWide(request.install_id.c_str(),
request.install_id.size(), &install_id_w)) {
return false;
}
if (!base::UTF8ToWide(language.c_str(), language.size(),
&language_w)) {
return false;
}
return true;
}()) {
std::move(callback).Run(UpdateService::Result::kServiceFailed);
return;
}
auto observer = MakeComObjectOrCrash<UpdaterObserver>(scope(), state_update,
std::move(callback));
if (interface2_) {
HRESULT hr = interface2_->Install2(
app_id_w.c_str(), brand_code_w.c_str(), brand_path_w.c_str(),
ap_w.c_str(), version_w.c_str(), existence_checker_path_w.c_str(),
client_install_data_w.c_str(), install_data_index_w.c_str(),
install_id_w.c_str(), static_cast<int>(priority), language_w.c_str(),
observer.Get());
if (FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater2::Install2: " << std::hex << hr;
observer->Disconnect().Run(base::unexpected(hr));
return;
}
} else {
HRESULT hr = get_interface()->Install(
app_id_w.c_str(), brand_code_w.c_str(), brand_path_w.c_str(),
ap_w.c_str(), version_w.c_str(), existence_checker_path_w.c_str(),
client_install_data_w.c_str(), install_data_index_w.c_str(),
static_cast<int>(priority), observer.Get());
if (FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::Install: " << std::hex << hr;
observer->Disconnect().Run(base::unexpected(hr));
return;
}
}
}
void CancelInstallsOnTaskRunner(const std::string& app_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (FAILED(ConnectToServer())) {
return;
}
if (HRESULT hr =
get_interface()->CancelInstalls(base::UTF8ToWide(app_id).c_str());
FAILED(hr)) {
VLOG(2) << "Failed to call IUpdater::CancelInstalls: " << std::hex << hr;
}
}
void RunInstallerOnTaskRunner(
const std::string& app_id,
const base::FilePath& installer_path,
const std::string& install_args,
const std::string& install_data,
const std::string& install_settings,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
if (HRESULT hr = ConnectToServer(); FAILED(hr)) {
std::move(callback).Run(base::unexpected(hr));
return;
}
std::wstring app_id_w;
std::wstring install_args_w;
std::wstring install_data_w;
std::wstring install_settings_w;
std::wstring language_w;
if (![&] {
if (!base::UTF8ToWide(app_id.c_str(), app_id.size(), &app_id_w)) {
return false;
}
if (!base::UTF8ToWide(install_args.c_str(), install_args.size(),
&install_args_w)) {
return false;
}
if (!base::UTF8ToWide(install_data.c_str(), install_data.size(),
&install_data_w)) {
return false;
}
if (!base::UTF8ToWide(install_settings.c_str(),
install_settings.size(), &install_settings_w)) {
return false;
}
if (!base::UTF8ToWide(language.c_str(), language.size(),
&language_w)) {
return false;
}
return true;
}()) {
std::move(callback).Run(UpdateService::Result::kServiceFailed);
return;
}
auto observer = MakeComObjectOrCrash<UpdaterObserver>(scope(), state_update,
std::move(callback));
if (interface2_) {
HRESULT hr = interface2_->RunInstaller2(
app_id_w.c_str(), installer_path.value().c_str(),
install_args_w.c_str(), install_data_w.c_str(),
install_settings_w.c_str(), language_w.c_str(), observer.Get());
if (SUCCEEDED(hr)) {
VLOG(2) << "IUpdater2 offline install completed successfully.";
} else {
VLOG(2) << "Failed to call IUpdater2::RunInstaller2: " << std::hex
<< hr;
observer->Disconnect().Run(base::unexpected(hr));
}
} else {
HRESULT hr = get_interface()->RunInstaller(
app_id_w.c_str(), installer_path.value().c_str(),
install_args_w.c_str(), install_data_w.c_str(),
install_settings_w.c_str(), observer.Get());
if (SUCCEEDED(hr)) {
VLOG(2) << "IUpdater offline install completed successfully.";
} else {
VLOG(2) << "Failed to call IUpdater::RunInstaller: " << std::hex << hr;
observer->Disconnect().Run(base::unexpected(hr));
}
}
}
};
UpdateServiceProxyImpl::UpdateServiceProxyImpl(UpdaterScope updater_scope)
: impl_(base::MakeRefCounted<UpdateServiceProxyImplImpl>(updater_scope)) {}
UpdateServiceProxyImpl::~UpdateServiceProxyImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
UpdateServiceProxyImplImpl::Destroy(std::move(impl_));
}
void UpdateServiceProxyImpl::GetVersion(
base::OnceCallback<void(base::expected<base::Version, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->GetVersion(base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::FetchPolicies(
policy::PolicyFetchReason /*reason*/,
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
// TODO(crbug.com/391394116): Add a new COM interface that accepts the
// `reason` during policy fetch.
impl_->FetchPolicies(base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::RegisterApp(
const RegistrationRequest& request,
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->RegisterApp(request,
base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::GetAppStates(
base::OnceCallback<void(base::expected<std::vector<UpdateService::AppState>,
RpcError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->GetAppStates(base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::RunPeriodicTasks(
base::OnceCallback<void(base::expected<int, RpcError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->RunPeriodicTasks(
base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::CheckForUpdate(
const std::string& app_id,
UpdateService::Priority priority,
UpdateService::PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->CheckForUpdate(
app_id, priority, policy_same_version_update, language,
base::BindPostTaskToCurrentDefault(state_update),
base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::Update(
const std::string& app_id,
const std::string& install_data_index,
UpdateService::Priority priority,
UpdateService::PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->Update(app_id, install_data_index, priority,
policy_same_version_update, language,
base::BindPostTaskToCurrentDefault(state_update),
base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::UpdateAll(
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->UpdateAll(base::BindPostTaskToCurrentDefault(state_update),
base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::Install(
const RegistrationRequest& registration,
const std::string& client_install_data,
const std::string& install_data_index,
UpdateService::Priority priority,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->Install(registration, client_install_data, install_data_index,
priority, language,
base::BindPostTaskToCurrentDefault(state_update),
base::BindPostTaskToCurrentDefault(std::move(callback)));
}
void UpdateServiceProxyImpl::CancelInstalls(const std::string& app_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->CancelInstalls(app_id);
}
void UpdateServiceProxyImpl::RunInstaller(
const std::string& app_id,
const base::FilePath& installer_path,
const std::string& install_args,
const std::string& install_data,
const std::string& install_settings,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
state_update,
base::OnceCallback<void(base::expected<UpdateService::Result, RpcError>)>
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
impl_->RunInstaller(app_id, installer_path, install_args, install_data,
install_settings, language,
base::BindPostTaskToCurrentDefault(state_update),
base::BindPostTaskToCurrentDefault(std::move(callback)));
}
scoped_refptr<UpdateService> CreateUpdateServiceProxy(
UpdaterScope updater_scope,
base::TimeDelta /*get_version_timeout*/) {
return base::MakeRefCounted<UpdateServiceProxy>(
base::MakeRefCounted<UpdateServiceProxyImpl>(updater_scope));
}
} // namespace updater