1 // Copyright 2018 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.
4
5 #include "services/device/media_transfer_protocol/mtp_device_manager.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "chromeos/dbus/dbus_thread_manager.h"
10 #include "dbus/bus.h"
11 #include "third_party/cros_system_api/dbus/service_constants.h"
12
13 namespace device {
14
15 namespace {
16
17 #if DCHECK_IS_ON()
18 MtpDeviceManager* g_mtp_device_manager = nullptr;
19 #endif
20
21 } // namespace
22
MtpDeviceManager()23 MtpDeviceManager::MtpDeviceManager()
24 : bus_(chromeos::DBusThreadManager::Get()->GetSystemBus()) {
25 // Listen for future mtpd service owner changes, in case it is not
26 // available right now. There is no guarantee that mtpd is running already.
27 dbus::Bus::ServiceOwnerChangeCallback mtpd_owner_changed_callback =
28 base::BindRepeating(&MtpDeviceManager::FinishSetupOnOriginThread,
29 weak_ptr_factory_.GetWeakPtr());
30 if (bus_) {
31 bus_->ListenForServiceOwnerChange(mtpd::kMtpdServiceName,
32 mtpd_owner_changed_callback);
33 bus_->GetServiceOwner(mtpd::kMtpdServiceName, mtpd_owner_changed_callback);
34 }
35 }
36
~MtpDeviceManager()37 MtpDeviceManager::~MtpDeviceManager() {
38 #if DCHECK_IS_ON()
39 DCHECK(g_mtp_device_manager);
40 g_mtp_device_manager = nullptr;
41 #endif
42
43 if (bus_) {
44 bus_->UnlistenForServiceOwnerChange(
45 mtpd::kMtpdServiceName,
46 base::BindRepeating(&MtpDeviceManager::FinishSetupOnOriginThread,
47 weak_ptr_factory_.GetWeakPtr()));
48 }
49
50 VLOG(1) << "MtpDeviceManager Shutdown completed";
51 }
52
AddReceiver(mojo::PendingReceiver<mojom::MtpManager> receiver)53 void MtpDeviceManager::AddReceiver(
54 mojo::PendingReceiver<mojom::MtpManager> receiver) {
55 receivers_.Add(this, std::move(receiver));
56 }
57
EnumerateStoragesAndSetClient(mojo::PendingAssociatedRemote<mojom::MtpManagerClient> client,EnumerateStoragesAndSetClientCallback callback)58 void MtpDeviceManager::EnumerateStoragesAndSetClient(
59 mojo::PendingAssociatedRemote<mojom::MtpManagerClient> client,
60 EnumerateStoragesAndSetClientCallback callback) {
61 DCHECK(thread_checker_.CalledOnValidThread());
62
63 // Return all available storage info.
64 std::vector<mojom::MtpStorageInfoPtr> storage_info_ptr_list;
65 storage_info_ptr_list.reserve(storage_info_map_.size());
66 for (const auto& info : storage_info_map_)
67 storage_info_ptr_list.push_back(info.second.Clone());
68 std::move(callback).Run(std::move(storage_info_ptr_list));
69
70 // Set client.
71 if (!client.is_valid())
72 return;
73
74 client_.Bind(std::move(client));
75 }
76
GetStorageInfo(const std::string & storage_name,GetStorageInfoCallback callback)77 void MtpDeviceManager::GetStorageInfo(const std::string& storage_name,
78 GetStorageInfoCallback callback) {
79 DCHECK(thread_checker_.CalledOnValidThread());
80 const auto it = storage_info_map_.find(storage_name);
81 mojom::MtpStorageInfoPtr storage_info =
82 it != storage_info_map_.end() ? it->second.Clone() : nullptr;
83 std::move(callback).Run(std::move(storage_info));
84 }
85
GetStorageInfoFromDevice(const std::string & storage_name,GetStorageInfoFromDeviceCallback callback)86 void MtpDeviceManager::GetStorageInfoFromDevice(
87 const std::string& storage_name,
88 GetStorageInfoFromDeviceCallback callback) {
89 DCHECK(thread_checker_.CalledOnValidThread());
90 if (!base::Contains(storage_info_map_, storage_name) || !mtp_client_) {
91 std::move(callback).Run(nullptr, true /* error */);
92 return;
93 }
94 get_storage_info_from_device_callbacks_.push(std::move(callback));
95 mtp_client_->GetStorageInfoFromDevice(
96 storage_name,
97 base::BindOnce(&MtpDeviceManager::OnGetStorageInfoFromDevice,
98 weak_ptr_factory_.GetWeakPtr()),
99 base::BindOnce(&MtpDeviceManager::OnGetStorageInfoFromDeviceError,
100 weak_ptr_factory_.GetWeakPtr()));
101 }
102
OpenStorage(const std::string & storage_name,const std::string & mode,OpenStorageCallback callback)103 void MtpDeviceManager::OpenStorage(const std::string& storage_name,
104 const std::string& mode,
105 OpenStorageCallback callback) {
106 DCHECK(thread_checker_.CalledOnValidThread());
107 if (!base::Contains(storage_info_map_, storage_name) || !mtp_client_) {
108 std::move(callback).Run(std::string(), true);
109 return;
110 }
111 open_storage_callbacks_.push(std::move(callback));
112 mtp_client_->OpenStorage(storage_name, mode,
113 base::BindOnce(&MtpDeviceManager::OnOpenStorage,
114 weak_ptr_factory_.GetWeakPtr()),
115 base::BindOnce(&MtpDeviceManager::OnOpenStorageError,
116 weak_ptr_factory_.GetWeakPtr()));
117 }
118
CloseStorage(const std::string & storage_handle,CloseStorageCallback callback)119 void MtpDeviceManager::CloseStorage(const std::string& storage_handle,
120 CloseStorageCallback callback) {
121 DCHECK(thread_checker_.CalledOnValidThread());
122 if (!base::Contains(handles_, storage_handle) || !mtp_client_) {
123 std::move(callback).Run(true);
124 return;
125 }
126 close_storage_callbacks_.push(
127 std::make_pair(std::move(callback), storage_handle));
128 mtp_client_->CloseStorage(
129 storage_handle,
130 base::BindOnce(&MtpDeviceManager::OnCloseStorage,
131 weak_ptr_factory_.GetWeakPtr()),
132 base::BindOnce(&MtpDeviceManager::OnCloseStorageError,
133 weak_ptr_factory_.GetWeakPtr()));
134 }
135
CreateDirectory(const std::string & storage_handle,uint32_t parent_id,const std::string & directory_name,CreateDirectoryCallback callback)136 void MtpDeviceManager::CreateDirectory(const std::string& storage_handle,
137 uint32_t parent_id,
138 const std::string& directory_name,
139 CreateDirectoryCallback callback) {
140 DCHECK(thread_checker_.CalledOnValidThread());
141 if (!base::Contains(handles_, storage_handle) || !mtp_client_) {
142 std::move(callback).Run(true /* error */);
143 return;
144 }
145 create_directory_callbacks_.push(std::move(callback));
146 mtp_client_->CreateDirectory(
147 storage_handle, parent_id, directory_name,
148 base::BindOnce(&MtpDeviceManager::OnCreateDirectory,
149 weak_ptr_factory_.GetWeakPtr()),
150 base::BindOnce(&MtpDeviceManager::OnCreateDirectoryError,
151 weak_ptr_factory_.GetWeakPtr()));
152 }
153
ReadDirectoryEntryIds(const std::string & storage_handle,uint32_t file_id,ReadDirectoryEntryIdsCallback callback)154 void MtpDeviceManager::ReadDirectoryEntryIds(
155 const std::string& storage_handle,
156 uint32_t file_id,
157 ReadDirectoryEntryIdsCallback callback) {
158 DCHECK(thread_checker_.CalledOnValidThread());
159 if (!base::Contains(handles_, storage_handle) || !mtp_client_) {
160 std::move(callback).Run(std::vector<uint32_t>(), /*error=*/true);
161 return;
162 }
163 read_directory_callbacks_.push(std::move(callback));
164 mtp_client_->ReadDirectoryEntryIds(
165 storage_handle, file_id,
166 base::BindOnce(&MtpDeviceManager::OnReadDirectoryEntryIds,
167 weak_ptr_factory_.GetWeakPtr()),
168 base::BindOnce(&MtpDeviceManager::OnReadDirectoryError,
169 weak_ptr_factory_.GetWeakPtr()));
170 }
171
ReadFileChunk(const std::string & storage_handle,uint32_t file_id,uint32_t offset,uint32_t count,ReadFileChunkCallback callback)172 void MtpDeviceManager::ReadFileChunk(const std::string& storage_handle,
173 uint32_t file_id,
174 uint32_t offset,
175 uint32_t count,
176 ReadFileChunkCallback callback) {
177 DCHECK(thread_checker_.CalledOnValidThread());
178 if (!base::Contains(handles_, storage_handle) || !mtp_client_) {
179 std::move(callback).Run(std::string(), true);
180 return;
181 }
182 read_file_callbacks_.push(std::move(callback));
183 mtp_client_->ReadFileChunk(storage_handle, file_id, offset, count,
184 base::BindOnce(&MtpDeviceManager::OnReadFile,
185 weak_ptr_factory_.GetWeakPtr()),
186 base::BindOnce(&MtpDeviceManager::OnReadFileError,
187 weak_ptr_factory_.GetWeakPtr()));
188 }
189
GetFileInfo(const std::string & storage_handle,const std::vector<uint32_t> & file_ids,GetFileInfoCallback callback)190 void MtpDeviceManager::GetFileInfo(const std::string& storage_handle,
191 const std::vector<uint32_t>& file_ids,
192 GetFileInfoCallback callback) {
193 DCHECK(thread_checker_.CalledOnValidThread());
194 if (!base::Contains(handles_, storage_handle) || !mtp_client_) {
195 std::move(callback).Run(std::vector<device::mojom::MtpFileEntryPtr>(),
196 /*error=*/true);
197 return;
198 }
199 get_file_info_callbacks_.push(std::move(callback));
200 mtp_client_->GetFileInfo(storage_handle, file_ids,
201 base::BindOnce(&MtpDeviceManager::OnGetFileInfo,
202 weak_ptr_factory_.GetWeakPtr()),
203 base::BindOnce(&MtpDeviceManager::OnGetFileInfoError,
204 weak_ptr_factory_.GetWeakPtr()));
205 }
206
RenameObject(const std::string & storage_handle,uint32_t object_id,const std::string & new_name,RenameObjectCallback callback)207 void MtpDeviceManager::RenameObject(const std::string& storage_handle,
208 uint32_t object_id,
209 const std::string& new_name,
210 RenameObjectCallback callback) {
211 DCHECK(thread_checker_.CalledOnValidThread());
212 if (!base::Contains(handles_, storage_handle) || !mtp_client_) {
213 std::move(callback).Run(true /* error */);
214 return;
215 }
216 rename_object_callbacks_.push(std::move(callback));
217 mtp_client_->RenameObject(
218 storage_handle, object_id, new_name,
219 base::BindOnce(&MtpDeviceManager::OnRenameObject,
220 weak_ptr_factory_.GetWeakPtr()),
221 base::BindOnce(&MtpDeviceManager::OnRenameObjectError,
222 weak_ptr_factory_.GetWeakPtr()));
223 }
224
CopyFileFromLocal(const std::string & storage_handle,int64_t source_file_descriptor,uint32_t parent_id,const std::string & file_name,CopyFileFromLocalCallback callback)225 void MtpDeviceManager::CopyFileFromLocal(const std::string& storage_handle,
226 int64_t source_file_descriptor,
227 uint32_t parent_id,
228 const std::string& file_name,
229 CopyFileFromLocalCallback callback) {
230 DCHECK(thread_checker_.CalledOnValidThread());
231 if (!base::Contains(handles_, storage_handle) || !mtp_client_) {
232 std::move(callback).Run(true /* error */);
233 return;
234 }
235 copy_file_from_local_callbacks_.push(std::move(callback));
236 mtp_client_->CopyFileFromLocal(
237 storage_handle, source_file_descriptor, parent_id, file_name,
238 base::BindOnce(&MtpDeviceManager::OnCopyFileFromLocal,
239 weak_ptr_factory_.GetWeakPtr()),
240 base::BindOnce(&MtpDeviceManager::OnCopyFileFromLocalError,
241 weak_ptr_factory_.GetWeakPtr()));
242 }
243
DeleteObject(const std::string & storage_handle,uint32_t object_id,DeleteObjectCallback callback)244 void MtpDeviceManager::DeleteObject(const std::string& storage_handle,
245 uint32_t object_id,
246 DeleteObjectCallback callback) {
247 DCHECK(thread_checker_.CalledOnValidThread());
248 if (!base::Contains(handles_, storage_handle) || !mtp_client_) {
249 std::move(callback).Run(true /* error */);
250 return;
251 }
252 delete_object_callbacks_.push(std::move(callback));
253 mtp_client_->DeleteObject(
254 storage_handle, object_id,
255 base::BindOnce(&MtpDeviceManager::OnDeleteObject,
256 weak_ptr_factory_.GetWeakPtr()),
257 base::BindOnce(&MtpDeviceManager::OnDeleteObjectError,
258 weak_ptr_factory_.GetWeakPtr()));
259 }
260
261 // private methods
OnStorageAttached(const std::string & storage_name)262 void MtpDeviceManager::OnStorageAttached(const std::string& storage_name) {
263 DCHECK(thread_checker_.CalledOnValidThread());
264 mtp_client_->GetStorageInfo(
265 storage_name,
266 base::BindOnce(&MtpDeviceManager::OnGetStorageInfo,
267 weak_ptr_factory_.GetWeakPtr()),
268 base::DoNothing::Once());
269 }
270
OnStorageDetached(const std::string & storage_name)271 void MtpDeviceManager::OnStorageDetached(const std::string& storage_name) {
272 DCHECK(thread_checker_.CalledOnValidThread());
273 if (storage_info_map_.erase(storage_name) == 0) {
274 // This can happen for a storage where
275 // MediaTransferProtocolDaemonClient::GetStorageInfo() failed.
276 // Return to avoid giving client phantom detach events.
277 return;
278 }
279
280 // Notify the bound MtpManagerClient.
281 if (client_) {
282 client_->StorageDetached(storage_name);
283 }
284 }
285
OnStorageChanged(bool is_attach,const std::string & storage_name)286 void MtpDeviceManager::OnStorageChanged(bool is_attach,
287 const std::string& storage_name) {
288 DCHECK(thread_checker_.CalledOnValidThread());
289 DCHECK(mtp_client_);
290 if (is_attach)
291 OnStorageAttached(storage_name);
292 else
293 OnStorageDetached(storage_name);
294 }
295
OnEnumerateStorages(const std::vector<std::string> & storage_names)296 void MtpDeviceManager::OnEnumerateStorages(
297 const std::vector<std::string>& storage_names) {
298 DCHECK(thread_checker_.CalledOnValidThread());
299 DCHECK(mtp_client_);
300 for (const auto& name : storage_names) {
301 if (base::Contains(storage_info_map_, name)) {
302 // OnStorageChanged() might have gotten called first.
303 continue;
304 }
305 OnStorageAttached(name);
306 }
307 }
308
OnGetStorageInfo(const mojom::MtpStorageInfo & storage_info)309 void MtpDeviceManager::OnGetStorageInfo(
310 const mojom::MtpStorageInfo& storage_info) {
311 DCHECK(thread_checker_.CalledOnValidThread());
312 const std::string& storage_name = storage_info.storage_name;
313 if (base::Contains(storage_info_map_, storage_name)) {
314 // This should not happen, since MtpDeviceManager should
315 // only call EnumerateStorages() once, which populates |storage_info_map_|
316 // with the already-attached devices.
317 // After that, all incoming signals are either for new storage
318 // attachments, which should not be in |storage_info_map_|, or for
319 // storage detachments, which do not add to |storage_info_map_|.
320 // Return to avoid giving client phantom detach events.
321 NOTREACHED();
322 return;
323 }
324
325 // New storage. Add it and let the bound client know.
326 storage_info_map_.insert(std::make_pair(storage_name, storage_info));
327
328 if (client_) {
329 client_->StorageAttached(storage_info.Clone());
330 }
331 }
332
OnGetStorageInfoFromDevice(const mojom::MtpStorageInfo & storage_info)333 void MtpDeviceManager::OnGetStorageInfoFromDevice(
334 const mojom::MtpStorageInfo& storage_info) {
335 std::move(get_storage_info_from_device_callbacks_.front())
336 .Run(storage_info.Clone(), false /* no error */);
337 get_storage_info_from_device_callbacks_.pop();
338 }
339
OnGetStorageInfoFromDeviceError()340 void MtpDeviceManager::OnGetStorageInfoFromDeviceError() {
341 std::move(get_storage_info_from_device_callbacks_.front())
342 .Run(nullptr, true /* error */);
343 get_storage_info_from_device_callbacks_.pop();
344 }
345
OnOpenStorage(const std::string & handle)346 void MtpDeviceManager::OnOpenStorage(const std::string& handle) {
347 DCHECK(thread_checker_.CalledOnValidThread());
348 if (!base::Contains(handles_, handle)) {
349 handles_.insert(handle);
350 std::move(open_storage_callbacks_.front()).Run(handle, false);
351 } else {
352 NOTREACHED();
353 std::move(open_storage_callbacks_.front()).Run(std::string(), true);
354 }
355 open_storage_callbacks_.pop();
356 }
357
OnOpenStorageError()358 void MtpDeviceManager::OnOpenStorageError() {
359 std::move(open_storage_callbacks_.front()).Run(std::string(), true);
360 open_storage_callbacks_.pop();
361 }
362
OnCloseStorage()363 void MtpDeviceManager::OnCloseStorage() {
364 DCHECK(thread_checker_.CalledOnValidThread());
365 const std::string& handle = close_storage_callbacks_.front().second;
366 if (base::Contains(handles_, handle)) {
367 handles_.erase(handle);
368 std::move(close_storage_callbacks_.front().first).Run(false);
369 } else {
370 NOTREACHED();
371 std::move(close_storage_callbacks_.front().first).Run(true);
372 }
373 close_storage_callbacks_.pop();
374 }
375
OnCloseStorageError()376 void MtpDeviceManager::OnCloseStorageError() {
377 DCHECK(thread_checker_.CalledOnValidThread());
378 std::move(close_storage_callbacks_.front()).first.Run(true);
379 close_storage_callbacks_.pop();
380 }
381
OnCreateDirectory()382 void MtpDeviceManager::OnCreateDirectory() {
383 DCHECK(thread_checker_.CalledOnValidThread());
384 std::move(create_directory_callbacks_.front()).Run(false /* no error */);
385 create_directory_callbacks_.pop();
386 }
387
OnCreateDirectoryError()388 void MtpDeviceManager::OnCreateDirectoryError() {
389 DCHECK(thread_checker_.CalledOnValidThread());
390 std::move(create_directory_callbacks_.front()).Run(true /* error */);
391 create_directory_callbacks_.pop();
392 }
393
OnReadDirectoryEntryIds(const std::vector<uint32_t> & file_ids)394 void MtpDeviceManager::OnReadDirectoryEntryIds(
395 const std::vector<uint32_t>& file_ids) {
396 DCHECK(thread_checker_.CalledOnValidThread());
397 std::move(read_directory_callbacks_.front()).Run(file_ids, /*error=*/false);
398 read_directory_callbacks_.pop();
399 }
400
OnReadDirectoryError()401 void MtpDeviceManager::OnReadDirectoryError() {
402 DCHECK(thread_checker_.CalledOnValidThread());
403 std::move(read_directory_callbacks_.front())
404 .Run(std::vector<uint32_t>(), /*error=*/true);
405 read_directory_callbacks_.pop();
406 }
407
OnReadFile(const std::string & data)408 void MtpDeviceManager::OnReadFile(const std::string& data) {
409 DCHECK(thread_checker_.CalledOnValidThread());
410 std::move(read_file_callbacks_.front()).Run(data, false);
411 read_file_callbacks_.pop();
412 }
413
OnReadFileError()414 void MtpDeviceManager::OnReadFileError() {
415 DCHECK(thread_checker_.CalledOnValidThread());
416 std::move(read_file_callbacks_.front()).Run(std::string(), true);
417 read_file_callbacks_.pop();
418 }
419
OnGetFileInfo(const std::vector<mojom::MtpFileEntry> & entries)420 void MtpDeviceManager::OnGetFileInfo(
421 const std::vector<mojom::MtpFileEntry>& entries) {
422 DCHECK(thread_checker_.CalledOnValidThread());
423
424 std::vector<device::mojom::MtpFileEntryPtr> ret(entries.size());
425 for (size_t i = 0; i < entries.size(); ++i)
426 ret[i] = entries[i].Clone();
427 std::move(get_file_info_callbacks_.front())
428 .Run(std::move(ret), /*error=*/false);
429 get_file_info_callbacks_.pop();
430 }
431
OnGetFileInfoError()432 void MtpDeviceManager::OnGetFileInfoError() {
433 DCHECK(thread_checker_.CalledOnValidThread());
434 std::move(get_file_info_callbacks_.front())
435 .Run(std::vector<device::mojom::MtpFileEntryPtr>(), /*error=*/true);
436 get_file_info_callbacks_.pop();
437 }
438
OnRenameObject()439 void MtpDeviceManager::OnRenameObject() {
440 DCHECK(thread_checker_.CalledOnValidThread());
441 std::move(rename_object_callbacks_.front()).Run(false /* no error */);
442 rename_object_callbacks_.pop();
443 }
444
OnRenameObjectError()445 void MtpDeviceManager::OnRenameObjectError() {
446 DCHECK(thread_checker_.CalledOnValidThread());
447 std::move(rename_object_callbacks_.front()).Run(true /* error */);
448 rename_object_callbacks_.pop();
449 }
450
OnCopyFileFromLocal()451 void MtpDeviceManager::OnCopyFileFromLocal() {
452 DCHECK(thread_checker_.CalledOnValidThread());
453 std::move(copy_file_from_local_callbacks_.front()).Run(false /* no error */);
454 copy_file_from_local_callbacks_.pop();
455 }
456
OnCopyFileFromLocalError()457 void MtpDeviceManager::OnCopyFileFromLocalError() {
458 DCHECK(thread_checker_.CalledOnValidThread());
459 std::move(copy_file_from_local_callbacks_.front()).Run(true /* error */);
460 copy_file_from_local_callbacks_.pop();
461 }
462
OnDeleteObject()463 void MtpDeviceManager::OnDeleteObject() {
464 DCHECK(thread_checker_.CalledOnValidThread());
465 std::move(delete_object_callbacks_.front()).Run(false /* no error */);
466 delete_object_callbacks_.pop();
467 }
468
OnDeleteObjectError()469 void MtpDeviceManager::OnDeleteObjectError() {
470 DCHECK(thread_checker_.CalledOnValidThread());
471 std::move(delete_object_callbacks_.front()).Run(true /* error */);
472 delete_object_callbacks_.pop();
473 }
474
FinishSetupOnOriginThread(const std::string & mtpd_service_owner)475 void MtpDeviceManager::FinishSetupOnOriginThread(
476 const std::string& mtpd_service_owner) {
477 DCHECK(thread_checker_.CalledOnValidThread());
478
479 if (mtpd_service_owner == current_mtpd_owner_)
480 return;
481
482 // In the case of a new service owner, clear |storage_info_map_|.
483 // Assume all storages have been disconnected. If there is a new service
484 // owner, reconnecting to it will reconnect all the storages as well.
485
486 // Save a copy of |storage_info_map_| keys as |storage_info_map_| can
487 // change in OnStorageDetached().
488 std::vector<std::string> storage_names;
489 storage_names.reserve(storage_info_map_.size());
490 for (const auto& info : storage_info_map_)
491 storage_names.push_back(info.first);
492
493 for (const auto& name : storage_names)
494 OnStorageDetached(name);
495
496 if (mtpd_service_owner.empty()) {
497 current_mtpd_owner_.clear();
498 mtp_client_.reset();
499 return;
500 }
501
502 current_mtpd_owner_ = mtpd_service_owner;
503
504 // |bus_| must be valid here. Otherwise, how did this method get called as a
505 // callback in the first place?
506 DCHECK(bus_);
507 mtp_client_ = MediaTransferProtocolDaemonClient::Create(bus_.get());
508
509 // Set up signals and start initializing |storage_info_map_|.
510 mtp_client_->ListenForChanges(base::BindRepeating(
511 &MtpDeviceManager::OnStorageChanged, weak_ptr_factory_.GetWeakPtr()));
512 mtp_client_->EnumerateStorages(
513 base::BindOnce(&MtpDeviceManager::OnEnumerateStorages,
514 weak_ptr_factory_.GetWeakPtr()),
515 base::DoNothing::Once());
516 }
517
518 // static
Initialize()519 std::unique_ptr<MtpDeviceManager> MtpDeviceManager::Initialize() {
520 auto manager = std::make_unique<MtpDeviceManager>();
521
522 VLOG(1) << "MtpDeviceManager initialized";
523
524 #if DCHECK_IS_ON()
525 DCHECK(!g_mtp_device_manager);
526 g_mtp_device_manager = manager.get();
527 #endif
528
529 return manager;
530 }
531
532 } // namespace device
533