| // Copyright (c) 2012 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 "device/media_transfer_protocol/media_transfer_protocol_manager.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/containers/flat_map.h" |
| #include "base/containers/flat_set.h" |
| #include "base/containers/queue.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/threading/thread_checker.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "dbus/bus.h" |
| #include "device/media_transfer_protocol/media_transfer_protocol_daemon_client.h" |
| #include "device/media_transfer_protocol/mtp_storage_info.pb.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| #if DCHECK_IS_ON() |
| MediaTransferProtocolManager* g_media_transfer_protocol_manager = nullptr; |
| #endif |
| |
| // When reading directory entries, this is the number of entries for |
| // GetFileInfo() to read in one operation. If set too low, efficiency goes down |
| // slightly due to the overhead of D-Bus calls. If set too high, then slow |
| // devices may trigger a D-Bus timeout. |
| // The value below is a good initial estimate. |
| const size_t kFileInfoToFetchChunkSize = 25; |
| |
| // On the first call to GetFileInfo, the offset to use is 0. |
| const size_t kInitialOffset = 0; |
| |
| // The MediaTransferProtocolManager implementation. |
| class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { |
| public: |
| MediaTransferProtocolManagerImpl() |
| : bus_(chromeos::DBusThreadManager::Get()->GetSystemBus()), |
| weak_ptr_factory_(this) { |
| // Listen for future mtpd service owner changes, in case it is not |
| // available right now. There is no guarantee that mtpd is running already. |
| mtpd_owner_changed_callback_ = |
| base::Bind(&MediaTransferProtocolManagerImpl::FinishSetupOnOriginThread, |
| weak_ptr_factory_.GetWeakPtr()); |
| if (bus_) { |
| bus_->ListenForServiceOwnerChange(mtpd::kMtpdServiceName, |
| mtpd_owner_changed_callback_); |
| bus_->GetServiceOwner(mtpd::kMtpdServiceName, |
| mtpd_owner_changed_callback_); |
| } |
| } |
| |
| ~MediaTransferProtocolManagerImpl() override { |
| #if DCHECK_IS_ON() |
| DCHECK(g_media_transfer_protocol_manager); |
| g_media_transfer_protocol_manager = nullptr; |
| #endif |
| if (bus_) { |
| bus_->UnlistenForServiceOwnerChange(mtpd::kMtpdServiceName, |
| mtpd_owner_changed_callback_); |
| } |
| |
| VLOG(1) << "MediaTransferProtocolManager Shutdown completed"; |
| } |
| |
| // MediaTransferProtocolManager override. |
| void AddObserver(Observer* observer) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| observers_.AddObserver(observer); |
| } |
| |
| // MediaTransferProtocolManager override. |
| void RemoveObserver(Observer* observer) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| observers_.RemoveObserver(observer); |
| } |
| |
| // MediaTransferProtocolManager override. |
| void GetStorages(GetStoragesCallback callback) const override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::vector<std::string> storages; |
| storages.reserve(storage_info_map_.size()); |
| for (const auto& info : storage_info_map_) |
| storages.push_back(info.first); |
| std::move(callback).Run(storages); |
| } |
| |
| // MediaTransferProtocolManager override. |
| const mojom::MtpStorageInfo* GetStorageInfo( |
| const std::string& storage_name) const override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| const auto it = storage_info_map_.find(storage_name); |
| return it != storage_info_map_.end() ? &it->second : nullptr; |
| } |
| |
| // MediaTransferProtocolManager override. |
| void GetStorageInfoFromDevice( |
| const std::string& storage_name, |
| const GetStorageInfoFromDeviceCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(storage_info_map_, storage_name) || !mtp_client_) { |
| mojom::MtpStorageInfo info; |
| callback.Run(info, true /* error */); |
| return; |
| } |
| get_storage_info_from_device_callbacks_.push(callback); |
| mtp_client_->GetStorageInfoFromDevice( |
| storage_name, |
| base::Bind( |
| &MediaTransferProtocolManagerImpl::OnGetStorageInfoFromDevice, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind( |
| &MediaTransferProtocolManagerImpl::OnGetStorageInfoFromDeviceError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| // MediaTransferProtocolManager override. |
| void OpenStorage(const std::string& storage_name, |
| const std::string& mode, |
| const OpenStorageCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(storage_info_map_, storage_name) || !mtp_client_) { |
| callback.Run(std::string(), true); |
| return; |
| } |
| open_storage_callbacks_.push(callback); |
| mtp_client_->OpenStorage( |
| storage_name, |
| mode, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorage, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorageError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| // MediaTransferProtocolManager override. |
| void CloseStorage(const std::string& storage_handle, |
| const CloseStorageCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, storage_handle) || !mtp_client_) { |
| callback.Run(true); |
| return; |
| } |
| close_storage_callbacks_.push(std::make_pair(callback, storage_handle)); |
| mtp_client_->CloseStorage( |
| storage_handle, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorage, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorageError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void CreateDirectory(const std::string& storage_handle, |
| const uint32_t parent_id, |
| const std::string& directory_name, |
| const CreateDirectoryCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, storage_handle) || !mtp_client_) { |
| callback.Run(true /* error */); |
| return; |
| } |
| create_directory_callbacks_.push(callback); |
| mtp_client_->CreateDirectory( |
| storage_handle, parent_id, directory_name, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnCreateDirectory, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnCreateDirectoryError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| // MediaTransferProtocolManager override. |
| void ReadDirectory(const std::string& storage_handle, |
| const uint32_t file_id, |
| const size_t max_size, |
| const ReadDirectoryCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, storage_handle) || !mtp_client_) { |
| callback.Run(std::vector<mojom::MtpFileEntry>(), |
| false /* no more entries */, |
| true /* error */); |
| return; |
| } |
| read_directory_callbacks_.push(callback); |
| mtp_client_->ReadDirectoryEntryIds( |
| storage_handle, file_id, |
| base::Bind(&MediaTransferProtocolManagerImpl:: |
| OnReadDirectoryEntryIdsToReadDirectory, |
| weak_ptr_factory_.GetWeakPtr(), storage_handle, max_size), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| // MediaTransferProtocolManager override. |
| void ReadFileChunk(const std::string& storage_handle, |
| uint32_t file_id, |
| uint32_t offset, |
| uint32_t count, |
| const ReadFileCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, storage_handle) || !mtp_client_) { |
| callback.Run(std::string(), true); |
| return; |
| } |
| read_file_callbacks_.push(callback); |
| mtp_client_->ReadFileChunk( |
| storage_handle, file_id, offset, count, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void GetFileInfo(const std::string& storage_handle, |
| uint32_t file_id, |
| const GetFileInfoCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, storage_handle) || !mtp_client_) { |
| callback.Run(mojom::MtpFileEntry(), true); |
| return; |
| } |
| std::vector<uint32_t> file_ids; |
| file_ids.push_back(file_id); |
| get_file_info_callbacks_.push(callback); |
| mtp_client_->GetFileInfo( |
| storage_handle, |
| file_ids, |
| kInitialOffset, |
| file_ids.size(), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void RenameObject(const std::string& storage_handle, |
| const uint32_t object_id, |
| const std::string& new_name, |
| const RenameObjectCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, storage_handle) || !mtp_client_) { |
| callback.Run(true /* error */); |
| return; |
| } |
| rename_object_callbacks_.push(callback); |
| mtp_client_->RenameObject( |
| storage_handle, object_id, new_name, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnRenameObject, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnRenameObjectError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void CopyFileFromLocal(const std::string& storage_handle, |
| const int source_file_descriptor, |
| const uint32_t parent_id, |
| const std::string& file_name, |
| const CopyFileFromLocalCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, storage_handle) || !mtp_client_) { |
| callback.Run(true /* error */); |
| return; |
| } |
| copy_file_from_local_callbacks_.push(callback); |
| mtp_client_->CopyFileFromLocal( |
| storage_handle, source_file_descriptor, parent_id, file_name, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnCopyFileFromLocal, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnCopyFileFromLocalError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void DeleteObject(const std::string& storage_handle, |
| const uint32_t object_id, |
| const DeleteObjectCallback& callback) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, storage_handle) || !mtp_client_) { |
| callback.Run(true /* error */); |
| return; |
| } |
| delete_object_callbacks_.push(callback); |
| mtp_client_->DeleteObject( |
| storage_handle, object_id, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnDeleteObject, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnDeleteObjectError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| private: |
| // Map of storage names to storage info. |
| using GetStorageInfoFromDeviceCallbackQueue = |
| base::queue<GetStorageInfoFromDeviceCallback>; |
| // Callback queues - DBus communication is in-order, thus callbacks are |
| // received in the same order as the requests. |
| using OpenStorageCallbackQueue = base::queue<OpenStorageCallback>; |
| // (callback, handle) |
| using CloseStorageCallbackQueue = |
| base::queue<std::pair<CloseStorageCallback, std::string>>; |
| using CreateDirectoryCallbackQueue = base::queue<CreateDirectoryCallback>; |
| using ReadDirectoryCallbackQueue = base::queue<ReadDirectoryCallback>; |
| using ReadFileCallbackQueue = base::queue<ReadFileCallback>; |
| using GetFileInfoCallbackQueue = base::queue<GetFileInfoCallback>; |
| using RenameObjectCallbackQueue = base::queue<RenameObjectCallback>; |
| using CopyFileFromLocalCallbackQueue = base::queue<CopyFileFromLocalCallback>; |
| using DeleteObjectCallbackQueue = base::queue<DeleteObjectCallback>; |
| |
| void OnStorageAttached(const std::string& storage_name) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| mtp_client_->GetStorageInfo( |
| storage_name, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&base::DoNothing)); |
| } |
| |
| void 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 observers phantom detach events. |
| return; |
| } |
| for (auto& observer : observers_) |
| observer.StorageChanged(false /* detach */, storage_name); |
| } |
| |
| void 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 OnEnumerateStorages(const std::vector<std::string>& storage_names) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(mtp_client_); |
| for (const auto& name : storage_names) { |
| if (base::ContainsKey(storage_info_map_, name)) { |
| // OnStorageChanged() might have gotten called first. |
| continue; |
| } |
| OnStorageAttached(name); |
| } |
| } |
| |
| void OnGetStorageInfo(const mojom::MtpStorageInfo& storage_info) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| const std::string& storage_name = storage_info.storage_name; |
| if (base::ContainsKey(storage_info_map_, storage_name)) { |
| // This should not happen, since MediaTransferProtocolManagerImpl 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 observers phantom detach events. |
| NOTREACHED(); |
| return; |
| } |
| |
| // New storage. Add it and let the observers know. |
| storage_info_map_.insert(std::make_pair(storage_name, storage_info)); |
| for (auto& observer : observers_) |
| observer.StorageChanged(true /* is attach */, storage_name); |
| } |
| |
| void OnGetStorageInfoFromDevice(const mojom::MtpStorageInfo& storage_info) { |
| get_storage_info_from_device_callbacks_.front().Run(storage_info, |
| false /* no error */); |
| get_storage_info_from_device_callbacks_.pop(); |
| } |
| |
| void OnGetStorageInfoFromDeviceError() { |
| mojom::MtpStorageInfo info; |
| get_storage_info_from_device_callbacks_.front().Run(info, true /* error */); |
| get_storage_info_from_device_callbacks_.pop(); |
| } |
| |
| void OnOpenStorage(const std::string& handle) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!base::ContainsKey(handles_, handle)) { |
| handles_.insert(handle); |
| open_storage_callbacks_.front().Run(handle, false); |
| } else { |
| NOTREACHED(); |
| open_storage_callbacks_.front().Run(std::string(), true); |
| } |
| open_storage_callbacks_.pop(); |
| } |
| |
| void OnOpenStorageError() { |
| open_storage_callbacks_.front().Run(std::string(), true); |
| open_storage_callbacks_.pop(); |
| } |
| |
| void OnCloseStorage() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| const std::string& handle = close_storage_callbacks_.front().second; |
| if (base::ContainsKey(handles_, handle)) { |
| handles_.erase(handle); |
| close_storage_callbacks_.front().first.Run(false); |
| } else { |
| NOTREACHED(); |
| close_storage_callbacks_.front().first.Run(true); |
| } |
| close_storage_callbacks_.pop(); |
| } |
| |
| void OnCloseStorageError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| close_storage_callbacks_.front().first.Run(true); |
| close_storage_callbacks_.pop(); |
| } |
| |
| void OnCreateDirectory() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| create_directory_callbacks_.front().Run(false /* no error */); |
| create_directory_callbacks_.pop(); |
| } |
| |
| void OnCreateDirectoryError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| create_directory_callbacks_.front().Run(true /* error */); |
| create_directory_callbacks_.pop(); |
| } |
| |
| void OnReadDirectoryEntryIdsToReadDirectory( |
| const std::string& storage_handle, |
| const size_t max_size, |
| const std::vector<uint32_t>& file_ids) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| if (file_ids.empty()) { |
| OnGotDirectoryEntries(storage_handle, file_ids, kInitialOffset, max_size, |
| file_ids, std::vector<mojom::MtpFileEntry>()); |
| return; |
| } |
| |
| std::vector<uint32_t> sorted_file_ids = file_ids; |
| std::sort(sorted_file_ids.begin(), sorted_file_ids.end()); |
| |
| const size_t chunk_size = |
| max_size == 0 ? kFileInfoToFetchChunkSize |
| : std::min(max_size, kFileInfoToFetchChunkSize); |
| |
| mtp_client_->GetFileInfo( |
| storage_handle, file_ids, kInitialOffset, chunk_size, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnGotDirectoryEntries, |
| weak_ptr_factory_.GetWeakPtr(), storage_handle, file_ids, |
| kInitialOffset, max_size, sorted_file_ids), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void OnGotDirectoryEntries( |
| const std::string& storage_handle, |
| const std::vector<uint32_t>& file_ids, |
| const size_t offset, |
| const size_t max_size, |
| const std::vector<uint32_t>& sorted_file_ids, |
| const std::vector<mojom::MtpFileEntry>& file_entries) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK_EQ(file_ids.size(), sorted_file_ids.size()); |
| |
| // Use |sorted_file_ids| to sanity check and make sure the results are a |
| // subset of the requested file ids. |
| for (const auto& entry : file_entries) { |
| std::vector<uint32_t>::const_iterator it = std::lower_bound( |
| sorted_file_ids.begin(), sorted_file_ids.end(), entry.item_id); |
| if (it == sorted_file_ids.end()) { |
| OnReadDirectoryError(); |
| return; |
| } |
| } |
| |
| const size_t directory_size = |
| max_size == 0 ? file_ids.size() : std::min(file_ids.size(), max_size); |
| size_t next_offset = directory_size; |
| if (offset < SIZE_MAX - kFileInfoToFetchChunkSize) |
| next_offset = std::min(next_offset, offset + kFileInfoToFetchChunkSize); |
| bool has_more = next_offset < directory_size; |
| read_directory_callbacks_.front().Run(file_entries, |
| has_more, |
| false /* no error */); |
| |
| if (has_more) { |
| const size_t chunk_size = |
| std::min(directory_size - next_offset, kFileInfoToFetchChunkSize); |
| |
| mtp_client_->GetFileInfo( |
| storage_handle, file_ids, next_offset, chunk_size, |
| base::Bind(&MediaTransferProtocolManagerImpl::OnGotDirectoryEntries, |
| weak_ptr_factory_.GetWeakPtr(), storage_handle, file_ids, |
| next_offset, max_size, sorted_file_ids), |
| base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError, |
| weak_ptr_factory_.GetWeakPtr())); |
| return; |
| } |
| read_directory_callbacks_.pop(); |
| } |
| |
| void OnReadDirectoryError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| read_directory_callbacks_.front().Run(std::vector<mojom::MtpFileEntry>(), |
| false /* no more entries */, |
| true /* error */); |
| read_directory_callbacks_.pop(); |
| } |
| |
| void OnReadFile(const std::string& data) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| read_file_callbacks_.front().Run(data, false); |
| read_file_callbacks_.pop(); |
| } |
| |
| void OnReadFileError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| read_file_callbacks_.front().Run(std::string(), true); |
| read_file_callbacks_.pop(); |
| } |
| |
| void OnGetFileInfo(const std::vector<mojom::MtpFileEntry>& entries) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (entries.size() == 1) { |
| get_file_info_callbacks_.front().Run(entries[0], false /* no error */); |
| get_file_info_callbacks_.pop(); |
| } else { |
| OnGetFileInfoError(); |
| } |
| } |
| |
| void OnGetFileInfoError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| get_file_info_callbacks_.front().Run(mojom::MtpFileEntry(), true); |
| get_file_info_callbacks_.pop(); |
| } |
| |
| void OnRenameObject() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| rename_object_callbacks_.front().Run(false /* no error */); |
| rename_object_callbacks_.pop(); |
| } |
| |
| void OnRenameObjectError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| rename_object_callbacks_.front().Run(true /* error */); |
| rename_object_callbacks_.pop(); |
| } |
| |
| void OnCopyFileFromLocal() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| copy_file_from_local_callbacks_.front().Run(false /* no error */); |
| copy_file_from_local_callbacks_.pop(); |
| } |
| |
| void OnCopyFileFromLocalError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| copy_file_from_local_callbacks_.front().Run(true /* error */); |
| copy_file_from_local_callbacks_.pop(); |
| } |
| |
| void OnDeleteObject() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| delete_object_callbacks_.front().Run(false /* no error */); |
| delete_object_callbacks_.pop(); |
| } |
| |
| void OnDeleteObjectError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| delete_object_callbacks_.front().Run(true /* error */); |
| delete_object_callbacks_.pop(); |
| } |
| |
| // Callback to finish initialization after figuring out if the mtpd service |
| // has an owner, or if the service owner has changed. |
| // |mtpd_service_owner| contains the name of the current owner, if any. |
| void 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::Bind(&MediaTransferProtocolManagerImpl::OnStorageChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| mtp_client_->EnumerateStorages( |
| base::Bind(&MediaTransferProtocolManagerImpl::OnEnumerateStorages, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&base::DoNothing)); |
| } |
| |
| // Mtpd DBus client. |
| std::unique_ptr<MediaTransferProtocolDaemonClient> mtp_client_; |
| |
| // And a D-Bus session for talking to mtpd. Note: In production, this is never |
| // a nullptr, but in tests it oftentimes is. It may be too much work for |
| // DBusThreadManager to provide a bus in unit tests. |
| scoped_refptr<dbus::Bus> const bus_; |
| |
| // Device attachment / detachment observers. |
| base::ObserverList<Observer> observers_; |
| |
| // Map to keep track of attached storages by name. |
| base::flat_map<std::string, mojom::MtpStorageInfo> storage_info_map_; |
| |
| // Set of open storage handles. |
| base::flat_set<std::string> handles_; |
| |
| dbus::Bus::GetServiceOwnerCallback mtpd_owner_changed_callback_; |
| |
| std::string current_mtpd_owner_; |
| |
| // Queued callbacks. |
| GetStorageInfoFromDeviceCallbackQueue get_storage_info_from_device_callbacks_; |
| OpenStorageCallbackQueue open_storage_callbacks_; |
| CloseStorageCallbackQueue close_storage_callbacks_; |
| CreateDirectoryCallbackQueue create_directory_callbacks_; |
| ReadDirectoryCallbackQueue read_directory_callbacks_; |
| ReadFileCallbackQueue read_file_callbacks_; |
| GetFileInfoCallbackQueue get_file_info_callbacks_; |
| RenameObjectCallbackQueue rename_object_callbacks_; |
| CopyFileFromLocalCallbackQueue copy_file_from_local_callbacks_; |
| DeleteObjectCallbackQueue delete_object_callbacks_; |
| |
| base::ThreadChecker thread_checker_; |
| |
| base::WeakPtrFactory<MediaTransferProtocolManagerImpl> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MediaTransferProtocolManagerImpl); |
| }; |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<MediaTransferProtocolManager> |
| MediaTransferProtocolManager::Initialize() { |
| auto manager = std::make_unique<MediaTransferProtocolManagerImpl>(); |
| |
| VLOG(1) << "MediaTransferProtocolManager initialized"; |
| |
| #if DCHECK_IS_ON() |
| DCHECK(!g_media_transfer_protocol_manager); |
| g_media_transfer_protocol_manager = manager.get(); |
| #endif |
| |
| return manager; |
| } |
| |
| } // namespace device |