chromium/chrome/updater/app/server/win/com_classes.cc

// 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/app/server/win/com_classes.h"

#include <wchar.h>
#include <wrl/client.h>
#include <wrl/implements.h>

#include <optional>
#include <string>
#include <utility>

#include "base/check.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/version.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_variant.h"
#include "base/win/variant_vector.h"
#include "chrome/updater/app/app_server_win.h"
#include "chrome/updater/app/server/win/com_classes_legacy.h"
#include "chrome/updater/registration_data.h"
#include "chrome/updater/update_service.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util/win_util.h"

namespace updater {
namespace {

// Maximum string length for COM strings.
constexpr size_t kMaxStringLen = 0x4000;  // 16KB.

using IUpdaterCallbackPtr = Microsoft::WRL::ComPtr<IUpdaterCallback>;
using IUpdaterInternalCallbackPtr =
    Microsoft::WRL::ComPtr<IUpdaterInternalCallback>;
using IUpdaterObserverPtr = Microsoft::WRL::ComPtr<IUpdaterObserver>;

// Implements `IUpdaterAppState`. Initialized with an `UpdateService::AppState`.
class UpdaterAppStateImpl : public IDispatchImpl<IUpdaterAppState> {
 public:
  UpdaterAppStateImpl()
      : IDispatchImpl<IUpdaterAppState>(IID_MAPS_USERSYSTEM(IUpdaterAppState)) {
  }
  UpdaterAppStateImpl(const UpdaterAppStateImpl&) = delete;
  UpdaterAppStateImpl& operator=(const UpdaterAppStateImpl&) = delete;

  HRESULT RuntimeClassInitialize(const UpdateService::AppState& app_state) {
    app_id_ = base::ASCIIToWide(app_state.app_id);
    version_ = base::ASCIIToWide(app_state.version.GetString());
    ap_ = base::ASCIIToWide(app_state.ap);
    brand_code_ = base::ASCIIToWide(app_state.brand_code);
    brand_path_ = app_state.brand_path.value();
    ecp_ = app_state.ecp.value();

    return S_OK;
  }

  IFACEMETHODIMP get_appId(BSTR* app_id) override {
    CHECK(app_id);

    *app_id = base::win::ScopedBstr(app_id_).Release();
    return S_OK;
  }

  IFACEMETHODIMP get_version(BSTR* version) override {
    CHECK(version);

    *version = base::win::ScopedBstr(version_).Release();
    return S_OK;
  }

  IFACEMETHODIMP get_ap(BSTR* ap) override {
    CHECK(ap);

    *ap = base::win::ScopedBstr(ap_).Release();
    return S_OK;
  }

  IFACEMETHODIMP get_brandCode(BSTR* brand_code) override {
    CHECK(brand_code);

    *brand_code = base::win::ScopedBstr(brand_code_).Release();
    return S_OK;
  }

  IFACEMETHODIMP get_brandPath(BSTR* brand_path) override {
    CHECK(brand_path);

    *brand_path = base::win::ScopedBstr(brand_path_).Release();
    return S_OK;
  }

  IFACEMETHODIMP get_ecp(BSTR* ecp) override {
    CHECK(ecp);

    *ecp = base::win::ScopedBstr(ecp_).Release();
    return S_OK;
  }

 private:
  ~UpdaterAppStateImpl() override = default;

