sbIPDMarshall.cpp
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 :miv */
3 /*
4 //=BEGIN SONGBIRD GPL
5 //
6 // This file is part of the Songbird web player.
7 //
8 // Copyright(c) 2005-2009 POTI, Inc.
9 // http://www.songbirdnest.com
10 //
11 // This file may be licensed under the terms of of the
12 // GNU General Public License Version 2 (the GPL).
13 //
14 // Software distributed under the License is distributed
15 // on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either
16 // express or implied. See the GPL for the specific language
17 // governing rights and limitations.
18 //
19 // You should have received a copy of the GPL along with this
20 // program. If not, go to http://www.gnu.org/licenses/gpl.html
21 // or write to the Free Software Foundation, Inc.,
22 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 //
24 //=END SONGBIRD GPL
25 */
26 
27 //------------------------------------------------------------------------------
28 //
29 // iPod device marshall services.
30 //
31 // The iPod device marshall services scan for iPod devices at startup and
32 // listen for added and removed devices. The marshall communicates these events
33 // to the other device services.
34 //
35 // Thread strategy:
36 //
37 // Most marshall interfaces are not typically called from more than one
38 // thread. The marshall device event handlers are always called from the main
39 // thread. A single lock is thus used to serialize thread access since multiple
40 // threads are not expected to be blocked.
41 // A few exceptions to this strategy will use alternate methods or no locking
42 // (e.g., GetId).
43 //
44 //------------------------------------------------------------------------------
45 
46 
52 //------------------------------------------------------------------------------
53 //
54 // iPod device marshall imported services.
55 //
56 //------------------------------------------------------------------------------
57 
58 // Local imports.
59 #include "sbIPDLog.h"
60 #include "sbIPDMarshall.h"
61 #include "sbIPDWindowsUtils.h"
62 
63 // Songbird imports.
64 #include <sbIDevice.h>
65 #include <sbIDeviceController.h>
66 #include <sbIDeviceControllerRegistrar.h>
67 #include <sbIDeviceEvent.h>
68 
69 // Mozilla imports.
70 #include <nsIClassInfoImpl.h>
71 #include <nsIProgrammingLanguage.h>
72 #include <nsIWritablePropertyBag.h>
73 #include <nsMemory.h>
74 #include <nsServiceManagerUtils.h>
75 #include <prprf.h>
76 
77 // Win32 imports.
78 #include <dbt.h>
79 #include <devioctl.h>
80 #include <tchar.h>
81 
82 #include <ntddstor.h>
83 
84 // Fix up some Win32 conflicts.
85 #undef CreateEvent
86 
87 
88 //------------------------------------------------------------------------------
89 //
90 // iPod device marshall nsISupports and nsIClassInfo implementation.
91 //
92 //------------------------------------------------------------------------------
93 
94 // nsISupports implementation.
95 // NS_IMPL_THREADSAFE_ISUPPORTS1_CI(sbIPDMarshall, sbIDeviceMarshall)
98 NS_IMPL_QUERY_INTERFACE1_CI(sbIPDMarshall, sbIDeviceMarshall)
100 
101 // nsIClassInfo implementation.
102 NS_IMPL_THREADSAFE_CI(sbIPDMarshall)
103 
104 
105 //------------------------------------------------------------------------------
106 //
107 // iPod device marshall sbIDeviceMarshall implementation.
108 //
109 //------------------------------------------------------------------------------
110 
116 NS_IMETHODIMP
117 sbIPDMarshall::LoadControllers(sbIDeviceControllerRegistrar* aRegistrar)
118 {
119  // Validate parameters.
120  NS_ENSURE_ARG_POINTER(aRegistrar);
121 
122  // Register the controllers.
123  RegisterControllers(aRegistrar);
124 
125  return NS_OK;
126 }
127 
128 
134 NS_IMETHODIMP
135 sbIPDMarshall::BeginMonitoring()
136 {
137  // Serialize.
138  nsAutoMonitor mon(mMonitor);
139 
140  // Initialize the event services.
141  EventInitialize();
142 
143  // Scan for connected devices.
144  ScanForConnectedDevices();
145 
146  return NS_OK;
147 }
148 
149 
155 NS_IMETHODIMP
156 sbIPDMarshall::StopMonitoring()
157 {
158  // Serialize.
159  nsAutoMonitor mon(mMonitor);
160 
161  // Finalize the event services.
162  EventFinalize();
163 
164  return NS_OK;
165 }
166 
167 
168 //
169 // Getters/setters.
170 //
171 
176 NS_IMETHODIMP
177 sbIPDMarshall::GetId(nsID** aId)
178 {
179  // Validate parameters.
180  NS_ENSURE_ARG_POINTER(aId);
181 
182  // Allocate an nsID.
183  nsID* pId = static_cast<nsID*>(NS_Alloc(sizeof(nsID)));
184  NS_ENSURE_TRUE(pId, NS_ERROR_OUT_OF_MEMORY);
185 
186  // Return the ID.
187  static nsID const id = SB_IPDMARSHALL_CID;
188  *pId = id;
189  *aId = pId;
190 
191  return NS_OK;
192 }
193 
194 
199 NS_IMETHODIMP
200 sbIPDMarshall::GetName(nsAString& aName)
201 {
202  aName.AssignLiteral(SB_IPDMARSHALL_CLASSNAME);
203  return NS_OK;
204 }
205 
206 
207 //------------------------------------------------------------------------------
208 //
209 // iPod device marshall public services.
210 //
211 //------------------------------------------------------------------------------
212 
219  mEventWindow((HWND) INVALID_HANDLE_VALUE),
220  mEventWindowClass(NULL)
221 {
222  // Initialize the logging services.
224 
225  // Initialize the device marshall services.
226  Initialize();
227 }
228 
229 
235 {
236  // Finalize the device marshall services.
237  Finalize();
238 }
239 
240 
241 //------------------------------------------------------------------------------
242 //
243 // iPod device marshall event services.
244 //
245 //------------------------------------------------------------------------------
246 
251 nsresult
252 sbIPDMarshall::EventInitialize()
253 {
254  // Create an event window class set up with the device event handler.
255  WNDCLASS wndClass;
256  ZeroMemory(&wndClass, sizeof(wndClass));
257  wndClass.hInstance = GetModuleHandle(NULL);
258  wndClass.lpfnWndProc = WindowEventHandler;
259  wndClass.lpszClassName = _T(SB_IPDMARSHALL_CLASSNAME);
260  mEventWindowClass = RegisterClass(&wndClass);
261  NS_ENSURE_TRUE(mEventWindowClass, NS_ERROR_UNEXPECTED);
262 
263  // Create a window for receiving device events.
264  mEventWindow = CreateWindow(MAKEINTATOM(mEventWindowClass),
265  NULL,
266  WS_POPUP,
267  0,
268  0,
269  1,
270  1,
271  NULL,
272  NULL,
273  GetModuleHandle(NULL),
274  NULL);
275  NS_ENSURE_TRUE(mEventWindow != INVALID_HANDLE_VALUE, NS_ERROR_UNEXPECTED);
276  SetLastError(0);
277  SetWindowLong(mEventWindow, GWL_USERDATA, (LONG) this);
278  NS_ENSURE_FALSE(GetLastError(), NS_ERROR_UNEXPECTED);
279 
280  return NS_OK;
281 }
282 
283 
288 void
289 sbIPDMarshall::EventFinalize()
290 {
291  // Destroy the device event window.
292  if (mEventWindow != INVALID_HANDLE_VALUE) {
293  DestroyWindow(mEventWindow);
294  mEventWindow = (HWND) INVALID_HANDLE_VALUE;
295  }
296 
297  // Unregister the device event window class.
298  if (mEventWindowClass) {
299  UnregisterClass(MAKEINTATOM(mEventWindowClass), GetModuleHandle(NULL));
300  mEventWindowClass = NULL;
301  }
302 }
303 
304 
318 /* static */
319 LRESULT CALLBACK
320 sbIPDMarshall::WindowEventHandler(HWND hwnd,
321  UINT msg,
322  WPARAM param1,
323  LPARAM param2)
324 {
325  // Trace execution.
326  FIELD_LOG(("Enter: sbIPDMarshall::WindowEventHandler\n"));
327 
328  // Handle the event within an object context.
329  sbIPDMarshall* marshall = (sbIPDMarshall *) GetWindowLong(hwnd, GWL_USERDATA);
330  NS_ENSURE_TRUE(marshall, 1);
331  marshall->_WindowEventHandler(hwnd, msg, param1, param2);
332 
333  // Trace execution.
334  FIELD_LOG(("Exit: sbIPDMarshall::WindowEventHandler\n"));
335 
336  return 1;
337 }
338 
339 void
340 sbIPDMarshall::_WindowEventHandler(HWND hwnd,
341  UINT msg,
342  WPARAM param1,
343  LPARAM param2)
344 {
345  // Only handle device events.
346  if (msg != WM_DEVICECHANGE)
347  return;
348 
349  // Only handle volume device events.
350  PDEV_BROADCAST_HDR pDevBroadcastHdr = (PDEV_BROADCAST_HDR) param2;
351  if (!pDevBroadcastHdr)
352  return;
353  if (pDevBroadcastHdr->dbch_devicetype != DBT_DEVTYP_VOLUME)
354  return;
355 
356  // Get the broadcast volume event data record.
357  PDEV_BROADCAST_VOLUME pDevBroadcastVolume = (PDEV_BROADCAST_VOLUME) param2;
358 
359  // Get the volume drive letter.
360  char driveLetter;
361  int unitmask = pDevBroadcastVolume->dbcv_unitmask;
362  for (driveLetter = 'A'; driveLetter <= 'Z'; driveLetter++) {
363  if (unitmask & 0x01)
364  break;
365  unitmask >>= 1;
366  }
367 
368  // Dispatch event handling.
369  switch (param1)
370  {
371  case DBT_DEVICEARRIVAL :
372  HandleAddedEvent(driveLetter);
373  break;
374 
375  case DBT_DEVICEREMOVECOMPLETE :
376  HandleRemovedEvent(driveLetter);
377  break;
378 
379  default :
380  break;
381  }
382 }
383 
384 
390 void
391 sbIPDMarshall::HandleAddedEvent(char aDriveLetter)
392 {
393  nsCOMPtr<sbIDevice> device;
394  nsresult rv;
395 
396  // Serialize.
397  nsAutoMonitor mon(mMonitor);
398 
399  // Do nothing if device is not an iPod.
400  if (!IsIPod(aDriveLetter))
401  return;
402 
403  // Log progress.
404  FIELD_LOG(("Enter: HandleAddedEvent %c\n", aDriveLetter));
405 
406  // Do nothing if device has already been added.
407  if (mDeviceList.Get(aDriveLetter, getter_AddRefs(device)))
408  return;
409 
410  // Set up the device properties.
411  nsCOMPtr<nsIWritablePropertyBag>
412  propBag = do_CreateInstance("@mozilla.org/hash-property-bag;1", &rv);
413  NS_ENSURE_SUCCESS(rv, /* void */);
414  rv = propBag->SetProperty(NS_LITERAL_STRING("DeviceType"),
415  sbIPDVariant("iPod").get());
416  NS_ENSURE_SUCCESS(rv, /* void */);
417  rv = propBag->SetProperty(NS_LITERAL_STRING("DriveLetter"),
418  sbIPDVariant(aDriveLetter).get());
419  NS_ENSURE_SUCCESS(rv, /* void */);
420 
421  // Find a controller for the device. Do nothing more if none found.
422  nsCOMPtr<sbIDeviceController> controller = FindCompatibleControllers(propBag);
423  if (!controller)
424  return;
425 
426  // Create a device.
427  rv = controller->CreateDevice(propBag, getter_AddRefs(device));
428  NS_ENSURE_SUCCESS(rv, /* void */);
429 
430  // Register the device.
431  rv = mDeviceRegistrar->RegisterDevice(device);
432  NS_ENSURE_SUCCESS(rv, /* void */);
433 
434  // Add device to device list.
435  mDeviceList.Put(aDriveLetter, device);
436 
437  // Send device added notification.
439  sbIPDVariant(device).get(),
440  (sbIDeviceMarshall *) this);
441 }
442 
443 
449 void
450 sbIPDMarshall::HandleRemovedEvent(char aDriveLetter)
451 {
452  nsresult rv;
453 
454  // Serialize.
455  nsAutoMonitor mon(mMonitor);
456 
457  // Get removed device. Do nothing if device has not been added.
458  nsCOMPtr<sbIDevice> device;
459  if (!mDeviceList.Get(aDriveLetter, getter_AddRefs(device)))
460  return;
461 
462  // Log progress.
463  FIELD_LOG(("Enter: HandleRemovedEvent %c\n", aDriveLetter));
464 
465  // Get the device and device controller registrars.
466  nsCOMPtr<sbIDeviceRegistrar> deviceRegistrar =
467  do_GetService("@songbirdnest.com/Songbird/DeviceManager;2", &rv);
468  NS_ENSURE_SUCCESS(rv, /* void */);
469  nsCOMPtr<sbIDeviceControllerRegistrar> deviceControllerRegistrar =
470  do_GetService("@songbirdnest.com/Songbird/DeviceManager;2", &rv);
471  NS_ENSURE_SUCCESS(rv, /* void */);
472 
473  // Get the device controller ID and set it up for auto-disposal.
474  nsID *controllerID = nsnull;
475  rv = device->GetControllerId(&controllerID);
476  sbAutoNSMemPtr autoControllerID(controllerID);
477 
478  // Get the device controller.
479  nsCOMPtr<sbIDeviceController> deviceController;
480  if (NS_SUCCEEDED(rv)) {
481  rv = deviceControllerRegistrar->GetController
482  (controllerID,
483  getter_AddRefs(deviceController));
484  }
485  if (NS_FAILED(rv)) {
486  NS_WARNING("Failed to get device controller.");
487  deviceController = nsnull;
488  }
489 
490  // Release the device from the controller.
491  if (deviceController) {
492  rv = deviceController->ReleaseDevice(device);
493  if (NS_FAILED(rv))
494  NS_WARNING("Failed to release device.");
495  }
496 
497  // Unregister the device.
498  rv = deviceRegistrar->UnregisterDevice(device);
499  if (NS_FAILED(rv))
500  NS_WARNING("Failed to unregister device.");
501 
502  // Remove device from device list.
503  mDeviceList.Remove(aDriveLetter);
504 
505  // Send device removed notification.
507  sbIPDVariant(device).get(),
508  (sbIDeviceMarshall *) this);
509 }
510 
511 
512 //------------------------------------------------------------------------------
513 //
514 // Internal iPod device marshall services.
515 //
516 //------------------------------------------------------------------------------
517 
522 nsresult
523 sbIPDMarshall::Initialize()
524 {
525  PRBool success;
526  nsresult rv;
527 
528  // Create the device marshall services monitor.
529  mMonitor = nsAutoMonitor::NewMonitor("sbIPDMarshall.mMonitor");
530  NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
531 
532  // Get the device manager and registrar.
533  mDeviceManager = do_GetService("@songbirdnest.com/Songbird/DeviceManager;2",
534  &rv);
535  NS_ENSURE_SUCCESS(rv, rv);
536  mDeviceRegistrar = do_QueryInterface(mDeviceManager, &rv);
537  NS_ENSURE_SUCCESS(rv, rv);
538 
539  // Initialize the device list.
540  success = mDeviceList.Init();
541  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
542 
543  return NS_OK;
544 }
545 
546 
551 void
552 sbIPDMarshall::Finalize()
553 {
554  // Dispose of the device marshall services monitor.
555  if (mMonitor)
556  {
557  nsAutoMonitor::DestroyMonitor(mMonitor);
558  mMonitor = nsnull;
559  }
560 
561  // Release object references.
562  mDeviceManager = nsnull;
563  mDeviceRegistrar = nsnull;
564 }
565 
566 
571 nsresult
572 sbIPDMarshall::ScanForConnectedDevices()
573 {
574  // Scan all drive letters for an iPod device and simulate a device added
575  // event.
576  for (char driveLetter = 'A'; driveLetter <= 'Z'; driveLetter++) {
577  HandleAddedEvent(driveLetter);
578  }
579 
580  return NS_OK;
581 }
582 
583 
592 PRBool
593 sbIPDMarshall::IsIPod(char aDriveLetter)
594 {
595  PRBool isIPod = PR_FALSE;
596  BOOL success;
597 
598  // Produce the device path.
599  char devicePath[8];
600  PR_snprintf(devicePath, sizeof(devicePath), "\\\\.\\%c:", aDriveLetter);
601 
602  // Trace execution.
603  FIELD_LOG(("1: IsIPod %s\n", devicePath));
604 
605  // Create a file interface for the device and set up auto-closing for the
606  // handle.
607  HANDLE hDev = CreateFileA(devicePath,
608  0,
609  0,
610  NULL,
611  OPEN_EXISTING,
612  0,
613  NULL);
614  if (hDev == INVALID_HANDLE_VALUE)
615  return PR_FALSE;
616  sbAutoHANDLE autoHDev(hDev);
617 
618  // Trace execution.
619  FIELD_LOG(("2: IsIPod\n"));
620 
621  // Query the device for information.
622  STORAGE_PROPERTY_QUERY query;
623  UCHAR queryData[512];
624  DWORD queryDataSize;
625  query.PropertyId = StorageDeviceProperty;
626  query.QueryType = PropertyStandardQuery;
627  success = DeviceIoControl(hDev,
628  IOCTL_STORAGE_QUERY_PROPERTY,
629  &query,
630  sizeof (STORAGE_PROPERTY_QUERY),
631  &queryData,
632  sizeof(queryData),
633  &queryDataSize,
634  NULL);
635  if (!success)
636  return PR_FALSE;
637 
638  // Get the device vendor and product IDs.
639  PSTORAGE_DEVICE_DESCRIPTOR pDevDesc = (PSTORAGE_DEVICE_DESCRIPTOR) queryData;
640  const char* vendorID = (const char *) &(queryData[pDevDesc->VendorIdOffset]);
641  const char* productID =
642  (const char *) &(queryData[pDevDesc->ProductIdOffset]);
643 
644  // Trace execution.
645  FIELD_LOG(("3: IsIPod \"%s\" \"%s\"\n", vendorID, productID));
646 
647  // Check for an iPod device.
648  isIPod = PR_TRUE;
649  if (strncmp("Apple", vendorID, strlen("Apple")))
650  isIPod = PR_FALSE;
651  else if (strncmp("iPod", productID, strlen("iPod")))
652  isIPod = PR_FALSE;
653 
654  /* Trace execution. */
655  FIELD_LOG(("4: IsIPod %d\n", isIPod));
656 
657  return isIPod;
658 }
659 
660 
661 nsresult
662 sbIPDMarshall::RemoveDevice(sbIDevice* aDevice)
663 {
664  return NS_ERROR_NOT_IMPLEMENTED;
665 }
return NS_OK
nsresult CreateAndDispatchDeviceManagerEvent(PRUint32 aType, nsIVariant *aData, nsISupports *aOrigin, PRUint32 aDeviceState, PRBool aAsync)
Definition: sbIPDUtils.cpp:72
NS_DECL_ISUPPORTS NS_DECL_SBIDEVICEMARSHALL NS_DECL_NSICLASSINFO sbIPDMarshall()
static void Initialize()
Definition: sbIPDLog.cpp:73
NS_IMPL_THREADSAFE_CI(sbMediaListEnumeratorWrapper)
Songbird iPod Device Windows Utility Definitions.
const unsigned long EVENT_DEVICE_ADDED
NS_IMPL_THREADSAFE_RELEASE(sbRequestItem)
NS_IMPL_THREADSAFE_ADDREF(sbRequestItem)
long LONG
Definition: MultiMonitor.h:38
sbIDeviceController * FindCompatibleControllers(nsIPropertyBag *deviceParams)
#define FIELD_LOG(args)
Definition: sbIPDLog.h:126
#define SB_IPDMARSHALL_CLASSNAME
Definition: sbIPDMarshall.h:74
Songbird iPod Device Logging Definitions.
const unsigned long EVENT_DEVICE_REMOVED
[UNIMPLEMENTED UNTIL AFTER 0.3]
_updateCookies aName
virtual ~sbIPDMarshall()
function msg
#define SB_IPDMARSHALL_CID
Definition: sbIPDMarshall.h:76
#define SB_DEVICE_CONTROLLER_CATEGORY
readonly attribute nsIDPtr id
NS_INTERFACE_MAP_END NS_IMPL_CI_INTERFACE_GETTER1(CDatabaseQuery, sbIDatabaseQuery) CDatabaseQuery