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"
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/location.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/observer_list.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/stl_util.h"
20 #include "base/threading/thread_checker.h"
22 #include "device/media_transfer_protocol/media_transfer_protocol_daemon_client.h"
23 #include "device/media_transfer_protocol/mtp_file_entry.pb.h"
24 #include "device/media_transfer_protocol/mtp_storage_info.pb.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
27 #if defined(OS_CHROMEOS)
28 #include "chromeos/dbus/dbus_thread_manager.h"
35 MediaTransferProtocolManager
* g_media_transfer_protocol_manager
= NULL
;
37 // When reading directory entries, this is the number of entries for
38 // GetFileInfo() to read in one operation. If set too low, efficiency goes down
39 // slightly due to the overhead of D-Bus calls. If set too high, then slow
40 // devices may trigger a D-Bus timeout.
41 // The value below is a good initial estimate.
42 const size_t kFileInfoToFetchChunkSize
= 25;
44 // On the first call to GetFileInfo, the offset to use is 0.
45 const size_t kInitialOffset
= 0;
47 // The MediaTransferProtocolManager implementation.
48 class MediaTransferProtocolManagerImpl
: public MediaTransferProtocolManager
{
50 explicit MediaTransferProtocolManagerImpl(
51 scoped_refptr
<base::SequencedTaskRunner
> task_runner
)
52 : weak_ptr_factory_(this) {
53 #if defined(OS_CHROMEOS)
54 DCHECK(!task_runner
.get());
56 DCHECK(task_runner
.get());
57 dbus::Bus::Options options
;
58 options
.bus_type
= dbus::Bus::SYSTEM
;
59 options
.connection_type
= dbus::Bus::PRIVATE
;
60 options
.dbus_task_runner
= task_runner
;
61 session_bus_
= new dbus::Bus(options
);
65 // Listen for future mtpd service owner changes, in case it is not
66 // available right now. There is no guarantee on Linux or ChromeOS that
67 // mtpd is running already.
68 mtpd_owner_changed_callback_
= base::Bind(
69 &MediaTransferProtocolManagerImpl::FinishSetupOnOriginThread
,
70 weak_ptr_factory_
.GetWeakPtr());
71 GetBus()->ListenForServiceOwnerChange(mtpd::kMtpdServiceName
,
72 mtpd_owner_changed_callback_
);
73 GetBus()->GetServiceOwner(mtpd::kMtpdServiceName
,
74 mtpd_owner_changed_callback_
);
78 ~MediaTransferProtocolManagerImpl() override
{
79 DCHECK(g_media_transfer_protocol_manager
);
80 g_media_transfer_protocol_manager
= NULL
;
82 GetBus()->UnlistenForServiceOwnerChange(mtpd::kMtpdServiceName
,
83 mtpd_owner_changed_callback_
);
86 #if !defined(OS_CHROMEOS)
87 session_bus_
->GetDBusTaskRunner()->PostTask(
88 FROM_HERE
, base::Bind(&dbus::Bus::ShutdownAndBlock
, session_bus_
));
91 VLOG(1) << "MediaTransferProtocolManager Shutdown completed";
94 // MediaTransferProtocolManager override.
95 void AddObserver(Observer
* observer
) override
{
96 DCHECK(thread_checker_
.CalledOnValidThread());
97 observers_
.AddObserver(observer
);
100 // MediaTransferProtocolManager override.
101 void RemoveObserver(Observer
* observer
) override
{
102 DCHECK(thread_checker_
.CalledOnValidThread());
103 observers_
.RemoveObserver(observer
);
106 // MediaTransferProtocolManager override.
107 const std::vector
<std::string
> GetStorages() const override
{
108 DCHECK(thread_checker_
.CalledOnValidThread());
109 std::vector
<std::string
> storages
;
110 for (StorageInfoMap::const_iterator it
= storage_info_map_
.begin();
111 it
!= storage_info_map_
.end();
113 storages
.push_back(it
->first
);
118 // MediaTransferProtocolManager override.
119 const MtpStorageInfo
* GetStorageInfo(
120 const std::string
& storage_name
) const override
{
121 DCHECK(thread_checker_
.CalledOnValidThread());
122 StorageInfoMap::const_iterator it
= storage_info_map_
.find(storage_name
);
123 return it
!= storage_info_map_
.end() ? &it
->second
: NULL
;
126 // MediaTransferProtocolManager override.
127 void OpenStorage(const std::string
& storage_name
,
128 const std::string
& mode
,
129 const OpenStorageCallback
& callback
) override
{
130 DCHECK(thread_checker_
.CalledOnValidThread());
131 if (!ContainsKey(storage_info_map_
, storage_name
) || !mtp_client_
) {
132 callback
.Run(std::string(), true);
135 open_storage_callbacks_
.push(callback
);
136 mtp_client_
->OpenStorage(
139 base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorage
,
140 weak_ptr_factory_
.GetWeakPtr()),
141 base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorageError
,
142 weak_ptr_factory_
.GetWeakPtr()));
145 // MediaTransferProtocolManager override.
146 void CloseStorage(const std::string
& storage_handle
,
147 const CloseStorageCallback
& callback
) override
{
148 DCHECK(thread_checker_
.CalledOnValidThread());
149 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
153 close_storage_callbacks_
.push(std::make_pair(callback
, storage_handle
));
154 mtp_client_
->CloseStorage(
156 base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorage
,
157 weak_ptr_factory_
.GetWeakPtr()),
158 base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorageError
,
159 weak_ptr_factory_
.GetWeakPtr()));
162 // MediaTransferProtocolManager override.
163 void ReadDirectory(const std::string
& storage_handle
,
164 const uint32 file_id
,
165 const size_t max_size
,
166 const ReadDirectoryCallback
& callback
) override
{
167 DCHECK(thread_checker_
.CalledOnValidThread());
168 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
169 callback
.Run(std::vector
<MtpFileEntry
>(),
170 false /* no more entries */,
174 read_directory_callbacks_
.push(callback
);
175 mtp_client_
->ReadDirectoryEntryIds(
176 storage_handle
, file_id
,
177 base::Bind(&MediaTransferProtocolManagerImpl::
178 OnReadDirectoryEntryIdsToReadDirectory
,
179 weak_ptr_factory_
.GetWeakPtr(), storage_handle
, max_size
),
180 base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError
,
181 weak_ptr_factory_
.GetWeakPtr()));
184 // MediaTransferProtocolManager override.
185 void ReadFileChunk(const std::string
& storage_handle
,
189 const ReadFileCallback
& callback
) override
{
190 DCHECK(thread_checker_
.CalledOnValidThread());
191 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
192 callback
.Run(std::string(), true);
195 read_file_callbacks_
.push(callback
);
196 mtp_client_
->ReadFileChunk(
197 storage_handle
, file_id
, offset
, count
,
198 base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile
,
199 weak_ptr_factory_
.GetWeakPtr()),
200 base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError
,
201 weak_ptr_factory_
.GetWeakPtr()));
204 void GetFileInfo(const std::string
& storage_handle
,
206 const GetFileInfoCallback
& callback
) override
{
207 DCHECK(thread_checker_
.CalledOnValidThread());
208 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
209 callback
.Run(MtpFileEntry(), true);
212 std::vector
<uint32
> file_ids
;
213 file_ids
.push_back(file_id
);
214 get_file_info_callbacks_
.push(callback
);
215 mtp_client_
->GetFileInfo(
220 base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo
,
221 weak_ptr_factory_
.GetWeakPtr()),
222 base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError
,
223 weak_ptr_factory_
.GetWeakPtr()));
226 void CopyFileFromLocal(const std::string
& storage_handle
,
227 const int source_file_descriptor
,
228 const uint32 parent_id
,
229 const std::string
& file_name
,
230 const CopyFileFromLocalCallback
& callback
) override
{
231 DCHECK(thread_checker_
.CalledOnValidThread());
232 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
233 callback
.Run(true /* error */);
236 copy_file_from_local_callbacks_
.push(callback
);
237 mtp_client_
->CopyFileFromLocal(
238 storage_handle
, source_file_descriptor
, parent_id
, file_name
,
239 base::Bind(&MediaTransferProtocolManagerImpl::OnCopyFileFromLocal
,
240 weak_ptr_factory_
.GetWeakPtr()),
241 base::Bind(&MediaTransferProtocolManagerImpl::OnCopyFileFromLocalError
,
242 weak_ptr_factory_
.GetWeakPtr()));
245 void DeleteObject(const std::string
& storage_handle
,
246 const uint32 object_id
,
247 const DeleteObjectCallback
& callback
) override
{
248 DCHECK(thread_checker_
.CalledOnValidThread());
249 if (!ContainsKey(handles_
, storage_handle
) || !mtp_client_
) {
250 callback
.Run(true /* error */);
253 delete_object_callbacks_
.push(callback
);
254 mtp_client_
->DeleteObject(
255 storage_handle
, object_id
,
256 base::Bind(&MediaTransferProtocolManagerImpl::OnDeleteObject
,
257 weak_ptr_factory_
.GetWeakPtr()),
258 base::Bind(&MediaTransferProtocolManagerImpl::OnDeleteObjectError
,
259 weak_ptr_factory_
.GetWeakPtr()));
263 // Map of storage names to storage info.
264 typedef std::map
<std::string
, MtpStorageInfo
> StorageInfoMap
;
265 // Callback queues - DBus communication is in-order, thus callbacks are
266 // received in the same order as the requests.
267 typedef std::queue
<OpenStorageCallback
> OpenStorageCallbackQueue
;
268 // (callback, handle)
269 typedef std::queue
<std::pair
<CloseStorageCallback
, std::string
>
270 > CloseStorageCallbackQueue
;
271 typedef std::queue
<ReadDirectoryCallback
> ReadDirectoryCallbackQueue
;
272 typedef std::queue
<ReadFileCallback
> ReadFileCallbackQueue
;
273 typedef std::queue
<GetFileInfoCallback
> GetFileInfoCallbackQueue
;
274 typedef std::queue
<CopyFileFromLocalCallback
> CopyFileFromLocalCallbackQueue
;
275 typedef std::queue
<DeleteObjectCallback
> DeleteObjectCallbackQueue
;
277 void OnStorageAttached(const std::string
& storage_name
) {
278 DCHECK(thread_checker_
.CalledOnValidThread());
279 mtp_client_
->GetStorageInfo(
281 base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo
,
282 weak_ptr_factory_
.GetWeakPtr()),
283 base::Bind(&base::DoNothing
));
286 void OnStorageDetached(const std::string
& storage_name
) {
287 DCHECK(thread_checker_
.CalledOnValidThread());
288 if (storage_info_map_
.erase(storage_name
) == 0) {
289 // This can happen for a storage where
290 // MediaTransferProtocolDaemonClient::GetStorageInfo() failed.
291 // Return to avoid giving observers phantom detach events.
294 FOR_EACH_OBSERVER(Observer
,
296 StorageChanged(false /* detach */, storage_name
));
299 void OnStorageChanged(bool is_attach
, const std::string
& storage_name
) {
300 DCHECK(thread_checker_
.CalledOnValidThread());
303 OnStorageAttached(storage_name
);
305 OnStorageDetached(storage_name
);
308 void OnEnumerateStorages(const std::vector
<std::string
>& storage_names
) {
309 DCHECK(thread_checker_
.CalledOnValidThread());
311 for (size_t i
= 0; i
< storage_names
.size(); ++i
) {
312 if (ContainsKey(storage_info_map_
, storage_names
[i
])) {
313 // OnStorageChanged() might have gotten called first.
316 OnStorageAttached(storage_names
[i
]);
320 void OnGetStorageInfo(const MtpStorageInfo
& storage_info
) {
321 DCHECK(thread_checker_
.CalledOnValidThread());
322 const std::string
& storage_name
= storage_info
.storage_name();
323 if (ContainsKey(storage_info_map_
, storage_name
)) {
324 // This should not happen, since MediaTransferProtocolManagerImpl should
325 // only call EnumerateStorages() once, which populates |storage_info_map_|
326 // with the already-attached devices.
327 // After that, all incoming signals are either for new storage
328 // attachments, which should not be in |storage_info_map_|, or for
329 // storage detachments, which do not add to |storage_info_map_|.
330 // Return to avoid giving observers phantom detach events.
335 // New storage. Add it and let the observers know.
336 storage_info_map_
.insert(std::make_pair(storage_name
, storage_info
));
337 FOR_EACH_OBSERVER(Observer
,
339 StorageChanged(true /* is attach */, storage_name
));
342 void OnOpenStorage(const std::string
& handle
) {
343 DCHECK(thread_checker_
.CalledOnValidThread());
344 if (!ContainsKey(handles_
, handle
)) {
345 handles_
.insert(handle
);
346 open_storage_callbacks_
.front().Run(handle
, false);
349 open_storage_callbacks_
.front().Run(std::string(), true);
351 open_storage_callbacks_
.pop();
354 void OnOpenStorageError() {
355 open_storage_callbacks_
.front().Run(std::string(), true);
356 open_storage_callbacks_
.pop();
359 void OnCloseStorage() {
360 DCHECK(thread_checker_
.CalledOnValidThread());
361 const std::string
& handle
= close_storage_callbacks_
.front().second
;
362 if (ContainsKey(handles_
, handle
)) {
363 handles_
.erase(handle
);
364 close_storage_callbacks_
.front().first
.Run(false);
367 close_storage_callbacks_
.front().first
.Run(true);
369 close_storage_callbacks_
.pop();
372 void OnCloseStorageError() {
373 DCHECK(thread_checker_
.CalledOnValidThread());
374 close_storage_callbacks_
.front().first
.Run(true);
375 close_storage_callbacks_
.pop();
378 void OnReadDirectoryEntryIdsToReadDirectory(
379 const std::string
& storage_handle
,
380 const size_t max_size
,
381 const std::vector
<uint32
>& file_ids
) {
382 DCHECK(thread_checker_
.CalledOnValidThread());
384 if (file_ids
.empty()) {
385 OnGotDirectoryEntries(storage_handle
, file_ids
, kInitialOffset
, max_size
,
386 file_ids
, std::vector
<MtpFileEntry
>());
390 std::vector
<uint32
> sorted_file_ids
= file_ids
;
391 std::sort(sorted_file_ids
.begin(), sorted_file_ids
.end());
393 const size_t chunk_size
=
394 max_size
== 0 ? kFileInfoToFetchChunkSize
395 : std::min(max_size
, kFileInfoToFetchChunkSize
);
397 mtp_client_
->GetFileInfo(
398 storage_handle
, file_ids
, kInitialOffset
, chunk_size
,
399 base::Bind(&MediaTransferProtocolManagerImpl::OnGotDirectoryEntries
,
400 weak_ptr_factory_
.GetWeakPtr(), storage_handle
, file_ids
,
401 kInitialOffset
, max_size
, sorted_file_ids
),
402 base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError
,
403 weak_ptr_factory_
.GetWeakPtr()));
406 void OnGotDirectoryEntries(const std::string
& storage_handle
,
407 const std::vector
<uint32
>& file_ids
,
409 const size_t max_size
,
410 const std::vector
<uint32
>& sorted_file_ids
,
411 const std::vector
<MtpFileEntry
>& file_entries
) {
412 DCHECK(thread_checker_
.CalledOnValidThread());
413 DCHECK_EQ(file_ids
.size(), sorted_file_ids
.size());
415 // Use |sorted_file_ids| to sanity check and make sure the results are a
416 // subset of the requested file ids.
417 for (size_t i
= 0; i
< file_entries
.size(); ++i
) {
418 std::vector
<uint32
>::const_iterator it
=
419 std::lower_bound(sorted_file_ids
.begin(),
420 sorted_file_ids
.end(),
421 file_entries
[i
].item_id());
422 if (it
== sorted_file_ids
.end()) {
423 OnReadDirectoryError();
428 const size_t directory_size
=
429 max_size
== 0 ? file_ids
.size() : std::min(file_ids
.size(), max_size
);
430 size_t next_offset
= directory_size
;
431 if (offset
< SIZE_MAX
- kFileInfoToFetchChunkSize
)
432 next_offset
= std::min(next_offset
, offset
+ kFileInfoToFetchChunkSize
);
433 bool has_more
= next_offset
< directory_size
;
434 read_directory_callbacks_
.front().Run(file_entries
,
436 false /* no error */);
439 const size_t chunk_size
=
440 std::min(directory_size
- next_offset
, kFileInfoToFetchChunkSize
);
442 mtp_client_
->GetFileInfo(
443 storage_handle
, file_ids
, next_offset
, chunk_size
,
444 base::Bind(&MediaTransferProtocolManagerImpl::OnGotDirectoryEntries
,
445 weak_ptr_factory_
.GetWeakPtr(), storage_handle
, file_ids
,
446 next_offset
, max_size
, sorted_file_ids
),
447 base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError
,
448 weak_ptr_factory_
.GetWeakPtr()));
451 read_directory_callbacks_
.pop();
454 void OnReadDirectoryError() {
455 DCHECK(thread_checker_
.CalledOnValidThread());
456 read_directory_callbacks_
.front().Run(std::vector
<MtpFileEntry
>(),
457 false /* no more entries */,
459 read_directory_callbacks_
.pop();
462 void OnReadFile(const std::string
& data
) {
463 DCHECK(thread_checker_
.CalledOnValidThread());
464 read_file_callbacks_
.front().Run(data
, false);
465 read_file_callbacks_
.pop();
468 void OnReadFileError() {
469 DCHECK(thread_checker_
.CalledOnValidThread());
470 read_file_callbacks_
.front().Run(std::string(), true);
471 read_file_callbacks_
.pop();
474 void OnGetFileInfo(const std::vector
<MtpFileEntry
>& entries
) {
475 DCHECK(thread_checker_
.CalledOnValidThread());
476 if (entries
.size() == 1) {
477 get_file_info_callbacks_
.front().Run(entries
[0], false /* no error */);
478 get_file_info_callbacks_
.pop();
480 OnGetFileInfoError();
484 void OnGetFileInfoError() {
485 DCHECK(thread_checker_
.CalledOnValidThread());
486 get_file_info_callbacks_
.front().Run(MtpFileEntry(), true);
487 get_file_info_callbacks_
.pop();
490 void OnCopyFileFromLocal() {
491 DCHECK(thread_checker_
.CalledOnValidThread());
492 copy_file_from_local_callbacks_
.front().Run(false /* no error */);
493 copy_file_from_local_callbacks_
.pop();
496 void OnCopyFileFromLocalError() {
497 DCHECK(thread_checker_
.CalledOnValidThread());
498 copy_file_from_local_callbacks_
.front().Run(true /* error */);
499 copy_file_from_local_callbacks_
.pop();
502 void OnDeleteObject() {
503 DCHECK(thread_checker_
.CalledOnValidThread());
504 delete_object_callbacks_
.front().Run(false /* no error */);
505 delete_object_callbacks_
.pop();
508 void OnDeleteObjectError() {
509 DCHECK(thread_checker_
.CalledOnValidThread());
510 delete_object_callbacks_
.front().Run(true /* error */);
511 delete_object_callbacks_
.pop();
514 // Get the Bus object used to communicate with mtpd.
515 dbus::Bus
* GetBus() {
516 DCHECK(thread_checker_
.CalledOnValidThread());
517 #if defined(OS_CHROMEOS)
518 return chromeos::DBusThreadManager::Get()->GetSystemBus();
520 return session_bus_
.get();
524 // Callback to finish initialization after figuring out if the mtpd service
525 // has an owner, or if the service owner has changed.
526 // |mtpd_service_owner| contains the name of the current owner, if any.
527 void FinishSetupOnOriginThread(const std::string
& mtpd_service_owner
) {
528 DCHECK(thread_checker_
.CalledOnValidThread());
530 if (mtpd_service_owner
== current_mtpd_owner_
)
533 // In the case of a new service owner, clear |storage_info_map_|.
534 // Assume all storages have been disconnected. If there is a new service
535 // owner, reconnecting to it will reconnect all the storages as well.
537 // Save a copy of |storage_info_map_| keys as |storage_info_map_| can
538 // change in OnStorageDetached().
539 std::vector
<std::string
> storage_names
;
540 for (StorageInfoMap::const_iterator it
= storage_info_map_
.begin();
541 it
!= storage_info_map_
.end();
543 storage_names
.push_back(it
->first
);
545 for (size_t i
= 0; i
!= storage_names
.size(); ++i
)
546 OnStorageDetached(storage_names
[i
]);
548 if (mtpd_service_owner
.empty()) {
549 current_mtpd_owner_
.clear();
554 current_mtpd_owner_
= mtpd_service_owner
;
556 mtp_client_
.reset(MediaTransferProtocolDaemonClient::Create(GetBus()));
558 // Set up signals and start initializing |storage_info_map_|.
559 mtp_client_
->ListenForChanges(
560 base::Bind(&MediaTransferProtocolManagerImpl::OnStorageChanged
,
561 weak_ptr_factory_
.GetWeakPtr()));
562 mtp_client_
->EnumerateStorages(
563 base::Bind(&MediaTransferProtocolManagerImpl::OnEnumerateStorages
,
564 weak_ptr_factory_
.GetWeakPtr()),
565 base::Bind(&base::DoNothing
));
569 scoped_ptr
<MediaTransferProtocolDaemonClient
> mtp_client_
;
571 #if !defined(OS_CHROMEOS)
572 // And a D-Bus session for talking to mtpd.
573 scoped_refptr
<dbus::Bus
> session_bus_
;
576 // Device attachment / detachment observers.
577 ObserverList
<Observer
> observers_
;
579 // Map to keep track of attached storages by name.
580 StorageInfoMap storage_info_map_
;
582 // Set of open storage handles.
583 std::set
<std::string
> handles_
;
585 dbus::Bus::GetServiceOwnerCallback mtpd_owner_changed_callback_
;
587 std::string current_mtpd_owner_
;
590 OpenStorageCallbackQueue open_storage_callbacks_
;
591 CloseStorageCallbackQueue close_storage_callbacks_
;
592 ReadDirectoryCallbackQueue read_directory_callbacks_
;
593 ReadFileCallbackQueue read_file_callbacks_
;
594 GetFileInfoCallbackQueue get_file_info_callbacks_
;
595 CopyFileFromLocalCallbackQueue copy_file_from_local_callbacks_
;
596 DeleteObjectCallbackQueue delete_object_callbacks_
;
598 base::ThreadChecker thread_checker_
;
600 base::WeakPtrFactory
<MediaTransferProtocolManagerImpl
> weak_ptr_factory_
;
602 DISALLOW_COPY_AND_ASSIGN(MediaTransferProtocolManagerImpl
);
608 MediaTransferProtocolManager
* MediaTransferProtocolManager::Initialize(
609 scoped_refptr
<base::SequencedTaskRunner
> task_runner
) {
610 DCHECK(!g_media_transfer_protocol_manager
);
612 g_media_transfer_protocol_manager
=
613 new MediaTransferProtocolManagerImpl(task_runner
);
614 VLOG(1) << "MediaTransferProtocolManager initialized";
616 return g_media_transfer_protocol_manager
;
619 } // namespace device