  std::wstring app_id_;
  std::wstring version_;
  std::wstring ap_;
  std::wstring brand_code_;
  std::wstring brand_path_;
  std::wstring ecp_;
};

}  // namespace

STDMETHODIMP UpdateStateImpl::get_state(LONG* state) {
  CHECK(state);
  *state = static_cast<LONG>(update_state_.state);
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_appId(BSTR* app_id) {
  CHECK(app_id);
  *app_id =
      base::win::ScopedBstr(base::UTF8ToWide(update_state_.app_id)).Release();
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_nextVersion(BSTR* next_version) {
  CHECK(next_version);
  *next_version =
      base::win::ScopedBstr(
          update_state_.next_version.IsValid()
              ? base::UTF8ToWide(update_state_.next_version.GetString())
              : L"")
          .Release();
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_downloadedBytes(LONGLONG* downloaded_bytes) {
  CHECK(downloaded_bytes);
  *downloaded_bytes = LONGLONG{update_state_.downloaded_bytes};
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_totalBytes(LONGLONG* total_bytes) {
  CHECK(total_bytes);
  *total_bytes = LONGLONG{update_state_.total_bytes};
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_installProgress(LONG* install_progress) {
  CHECK(install_progress);
  *install_progress = LONG{update_state_.install_progress};
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_errorCategory(LONG* error_category) {
  CHECK(error_category);
  *error_category = static_cast<LONG>(update_state_.error_category);
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_errorCode(LONG* error_code) {
  CHECK(error_code);
  *error_code = LONG{update_state_.error_code};
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_extraCode1(LONG* extra_code1) {
  CHECK(extra_code1);
  *extra_code1 = LONG{update_state_.extra_code1};
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_installerText(BSTR* installer_text) {
  CHECK(installer_text);
  *installer_text =
      base::win::ScopedBstr(base::UTF8ToWide(update_state_.installer_text))
          .Release();
  return S_OK;
}

STDMETHODIMP UpdateStateImpl::get_installerCommandLine(
    BSTR* installer_cmd_line) {
  CHECK(installer_cmd_line);
  *installer_cmd_line =
      base::win::ScopedBstr(base::UTF8ToWide(update_state_.installer_cmd_line))
          .Release();
  return S_OK;
}

STDMETHODIMP CompleteStatusImpl::get_statusCode(LONG* code) {
  CHECK(code);
  *code = code_;
  return S_OK;
}

STDMETHODIMP CompleteStatusImpl::get_statusMessage(BSTR* message) {
  CHECK(message);
  *message = base::win::ScopedBstr(message_).Release();
  return S_OK;
}

HRESULT UpdaterImpl::RuntimeClassInitialize() {
  return IsCOMCallerAllowed();
}

HRESULT UpdaterImpl::GetVersion(BSTR* version) {
  CHECK(version);

  // Return the hardcoded version instead of calling the corresponding
  // non-blocking function of `UpdateServiceImpl`. This results in some
  // code duplication but it avoids the complexities of making this function
  // non-blocking.
  *version = base::win::ScopedBstr(kUpdaterVersionUtf16).Release();
  return S_OK;
}

HRESULT UpdaterImpl::FetchPolicies(IUpdaterCallback* callback) {
  if (!callback) {
    return E_INVALIDARG;
  }

  base::OnceCallback<void(int)> updater_callback = base::BindPostTask(
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
      base::BindOnce(
          [](IUpdaterCallbackPtr callback, int result) {
            HRESULT hr = callback->Run(result);
            VLOG(2) << "IUpdaterImpl::FetchPolicies. "
                    << "IUpdaterCallback::Run returned " << std::hex << hr;
          },
          IUpdaterCallbackPtr(callback)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](base::OnceCallback<void(int)> updater_callback) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(updater_callback).Run(-1);
          return;
        }
        update_service->FetchPolicies(std::move(updater_callback));
      },
      std::move(updater_callback)));
  return S_OK;
}

HRESULT UpdaterImpl::RegisterApp(const wchar_t* app_id,
                                 const wchar_t* brand_code,
                                 const wchar_t* brand_path,
                                 const wchar_t* ap,
                                 const wchar_t* version,
                                 const wchar_t* existence_checker_path,
                                 IUpdaterCallback* callback) {
  if (!callback) {
    return E_INVALIDARG;
  }

  // Validates that string parameters are not longer than 16K characters.
  std::optional<RegistrationRequest> request =
      [app_id, brand_code, brand_path, ap, version,
       existence_checker_path]() -> decltype(request) {
    for (const auto* str : {app_id, brand_code, brand_path, ap, version,
                            existence_checker_path}) {
      if (wcsnlen_s(str, kMaxStringLen) == kMaxStringLen) {
        return std::nullopt;
      }
    }

    RegistrationRequest request;
    if (!app_id || !base::WideToUTF8(app_id, wcslen(app_id), &request.app_id)) {
      return std::nullopt;
    }
    if (!brand_code || !base::WideToUTF8(brand_code, wcslen(brand_code),
                                         &request.brand_code)) {
      return std::nullopt;
    }
    request.brand_path = base::FilePath(brand_path);
    if (!ap || !base::WideToUTF8(ap, wcslen(ap), &request.ap)) {
      return std::nullopt;
    }
    std::string version_str;
    if (!version || !base::WideToUTF8(version, wcslen(version), &version_str)) {
      return std::nullopt;
    }
    request.version = base::Version(version_str);
    if (!request.version.IsValid()) {
      return std::nullopt;
    }
    request.existence_checker_path = base::FilePath(existence_checker_path);

    return request;
  }();

  if (!request) {
    return E_INVALIDARG;
  }

  base::OnceCallback<void(int)> updater_callback = base::BindPostTask(
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
      base::BindOnce(
          [](IUpdaterCallbackPtr callback, int result) {
            HRESULT hr = callback->Run(result);
            VLOG(2) << "IUpdaterImpl::RegisterApp. "
                    << "IUpdaterCallback::Run returned " << std::hex << hr;
          },
          Microsoft::WRL::ComPtr<IUpdaterCallback>(callback)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](const RegistrationRequest& request,
         base::OnceCallback<void(int)> updater_callback) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(updater_callback).Run(-1);
          return;
        }
        update_service->RegisterApp(request, std::move(updater_callback));
      },
      *request, std::move(updater_callback)));
  return S_OK;
}

// Called by the COM RPC runtime on one of its threads. Invokes the in-process
// `update_service` on the main sequence. The callbacks received from
// `update_service` arrive in the main sequence too.
HRESULT UpdaterImpl::RunPeriodicTasks(IUpdaterCallback* callback) {
  if (!callback) {
    return E_INVALIDARG;
  }
  AppServerWin::PostRpcTask(base::BindOnce(
      [](base::OnceClosure callback_closure) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(callback_closure).Run();
          return;
        }
        update_service->RunPeriodicTasks(std::move(callback_closure));
      },
      base::BindPostTask(
          base::ThreadPool::CreateSequencedTaskRunner(
              {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
          base::BindOnce(base::IgnoreResult(&IUpdaterCallback::Run),
                         Microsoft::WRL::ComPtr<IUpdaterCallback>(callback),
                         0))));
  return S_OK;
}

namespace {

// Filters the download progress events to avoid spamming the RPC client
// with too many download progress notifications. The filter only notifies
// the client at most once for every unit of download progress made.
//
// The instance of this class is owned by the repeating callback which is
// invoking `OnStateChange`.
class StateChangeCallbackFilter {
 public:
  StateChangeCallbackFilter(
      scoped_refptr<base::SequencedTaskRunner> task_runner,
      Microsoft::WRL::ComPtr<IUpdaterObserver> observer)
      : task_runner_(task_runner), observer_(observer) {
    CHECK(observer);
  }
  StateChangeCallbackFilter(const StateChangeCallbackFilter&) = delete;
  StateChangeCallbackFilter& operator=(const StateChangeCallbackFilter&) =
      delete;

  void OnStateChange(const UpdateService::UpdateState& update_state) {
    int cur_progress = GetDownloadProgress(update_state.downloaded_bytes,
                                           update_state.total_bytes);
    if (update_state.state == UpdateService::UpdateState::State::kDownloading &&
        progress_seen_ && *progress_seen_ == cur_progress) {
      return;
    }
    progress_seen_ = cur_progress;
    task_runner_->PostTaskAndReplyWithResult(
        FROM_HERE,
        base::BindOnce(&IUpdaterObserver::OnStateChange, observer_,
                       MakeComObjectOrCrash<UpdateStateImpl>(update_state)),
        base::BindOnce([](HRESULT hr) {
          VLOG(4) << "IUpdaterObserver::OnStateChange returned " << std::hex
                  << hr;
        }));
  }

 private:
  // Calls the COM function IUpdaterObserver::OnStateChange on `observer_`.
  scoped_refptr<base::SequencedTaskRunner> task_runner_;
  Microsoft::WRL::ComPtr<IUpdaterObserver> observer_;

  // Most recent download progress value the client has been notified about.
  std::optional<int> progress_seen_;
};

}  // namespace

HRESULT UpdaterImpl::CheckForUpdate(const wchar_t* app_id,
                                    LONG priority,
                                    BOOL same_version_update_allowed,
                                    IUpdaterObserver* observer) {
  if (!observer) {
    return E_INVALIDARG;
  }

  auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
      {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
  base::RepeatingCallback<void(const UpdateService::UpdateState&)>
      state_change_callback = base::BindRepeating(
          &StateChangeCallbackFilter::OnStateChange,
          base::Owned(new StateChangeCallbackFilter(task_runner, observer)));
  base::OnceCallback<void(UpdateService::Result)> complete_callback =
      base::BindPostTask(
          task_runner,
          base::BindOnce(
              [](IUpdaterObserverPtr observer, UpdateService::Result result) {
                HRESULT hr = observer->OnComplete(
                    MakeComObjectOrCrash<CompleteStatusImpl>(
                        static_cast<int>(result), L"")
                        .Get());
                VLOG(2) << "IUpdaterImpl::CheckForUpdate. "
                        << "IUpdaterObserver::OnComplete returned " << std::hex
                        << hr;
              },
              IUpdaterObserverPtr(observer)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](const std::string& app_id, UpdateService::Priority priority,
         bool same_version_update_allowed,
         base::RepeatingCallback<void(const UpdateService::UpdateState&)>
             state_change_callback,
         base::OnceCallback<void(UpdateService::Result)> complete_callback) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(complete_callback)
              .Run(UpdateService::Result::kServiceStopped);
          return;
        }
        update_service->CheckForUpdate(
            app_id, priority,
            same_version_update_allowed
                ? UpdateService::PolicySameVersionUpdate::kAllowed
                : UpdateService::PolicySameVersionUpdate::kNotAllowed,
            std::move(state_change_callback), std::move(complete_callback));
      },
      base::WideToUTF8(app_id), static_cast<UpdateService::Priority>(priority),
      same_version_update_allowed, std::move(state_change_callback),
      std::move(complete_callback)));
  return S_OK;
}

// Called by the COM RPC runtime on one of its threads. Invokes the in-process
// `update_service` on the main sequence. The callbacks received from
// `update_service` arrive in the main sequence too. Since handling these
// callbacks involves issuing outgoing COM RPC calls, which block, such COM
// calls must be done through a task runner, bound to the closures provided
// as parameters for the UpdateService::Update call.
HRESULT UpdaterImpl::Update(const wchar_t* app_id,
                            const wchar_t* install_data_index,
                            LONG priority,
                            BOOL same_version_update_allowed,
                            IUpdaterObserver* observer) {
  if (!observer) {
    return E_INVALIDARG;
  }

  auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
      {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
  base::RepeatingCallback<void(const UpdateService::UpdateState&)>
      state_change_callback = base::BindRepeating(
          &StateChangeCallbackFilter::OnStateChange,
          base::Owned(new StateChangeCallbackFilter(task_runner, observer)));
  base::OnceCallback<void(UpdateService::Result)> complete_callback =
      base::BindPostTask(
          task_runner,
          base::BindOnce(
              [](IUpdaterObserverPtr observer, UpdateService::Result result) {
                HRESULT hr = observer->OnComplete(
                    MakeComObjectOrCrash<CompleteStatusImpl>(
                        static_cast<int>(result), L"")
                        .Get());
                VLOG(2) << "IUpdaterImpl::Update. "
                        << "IUpdaterObserver::OnComplete returned " << std::hex
                        << hr;
              },
              IUpdaterObserverPtr(observer)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](const std::string& app_id, const std::string& install_data_index,
         UpdateService::Priority priority, bool same_version_update_allowed,
         base::RepeatingCallback<void(const UpdateService::UpdateState&)>
             state_change_callback,
         base::OnceCallback<void(UpdateService::Result)> complete_callback) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(complete_callback)
              .Run(UpdateService::Result::kServiceStopped);
          return;
        }
        update_service->Update(
            app_id, install_data_index, priority,
            same_version_update_allowed
                ? UpdateService::PolicySameVersionUpdate::kAllowed
                : UpdateService::PolicySameVersionUpdate::kNotAllowed,
            std::move(state_change_callback), std::move(complete_callback));
      },
      base::WideToUTF8(app_id), base::WideToUTF8(install_data_index),
      static_cast<UpdateService::Priority>(priority),
      same_version_update_allowed, std::move(state_change_callback),
      std::move(complete_callback)));
  return S_OK;
}

