1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/location.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/observer_list.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/stl_util.h"
19 #include "base/threading/thread_checker.h"
21 #include "device/media_transfer_protocol/media_transfer_protocol_daemon_client.h"
22 #include "device/media_transfer_protocol/mtp_file_entry.pb.h"
23 #include "device/media_transfer_protocol/mtp_storage_info.pb.h"
24 #include "third_party/cros_system_api/dbus/service_constants.h"
26 #if defined(OS_CHROMEOS)
27 #include "chromeos/dbus/dbus_thread_manager.h"
34 MediaTransferProtocolManager
* g_media_transfer_protocol_manager
= NULL
;
36 // The MediaTransferProtocolManager implementation.
37 class MediaTransferProtocolManagerImpl
: public MediaTransferProtocolManager
{
39 explicit MediaTransferProtocolManagerImpl(
40 scoped_refptr
<base::SequencedTaskRunner
> task_runner
)
41 : weak_ptr_factory_(this) {
42 #if defined(OS_CHROMEOS)
43 DCHECK(!task_runner
.get());
45 DCHECK(task_runner
.get());
46 dbus::Bus::Options options
;
47 options
.bus_type
= dbus::Bus::SYSTEM
;
48 options
.connection_type
= dbus::Bus::PRIVATE
;
49 options
.dbus_task_runner
= task_runner
;
50 session_bus_
= new dbus::Bus(options
);
53 // Listen for future mtpd service owner changes, in case it is not
54 // available right now. There is no guarantee on Linux or ChromeOS that
55 // mtpd is running already.
56 mtpd_owner_changed_callback_
=
57 base::Bind(&MediaTransferProtocolManagerImpl::FinishSetupOnOriginThread
,
58 weak_ptr_factory_
.GetWeakPtr());
59 GetBus()->ListenForServiceOwnerChange(mtpd::kMtpdServiceName
,
60 mtpd_owner_changed_callback_
);
61 GetBus()->GetServiceOwner(mtpd::kMtpdServiceName
,
62 mtpd_owner_changed_callback_
);
65 virtual ~MediaTransferProtocolManagerImpl() {
66 DCHECK(g_media_transfer_protocol_manager
);
67 g_media_transfer_protocol_manager
= NULL
;
68 GetBus()->UnlistenForServiceOwnerChange(mtpd::kMtpdServiceName
,
69 mtpd_owner_changed_callback_
);
71 #if !defined(OS_CHROMEOS)
72 session_bus_
->PostTaskToDBusThread(
73 FROM_HERE
, base::Bind(&dbus::Bus::ShutdownAndBlock
, session_bus_
));
76 VLOG(1) << "MediaTransferProtocolManager Shutdown completed";
79 // MediaTransferProtocolManager override.
80 virtual void AddObserver(Observer
* observer
) OVERRIDE
{
81 observers_
.AddObserver(observer
);
84 // MediaTransferProtocolManager override.
85 virtual void RemoveObserver(Observer
* observer
) OVERRIDE
{
86 observers_
.RemoveObserver(observer
);
89 // MediaTransferProtocolManager override.
90 virtual const std::vector
<std::string
> GetStorages() const OVERRIDE
{
91 DCHECK(thread_checker_
.CalledOnValidThread());
92 std::vector
<std::string
> storages
;
93 for (StorageInfoMap::const_iterator it
= storage_info_map_
.begin();
94 it
!= storage_info_map_
.end();
96 storages
.push_back(it
->first
);
101 // MediaTransferProtocolManager override.
102 virtual const MtpStorageInfo
* GetStorageInfo(
103 const std::string
& storage_name
) const OVERRIDE
{
104 DCHECK(thread_checker_
.CalledOnValidThread());
105 StorageInfoMap::const_iterator it
= storage_info_map_
.find(storage_name
);
106 return it
!= storage_info_map_
.end() ? &it
->second
: NULL
;
109 // MediaTransferProtocolManager override.
110 virtual void OpenStorage(const std::string
& storage_name
,
111 const std::string
& mode
,
112 const OpenStorageCallback
& callback
) OVERRIDE
{
113 DCHECK(thread_checker_
.CalledOnValidThread());
114 if (!ContainsKey(storage_info_map_
, storage_name
) || !mtp_client_
) {
115 callback
.Run(std::string(), true);
118 open_storage_callbacks_
.push(callback
);
119 mtp_client_
->OpenStorage(
122 base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorage
,
123 weak_ptr_factory_
.GetWeakPtr()),
124 base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorageError
,
125 weak_ptr_factory_
.GetWeakPtr()));
128 // MediaTransferProtocolManager override.
129 virtual void CloseStorage(const std::string
& storage_handle
,
130 const CloseStorageCallback
& callback
) OVERRIDE
{
131 DCHECK(thread_checker_
.CalledOnValidThread());
132 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
136 close_storage_callbacks_
.push(std::make_pair(callback
, storage_handle
));
137 mtp_client_
->CloseStorage(
139 base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorage
,
140 weak_ptr_factory_
.GetWeakPtr()),
141 base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorageError
,
142 weak_ptr_factory_
.GetWeakPtr()));
145 // MediaTransferProtocolManager override.
146 virtual void ReadDirectoryByPath(
147 const std::string
& storage_handle
,
148 const std::string
& path
,
149 const ReadDirectoryCallback
& callback
) OVERRIDE
{
150 DCHECK(thread_checker_
.CalledOnValidThread());
151 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
152 callback
.Run(std::vector
<MtpFileEntry
>(), true);
155 read_directory_callbacks_
.push(callback
);
156 mtp_client_
->ReadDirectoryByPath(
159 base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectory
,
160 weak_ptr_factory_
.GetWeakPtr()),
161 base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError
,
162 weak_ptr_factory_
.GetWeakPtr()));
165 // MediaTransferProtocolManager override.
166 virtual void ReadDirectoryById(
167 const std::string
& storage_handle
,
169 const ReadDirectoryCallback
& callback
) OVERRIDE
{
170 DCHECK(thread_checker_
.CalledOnValidThread());
171 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
172 callback
.Run(std::vector
<MtpFileEntry
>(), true);
175 read_directory_callbacks_
.push(callback
);
176 mtp_client_
->ReadDirectoryById(
179 base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectory
,
180 weak_ptr_factory_
.GetWeakPtr()),
181 base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError
,
182 weak_ptr_factory_
.GetWeakPtr()));
185 // MediaTransferProtocolManager override.
186 virtual void ReadFileChunkByPath(const std::string
& storage_handle
,
187 const std::string
& path
,
190 const ReadFileCallback
& callback
) OVERRIDE
{
191 DCHECK(thread_checker_
.CalledOnValidThread());
192 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
193 callback
.Run(std::string(), true);
196 read_file_callbacks_
.push(callback
);
197 mtp_client_
->ReadFileChunkByPath(
198 storage_handle
, path
, offset
, count
,
199 base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile
,
200 weak_ptr_factory_
.GetWeakPtr()),
201 base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError
,
202 weak_ptr_factory_
.GetWeakPtr()));
205 // MediaTransferProtocolManager override.
206 virtual void ReadFileChunkById(const std::string
& storage_handle
,
210 const ReadFileCallback
& callback
) OVERRIDE
{
211 DCHECK(thread_checker_
.CalledOnValidThread());
212 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
213 callback
.Run(std::string(), true);
216 read_file_callbacks_
.push(callback
);
217 mtp_client_
->ReadFileChunkById(
218 storage_handle
, file_id
, offset
, count
,
219 base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile
,
220 weak_ptr_factory_
.GetWeakPtr()),
221 base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError
,
222 weak_ptr_factory_
.GetWeakPtr()));
225 virtual void GetFileInfoByPath(const std::string
& storage_handle
,
226 const std::string
& path
,
227 const GetFileInfoCallback
& callback
) OVERRIDE
{
228 DCHECK(thread_checker_
.CalledOnValidThread());
229 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
230 callback
.Run(MtpFileEntry(), true);
233 get_file_info_callbacks_
.push(callback
);
234 mtp_client_
->GetFileInfoByPath(
237 base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo
,
238 weak_ptr_factory_
.GetWeakPtr()),
239 base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError
,
240 weak_ptr_factory_
.GetWeakPtr()));
243 virtual void GetFileInfoById(const std::string
& storage_handle
,
245 const GetFileInfoCallback
& callback
) OVERRIDE
{
246 DCHECK(thread_checker_
.CalledOnValidThread());
247 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
248 callback
.Run(MtpFileEntry(), true);
251 get_file_info_callbacks_
.push(callback
);
252 mtp_client_
->GetFileInfoById(
255 base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo
,
256 weak_ptr_factory_
.GetWeakPtr()),
257 base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError
,
258 weak_ptr_factory_
.GetWeakPtr()));
262 // Map of storage names to storage info.
263 typedef std::map
<std::string
, MtpStorageInfo
> StorageInfoMap
;
264 // Callback queues - DBus communication is in-order, thus callbacks are
265 // received in the same order as the requests.
266 typedef std::queue
<OpenStorageCallback
> OpenStorageCallbackQueue
;
267 // (callback, handle)
268 typedef std::queue
<std::pair
<CloseStorageCallback
, std::string
>
269 > CloseStorageCallbackQueue
;
270 typedef std::queue
<ReadDirectoryCallback
> ReadDirectoryCallbackQueue
;
271 typedef std::queue
<ReadFileCallback
> ReadFileCallbackQueue
;
272 typedef std::queue
<GetFileInfoCallback
> GetFileInfoCallbackQueue
;
274 void OnStorageChanged(bool is_attach
, const std::string
& storage_name
) {
275 DCHECK(thread_checker_
.CalledOnValidThread());
278 mtp_client_
->GetStorageInfo(
280 base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo
,
281 weak_ptr_factory_
.GetWeakPtr()),
282 base::Bind(&base::DoNothing
));
287 StorageInfoMap::iterator it
= storage_info_map_
.find(storage_name
);
288 if (it
== storage_info_map_
.end()) {
289 // This might happen during initialization when |storage_info_map_| has
290 // not been fully populated yet?
293 storage_info_map_
.erase(it
);
294 FOR_EACH_OBSERVER(Observer
,
296 StorageChanged(false /* detach */, storage_name
));
299 void OnEnumerateStorages(const std::vector
<std::string
>& storage_names
) {
300 DCHECK(thread_checker_
.CalledOnValidThread());
302 for (size_t i
= 0; i
< storage_names
.size(); ++i
) {
303 mtp_client_
->GetStorageInfo(
305 base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo
,
306 weak_ptr_factory_
.GetWeakPtr()),
307 base::Bind(&base::DoNothing
));
311 void OnGetStorageInfo(const MtpStorageInfo
& storage_info
) {
312 DCHECK(thread_checker_
.CalledOnValidThread());
313 const std::string
& storage_name
= storage_info
.storage_name();
314 if (ContainsKey(storage_info_map_
, storage_name
)) {
315 // This should not happen, since MediaTransferProtocolManagerImpl should
316 // only call EnumerateStorages() once, which populates |storage_info_map_|
317 // with the already-attached devices.
318 // After that, all incoming signals are either for new storage
319 // attachments, which should not be in |storage_info_map_|, or for
320 // storage detachements, which do not add to |storage_info_map_|.
325 // New storage. Add it and let the observers know.
326 storage_info_map_
.insert(std::make_pair(storage_name
, storage_info
));
327 FOR_EACH_OBSERVER(Observer
,
329 StorageChanged(true /* is attach */, storage_name
));
332 void OnOpenStorage(const std::string
& handle
) {
333 DCHECK(thread_checker_
.CalledOnValidThread());
334 if (!ContainsKey(handles_
, handle
)) {
335 handles_
.insert(handle
);
336 open_storage_callbacks_
.front().Run(handle
, false);
339 open_storage_callbacks_
.front().Run(std::string(), true);
341 open_storage_callbacks_
.pop();
344 void OnOpenStorageError() {
345 open_storage_callbacks_
.front().Run(std::string(), true);
346 open_storage_callbacks_
.pop();
349 void OnCloseStorage() {
350 DCHECK(thread_checker_
.CalledOnValidThread());
351 const std::string
& handle
= close_storage_callbacks_
.front().second
;
352 if (ContainsKey(handles_
, handle
)) {
353 handles_
.erase(handle
);
354 close_storage_callbacks_
.front().first
.Run(false);
357 close_storage_callbacks_
.front().first
.Run(true);
359 close_storage_callbacks_
.pop();
362 void OnCloseStorageError() {
363 DCHECK(thread_checker_
.CalledOnValidThread());
364 close_storage_callbacks_
.front().first
.Run(true);
365 close_storage_callbacks_
.pop();
368 void OnReadDirectory(const std::vector
<MtpFileEntry
>& file_entries
) {
369 DCHECK(thread_checker_
.CalledOnValidThread());
370 read_directory_callbacks_
.front().Run(file_entries
, false);
371 read_directory_callbacks_
.pop();
374 void OnReadDirectoryError() {
375 DCHECK(thread_checker_
.CalledOnValidThread());
376 read_directory_callbacks_
.front().Run(std::vector
<MtpFileEntry
>(), true);
377 read_directory_callbacks_
.pop();
380 void OnReadFile(const std::string
& data
) {
381 DCHECK(thread_checker_
.CalledOnValidThread());
382 read_file_callbacks_
.front().Run(data
, false);
383 read_file_callbacks_
.pop();
386 void OnReadFileError() {
387 DCHECK(thread_checker_
.CalledOnValidThread());
388 read_file_callbacks_
.front().Run(std::string(), true);
389 read_file_callbacks_
.pop();
392 void OnGetFileInfo(const MtpFileEntry
& entry
) {
393 DCHECK(thread_checker_
.CalledOnValidThread());
394 get_file_info_callbacks_
.front().Run(entry
, false);
395 get_file_info_callbacks_
.pop();
398 void OnGetFileInfoError() {
399 DCHECK(thread_checker_
.CalledOnValidThread());
400 get_file_info_callbacks_
.front().Run(MtpFileEntry(), true);
401 get_file_info_callbacks_
.pop();
404 // Get the Bus object used to communicate with mtpd.
405 dbus::Bus
* GetBus() {
406 DCHECK(thread_checker_
.CalledOnValidThread());
407 #if defined(OS_CHROMEOS)
408 return chromeos::DBusThreadManager::Get()->GetSystemBus();
410 return session_bus_
.get();
414 // Callback to finish initialization after figuring out if the mtp service
415 // has an owner, or if the service owner has changed.
416 // |mtpd_service_owner| contains the name of the current owner, if any.
417 void FinishSetupOnOriginThread(const std::string
& mtpd_service_owner
) {
418 DCHECK(thread_checker_
.CalledOnValidThread());
420 if (mtpd_service_owner
== current_mtpd_owner_
)
423 if (mtpd_service_owner
.empty()) {
424 current_mtpd_owner_
.clear();
429 current_mtpd_owner_
= mtpd_service_owner
;
432 MediaTransferProtocolDaemonClient::Create(GetBus(),
433 false /* not stub */));
435 // Set up signals and start initializing |storage_info_map_|.
436 mtp_client_
->SetUpConnections(
437 base::Bind(&MediaTransferProtocolManagerImpl::OnStorageChanged
,
438 weak_ptr_factory_
.GetWeakPtr()));
439 mtp_client_
->EnumerateStorages(
440 base::Bind(&MediaTransferProtocolManagerImpl::OnEnumerateStorages
,
441 weak_ptr_factory_
.GetWeakPtr()),
442 base::Bind(&base::DoNothing
));
446 scoped_ptr
<MediaTransferProtocolDaemonClient
> mtp_client_
;
448 #if !defined(OS_CHROMEOS)
449 // And a D-Bus session for talking to mtpd.
450 scoped_refptr
<dbus::Bus
> session_bus_
;
453 // Device attachment / detachment observers.
454 ObserverList
<Observer
> observers_
;
456 base::WeakPtrFactory
<MediaTransferProtocolManagerImpl
> weak_ptr_factory_
;
458 // Everything below is only accessed on the UI thread.
460 // Map to keep track of attached storages by name.
461 StorageInfoMap storage_info_map_
;
463 // Set of open storage handles.
464 std::set
<std::string
> handles_
;
466 dbus::Bus::GetServiceOwnerCallback mtpd_owner_changed_callback_
;
468 std::string current_mtpd_owner_
;
471 OpenStorageCallbackQueue open_storage_callbacks_
;
472 CloseStorageCallbackQueue close_storage_callbacks_
;
473 ReadDirectoryCallbackQueue read_directory_callbacks_
;
474 ReadFileCallbackQueue read_file_callbacks_
;
475 GetFileInfoCallbackQueue get_file_info_callbacks_
;
477 base::ThreadChecker thread_checker_
;
479 DISALLOW_COPY_AND_ASSIGN(MediaTransferProtocolManagerImpl
);
485 MediaTransferProtocolManager
* MediaTransferProtocolManager::Initialize(
486 scoped_refptr
<base::SequencedTaskRunner
> task_runner
) {
487 DCHECK(!g_media_transfer_protocol_manager
);
489 g_media_transfer_protocol_manager
=
490 new MediaTransferProtocolManagerImpl(task_runner
);
491 VLOG(1) << "MediaTransferProtocolManager initialized";
493 return g_media_transfer_protocol_manager
;
496 } // namespace device