// 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_daemon_client.h" #include #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 "device/media_transfer_protocol/mtp_file_entry.pb.h" #include "device/media_transfer_protocol/mtp_storage_info.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. // 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(const EnumerateStoragesCallback& callback, const ErrorCallback& error_callback) override { dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kEnumerateStorages); proxy_->CallMethod( &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, base::Bind(&MediaTransferProtocolDaemonClientImpl::OnEnumerateStorages, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } // MediaTransferProtocolDaemonClient override. void GetStorageInfo(const std::string& storage_name, const GetStorageInfoCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnGetStorageInfo, weak_ptr_factory_.GetWeakPtr(), storage_name, callback, error_callback)); } void GetStorageInfoFromDevice(const std::string& storage_name, const GetStorageInfoCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnGetStorageInfo, weak_ptr_factory_.GetWeakPtr(), storage_name, callback, error_callback)); } // MediaTransferProtocolDaemonClient override. void OpenStorage(const std::string& storage_name, const std::string& mode, const OpenStorageCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnOpenStorage, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } // MediaTransferProtocolDaemonClient override. void CloseStorage(const std::string& handle, const CloseStorageCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnCloseStorage, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } void CreateDirectory(const std::string& handle, const uint32_t parent_id, const std::string& directory_name, const CreateDirectoryCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnCreateDirectory, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } // MediaTransferProtocolDaemonClient override. void ReadDirectoryEntryIds(const std::string& handle, uint32_t file_id, const ReadDirectoryEntryIdsCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnReadDirectoryIds, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } void GetFileInfo(const std::string& handle, const std::vector& file_ids, size_t offset, size_t entries_to_read, const GetFileInfoCallback& callback, const ErrorCallback& error_callback) override { if (offset >= file_ids.size()) { error_callback.Run(); return; } dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kGetFileInfo); dbus::MessageWriter writer(&method_call); writer.AppendString(handle); { dbus::MessageWriter array_writer(NULL); writer.OpenArray("u", &array_writer); size_t end_offset = file_ids.size(); if (offset <= SIZE_MAX - entries_to_read) end_offset = std::min(end_offset, offset + entries_to_read); for (size_t i = offset; i < end_offset; ++i) array_writer.AppendUint32(file_ids[i]); writer.CloseContainer(&array_writer); } proxy_->CallMethod( &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, base::Bind(&MediaTransferProtocolDaemonClientImpl::OnGetFileInfo, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } // MediaTransferProtocolDaemonClient override. void ReadFileChunk(const std::string& handle, uint32_t file_id, uint32_t offset, uint32_t bytes_to_read, const ReadFileCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnReadFile, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } void RenameObject(const std::string& handle, const uint32_t object_id, const std::string& new_name, const RenameObjectCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnRenameObject, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } void CopyFileFromLocal(const std::string& handle, const int source_file_descriptor, const uint32_t parent_id, const std::string& file_name, const CopyFileFromLocalCallback& callback, const ErrorCallback& error_callback) override { dbus::FileDescriptor file_descriptor(source_file_descriptor); file_descriptor.CheckValidity(); dbus::MethodCall method_call(mtpd::kMtpdInterface, mtpd::kCopyFileFromLocal); dbus::MessageWriter writer(&method_call); writer.AppendString(handle); writer.AppendFileDescriptor(file_descriptor); writer.AppendUint32(parent_id); writer.AppendString(file_name); proxy_->CallMethod( &method_call, dbus::ObjectProxy::TIMEOUT_INFINITE, base::Bind(&MediaTransferProtocolDaemonClientImpl::OnCopyFileFromLocal, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } void DeleteObject(const std::string& handle, const uint32_t object_id, const DeleteObjectCallback& callback, const 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::Bind(&MediaTransferProtocolDaemonClientImpl::OnDeleteObject, weak_ptr_factory_.GetWeakPtr(), callback, 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 }, }; const size_t kNumSignalEventTuples = arraysize(kSignalEventTuples); for (size_t i = 0; i < kNumSignalEventTuples; ++i) { proxy_->ConnectToSignal( mtpd::kMtpdInterface, kSignalEventTuples[i].signal_name, base::Bind(&MediaTransferProtocolDaemonClientImpl::OnMTPStorageSignal, weak_ptr_factory_.GetWeakPtr(), handler, kSignalEventTuples[i].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(const EnumerateStoragesCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } dbus::MessageReader reader(response); std::vector storage_names; if (!reader.PopArrayOfStrings(&storage_names)) { LOG(ERROR) << kInvalidResponseMsg << response->ToString(); error_callback.Run(); return; } callback.Run(storage_names); } // Handles the result of GetStorageInfo and calls |callback| or // |error_callback|. void OnGetStorageInfo(const std::string& storage_name, const GetStorageInfoCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } dbus::MessageReader reader(response); MtpStorageInfo protobuf; if (!reader.PopArrayOfBytesAsProto(&protobuf)) { LOG(ERROR) << kInvalidResponseMsg << response->ToString(); error_callback.Run(); return; } callback.Run(protobuf); } // Handles the result of OpenStorage and calls |callback| or |error_callback|. void OnOpenStorage(const OpenStorageCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } dbus::MessageReader reader(response); std::string handle; if (!reader.PopString(&handle)) { LOG(ERROR) << kInvalidResponseMsg << response->ToString(); error_callback.Run(); return; } callback.Run(handle); } // Handles the result of CloseStorage and calls |callback| or // |error_callback|. void OnCloseStorage(const CloseStorageCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } callback.Run(); } void OnCreateDirectory(const CreateDirectoryCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } callback.Run(); } // Handles the result of ReadDirectoryEntryIds and calls |callback| or // |error_callback|. void OnReadDirectoryIds(const ReadDirectoryEntryIdsCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } std::vector file_ids; dbus::MessageReader reader(response); dbus::MessageReader array_reader(NULL); if (!reader.PopArray(&array_reader) || reader.HasMoreData()) { LOG(ERROR) << kInvalidResponseMsg << response->ToString(); error_callback.Run(); return; } while (array_reader.HasMoreData()) { uint32_t file_id; if (array_reader.PopUint32(&file_id)) { file_ids.push_back(file_id); } else { LOG(ERROR) << kInvalidResponseMsg << response->ToString(); error_callback.Run(); return; } } callback.Run(file_ids); } // Handles the result of GetFileInfo and calls |callback| or |error_callback|. void OnGetFileInfo(const GetFileInfoCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } std::vector file_entries; dbus::MessageReader reader(response); MtpFileEntries entries_protobuf; if (!reader.PopArrayOfBytesAsProto(&entries_protobuf)) { LOG(ERROR) << kInvalidResponseMsg << response->ToString(); error_callback.Run(); return; } for (int i = 0; i < entries_protobuf.file_entries_size(); ++i) file_entries.push_back(entries_protobuf.file_entries(i)); callback.Run(file_entries); } // Handles the result of ReadFileChunk and calls |callback| or // |error_callback|. void OnReadFile(const ReadFileCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } const uint8_t* data_bytes = NULL; size_t data_length = 0; dbus::MessageReader reader(response); if (!reader.PopArrayOfBytes(&data_bytes, &data_length)) { error_callback.Run(); return; } std::string data(reinterpret_cast(data_bytes), data_length); callback.Run(data); } void OnRenameObject(const RenameObjectCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } callback.Run(); } void OnCopyFileFromLocal(const CopyFileFromLocalCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } callback.Run(); } void OnDeleteObject(const DeleteObjectCallback& callback, const ErrorCallback& error_callback, dbus::Response* response) { if (!response) { error_callback.Run(); return; } 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* 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 weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(MediaTransferProtocolDaemonClientImpl); }; } // namespace //////////////////////////////////////////////////////////////////////////////// // MediaTransferProtocolDaemonClient MediaTransferProtocolDaemonClient::MediaTransferProtocolDaemonClient() {} MediaTransferProtocolDaemonClient::~MediaTransferProtocolDaemonClient() {} // static MediaTransferProtocolDaemonClient* MediaTransferProtocolDaemonClient::Create( dbus::Bus* bus) { return new MediaTransferProtocolDaemonClientImpl(bus); } } // namespace device