// See the comment for the UpdaterImpl::Update.
HRESULT UpdaterImpl::UpdateAll(IUpdaterObserver* observer) {
  if (!observer) {
    return E_INVALIDARG;
  }

  base::OnceCallback<void(UpdateService::Result)> complete_callback =
      base::BindPostTask(
          base::ThreadPool::CreateSequencedTaskRunner(
              {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
          base::BindOnce(
              [](IUpdaterObserverPtr observer, UpdateService::Result result) {
                HRESULT hr = observer->OnComplete(
                    MakeComObjectOrCrash<CompleteStatusImpl>(
                        static_cast<int>(result), L"")
                        .Get());
                VLOG(2) << "IUpdaterImpl::UpdateAll. "
                        << "IUpdaterObserver::OnComplete returned " << std::hex
                        << hr;
              },
              IUpdaterObserverPtr(observer)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](base::OnceCallback<void(UpdateService::Result)> complete_callback) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(complete_callback)
              .Run(UpdateService::Result::kServiceStopped);
          return;
        }
        update_service->UpdateAll(base::DoNothing(),
                                  std::move(complete_callback));
      },
      std::move(complete_callback)));
  return S_OK;
}

HRESULT UpdaterImpl::Install(const wchar_t* app_id,
                             const wchar_t* brand_code,
                             const wchar_t* brand_path,
                             const wchar_t* ap,
                             const wchar_t* version,
                             const wchar_t* existence_checker_path,
                             const wchar_t* client_install_data,
                             const wchar_t* install_data_index,
                             LONG priority,
                             IUpdaterObserver* observer) {
  if (!observer) {
    return E_INVALIDARG;
  }

  // Validates that string parameters are not longer than 16K characters.
  std::optional<RegistrationRequest> request =
      [app_id, brand_code, brand_path, ap, version, existence_checker_path,
       client_install_data, install_data_index]() -> decltype(request) {
    for (const auto* str :
         {app_id, brand_code, brand_path, ap, version, existence_checker_path,
          client_install_data, install_data_index}) {
      if (wcsnlen_s(str, kMaxStringLen) == kMaxStringLen) {
        return std::nullopt;
      }
    }

    RegistrationRequest request;
    if (!app_id || !base::WideToUTF8(app_id, wcslen(app_id), &request.app_id)) {
      return std::nullopt;
    }
    if (!brand_code || !base::WideToUTF8(brand_code, wcslen(brand_code),
                                         &request.brand_code)) {
      return std::nullopt;
    }
    request.brand_path = base::FilePath(brand_path);
    if (!ap || !base::WideToUTF8(ap, wcslen(ap), &request.ap)) {
      return std::nullopt;
    }
    std::string version_str;
    if (!version || !base::WideToUTF8(version, wcslen(version), &version_str)) {
      return std::nullopt;
    }
    request.version = base::Version(version_str);
    if (!request.version.IsValid()) {
      return std::nullopt;
    }
    request.existence_checker_path = base::FilePath(existence_checker_path);

    return request;
  }();

  if (!request) {
    return E_INVALIDARG;
  }

  auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
      {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
  base::RepeatingCallback<void(const UpdateService::UpdateState&)>
      state_change_callback = base::BindRepeating(
          &StateChangeCallbackFilter::OnStateChange,
          base::Owned(new StateChangeCallbackFilter(task_runner, observer)));
  base::OnceCallback<void(UpdateService::Result)> complete_callback =
      base::BindPostTask(
          task_runner,
          base::BindOnce(
              [](IUpdaterObserverPtr observer, UpdateService::Result result) {
                HRESULT hr = observer->OnComplete(
                    MakeComObjectOrCrash<CompleteStatusImpl>(
                        static_cast<int>(result), L"")
                        .Get());
                VLOG(2) << "IUpdaterImpl::Install. "
                        << "IUpdaterObserver::OnComplete returned " << std::hex
                        << hr;
              },
              IUpdaterObserverPtr(observer)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](const RegistrationRequest& request,
         const std::string& client_install_data,
         const std::string& install_data_index,
         UpdateService::Priority priority,
         base::RepeatingCallback<void(const UpdateService::UpdateState&)>
             state_change_callback,
         base::OnceCallback<void(UpdateService::Result)> complete_callback) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(complete_callback)
              .Run(UpdateService::Result::kServiceStopped);
          return;
        }
        update_service->Install(
            request, client_install_data, install_data_index, priority,
            std::move(state_change_callback), std::move(complete_callback));
      },
      *request, base::WideToUTF8(client_install_data),
      base::WideToUTF8(install_data_index),
      static_cast<UpdateService::Priority>(priority),
      std::move(state_change_callback), std::move(complete_callback)));
  return S_OK;
}

