portable_device_watcher_win.cc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. // Copyright 2014 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. // Any tasks that communicates with the portable device may take >100ms to
  6. // complete. Those tasks should be run on an blocking thread instead of the
  7. // UI thread.
  8. #include "components/storage_monitor/portable_device_watcher_win.h"
  9. #include <dbt.h>
  10. #include <objbase.h>
  11. #include <portabledevice.h>
  12. #include <wrl/client.h>
  13. #include "base/bind.h"
  14. #include "base/containers/contains.h"
  15. #include "base/files/file_path.h"
  16. #include "base/logging.h"
  17. #include "base/strings/string_util.h"
  18. #include "base/strings/utf_string_conversions.h"
  19. #include "base/task/thread_pool.h"
  20. #include "base/threading/scoped_blocking_call.h"
  21. #include "base/win/scoped_co_mem.h"
  22. #include "base/win/scoped_propvariant.h"
  23. #include "components/storage_monitor/removable_device_constants.h"
  24. #include "components/storage_monitor/storage_info.h"
  25. #include "content/public/browser/browser_thread.h"
  26. namespace storage_monitor {
  27. namespace {
  28. // Name of the client application that communicates with the MTP device.
  29. const wchar_t kClientName[] = L"Chromium";
  30. // Returns true if |data| represents a class of portable devices.
  31. bool IsPortableDeviceStructure(LPARAM data) {
  32. DEV_BROADCAST_HDR* broadcast_hdr =
  33. reinterpret_cast<DEV_BROADCAST_HDR*>(data);
  34. if (!broadcast_hdr ||
  35. (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) {
  36. return false;
  37. }
  38. GUID guidDevInterface = GUID_NULL;
  39. if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface)))
  40. return false;
  41. DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
  42. reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
  43. return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0);
  44. }
  45. // Returns the portable device plug and play device ID string.
  46. std::wstring GetPnpDeviceId(LPARAM data) {
  47. DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
  48. reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
  49. if (!dev_interface)
  50. return std::wstring();
  51. std::wstring device_id(dev_interface->dbcc_name);
  52. DCHECK(base::IsStringASCII(device_id));
  53. return base::ToLowerASCII(device_id);
  54. }
  55. // Gets the friendly name of the device specified by the |pnp_device_id|. On
  56. // success, returns true and fills in |name|.
  57. bool GetFriendlyName(const std::wstring& pnp_device_id,
  58. IPortableDeviceManager* device_manager,
  59. std::wstring* name) {
  60. DCHECK(device_manager);
  61. DCHECK(name);
  62. DWORD name_len = 0;
  63. HRESULT hr = device_manager->GetDeviceFriendlyName(pnp_device_id.c_str(),
  64. nullptr, &name_len);
  65. if (FAILED(hr))
  66. return false;
  67. hr = device_manager->GetDeviceFriendlyName(
  68. pnp_device_id.c_str(), base::WriteInto(name, name_len), &name_len);
  69. return (SUCCEEDED(hr) && !name->empty());
  70. }
  71. // Gets the manufacturer name of the device specified by the |pnp_device_id|.
  72. // On success, returns true and fills in |name|.
  73. bool GetManufacturerName(const std::wstring& pnp_device_id,
  74. IPortableDeviceManager* device_manager,
  75. std::wstring* name) {
  76. DCHECK(device_manager);
  77. DCHECK(name);
  78. DWORD name_len = 0;
  79. HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
  80. nullptr, &name_len);
  81. if (FAILED(hr))
  82. return false;
  83. hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
  84. base::WriteInto(name, name_len),
  85. &name_len);
  86. return (SUCCEEDED(hr) && !name->empty());
  87. }
  88. // Gets the description of the device specified by the |pnp_device_id|. On
  89. // success, returns true and fills in |description|.
  90. bool GetDeviceDescription(const std::wstring& pnp_device_id,
  91. IPortableDeviceManager* device_manager,
  92. std::wstring* description) {
  93. DCHECK(device_manager);
  94. DCHECK(description);
  95. DWORD desc_len = 0;
  96. HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(),
  97. nullptr, &desc_len);
  98. if (FAILED(hr))
  99. return false;
  100. hr = device_manager->GetDeviceDescription(
  101. pnp_device_id.c_str(), base::WriteInto(description, desc_len), &desc_len);
  102. return (SUCCEEDED(hr) && !description->empty());
  103. }
  104. // On success, returns true and updates |client_info| with a reference to an
  105. // IPortableDeviceValues interface that holds information about the
  106. // application that communicates with the device.
  107. bool GetClientInformation(
  108. Microsoft::WRL::ComPtr<IPortableDeviceValues>* client_info) {
  109. HRESULT hr =
  110. ::CoCreateInstance(__uuidof(PortableDeviceValues), nullptr,
  111. CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&(*client_info)));
  112. if (FAILED(hr)) {
  113. DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
  114. return false;
  115. }
  116. // Attempt to set client details.
  117. (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName);
  118. (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0);
  119. (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
  120. (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0);
  121. (*client_info)->SetUnsignedIntegerValue(
  122. WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
  123. (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS,
  124. GENERIC_READ);
  125. return true;
  126. }
  127. // Opens the device for communication. |pnp_device_id| specifies the plug and
  128. // play device ID string. On success, returns true and updates |device| with a
  129. // reference to the portable device interface.
  130. bool SetUp(const std::wstring& pnp_device_id,
  131. Microsoft::WRL::ComPtr<IPortableDevice>* device) {
  132. Microsoft::WRL::ComPtr<IPortableDeviceValues> client_info;
  133. if (!GetClientInformation(&client_info))
  134. return false;
  135. HRESULT hr =
  136. ::CoCreateInstance(__uuidof(PortableDevice), nullptr,
  137. CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&(*device)));
  138. if (FAILED(hr)) {
  139. DPLOG(ERROR) << "Failed to create an instance of IPortableDevice";
  140. return false;
  141. }
  142. hr = (*device)->Open(pnp_device_id.c_str(), client_info.Get());
  143. if (SUCCEEDED(hr))
  144. return true;
  145. if (hr == E_ACCESSDENIED)
  146. DPLOG(ERROR) << "Access denied to open the device";
  147. return false;
  148. }
  149. // Returns the unique id property key of the object specified by the
  150. // |object_id|.
  151. REFPROPERTYKEY GetUniqueIdPropertyKey(const std::wstring& object_id) {
  152. return (object_id == WPD_DEVICE_OBJECT_ID) ?
  153. WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID;
  154. }
  155. // On success, returns true and populates |properties_to_read| with the
  156. // property key of the object specified by the |object_id|.
  157. bool PopulatePropertyKeyCollection(
  158. const std::wstring& object_id,
  159. Microsoft::WRL::ComPtr<IPortableDeviceKeyCollection>* properties_to_read) {
  160. HRESULT hr = ::CoCreateInstance(__uuidof(PortableDeviceKeyCollection),
  161. nullptr, CLSCTX_INPROC_SERVER,
  162. IID_PPV_ARGS(&(*properties_to_read)));
  163. if (FAILED(hr)) {
  164. DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
  165. return false;
  166. }
  167. REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
  168. hr = (*properties_to_read)->Add(key);
  169. return SUCCEEDED(hr);
  170. }
  171. // Wrapper function to get content property string value.
  172. bool GetStringPropertyValue(IPortableDeviceValues* properties_values,
  173. REFPROPERTYKEY key,
  174. std::wstring* value) {
  175. DCHECK(properties_values);
  176. DCHECK(value);
  177. base::win::ScopedCoMem<wchar_t> buffer;
  178. HRESULT hr = properties_values->GetStringValue(key, &buffer);
  179. if (FAILED(hr))
  180. return false;
  181. *value = static_cast<const wchar_t*>(buffer);
  182. return true;
  183. }
  184. // Constructs a unique identifier for the object specified by the |object_id|.
  185. // On success, returns true and fills in |unique_id|.
  186. bool GetObjectUniqueId(IPortableDevice* device,
  187. const std::wstring& object_id,
  188. std::wstring* unique_id) {
  189. DCHECK(device);
  190. DCHECK(unique_id);
  191. Microsoft::WRL::ComPtr<IPortableDeviceContent> content;
  192. HRESULT hr = device->Content(&content);
  193. if (FAILED(hr)) {
  194. DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
  195. return false;
  196. }
  197. Microsoft::WRL::ComPtr<IPortableDeviceProperties> properties;
  198. hr = content->Properties(&properties);
  199. if (FAILED(hr)) {
  200. DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
  201. return false;
  202. }
  203. Microsoft::WRL::ComPtr<IPortableDeviceKeyCollection> properties_to_read;
  204. if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
  205. return false;
  206. Microsoft::WRL::ComPtr<IPortableDeviceValues> properties_values;
  207. if (FAILED(properties->GetValues(object_id.c_str(), properties_to_read.Get(),
  208. &properties_values))) {
  209. return false;
  210. }
  211. REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
  212. return GetStringPropertyValue(properties_values.Get(), key, unique_id);
  213. }
  214. // Constructs the device storage unique identifier using |device_serial_num| and
  215. // |storage_id|. On success, returns true and fills in |device_storage_id|.
  216. bool ConstructDeviceStorageUniqueId(const std::wstring& device_serial_num,
  217. const std::wstring& storage_id,
  218. std::string* device_storage_id) {
  219. if (device_serial_num.empty() && storage_id.empty())
  220. return false;
  221. DCHECK(device_storage_id);
  222. *device_storage_id = StorageInfo::MakeDeviceId(
  223. StorageInfo::MTP_OR_PTP,
  224. base::WideToUTF8(storage_id + L':' + device_serial_num));
  225. return true;
  226. }
  227. // Gets a list of removable storage object identifiers present in |device|.
  228. // On success, returns true and fills in |storage_object_ids|.
  229. bool GetRemovableStorageObjectIds(
  230. IPortableDevice* device,
  231. PortableDeviceWatcherWin::StorageObjectIDs* storage_object_ids) {
  232. DCHECK(device);
  233. DCHECK(storage_object_ids);
  234. Microsoft::WRL::ComPtr<IPortableDeviceCapabilities> capabilities;
  235. HRESULT hr = device->Capabilities(&capabilities);
  236. if (FAILED(hr)) {
  237. DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
  238. return false;
  239. }
  240. Microsoft::WRL::ComPtr<IPortableDevicePropVariantCollection> storage_ids;
  241. hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
  242. &storage_ids);
  243. if (FAILED(hr)) {
  244. DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
  245. return false;
  246. }
  247. DWORD num_storage_obj_ids = 0;
  248. hr = storage_ids->GetCount(&num_storage_obj_ids);
  249. if (FAILED(hr))
  250. return false;
  251. for (DWORD index = 0; index < num_storage_obj_ids; ++index) {
  252. base::win::ScopedPropVariant object_id;
  253. hr = storage_ids->GetAt(index, object_id.Receive());
  254. if (SUCCEEDED(hr) && object_id.get().vt == VT_LPWSTR &&
  255. object_id.get().pwszVal != nullptr) {
  256. storage_object_ids->push_back(object_id.get().pwszVal);
  257. }
  258. }
  259. return true;
  260. }
  261. // Returns true if the portable device belongs to a mass storage class.
  262. // |pnp_device_id| specifies the plug and play device id.
  263. // |device_name| specifies the name of the device.
  264. bool IsMassStoragePortableDevice(const std::wstring& pnp_device_id,
  265. const std::wstring& device_name) {
  266. // Based on testing, if the pnp device id starts with "\\?\wpdbusenumroot#",
  267. // then the attached device belongs to a mass storage class.
  268. if (base::StartsWith(pnp_device_id, L"\\\\?\\wpdbusenumroot#",
  269. base::CompareCase::INSENSITIVE_ASCII))
  270. return true;
  271. // If the device is a volume mounted device, |device_name| will be
  272. // the volume name.
  273. return ((device_name.length() >= 2) && (device_name[1] == L':') &&
  274. (((device_name[0] >= L'A') && (device_name[0] <= L'Z')) ||
  275. ((device_name[0] >= L'a') && (device_name[0] <= L'z'))));
  276. }
  277. // Returns the name of the device specified by |pnp_device_id|.
  278. std::wstring GetDeviceNameOnBlockingThread(
  279. IPortableDeviceManager* portable_device_manager,
  280. const std::wstring& pnp_device_id) {
  281. DCHECK(portable_device_manager);
  282. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  283. base::BlockingType::MAY_BLOCK);
  284. std::wstring name;
  285. GetFriendlyName(pnp_device_id, portable_device_manager, &name) ||
  286. GetDeviceDescription(pnp_device_id, portable_device_manager, &name) ||
  287. GetManufacturerName(pnp_device_id, portable_device_manager, &name);
  288. return name;
  289. }
  290. // Access the device and gets the device storage details. On success, returns
  291. // true and populates |storage_objects| with device storage details.
  292. bool GetDeviceStorageObjectsOnBlockingThread(
  293. const std::wstring& pnp_device_id,
  294. PortableDeviceWatcherWin::StorageObjects* storage_objects) {
  295. DCHECK(storage_objects);
  296. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  297. base::BlockingType::MAY_BLOCK);
  298. Microsoft::WRL::ComPtr<IPortableDevice> device;
  299. if (!SetUp(pnp_device_id, &device))
  300. return false;
  301. std::wstring device_serial_num;
  302. if (!GetObjectUniqueId(device.Get(), WPD_DEVICE_OBJECT_ID,
  303. &device_serial_num)) {
  304. return false;
  305. }
  306. PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids;
  307. if (!GetRemovableStorageObjectIds(device.Get(), &storage_obj_ids))
  308. return false;
  309. for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator id_iter =
  310. storage_obj_ids.begin(); id_iter != storage_obj_ids.end(); ++id_iter) {
  311. std::wstring storage_persistent_id;
  312. if (!GetObjectUniqueId(device.Get(), *id_iter, &storage_persistent_id))
  313. continue;
  314. std::string device_storage_id;
  315. if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id,
  316. &device_storage_id)) {
  317. storage_objects->push_back(PortableDeviceWatcherWin::DeviceStorageObject(
  318. *id_iter, device_storage_id));
  319. }
  320. }
  321. return true;
  322. }
  323. // Accesses the device and gets the device details (name, storage info, etc).
  324. // On success returns true and fills in |device_details|. On failure, returns
  325. // false. |pnp_device_id| specifies the plug and play device ID string.
  326. bool GetDeviceInfoOnBlockingThread(
  327. IPortableDeviceManager* portable_device_manager,
  328. const std::wstring& pnp_device_id,
  329. PortableDeviceWatcherWin::DeviceDetails* device_details) {
  330. DCHECK(portable_device_manager);
  331. DCHECK(device_details);
  332. DCHECK(!pnp_device_id.empty());
  333. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  334. base::BlockingType::MAY_BLOCK);
  335. device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager,
  336. pnp_device_id);
  337. if (IsMassStoragePortableDevice(pnp_device_id, device_details->name))
  338. return false;
  339. device_details->location = pnp_device_id;
  340. PortableDeviceWatcherWin::StorageObjects storage_objects;
  341. return GetDeviceStorageObjectsOnBlockingThread(
  342. pnp_device_id, &device_details->storage_objects);
  343. }
  344. // Wrapper function to get an instance of portable device manager. On success,
  345. // returns true and fills in |portable_device_mgr|. On failure, returns false.
  346. bool GetPortableDeviceManager(
  347. Microsoft::WRL::ComPtr<IPortableDeviceManager>* portable_device_mgr) {
  348. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  349. base::BlockingType::MAY_BLOCK);
  350. HRESULT hr = ::CoCreateInstance(__uuidof(PortableDeviceManager), nullptr,
  351. CLSCTX_INPROC_SERVER,
  352. IID_PPV_ARGS(&(*portable_device_mgr)));
  353. if (SUCCEEDED(hr))
  354. return true;
  355. // Either there is no portable device support (Windows XP with old versions of
  356. // Media Player) or the thread does not have COM initialized.
  357. DCHECK_NE(CO_E_NOTINITIALIZED, hr);
  358. return false;
  359. }
  360. // Enumerates the attached portable devices. On success, returns true and fills
  361. // in |devices| with the attached portable device details. On failure, returns
  362. // false.
  363. bool EnumerateAttachedDevicesOnBlockingThread(
  364. PortableDeviceWatcherWin::Devices* devices) {
  365. DCHECK(devices);
  366. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  367. base::BlockingType::MAY_BLOCK);
  368. Microsoft::WRL::ComPtr<IPortableDeviceManager> portable_device_mgr;
  369. if (!GetPortableDeviceManager(&portable_device_mgr))
  370. return false;
  371. // Get the total number of devices found on the system.
  372. DWORD pnp_device_count = 0;
  373. HRESULT hr = portable_device_mgr->GetDevices(nullptr, &pnp_device_count);
  374. if (FAILED(hr))
  375. return false;
  376. std::unique_ptr<wchar_t*[]> pnp_device_ids(new wchar_t*[pnp_device_count]);
  377. hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count);
  378. if (FAILED(hr))
  379. return false;
  380. for (DWORD index = 0; index < pnp_device_count; ++index) {
  381. PortableDeviceWatcherWin::DeviceDetails device_details;
  382. if (GetDeviceInfoOnBlockingThread(portable_device_mgr.Get(),
  383. pnp_device_ids[index], &device_details))
  384. devices->push_back(device_details);
  385. CoTaskMemFree(pnp_device_ids[index]);
  386. }
  387. return !devices->empty();
  388. }
  389. // Handles the device attach event message on a media task runner.
  390. // |pnp_device_id| specifies the attached plug and play device ID string. On
  391. // success, returns true and populates |device_details| with device information.
  392. // On failure, returns false.
  393. bool HandleDeviceAttachedEventOnBlockingThread(
  394. const std::wstring& pnp_device_id,
  395. PortableDeviceWatcherWin::DeviceDetails* device_details) {
  396. DCHECK(device_details);
  397. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  398. base::BlockingType::MAY_BLOCK);
  399. Microsoft::WRL::ComPtr<IPortableDeviceManager> portable_device_mgr;
  400. if (!GetPortableDeviceManager(&portable_device_mgr))
  401. return false;
  402. // Sometimes, portable device manager doesn't have the new device details.
  403. // Refresh the manager device list to update its details.
  404. portable_device_mgr->RefreshDeviceList();
  405. return GetDeviceInfoOnBlockingThread(portable_device_mgr.Get(), pnp_device_id,
  406. device_details);
  407. }
  408. // Registers |hwnd| to receive portable device notification details. On success,
  409. // returns the device notifications handle else returns NULL.
  410. HDEVNOTIFY RegisterPortableDeviceNotification(HWND hwnd) {
  411. GUID dev_interface_guid = GUID_NULL;
  412. HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &dev_interface_guid);
  413. if (FAILED(hr))
  414. return nullptr;
  415. DEV_BROADCAST_DEVICEINTERFACE db = {
  416. sizeof(DEV_BROADCAST_DEVICEINTERFACE),
  417. DBT_DEVTYP_DEVICEINTERFACE,
  418. 0,
  419. dev_interface_guid
  420. };
  421. return RegisterDeviceNotification(hwnd, &db, DEVICE_NOTIFY_WINDOW_HANDLE);
  422. }
  423. } // namespace
  424. // PortableDeviceWatcherWin ---------------------------------------------------
  425. PortableDeviceWatcherWin::DeviceStorageObject::DeviceStorageObject(
  426. const std::wstring& temporary_id,
  427. const std::string& persistent_id)
  428. : object_temporary_id(temporary_id), object_persistent_id(persistent_id) {}
  429. PortableDeviceWatcherWin::DeviceDetails::DeviceDetails() {
  430. }
  431. PortableDeviceWatcherWin::DeviceDetails::DeviceDetails(
  432. const DeviceDetails& other) = default;
  433. PortableDeviceWatcherWin::DeviceDetails::~DeviceDetails() {
  434. }
  435. PortableDeviceWatcherWin::PortableDeviceWatcherWin()
  436. : notifications_(nullptr), storage_notifications_(nullptr) {}
  437. PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
  438. UnregisterDeviceNotification(notifications_);
  439. }
  440. void PortableDeviceWatcherWin::Init(HWND hwnd) {
  441. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  442. notifications_ = RegisterPortableDeviceNotification(hwnd);
  443. media_task_runner_ = base::ThreadPool::CreateCOMSTATaskRunner(
  444. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  445. base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
  446. EnumerateAttachedDevices();
  447. }
  448. void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
  449. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  450. if (!IsPortableDeviceStructure(data))
  451. return;
  452. std::wstring device_id = GetPnpDeviceId(data);
  453. if (event_type == DBT_DEVICEARRIVAL)
  454. HandleDeviceAttachEvent(device_id);
  455. else if (event_type == DBT_DEVICEREMOVECOMPLETE)
  456. HandleDeviceDetachEvent(device_id);
  457. }
  458. bool PortableDeviceWatcherWin::GetMTPStorageInfoFromDeviceId(
  459. const std::string& storage_device_id,
  460. std::wstring* device_location,
  461. std::wstring* storage_object_id) const {
  462. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  463. DCHECK(device_location);
  464. DCHECK(storage_object_id);
  465. MTPStorageMap::const_iterator storage_map_iter =
  466. storage_map_.find(storage_device_id);
  467. if (storage_map_iter == storage_map_.end())
  468. return false;
  469. MTPDeviceMap::const_iterator device_iter =
  470. device_map_.find(storage_map_iter->second.location());
  471. if (device_iter == device_map_.end())
  472. return false;
  473. const StorageObjects& storage_objects = device_iter->second;
  474. for (StorageObjects::const_iterator storage_object_iter =
  475. storage_objects.begin(); storage_object_iter != storage_objects.end();
  476. ++storage_object_iter) {
  477. if (storage_device_id == storage_object_iter->object_persistent_id) {
  478. *device_location = storage_map_iter->second.location();
  479. *storage_object_id = storage_object_iter->object_temporary_id;
  480. return true;
  481. }
  482. }
  483. return false;
  484. }
  485. // static
  486. std::wstring PortableDeviceWatcherWin::GetStoragePathFromStorageId(
  487. const std::string& storage_unique_id) {
  488. // Construct a dummy device path using the storage name. This is only used
  489. // for registering the device media file system.
  490. DCHECK(!storage_unique_id.empty());
  491. return base::UTF8ToWide("\\\\" + storage_unique_id);
  492. }
  493. void PortableDeviceWatcherWin::SetNotifications(
  494. StorageMonitor::Receiver* notifications) {
  495. storage_notifications_ = notifications;
  496. }
  497. void PortableDeviceWatcherWin::EjectDevice(
  498. const std::string& device_id,
  499. base::OnceCallback<void(StorageMonitor::EjectStatus)> callback) {
  500. // MTP devices on Windows don't have a detach API needed -- signal
  501. // the object as if the device is gone and tell the caller it is OK
  502. // to remove.
  503. std::wstring device_location; // The device_map_ key.
  504. std::wstring storage_object_id;
  505. if (!GetMTPStorageInfoFromDeviceId(device_id,
  506. &device_location, &storage_object_id)) {
  507. std::move(callback).Run(StorageMonitor::EJECT_NO_SUCH_DEVICE);
  508. return;
  509. }
  510. HandleDeviceDetachEvent(device_location);
  511. std::move(callback).Run(StorageMonitor::EJECT_OK);
  512. }
  513. void PortableDeviceWatcherWin::EnumerateAttachedDevices() {
  514. DCHECK(media_task_runner_);
  515. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  516. Devices* devices = new Devices;
  517. base::PostTaskAndReplyWithResult(
  518. media_task_runner_.get(), FROM_HERE,
  519. base::BindOnce(&EnumerateAttachedDevicesOnBlockingThread, devices),
  520. base::BindOnce(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices,
  521. weak_ptr_factory_.GetWeakPtr(), base::Owned(devices)));
  522. }
  523. void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices(
  524. const Devices* devices, const bool result) {
  525. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  526. DCHECK(devices);
  527. if (!result)
  528. return;
  529. for (Devices::const_iterator device_iter = devices->begin();
  530. device_iter != devices->end(); ++device_iter) {
  531. OnDidHandleDeviceAttachEvent(&(*device_iter), result);
  532. }
  533. }
  534. void PortableDeviceWatcherWin::HandleDeviceAttachEvent(
  535. const std::wstring& pnp_device_id) {
  536. DCHECK(media_task_runner_);
  537. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  538. DeviceDetails* device_details = new DeviceDetails;
  539. base::PostTaskAndReplyWithResult(
  540. media_task_runner_.get(), FROM_HERE,
  541. base::BindOnce(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id,
  542. device_details),
  543. base::BindOnce(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent,
  544. weak_ptr_factory_.GetWeakPtr(),
  545. base::Owned(device_details)));
  546. }
  547. void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent(
  548. const DeviceDetails* device_details, const bool result) {
  549. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  550. DCHECK(device_details);
  551. if (!result)
  552. return;
  553. const StorageObjects& storage_objects = device_details->storage_objects;
  554. const std::wstring& name = device_details->name;
  555. const std::wstring& location = device_details->location;
  556. DCHECK(!base::Contains(device_map_, location));
  557. for (StorageObjects::const_iterator storage_iter = storage_objects.begin();
  558. storage_iter != storage_objects.end(); ++storage_iter) {
  559. const std::string& storage_id = storage_iter->object_persistent_id;
  560. DCHECK(!base::Contains(storage_map_, storage_id));
  561. if (storage_id.empty() || name.empty())
  562. return;
  563. // Device can have several data partitions. Therefore, add the
  564. // partition identifier to the model name. E.g.: "Nexus 7 (s10001)"
  565. std::wstring model_name(name + L" (" + storage_iter->object_temporary_id +
  566. L')');
  567. StorageInfo info(storage_id, location, std::u16string(), std::u16string(),
  568. base::WideToUTF16(model_name), 0);
  569. storage_map_[storage_id] = info;
  570. if (storage_notifications_) {
  571. info.set_location(GetStoragePathFromStorageId(storage_id));
  572. storage_notifications_->ProcessAttach(info);
  573. }
  574. }
  575. device_map_[location] = storage_objects;
  576. }
  577. void PortableDeviceWatcherWin::HandleDeviceDetachEvent(
  578. const std::wstring& pnp_device_id) {
  579. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  580. MTPDeviceMap::iterator device_iter = device_map_.find(pnp_device_id);
  581. if (device_iter == device_map_.end())
  582. return;
  583. const StorageObjects& storage_objects = device_iter->second;
  584. for (StorageObjects::const_iterator storage_object_iter =
  585. storage_objects.begin(); storage_object_iter != storage_objects.end();
  586. ++storage_object_iter) {
  587. std::string storage_id = storage_object_iter->object_persistent_id;
  588. MTPStorageMap::iterator storage_map_iter = storage_map_.find(storage_id);
  589. DCHECK(storage_map_iter != storage_map_.end());
  590. if (storage_notifications_) {
  591. storage_notifications_->ProcessDetach(
  592. storage_map_iter->second.device_id());
  593. }
  594. storage_map_.erase(storage_map_iter);
  595. }
  596. device_map_.erase(device_iter);
  597. }
  598. } // namespace storage_monitor