chromium/chrome/browser/ash/android_sms/connection_manager.cc

// Copyright 2018 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/browser/ash/android_sms/connection_manager.h"

#include <utility>

#include "chrome/browser/ash/android_sms/android_sms_urls.h"
#include "chrome/browser/ash/android_sms/connection_establisher.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "components/session_manager/core/session_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/storage_partition.h"

namespace ash {
namespace android_sms {

ConnectionManager::ServiceWorkerProvider::ServiceWorkerProvider() = default;

ConnectionManager::ServiceWorkerProvider::~ServiceWorkerProvider() = default;

content::ServiceWorkerContext* ConnectionManager::ServiceWorkerProvider::Get(
    const GURL& url,
    Profile* profile) {
  return profile->GetDefaultStoragePartition()->GetServiceWorkerContext();
}

ConnectionManager::ConnectionManager(
    std::unique_ptr<ConnectionEstablisher> connection_establisher,
    Profile* profile,
    AndroidSmsAppManager* android_sms_app_manager,
    multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client)
    : ConnectionManager(std::move(connection_establisher),
                        profile,
                        android_sms_app_manager,
                        multidevice_setup_client,
                        std::make_unique<ServiceWorkerProvider>()) {}

ConnectionManager::ConnectionManager(
    std::unique_ptr<ConnectionEstablisher> connection_establisher,
    Profile* profile,
    AndroidSmsAppManager* android_sms_app_manager,
    multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
    std::unique_ptr<ServiceWorkerProvider> service_worker_provider)
    : connection_establisher_(std::move(connection_establisher)),
      profile_(profile),
      android_sms_app_manager_(android_sms_app_manager),
      multidevice_setup_client_(multidevice_setup_client),
      service_worker_provider_(std::move(service_worker_provider)) {
  android_sms_app_manager_->AddObserver(this);
  multidevice_setup_client_->AddObserver(this);

  UpdateConnectionStatus();
}

ConnectionManager::~ConnectionManager() {
  android_sms_app_manager_->RemoveObserver(this);
  multidevice_setup_client_->RemoveObserver(this);

  if (GetCurrentServiceWorkerContext())
    GetCurrentServiceWorkerContext()->RemoveObserver(this);
}

void ConnectionManager::StartConnection() {
  if (!enabled_pwa_url_) {
    return;
  }
  PA_LOG(INFO) << "ConnectionManager::StartConnection(): Establishing "
               << "connection to PWA at " << *enabled_pwa_url_ << ".";
  connection_establisher_->EstablishConnection(
      *enabled_pwa_url_,
      ConnectionEstablisher::ConnectionMode::kStartConnection,
      GetCurrentServiceWorkerContext());
}

void ConnectionManager::OnVersionActivated(int64_t version_id,
                                           const GURL& scope) {
  if (!enabled_pwa_url_ || !scope.EqualsIgnoringRef(*enabled_pwa_url_))
    return;

  prev_active_version_id_ = active_version_id_;
  active_version_id_ = version_id;

  connection_establisher_->EstablishConnection(
      *enabled_pwa_url_,
      ConnectionEstablisher::ConnectionMode::kResumeExistingConnection,
      GetCurrentServiceWorkerContext());
}

void ConnectionManager::OnVersionRedundant(int64_t version_id,
                                           const GURL& scope) {
  if (!enabled_pwa_url_ || !scope.EqualsIgnoringRef(*enabled_pwa_url_))
    return;

  if (active_version_id_ != version_id)
    return;

  // If the active version is marked redundant then it cannot handle messages
  // anymore, so stop tracking it.
  prev_active_version_id_ = active_version_id_;
  active_version_id_.reset();
}

void ConnectionManager::OnNoControllees(int64_t version_id, const GURL& scope) {
  if (!enabled_pwa_url_ || !scope.EqualsIgnoringRef(*enabled_pwa_url_))
    return;

  // Set active_version_id_ in case we missed version activated.
  // This is unlikely but protects against a case where a Android Messages for
  // Web page may have opened before the ConnectionManager is created.
  if (!active_version_id_ && prev_active_version_id_ != version_id)
    active_version_id_ = version_id;

  if (active_version_id_ != version_id)
    return;

  connection_establisher_->EstablishConnection(
      *enabled_pwa_url_,
      ConnectionEstablisher::ConnectionMode::kResumeExistingConnection,
      GetCurrentServiceWorkerContext());
}

void ConnectionManager::OnInstalledAppUrlChanged() {
  UpdateConnectionStatus();
}

void ConnectionManager::OnFeatureStatesChanged(
    const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
        feature_states_map) {
  UpdateConnectionStatus();
}

void ConnectionManager::UpdateConnectionStatus() {
  std::optional<GURL> updated_pwa_url =
      ConnectionManager::GenerateEnabledPwaUrl();
  if (enabled_pwa_url_ == updated_pwa_url)
    return;

  // If the URL was previously enabled, stop the ServiceWorker. This occurs when
  // the feature was disabled and when migrating from the old URL to the new
  // one.
  if (enabled_pwa_url_) {
    PA_LOG(INFO) << "ConnectionManager::UpdateConnectionStatus(): Stopping "
                 << "connection to PWA at " << *enabled_pwa_url_ << ".";
    connection_establisher_->TearDownConnection(
        *enabled_pwa_url_, GetCurrentServiceWorkerContext());
    GetCurrentServiceWorkerContext()->RemoveObserver(this);
    active_version_id_.reset();
    prev_active_version_id_.reset();
  }

  enabled_pwa_url_ = updated_pwa_url;
  if (!enabled_pwa_url_)
    return;

  GetCurrentServiceWorkerContext()->AddObserver(this);
  StartConnection();
}

std::optional<GURL> ConnectionManager::GenerateEnabledPwaUrl() {
  const auto it = multidevice_setup_client_->GetFeatureStates().find(
      multidevice_setup::mojom::Feature::kMessages);

  // If the feature is not enabled, there is no enabled URL.
  if (it->second != multidevice_setup::mojom::FeatureState::kEnabledByUser)
    return std::nullopt;

  // Return the installed app URL if the PWA is installed.
  std::optional<GURL> installed_url =
      android_sms_app_manager_->GetCurrentAppUrl();
  if (installed_url)
    return installed_url;

  // Otherwise, return the default URL.
  return GetAndroidMessagesURL();
}

content::ServiceWorkerContext*
ConnectionManager::GetCurrentServiceWorkerContext() {
  if (!enabled_pwa_url_)
    return nullptr;

  return service_worker_provider_->Get(*enabled_pwa_url_, profile_);
}

void ConnectionManager::SetServiceWorkerProviderForTesting(
    std::unique_ptr<ServiceWorkerProvider> service_worker_provider) {
  service_worker_provider_ = std::move(service_worker_provider);
}

}  // namespace android_sms
}  // namespace ash