| // Copyright 2018 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 "services/device/media_transfer_protocol/mtp_device_manager.h" |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/containers/contains.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "dbus/bus.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| #if DCHECK_IS_ON() |
| MtpDeviceManager* g_mtp_device_manager = nullptr; |
| #endif |
| |
| } // namespace |
| |
| MtpDeviceManager::MtpDeviceManager() |
| : bus_(chromeos::DBusThreadManager::Get()->GetSystemBus()) { |
| // Listen for future mtpd service owner changes, in case it is not |
| // available right now. There is no guarantee that mtpd is running already. |
| dbus::Bus::ServiceOwnerChangeCallback mtpd_owner_changed_callback = |
| base::BindRepeating(&MtpDeviceManager::FinishSetupOnOriginThread, |
| weak_ptr_factory_.GetWeakPtr()); |
| if (bus_) { |
| bus_->ListenForServiceOwnerChange(mtpd::kMtpdServiceName, |
| mtpd_owner_changed_callback); |
| bus_->GetServiceOwner(mtpd::kMtpdServiceName, mtpd_owner_changed_callback); |
| } |
| } |
| |
| MtpDeviceManager::~MtpDeviceManager() { |
| #if DCHECK_IS_ON() |
| DCHECK(g_mtp_device_manager); |
| g_mtp_device_manager = nullptr; |
| #endif |
| |
| if (bus_) { |
| bus_->UnlistenForServiceOwnerChange( |
| mtpd::kMtpdServiceName, |
| base::BindRepeating(&MtpDeviceManager::FinishSetupOnOriginThread, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| VLOG(1) << "MtpDeviceManager Shutdown completed"; |
| } |
| |
| void MtpDeviceManager::AddReceiver( |
| mojo::PendingReceiver<mojom::MtpManager> receiver) { |
| receivers_.Add(this, std::move(receiver)); |
| } |
| |
| void MtpDeviceManager::EnumerateStoragesAndSetClient( |
| mojo::PendingAssociatedRemote<mojom::MtpManagerClient> client, |
| EnumerateStoragesAndSetClientCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // Return all available storage info. |
| std::vector<mojom::MtpStorageInfoPtr> storage_info_ptr_list; |
| storage_info_ptr_list.reserve(storage_info_map_.size()); |
| for (const auto& info : storage_info_map_) |
| storage_info_ptr_list.push_back(info.second.Clone()); |
| std::move(callback).Run(std::move(storage_info_ptr_list)); |
| |
| // Set client. |
| if (!client.is_valid()) |
| return; |
| |
| client_.Bind(std::move(client)); |
| } |
| |
| void MtpDeviceManager::GetStorageInfo(const std::string& storage_name, |
| GetStorageInfoCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| const auto it = storage_info_map_.find(storage_name); |
| mojom::MtpStorageInfoPtr storage_info = |
| it != storage_info_map_.end() ? it->second.Clone() : nullptr; |
| std::move(callback).Run(std::move(storage_info)); |
| } |
| |
| void MtpDeviceManager::GetStorageInfoFromDevice( |
| const std::string& storage_name, |
| GetStorageInfoFromDeviceCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(storage_info_map_, storage_name) || !mtp_client_) { |
| std::move(callback).Run(nullptr, true /* error */); |
| return; |
| } |
| get_storage_info_from_device_callbacks_.push(std::move(callback)); |
| mtp_client_->GetStorageInfoFromDevice( |
| storage_name, |
| base::BindOnce(&MtpDeviceManager::OnGetStorageInfoFromDevice, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnGetStorageInfoFromDeviceError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::OpenStorage(const std::string& storage_name, |
| const std::string& mode, |
| OpenStorageCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(storage_info_map_, storage_name) || !mtp_client_) { |
| std::move(callback).Run(std::string(), true); |
| return; |
| } |
| open_storage_callbacks_.push(std::move(callback)); |
| mtp_client_->OpenStorage(storage_name, mode, |
| base::BindOnce(&MtpDeviceManager::OnOpenStorage, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnOpenStorageError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::CloseStorage(const std::string& storage_handle, |
| CloseStorageCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, storage_handle) || !mtp_client_) { |
| std::move(callback).Run(true); |
| return; |
| } |
| close_storage_callbacks_.push( |
| std::make_pair(std::move(callback), storage_handle)); |
| mtp_client_->CloseStorage( |
| storage_handle, |
| base::BindOnce(&MtpDeviceManager::OnCloseStorage, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnCloseStorageError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::CreateDirectory(const std::string& storage_handle, |
| uint32_t parent_id, |
| const std::string& directory_name, |
| CreateDirectoryCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, storage_handle) || !mtp_client_) { |
| std::move(callback).Run(true /* error */); |
| return; |
| } |
| create_directory_callbacks_.push(std::move(callback)); |
| mtp_client_->CreateDirectory( |
| storage_handle, parent_id, directory_name, |
| base::BindOnce(&MtpDeviceManager::OnCreateDirectory, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnCreateDirectoryError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::ReadDirectoryEntryIds( |
| const std::string& storage_handle, |
| uint32_t file_id, |
| ReadDirectoryEntryIdsCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, storage_handle) || !mtp_client_) { |
| std::move(callback).Run(std::vector<uint32_t>(), /*error=*/true); |
| return; |
| } |
| read_directory_callbacks_.push(std::move(callback)); |
| mtp_client_->ReadDirectoryEntryIds( |
| storage_handle, file_id, |
| base::BindOnce(&MtpDeviceManager::OnReadDirectoryEntryIds, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnReadDirectoryError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::ReadFileChunk(const std::string& storage_handle, |
| uint32_t file_id, |
| uint32_t offset, |
| uint32_t count, |
| ReadFileChunkCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, storage_handle) || !mtp_client_) { |
| std::move(callback).Run(std::string(), true); |
| return; |
| } |
| read_file_callbacks_.push(std::move(callback)); |
| mtp_client_->ReadFileChunk(storage_handle, file_id, offset, count, |
| base::BindOnce(&MtpDeviceManager::OnReadFile, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnReadFileError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::GetFileInfo(const std::string& storage_handle, |
| const std::vector<uint32_t>& file_ids, |
| GetFileInfoCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, storage_handle) || !mtp_client_) { |
| std::move(callback).Run(std::vector<device::mojom::MtpFileEntryPtr>(), |
| /*error=*/true); |
| return; |
| } |
| get_file_info_callbacks_.push(std::move(callback)); |
| mtp_client_->GetFileInfo(storage_handle, file_ids, |
| base::BindOnce(&MtpDeviceManager::OnGetFileInfo, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnGetFileInfoError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::RenameObject(const std::string& storage_handle, |
| uint32_t object_id, |
| const std::string& new_name, |
| RenameObjectCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, storage_handle) || !mtp_client_) { |
| std::move(callback).Run(true /* error */); |
| return; |
| } |
| rename_object_callbacks_.push(std::move(callback)); |
| mtp_client_->RenameObject( |
| storage_handle, object_id, new_name, |
| base::BindOnce(&MtpDeviceManager::OnRenameObject, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnRenameObjectError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::CopyFileFromLocal(const std::string& storage_handle, |
| int64_t source_file_descriptor, |
| uint32_t parent_id, |
| const std::string& file_name, |
| CopyFileFromLocalCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, storage_handle) || !mtp_client_) { |
| std::move(callback).Run(true /* error */); |
| return; |
| } |
| copy_file_from_local_callbacks_.push(std::move(callback)); |
| mtp_client_->CopyFileFromLocal( |
| storage_handle, source_file_descriptor, parent_id, file_name, |
| base::BindOnce(&MtpDeviceManager::OnCopyFileFromLocal, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnCopyFileFromLocalError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void MtpDeviceManager::DeleteObject(const std::string& storage_handle, |
| uint32_t object_id, |
| DeleteObjectCallback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, storage_handle) || !mtp_client_) { |
| std::move(callback).Run(true /* error */); |
| return; |
| } |
| delete_object_callbacks_.push(std::move(callback)); |
| mtp_client_->DeleteObject( |
| storage_handle, object_id, |
| base::BindOnce(&MtpDeviceManager::OnDeleteObject, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&MtpDeviceManager::OnDeleteObjectError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| // private methods |
| void MtpDeviceManager::OnStorageAttached(const std::string& storage_name) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| mtp_client_->GetStorageInfo( |
| storage_name, |
| base::BindOnce(&MtpDeviceManager::OnGetStorageInfo, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::DoNothing::Once()); |
| } |
| |
| void MtpDeviceManager::OnStorageDetached(const std::string& storage_name) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (storage_info_map_.erase(storage_name) == 0) { |
| // This can happen for a storage where |
| // MediaTransferProtocolDaemonClient::GetStorageInfo() failed. |
| // Return to avoid giving client phantom detach events. |
| return; |
| } |
| |
| // Notify the bound MtpManagerClient. |
| if (client_) { |
| client_->StorageDetached(storage_name); |
| } |
| } |
| |
| void MtpDeviceManager::OnStorageChanged(bool is_attach, |
| const std::string& storage_name) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(mtp_client_); |
| if (is_attach) |
| OnStorageAttached(storage_name); |
| else |
| OnStorageDetached(storage_name); |
| } |
| |
| void MtpDeviceManager::OnEnumerateStorages( |
| const std::vector<std::string>& storage_names) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(mtp_client_); |
| for (const auto& name : storage_names) { |
| if (base::Contains(storage_info_map_, name)) { |
| // OnStorageChanged() might have gotten called first. |
| continue; |
| } |
| OnStorageAttached(name); |
| } |
| } |
| |
| void MtpDeviceManager::OnGetStorageInfo( |
| const mojom::MtpStorageInfo& storage_info) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| const std::string& storage_name = storage_info.storage_name; |
| if (base::Contains(storage_info_map_, storage_name)) { |
| // This should not happen, since MtpDeviceManager should |
| // only call EnumerateStorages() once, which populates |storage_info_map_| |
| // with the already-attached devices. |
| // After that, all incoming signals are either for new storage |
| // attachments, which should not be in |storage_info_map_|, or for |
| // storage detachments, which do not add to |storage_info_map_|. |
| // Return to avoid giving client phantom detach events. |
| NOTREACHED(); |
| return; |
| } |
| |
| // New storage. Add it and let the bound client know. |
| storage_info_map_.insert(std::make_pair(storage_name, storage_info)); |
| |
| if (client_) { |
| client_->StorageAttached(storage_info.Clone()); |
| } |
| } |
| |
| void MtpDeviceManager::OnGetStorageInfoFromDevice( |
| const mojom::MtpStorageInfo& storage_info) { |
| std::move(get_storage_info_from_device_callbacks_.front()) |
| .Run(storage_info.Clone(), false /* no error */); |
| get_storage_info_from_device_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnGetStorageInfoFromDeviceError() { |
| std::move(get_storage_info_from_device_callbacks_.front()) |
| .Run(nullptr, true /* error */); |
| get_storage_info_from_device_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnOpenStorage(const std::string& handle) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::Contains(handles_, handle)) { |
| handles_.insert(handle); |
| std::move(open_storage_callbacks_.front()).Run(handle, false); |
| } else { |
| NOTREACHED(); |
| std::move(open_storage_callbacks_.front()).Run(std::string(), true); |
| } |
| open_storage_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnOpenStorageError() { |
| std::move(open_storage_callbacks_.front()).Run(std::string(), true); |
| open_storage_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnCloseStorage() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| const std::string& handle = close_storage_callbacks_.front().second; |
| if (base::Contains(handles_, handle)) { |
| handles_.erase(handle); |
| std::move(close_storage_callbacks_.front().first).Run(false); |
| } else { |
| NOTREACHED(); |
| std::move(close_storage_callbacks_.front().first).Run(true); |
| } |
| close_storage_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnCloseStorageError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(close_storage_callbacks_.front()).first.Run(true); |
| close_storage_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnCreateDirectory() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(create_directory_callbacks_.front()).Run(false /* no error */); |
| create_directory_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnCreateDirectoryError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(create_directory_callbacks_.front()).Run(true /* error */); |
| create_directory_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnReadDirectoryEntryIds( |
| const std::vector<uint32_t>& file_ids) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(read_directory_callbacks_.front()).Run(file_ids, /*error=*/false); |
| read_directory_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnReadDirectoryError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(read_directory_callbacks_.front()) |
| .Run(std::vector<uint32_t>(), /*error=*/true); |
| read_directory_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnReadFile(const std::string& data) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(read_file_callbacks_.front()).Run(data, false); |
| read_file_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnReadFileError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(read_file_callbacks_.front()).Run(std::string(), true); |
| read_file_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnGetFileInfo( |
| const std::vector<mojom::MtpFileEntry>& entries) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| std::vector<device::mojom::MtpFileEntryPtr> ret(entries.size()); |
| for (size_t i = 0; i < entries.size(); ++i) |
| ret[i] = entries[i].Clone(); |
| std::move(get_file_info_callbacks_.front()) |
| .Run(std::move(ret), /*error=*/false); |
| get_file_info_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnGetFileInfoError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(get_file_info_callbacks_.front()) |
| .Run(std::vector<device::mojom::MtpFileEntryPtr>(), /*error=*/true); |
| get_file_info_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnRenameObject() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(rename_object_callbacks_.front()).Run(false /* no error */); |
| rename_object_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnRenameObjectError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(rename_object_callbacks_.front()).Run(true /* error */); |
| rename_object_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnCopyFileFromLocal() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(copy_file_from_local_callbacks_.front()).Run(false /* no error */); |
| copy_file_from_local_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnCopyFileFromLocalError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(copy_file_from_local_callbacks_.front()).Run(true /* error */); |
| copy_file_from_local_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnDeleteObject() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(delete_object_callbacks_.front()).Run(false /* no error */); |
| delete_object_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::OnDeleteObjectError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::move(delete_object_callbacks_.front()).Run(true /* error */); |
| delete_object_callbacks_.pop(); |
| } |
| |
| void MtpDeviceManager::FinishSetupOnOriginThread( |
| const std::string& mtpd_service_owner) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| if (mtpd_service_owner == current_mtpd_owner_) |
| return; |
| |
| // In the case of a new service owner, clear |storage_info_map_|. |
| // Assume all storages have been disconnected. If there is a new service |
| // owner, reconnecting to it will reconnect all the storages as well. |
| |
| // Save a copy of |storage_info_map_| keys as |storage_info_map_| can |
| // change in OnStorageDetached(). |
| std::vector<std::string> storage_names; |
| storage_names.reserve(storage_info_map_.size()); |
| for (const auto& info : storage_info_map_) |
| storage_names.push_back(info.first); |
| |
| for (const auto& name : storage_names) |
| OnStorageDetached(name); |
| |
| if (mtpd_service_owner.empty()) { |
| current_mtpd_owner_.clear(); |
| mtp_client_.reset(); |
| return; |
| } |
| |
| current_mtpd_owner_ = mtpd_service_owner; |
| |
| // |bus_| must be valid here. Otherwise, how did this method get called as a |
| // callback in the first place? |
| DCHECK(bus_); |
| mtp_client_ = MediaTransferProtocolDaemonClient::Create(bus_.get()); |
| |
| // Set up signals and start initializing |storage_info_map_|. |
| mtp_client_->ListenForChanges(base::BindRepeating( |
| &MtpDeviceManager::OnStorageChanged, weak_ptr_factory_.GetWeakPtr())); |
| mtp_client_->EnumerateStorages( |
| base::BindOnce(&MtpDeviceManager::OnEnumerateStorages, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::DoNothing::Once()); |
| } |
| |
| // static |
| std::unique_ptr<MtpDeviceManager> MtpDeviceManager::Initialize() { |
| auto manager = std::make_unique<MtpDeviceManager>(); |
| |
| VLOG(1) << "MtpDeviceManager initialized"; |
| |
| #if DCHECK_IS_ON() |
| DCHECK(!g_mtp_device_manager); |
| g_mtp_device_manager = manager.get(); |
| #endif |
| |
| return manager; |
| } |
| |
| } // namespace device |