diff options
Diffstat (limited to 'device/media_transfer_protocol/media_transfer_protocol_manager.cc')
-rw-r--r-- | device/media_transfer_protocol/media_transfer_protocol_manager.cc | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/device/media_transfer_protocol/media_transfer_protocol_manager.cc b/device/media_transfer_protocol/media_transfer_protocol_manager.cc new file mode 100644 index 0000000..dba7d18 --- /dev/null +++ b/device/media_transfer_protocol/media_transfer_protocol_manager.cc @@ -0,0 +1,453 @@ +// 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 <map> +#include <queue> +#include <set> +#include <utility> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "base/stl_util.h" +#include "chrome/common/chrome_switches.h" +#include "content/public/browser/browser_thread.h" +#include "device/media_transfer_protocol/media_transfer_protocol_daemon_client.h" +#include "device/media_transfer_protocol/mtp_file_entry.pb.h" +#include "device/media_transfer_protocol/mtp_storage_info.pb.h" + +#if defined(OS_CHROMEOS) +#include "chromeos/dbus/dbus_thread_manager.h" +#else +#include "dbus/bus.h" +#endif + +using content::BrowserThread; + +namespace device { + +namespace { + +MediaTransferProtocolManager* g_media_transfer_protocol_manager = NULL; + +// The MediaTransferProtocolManager implementation. +class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { + public: + MediaTransferProtocolManagerImpl() : weak_ptr_factory_(this) { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) + return; + + dbus::Bus* bus = NULL; +#if defined(OS_CHROMEOS) + chromeos::DBusThreadManager* dbus_thread_manager = + chromeos::DBusThreadManager::Get(); + bus = dbus_thread_manager->GetSystemBus(); + if (!bus) + return; +#else + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + options.connection_type = dbus::Bus::PRIVATE; + options.dbus_thread_message_loop_proxy = + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE); + session_bus_ = new dbus::Bus(options); + bus = session_bus_.get(); +#endif + + DCHECK(bus); + mtp_client_.reset( + MediaTransferProtocolDaemonClient::Create(bus, false /* not stub */)); + + // Set up signals and start initializing |storage_info_map_|. + mtp_client_->SetUpConnections( + base::Bind(&MediaTransferProtocolManagerImpl::OnStorageChanged, + weak_ptr_factory_.GetWeakPtr())); + mtp_client_->EnumerateStorages( + base::Bind(&MediaTransferProtocolManagerImpl::OnEnumerateStorages, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&base::DoNothing)); + } + + virtual ~MediaTransferProtocolManagerImpl() { + } + + // MediaTransferProtocolManager override. + virtual void AddObserver(Observer* observer) OVERRIDE { + observers_.AddObserver(observer); + } + + // MediaTransferProtocolManager override. + virtual void RemoveObserver(Observer* observer) OVERRIDE { + observers_.RemoveObserver(observer); + } + + // MediaTransferProtocolManager override. + const std::vector<std::string> GetStorages() const OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + std::vector<std::string> storages; + for (StorageInfoMap::const_iterator it = storage_info_map_.begin(); + it != storage_info_map_.end(); + ++it) { + storages.push_back(it->first); + } + return storages; + } + + // MediaTransferProtocolManager override. + virtual const MtpStorageInfo* GetStorageInfo( + const std::string& storage_name) const OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + StorageInfoMap::const_iterator it = storage_info_map_.find(storage_name); + if (it == storage_info_map_.end()) + return NULL; + return &it->second; + } + + // MediaTransferProtocolManager override. + virtual void OpenStorage(const std::string& storage_name, + const std::string& mode, + const OpenStorageCallback& callback) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ContainsKey(storage_info_map_, storage_name)) { + callback.Run("", 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. + virtual void CloseStorage(const std::string& storage_handle, + const CloseStorageCallback& callback) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ContainsKey(handles_, storage_handle)) { + 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())); + } + + // MediaTransferProtocolManager override. + virtual void ReadDirectoryByPath( + const std::string& storage_handle, + const std::string& path, + const ReadDirectoryCallback& callback) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ContainsKey(handles_, storage_handle)) { + callback.Run(std::vector<MtpFileEntry>(), true); + return; + } + read_directory_callbacks_.push(callback); + mtp_client_->ReadDirectoryByPath( + storage_handle, + path, + base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectory, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError, + weak_ptr_factory_.GetWeakPtr())); + } + + // MediaTransferProtocolManager override. + virtual void ReadDirectoryById( + const std::string& storage_handle, + uint32 file_id, + const ReadDirectoryCallback& callback) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ContainsKey(handles_, storage_handle)) { + callback.Run(std::vector<MtpFileEntry>(), true); + return; + } + read_directory_callbacks_.push(callback); + mtp_client_->ReadDirectoryById( + storage_handle, + file_id, + base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectory, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError, + weak_ptr_factory_.GetWeakPtr())); + } + + // MediaTransferProtocolManager override. + virtual void ReadFileChunkByPath(const std::string& storage_handle, + const std::string& path, + uint32 offset, + uint32 count, + const ReadFileCallback& callback) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ContainsKey(handles_, storage_handle)) { + callback.Run(std::string(), true); + return; + } + read_file_callbacks_.push(callback); + mtp_client_->ReadFileChunkByPath( + storage_handle, path, offset, count, + base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError, + weak_ptr_factory_.GetWeakPtr())); + } + + // MediaTransferProtocolManager override. + virtual void ReadFileChunkById(const std::string& storage_handle, + uint32 file_id, + uint32 offset, + uint32 count, + const ReadFileCallback& callback) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ContainsKey(handles_, storage_handle)) { + callback.Run(std::string(), true); + return; + } + read_file_callbacks_.push(callback); + mtp_client_->ReadFileChunkById( + storage_handle, file_id, offset, count, + base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError, + weak_ptr_factory_.GetWeakPtr())); + } + + virtual void GetFileInfoByPath(const std::string& storage_handle, + const std::string& path, + const GetFileInfoCallback& callback) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ContainsKey(handles_, storage_handle)) { + callback.Run(MtpFileEntry(), true); + return; + } + get_file_info_callbacks_.push(callback); + mtp_client_->GetFileInfoByPath( + storage_handle, + path, + base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError, + weak_ptr_factory_.GetWeakPtr())); + } + + virtual void GetFileInfoById(const std::string& storage_handle, + uint32 file_id, + const GetFileInfoCallback& callback) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ContainsKey(handles_, storage_handle)) { + callback.Run(MtpFileEntry(), true); + return; + } + get_file_info_callbacks_.push(callback); + mtp_client_->GetFileInfoById( + storage_handle, + file_id, + base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError, + weak_ptr_factory_.GetWeakPtr())); + } + + private: + // Map of storage names to storage info. + typedef std::map<std::string, MtpStorageInfo> StorageInfoMap; + // Callback queues - DBus communication is in-order, thus callbacks are + // received in the same order as the requests. + typedef std::queue<OpenStorageCallback> OpenStorageCallbackQueue; + // (callback, handle) + typedef std::queue<std::pair<CloseStorageCallback, std::string> + > CloseStorageCallbackQueue; + typedef std::queue<ReadDirectoryCallback> ReadDirectoryCallbackQueue; + typedef std::queue<ReadFileCallback> ReadFileCallbackQueue; + typedef std::queue<GetFileInfoCallback> GetFileInfoCallbackQueue; + + void OnStorageChanged(bool is_attach, const std::string& storage_name) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (is_attach) { + mtp_client_->GetStorageInfo( + storage_name, + base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&base::DoNothing)); + return; + } + + // Detach case. + StorageInfoMap::iterator it = storage_info_map_.find(storage_name); + if (it == storage_info_map_.end()) { + // This might happen during initialization when |storage_info_map_| has + // not been fully populated yet? + return; + } + storage_info_map_.erase(it); + FOR_EACH_OBSERVER(Observer, + observers_, + StorageChanged(false /* detach */, storage_name)); + } + + void OnEnumerateStorages(const std::vector<std::string>& storage_names) { + for (size_t i = 0; i < storage_names.size(); ++i) { + mtp_client_->GetStorageInfo( + storage_names[i], + base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&base::DoNothing)); + } + } + + void OnGetStorageInfo(const MtpStorageInfo& storage_info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const std::string& storage_name = storage_info.storage_name(); + if (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 detachements, which do not add to |storage_info_map_|. + NOTREACHED(); + return; + } + + // New storage. Add it and let the observers know. + storage_info_map_.insert(std::make_pair(storage_name, storage_info)); + FOR_EACH_OBSERVER(Observer, + observers_, + StorageChanged(true /* is attach */, storage_name)); + } + + void OnOpenStorage(const std::string& handle) { + if (!ContainsKey(handles_, handle)) { + handles_.insert(handle); + open_storage_callbacks_.front().Run(handle, false); + } else { + NOTREACHED(); + open_storage_callbacks_.front().Run("", true); + } + open_storage_callbacks_.pop(); + } + + void OnOpenStorageError() { + open_storage_callbacks_.front().Run("", true); + open_storage_callbacks_.pop(); + } + + void OnCloseStorage() { + const std::string& handle = close_storage_callbacks_.front().second; + if (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() { + close_storage_callbacks_.front().first.Run(true); + close_storage_callbacks_.pop(); + } + + void OnReadDirectory(const std::vector<MtpFileEntry>& file_entries) { + read_directory_callbacks_.front().Run(file_entries, false); + read_directory_callbacks_.pop(); + } + + void OnReadDirectoryError() { + read_directory_callbacks_.front().Run(std::vector<MtpFileEntry>(), true); + read_directory_callbacks_.pop(); + } + + void OnReadFile(const std::string& data) { + read_file_callbacks_.front().Run(data, false); + read_file_callbacks_.pop(); + } + + void OnReadFileError() { + read_file_callbacks_.front().Run(std::string(), true); + read_file_callbacks_.pop(); + } + + void OnGetFileInfo(const MtpFileEntry& entry) { + get_file_info_callbacks_.front().Run(entry, false); + get_file_info_callbacks_.pop(); + } + + void OnGetFileInfoError() { + get_file_info_callbacks_.front().Run(MtpFileEntry(), true); + get_file_info_callbacks_.pop(); + } + + // Mtpd DBus client. + scoped_ptr<MediaTransferProtocolDaemonClient> mtp_client_; + +#if !defined(OS_CHROMEOS) + // And a D-Bus session for talking to mtpd. + scoped_refptr<dbus::Bus> session_bus_; +#endif + + // Device attachment / detachment observers. + ObserverList<Observer> observers_; + + base::WeakPtrFactory<MediaTransferProtocolManagerImpl> weak_ptr_factory_; + + // Everything below is only accessed on the UI thread. + + // Map to keep track of attached storages by name. + StorageInfoMap storage_info_map_; + + // Set of open storage handles. + std::set<std::string> handles_; + + // Queued callbacks. + OpenStorageCallbackQueue open_storage_callbacks_; + CloseStorageCallbackQueue close_storage_callbacks_; + ReadDirectoryCallbackQueue read_directory_callbacks_; + ReadFileCallbackQueue read_file_callbacks_; + GetFileInfoCallbackQueue get_file_info_callbacks_; + + DISALLOW_COPY_AND_ASSIGN(MediaTransferProtocolManagerImpl); +}; + +} // namespace + +// static +void MediaTransferProtocolManager::Initialize() { + if (g_media_transfer_protocol_manager) { + LOG(WARNING) << "MediaTransferProtocolManager was already initialized"; + return; + } + g_media_transfer_protocol_manager = new MediaTransferProtocolManagerImpl(); + VLOG(1) << "MediaTransferProtocolManager initialized"; +} + +// static +void MediaTransferProtocolManager::Shutdown() { + if (!g_media_transfer_protocol_manager) { + LOG(WARNING) << "MediaTransferProtocolManager::Shutdown() called with " + << "NULL manager"; + return; + } + delete g_media_transfer_protocol_manager; + g_media_transfer_protocol_manager = NULL; + VLOG(1) << "MediaTransferProtocolManager Shutdown completed"; +} + +// static +MediaTransferProtocolManager* MediaTransferProtocolManager::GetInstance() { + return g_media_transfer_protocol_manager; +} + +} // namespace device |