chromium/chrome/browser/ash/crosapi/volume_manager_ash.cc

// Copyright 2022 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/crosapi/volume_manager_ash.h"

#include <utility>
#include <vector>

#include "base/memory/weak_ptr.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/profiles/profile.h"

namespace crosapi {

namespace {

bool IsVolumeAvailableToLacros(const file_manager::Volume& volume) {
  // Use type (available only in ash-chrome) to decide whether a volume can be
  // used by lacros-chrome. This is needed because virtual file system support
  // for lacros is WIP.
  // TODO(crbug.com/1296438): Adjust as needed re. VFS support progress, being
  // mindful of version skew and its limits.
  auto type = volume.type();
  return type == file_manager::VOLUME_TYPE_DOWNLOADS_DIRECTORY ||
         type == file_manager::VOLUME_TYPE_REMOVABLE_DISK_PARTITION ||
         type == file_manager::VOLUME_TYPE_MOUNTED_ARCHIVE_FILE ||
         type == file_manager::VOLUME_TYPE_SMB;
}

crosapi::mojom::VolumePtr ConvertVolumeToMojom(
    const file_manager::Volume& src_volume) {
  crosapi::mojom::VolumePtr dst_volume = crosapi::mojom::Volume::New();
  dst_volume->volume_id = src_volume.volume_id();
  dst_volume->volume_label = src_volume.volume_label();
  dst_volume->writable = !src_volume.is_read_only();
  // TODO(crbug.com/1296438): Deprecate once VFS works fully, being mindful of
  // version skew and its limits.
  dst_volume->is_available_to_lacros = IsVolumeAvailableToLacros(src_volume);
  dst_volume->mount_path = src_volume.mount_path();
  return dst_volume;
}

std::vector<crosapi::mojom::VolumePtr> ConvertVolumeListToMojom(
    std::vector<base::WeakPtr<file_manager::Volume>> src_volume_list) {
  std::vector<crosapi::mojom::VolumePtr> dst_volume_list;
  for (const auto& src_volume : src_volume_list) {
    if (src_volume.get())
      dst_volume_list.emplace_back(ConvertVolumeToMojom(*src_volume));
  }
  return dst_volume_list;
}

// Returns volume list converted to crosapi form.
std::vector<crosapi::mojom::VolumePtr> ReadVolumeList(Profile* profile) {
  const std::vector<base::WeakPtr<file_manager::Volume>> volume_list =
      file_manager::VolumeManager::Get(profile)->GetVolumeList();
  return ConvertVolumeListToMojom(volume_list);
}

}  // namespace

VolumeManagerAsh::VolumeManagerAsh() = default;

VolumeManagerAsh::~VolumeManagerAsh() = default;

void VolumeManagerAsh::SetProfile(Profile* profile) {
  CHECK(profile);
  if (profile_ == profile) {
    VLOG(1) << "VolumeManagerAsh service is already initialized. Skip init.";
    return;
  }

  profile_ = profile;
  profile_observation_.Observe(profile_);
}

void VolumeManagerAsh::BindReceiver(
    mojo::PendingReceiver<mojom::VolumeManager> receiver) {
  // profile_ should be set beforehand.
  CHECK(profile_);
  receivers_.Add(this, std::move(receiver));
}

void VolumeManagerAsh::AddVolumeListObserver(
    mojo::PendingRemote<mojom::VolumeListObserver> observer) {
  if (!is_observing_volume_manager_ && profile_) {
    auto* volume_manager = file_manager::VolumeManager::Get(profile_);
    volume_manager->AddObserver(this);
    is_observing_volume_manager_ = true;
  }

  mojo::Remote<mojom::VolumeListObserver> remote(std::move(observer));
  // Note: The observer is NOT provided with the initial value.

  volume_list_observers_.Add(std::move(remote));
}

void VolumeManagerAsh::GetFullVolumeList(GetFullVolumeListCallback callback) {
  if (profile_) {
    std::move(callback).Run(ReadVolumeList(profile_));
  }
}

void VolumeManagerAsh::GetVolumeMountInfo(const std::string& volume_id,
                                          GetVolumeMountInfoCallback callback) {
  if (profile_) {
    base::WeakPtr<file_manager::Volume> src_volume =
        file_manager::VolumeManager::Get(profile_)->FindVolumeById(volume_id);
    std::move(callback).Run(src_volume.get() ? ConvertVolumeToMojom(*src_volume)
                                             : nullptr);
  }
}

void VolumeManagerAsh::OnVolumeMounted(ash::MountError error_code,
                                       const file_manager::Volume& volume) {
  DispatchVolumeList();
}

void VolumeManagerAsh::OnVolumeUnmounted(ash::MountError error_code,
                                         const file_manager::Volume& volume) {
  DispatchVolumeList();
}

void VolumeManagerAsh::OnShutdownStart(
    file_manager::VolumeManager* volume_manager) {
  // Using DCHECK since this function gets called only if observing.
  DCHECK(is_observing_volume_manager_);
  volume_manager->RemoveObserver(this);
  is_observing_volume_manager_ = false;
}

void VolumeManagerAsh::OnProfileWillBeDestroyed(Profile* profile) {
  CHECK_EQ(profile_, profile);
  profile_ = nullptr;
  profile_observation_.Reset();
}

void VolumeManagerAsh::DispatchVolumeList() {
  if (volume_list_observers_.empty() || !profile_) {
    return;
  }
  std::vector<crosapi::mojom::VolumePtr> volume_list = ReadVolumeList(profile_);
  for (auto& observer : volume_list_observers_) {
    std::vector<crosapi::mojom::VolumePtr> volume_list_copy;
    for (auto& volume : volume_list) {
      volume_list_copy.emplace_back(volume->Clone());
    }
    observer->OnVolumeListChanged(std::move(volume_list_copy));
  }
}

}  // namespace crosapi