HRESULT UpdaterImpl::CancelInstalls(const wchar_t* app_id) {
  std::string app_id_str;
  if (wcsnlen_s(app_id, kMaxStringLen) >= kMaxStringLen || !app_id ||
      !base::WideToUTF8(app_id, wcslen(app_id), &app_id_str)) {
    return E_INVALIDARG;
  }
  AppServerWin::PostRpcTask(base::BindOnce(
      [](const std::string& app_id_str) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          return;
        }
        update_service->CancelInstalls(app_id_str);
      },
      app_id_str));
  return S_OK;
}

HRESULT UpdaterImpl::RunInstaller(const wchar_t* app_id,
                                  const wchar_t* installer_path,
                                  const wchar_t* install_args,
                                  const wchar_t* install_data,
                                  const wchar_t* install_settings,
                                  IUpdaterObserver* observer) {
  VLOG(1) << __func__;

  if (!observer) {
    return E_INVALIDARG;
  }

  for (const wchar_t* str :
       {app_id, installer_path, install_args, install_data, install_settings}) {
    if (wcsnlen_s(str, kMaxStringLen) >= kMaxStringLen) {
      return E_INVALIDARG;
    }
  }

  std::string app_id_str;
  if (!app_id || !base::WideToUTF8(app_id, wcslen(app_id), &app_id_str)) {
    return E_INVALIDARG;
  }

  if (!installer_path) {
    return E_INVALIDARG;
  }

  std::string install_args_str;
  if (install_args && !base::WideToUTF8(install_args, wcslen(install_args),
                                        &install_args_str)) {
    return E_INVALIDARG;
  }

  std::string install_settings_str;
  if (install_settings &&
      !base::WideToUTF8(install_settings, wcslen(install_settings),
                        &install_settings_str)) {
    return E_INVALIDARG;
  }

  std::string install_data_str;
  if (install_data && !base::WideToUTF8(install_data, wcslen(install_data),
                                        &install_data_str)) {
    return E_INVALIDARG;
  }

  auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
      {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
  base::RepeatingCallback<void(const UpdateService::UpdateState&)>
      state_change_callback = base::BindRepeating(
          &StateChangeCallbackFilter::OnStateChange,
          base::Owned(new StateChangeCallbackFilter(task_runner, observer)));
  base::OnceCallback<void(UpdateService::Result)> complete_callback =
      base::BindPostTask(
          task_runner,
          base::BindOnce(
              [](IUpdaterObserverPtr observer, UpdateService::Result result) {
                HRESULT hr = observer->OnComplete(
                    MakeComObjectOrCrash<CompleteStatusImpl>(
                        static_cast<int>(result), L"")
                        .Get());
                VLOG(2) << "IUpdaterImpl::RunInstaller. "
                        << "IUpdaterObserver::OnComplete returned " << std::hex
                        << hr;
              },
              IUpdaterObserverPtr(observer)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](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,
         base::RepeatingCallback<void(const UpdateService::UpdateState&)>
             state_change_callback,
         base::OnceCallback<void(UpdateService::Result)> complete_callback) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(complete_callback)
              .Run(UpdateService::Result::kServiceStopped);
          return;
        }
        update_service->RunInstaller(app_id, installer_path, install_args,
                                     install_data, install_settings,
                                     std::move(state_change_callback),
                                     std::move(complete_callback));
      },
      app_id_str, base::FilePath(installer_path), install_args_str,
      install_data_str, install_settings_str, std::move(state_change_callback),
      std::move(complete_callback)));
  return S_OK;
}

