| // 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 "services/device/media_transfer_protocol/media_transfer_protocol_daemon_client.h" |
| |
| #include <algorithm> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/memory/weak_ptr.h" |
| #include "dbus/bus.h" |
| #include "dbus/message.h" |
| #include "dbus/object_path.h" |
| #include "dbus/object_proxy.h" |
| #include "services/device/media_transfer_protocol/mtp_storage_info.pb.h" |
| #include "services/device/media_transfer_protocol/mtp_file_entry.pb.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| const char kInvalidResponseMsg[] = "Invalid Response: "; |
| uint32_t kMaxChunkSize = 1024 * 1024; // D-Bus has message size limits. |
| |
| mojom::MtpFileEntry GetMojoMtpFileEntryFromProtobuf( |
| const MtpFileEntry& entry) { |
| return mojom::MtpFileEntry( |
| entry.item_id(), |
| entry.parent_id(), |
| entry.file_name(), |
| entry.file_size(), |
| entry.modification_time(), |
| static_cast<mojom::MtpFileEntry::FileType>(entry.file_type())); |
| } |
| |
| mojom::MtpStorageInfo GetMojoMtpStorageInfoFromProtobuf( |
| const MtpStorageInfo& protobuf) { |
| return mojom::MtpStorageInfo( |
| protobuf.storage_name(), |
| protobuf.vendor(), |
| protobuf.vendor_id(), |
| protobuf.product(), |
| protobuf.product_id(), |
| protobuf.device_flags(), |
| protobuf.storage_type(), |
| protobuf.filesystem_type(), |
| protobuf.access_capability(), |
| protobuf.max_capacity(), |
| protobuf.free_space_in_bytes(), |
| protobuf.free_space_in_objects(), |
| protobuf.storage_description(), |
| protobuf.volume_identifier()); |
| } |
| |
| // The MediaTransferProtocolDaemonClient implementation. |
| class MediaTransferProtocolDaemonClientImpl |
| : public MediaTransferProtocolDaemonClient { |
| public: |
| explicit MediaTransferProtocolDaemonClientImpl(dbus::Bus* bus) |
| : proxy_(bus->GetObjectProxy( |
| mtpd::kMtpdServiceName, |
| dbus::ObjectPath(mtpd::kMtpdServicePath))), |
| listen_for_changes_called_(false), |
| weak_ptr_factory_(this) { |
| } |
| |
| // MediaTransferProtocolDaemonClient override. |
| void EnumerateStorages(EnumerateStoragesCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, |
| mtpd::kEnumerateStorages); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce( |
| &MediaTransferProtocolDaemonClientImpl::OnEnumerateStorages, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| // MediaTransferProtocolDaemonClient override. |
| void GetStorageInfo(const std::string& storage_name, |
| GetStorageInfoCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kGetStorageInfo); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(storage_name); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&MediaTransferProtocolDaemonClientImpl::OnGetStorageInfo, |
| weak_ptr_factory_.GetWeakPtr(), storage_name, |
| std::move(callback), std::move(error_callback))); |
| } |
| |
| void GetStorageInfoFromDevice(const std::string& storage_name, |
| GetStorageInfoCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, |
| mtpd::kGetStorageInfoFromDevice); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(storage_name); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&MediaTransferProtocolDaemonClientImpl::OnGetStorageInfo, |
| weak_ptr_factory_.GetWeakPtr(), storage_name, |
| std::move(callback), std::move(error_callback))); |
| } |
| |
| // MediaTransferProtocolDaemonClient override. |
| void OpenStorage(const std::string& storage_name, |
| const std::string& mode, |
| OpenStorageCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kOpenStorage); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(storage_name); |
| DCHECK(mode == mtpd::kReadOnlyMode || mode == mtpd::kReadWriteMode); |
| writer.AppendString(mode); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&MediaTransferProtocolDaemonClientImpl::OnOpenStorage, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| // MediaTransferProtocolDaemonClient override. |
| void CloseStorage(const std::string& handle, |
| CloseStorageCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kCloseStorage); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(handle); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&MediaTransferProtocolDaemonClientImpl::OnCloseStorage, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| void CreateDirectory(const std::string& handle, |
| const uint32_t parent_id, |
| const std::string& directory_name, |
| CreateDirectoryCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kCreateDirectory); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(handle); |
| writer.AppendUint32(parent_id); |
| writer.AppendString(directory_name); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce( |
| &MediaTransferProtocolDaemonClientImpl::OnCreateDirectory, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| // MediaTransferProtocolDaemonClient override. |
| void ReadDirectoryEntryIds(const std::string& handle, |
| uint32_t file_id, |
| ReadDirectoryEntryIdsCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, |
| mtpd::kReadDirectoryEntryIds); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(handle); |
| writer.AppendUint32(file_id); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce( |
| &MediaTransferProtocolDaemonClientImpl::OnReadDirectoryIds, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| void GetFileInfo(const std::string& handle, |
| const std::vector<uint32_t>& file_ids, |
| GetFileInfoCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kGetFileInfo); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(handle); |
| { |
| dbus::MessageWriter array_writer(nullptr); |
| writer.OpenArray("u", &array_writer); |
| |
| for (uint32_t file_id : file_ids) |
| array_writer.AppendUint32(file_id); |
| |
| writer.CloseContainer(&array_writer); |
| } |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&MediaTransferProtocolDaemonClientImpl::OnGetFileInfo, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| // MediaTransferProtocolDaemonClient override. |
| void ReadFileChunk(const std::string& handle, |
| uint32_t file_id, |
| uint32_t offset, |
| uint32_t bytes_to_read, |
| ReadFileCallback callback, |
| ErrorCallback error_callback) override { |
| DCHECK_LE(bytes_to_read, kMaxChunkSize); |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kReadFileChunk); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(handle); |
| writer.AppendUint32(file_id); |
| writer.AppendUint32(offset); |
| writer.AppendUint32(bytes_to_read); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&MediaTransferProtocolDaemonClientImpl::OnReadFile, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| void RenameObject(const std::string& handle, |
| const uint32_t object_id, |
| const std::string& new_name, |
| RenameObjectCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kRenameObject); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(handle); |
| writer.AppendUint32(object_id); |
| writer.AppendString(new_name); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&MediaTransferProtocolDaemonClientImpl::OnRenameObject, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| void CopyFileFromLocal(const std::string& handle, |
| const int source_file_descriptor, |
| const uint32_t parent_id, |
| const std::string& file_name, |
| CopyFileFromLocalCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, |
| mtpd::kCopyFileFromLocal); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(handle); |
| writer.AppendFileDescriptor(source_file_descriptor); |
| writer.AppendUint32(parent_id); |
| writer.AppendString(file_name); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_INFINITE, |
| base::BindOnce( |
| &MediaTransferProtocolDaemonClientImpl::OnCopyFileFromLocal, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| void DeleteObject(const std::string& handle, |
| const uint32_t object_id, |
| DeleteObjectCallback callback, |
| ErrorCallback error_callback) override { |
| dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kDeleteObject); |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(handle); |
| writer.AppendUint32(object_id); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&MediaTransferProtocolDaemonClientImpl::OnDeleteObject, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| std::move(error_callback))); |
| } |
| |
| // MediaTransferProtocolDaemonClient override. |
| void ListenForChanges(const MTPStorageEventHandler& handler) override { |
| DCHECK(!listen_for_changes_called_); |
| listen_for_changes_called_ = true; |
| |
| static const SignalEventTuple kSignalEventTuples[] = { |
| { mtpd::kMTPStorageAttached, true }, |
| { mtpd::kMTPStorageDetached, false }, |
| }; |
| for (const auto& event : kSignalEventTuples) { |
| proxy_->ConnectToSignal( |
| mtpd::kMtpdInterface, event.signal_name, |
| base::Bind(&MediaTransferProtocolDaemonClientImpl::OnMTPStorageSignal, |
| weak_ptr_factory_.GetWeakPtr(), handler, event.is_attach), |
| base::Bind(&MediaTransferProtocolDaemonClientImpl::OnSignalConnected, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| } |
| |
| private: |
| // A struct to contain a pair of signal name and attachment event type. |
| // Used by SetUpConnections. |
| struct SignalEventTuple { |
| const char* signal_name; |
| bool is_attach; |
| }; |
| |
| // Handles the result of EnumerateStorages and calls |callback| or |
| // |error_callback|. |
| void OnEnumerateStorages(EnumerateStoragesCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| dbus::MessageReader reader(response); |
| std::vector<std::string> storage_names; |
| if (!reader.PopArrayOfStrings(&storage_names)) { |
| LOG(ERROR) << kInvalidResponseMsg << response->ToString(); |
| std::move(error_callback).Run(); |
| return; |
| } |
| std::move(callback).Run(storage_names); |
| } |
| |
| // Handles the result of GetStorageInfo and calls |callback| or |
| // |error_callback|. |
| void OnGetStorageInfo(const std::string& storage_name, |
| GetStorageInfoCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| dbus::MessageReader reader(response); |
| MtpStorageInfo protobuf; |
| if (!reader.PopArrayOfBytesAsProto(&protobuf)) { |
| LOG(ERROR) << kInvalidResponseMsg << response->ToString(); |
| std::move(error_callback).Run(); |
| return; |
| } |
| std::move(callback).Run(GetMojoMtpStorageInfoFromProtobuf(protobuf)); |
| } |
| |
| // Handles the result of OpenStorage and calls |callback| or |error_callback|. |
| void OnOpenStorage(OpenStorageCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| dbus::MessageReader reader(response); |
| std::string handle; |
| if (!reader.PopString(&handle)) { |
| LOG(ERROR) << kInvalidResponseMsg << response->ToString(); |
| std::move(error_callback).Run(); |
| return; |
| } |
| std::move(callback).Run(handle); |
| } |
| |
| // Handles the result of CloseStorage and calls |callback| or |
| // |error_callback|. |
| void OnCloseStorage(CloseStorageCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| std::move(callback).Run(); |
| } |
| |
| void OnCreateDirectory(CreateDirectoryCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| std::move(callback).Run(); |
| } |
| |
| // Handles the result of ReadDirectoryEntryIds and calls |callback| or |
| // |error_callback|. |
| void OnReadDirectoryIds(ReadDirectoryEntryIdsCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| dbus::MessageReader reader(response); |
| dbus::MessageReader array_reader(nullptr); |
| if (!reader.PopArray(&array_reader) || reader.HasMoreData()) { |
| LOG(ERROR) << kInvalidResponseMsg << response->ToString(); |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| std::vector<uint32_t> file_ids; |
| while (array_reader.HasMoreData()) { |
| uint32_t file_id; |
| if (!array_reader.PopUint32(&file_id)) { |
| LOG(ERROR) << kInvalidResponseMsg << response->ToString(); |
| std::move(error_callback).Run(); |
| return; |
| } |
| file_ids.push_back(file_id); |
| } |
| std::move(callback).Run(file_ids); |
| } |
| |
| // Handles the result of GetFileInfo and calls |callback| or |error_callback|. |
| void OnGetFileInfo(GetFileInfoCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| dbus::MessageReader reader(response); |
| MtpFileEntries entries_protobuf; |
| if (!reader.PopArrayOfBytesAsProto(&entries_protobuf)) { |
| LOG(ERROR) << kInvalidResponseMsg << response->ToString(); |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| std::vector<mojom::MtpFileEntry> file_entries; |
| file_entries.reserve(entries_protobuf.file_entries_size()); |
| for (int i = 0; i < entries_protobuf.file_entries_size(); ++i) { |
| const auto& entry = entries_protobuf.file_entries(i); |
| file_entries.push_back( |
| GetMojoMtpFileEntryFromProtobuf(entry)); |
| } |
| std::move(callback).Run(file_entries); |
| } |
| |
| // Handles the result of ReadFileChunk and calls |callback| or |
| // |error_callback|. |
| void OnReadFile(ReadFileCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| const uint8_t* data_bytes = nullptr; |
| size_t data_length = 0; |
| dbus::MessageReader reader(response); |
| if (!reader.PopArrayOfBytes(&data_bytes, &data_length)) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| std::string data(reinterpret_cast<const char*>(data_bytes), data_length); |
| std::move(callback).Run(data); |
| } |
| |
| void OnRenameObject(RenameObjectCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| std::move(callback).Run(); |
| } |
| |
| void OnCopyFileFromLocal(CopyFileFromLocalCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| std::move(callback).Run(); |
| } |
| |
| void OnDeleteObject(DeleteObjectCallback callback, |
| ErrorCallback error_callback, |
| dbus::Response* response) { |
| if (!response) { |
| std::move(error_callback).Run(); |
| return; |
| } |
| |
| std::move(callback).Run(); |
| } |
| |
| // Handles MTPStorageAttached/Dettached signals and calls |handler|. |
| void OnMTPStorageSignal(MTPStorageEventHandler handler, |
| bool is_attach, |
| dbus::Signal* signal) { |
| dbus::MessageReader reader(signal); |
| std::string storage_name; |
| if (!reader.PopString(&storage_name)) { |
| LOG(ERROR) << "Invalid signal: " << signal->ToString(); |
| return; |
| } |
| DCHECK(!storage_name.empty()); |
| handler.Run(is_attach, storage_name); |
| } |
| |
| |
| // Handles the result of signal connection setup. |
| void OnSignalConnected(const std::string& interface, |
| const std::string& signal, |
| bool succeeded) { |
| LOG_IF(ERROR, !succeeded) << "Connect to " << interface << " " |
| << signal << " failed."; |
| } |
| |
| dbus::ObjectProxy* const proxy_; |
| |
| bool listen_for_changes_called_; |
| |
| // Note: This should remain the last member so it'll be destroyed and |
| // invalidate its weak pointers before any other members are destroyed. |
| base::WeakPtrFactory<MediaTransferProtocolDaemonClientImpl> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MediaTransferProtocolDaemonClientImpl); |
| }; |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // MediaTransferProtocolDaemonClient |
| |
| MediaTransferProtocolDaemonClient::MediaTransferProtocolDaemonClient() = |
| default; |
| |
| MediaTransferProtocolDaemonClient::~MediaTransferProtocolDaemonClient() = |
| default; |
| |
| // static |
| std::unique_ptr<MediaTransferProtocolDaemonClient> |
| MediaTransferProtocolDaemonClient::Create(dbus::Bus* bus) { |
| return std::make_unique<MediaTransferProtocolDaemonClientImpl>(bus); |
| } |
| |
| } // namespace device |