HRESULT UpdaterImpl::GetAppStates(IUpdaterAppStatesCallback* callback) {
  if (!callback) {
    return E_INVALIDARG;
  }

  base::OnceCallback<void(const std::vector<UpdateService::AppState>&)>
      get_app_states_callback = base::BindPostTask(
          base::ThreadPool::CreateSequencedTaskRunner(
              {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
          base::BindOnce(
              [](Microsoft::WRL::ComPtr<IUpdaterAppStatesCallback> callback,
                 const std::vector<UpdateService::AppState>& app_states) {
                // Converts `app_states` into a `SAFEARRAY` of `IDispatch`
                // and calls `IUpdaterAppStatesCallback::Run` with the
                // resulting `VARIANT`.
                base::win::VariantVector updater_app_states;
                for (const auto& app_state : app_states) {
                  Microsoft::WRL::ComPtr<IDispatch> dispatch;
                  CHECK(
                      SUCCEEDED(MakeAndInitializeComObject<UpdaterAppStateImpl>(
                          dispatch, app_state)));
                  updater_app_states.Insert<VT_DISPATCH>(dispatch.Get());
                }
                base::win::ScopedVariant variant;
                variant.Reset(updater_app_states.ReleaseAsSafearrayVariant());
                callback->Run(variant);
              },
              Microsoft::WRL::ComPtr<IUpdaterAppStatesCallback>(callback)));
  AppServerWin::PostRpcTask(base::BindOnce(
      [](base::OnceCallback<void(const std::vector<UpdateService::AppState>&)>
             get_app_states_callback) {
        scoped_refptr<UpdateService> update_service =
            GetAppServerWinInstance()->update_service();
        if (!update_service) {
          std::move(get_app_states_callback)
              .Run(std::vector<UpdateService::AppState>());
          return;
        }
        update_service->GetAppStates(std::move(get_app_states_callback));
      },
      std::move(get_app_states_callback)));
  return S_OK;
}

HRESULT UpdaterInternalImpl::RuntimeClassInitialize() {
  return IsCOMCallerAllowed();
}

// See the comment for the UpdaterImpl::Update.
HRESULT UpdaterInternalImpl::Run(IUpdaterInternalCallback* callback) {
  if (!callback) {
    return E_INVALIDARG;
  }

  base::OnceClosure updater_internal_callback = base::BindPostTask(
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
      base::BindOnce(
          [](IUpdaterInternalCallbackPtr callback) {
            HRESULT hr = callback->Run(0);
            VLOG(2) << "UpdaterInternalImpl::Run. "
                    << "IUpdaterInternalCallback::Run returned " << std::hex
                    << hr;
          },
          IUpdaterInternalCallbackPtr(callback)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](base::OnceClosure updater_internal_callback) {
        scoped_refptr<UpdateServiceInternal> update_service_internal =
            GetAppServerWinInstance()->update_service_internal();
        if (!update_service_internal) {
          std::move(updater_internal_callback).Run();
          return;
        }
        update_service_internal->Run(std::move(updater_internal_callback));
      },
      std::move(updater_internal_callback)));
  return S_OK;
}

HRESULT UpdaterInternalImpl::Hello(IUpdaterInternalCallback* callback) {
  if (!callback) {
    return E_INVALIDARG;
  }

  base::OnceClosure updater_internal_callback = base::BindPostTask(
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
      base::BindOnce(
          [](IUpdaterInternalCallbackPtr callback) {
            HRESULT hr = callback->Run(0);
            VLOG(2) << "UpdaterInternalImpl::Hello. "
                    << "IUpdaterInternalCallback::Run returned " << std::hex
                    << hr;
          },
          IUpdaterInternalCallbackPtr(callback)));

  AppServerWin::PostRpcTask(base::BindOnce(
      [](base::OnceClosure updater_internal_callback) {
        scoped_refptr<UpdateServiceInternal> update_service_internal =
            GetAppServerWinInstance()->update_service_internal();
        if (!update_service_internal) {
          std::move(updater_internal_callback).Run();
          return;
        }
        update_service_internal->Hello(std::move(updater_internal_callback));
      },
      std::move(updater_internal_callback)));
  return S_OK;
}

}  // namespace updater