sbDeviceManager.cpp
Go to the documentation of this file.
1 /* vim: set sw=2 :miv */
2 /*
3 //
4 // BEGIN SONGBIRD GPL
5 //
6 // This file is part of the Songbird web player.
7 //
8 // Copyright(c) 2005-2008 POTI, Inc.
9 // http://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 #include "sbDeviceManager.h"
29 
30 #include <nsIAppStartupNotifier.h>
31 #include <nsIClassInfoImpl.h>
32 #include <nsIMutableArray.h>
33 #include <nsIObserverService.h>
34 #include <nsIProgrammingLanguage.h>
35 #include <nsISupportsPrimitives.h>
36 
37 #include <nsArrayUtils.h>
38 #include <nsAutoLock.h>
39 #include <nsAutoPtr.h>
40 #include <nsComponentManagerUtils.h>
41 #include <nsMemory.h>
42 #include <nsServiceManagerUtils.h>
43 #include <nsIDOMWindow.h>
44 #include <nsIPromptService.h>
45 
46 #include <sbStringBundle.h>
47 
48 #include "sbIDeviceController.h"
49 #include "sbDeviceEvent.h"
51 #include "sbDeviceUtils.h"
52 
53 #include <sbIPrompter.h>
54 #include <sbILibraryManager.h>
55 #include <sbIServiceManager.h>
56 
57 /* observer topics */
58 #define NS_PROFILE_STARTUP_OBSERVER_ID "profile-after-change"
59 #define NS_QUIT_APPLICATION_REQUESTED_OBSERVER_ID "quit-application-requested"
60 #define NS_QUIT_APPLICATION_GRANTED_OBSERVER_ID "quit-application-granted"
61 #define NS_PROFILE_SHUTDOWN_OBSERVER_ID "profile-before-change"
62 #define SB_MAIN_LIBRARY_READY_OBSERVER_ID "songbird-main-library-ready"
63 
74 NS_IMPL_CI_INTERFACE_GETTER5(sbDeviceManager,
75  sbIDeviceManager2,
76  sbIDeviceControllerRegistrar,
77  sbIDeviceRegistrar,
78  sbIDeviceEventTarget,
79  nsISupportsWeakReference)
80 
81 NS_DECL_CLASSINFO(sbDeviceManager)
82 NS_IMPL_THREADSAFE_CI(sbDeviceManager)
83 
84 sbDeviceManager::sbDeviceManager()
85  : mMonitor(nsnull),
86  mHasAllowedShutdown(PR_FALSE)
87 {
88 }
89 
90 sbDeviceManager::~sbDeviceManager()
91 {
92  /* destructor code */
93 }
94 
95 template<class T>
96 PLDHashOperator sbDeviceManager::EnumerateIntoArray(const nsID& aKey,
97  T* aData,
98  void* aArray)
99 {
100  nsIMutableArray *array = (nsIMutableArray*)aArray;
101  nsresult rv;
102  nsCOMPtr<nsISupports> supports = do_QueryInterface(aData, &rv);
103  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
104 
105  rv = array->AppendElement(aData, false);
106  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
107 
108  return PL_DHASH_NEXT;
109 }
110 
111 /* readonly attribute nsIArray sbIDeviceManager::marshalls; */
112 NS_IMETHODIMP sbDeviceManager::GetMarshalls(nsIArray * *aMarshalls)
113 {
114  NS_ENSURE_ARG_POINTER(aMarshalls);
115 
116  nsresult rv;
117 
118  if (!mMonitor) {
119  // when EM_NO_RESTART is set, we don't see the appropriate app startup
120  // attempt to manually initialize.
121  rv = Init();
122  NS_ENSURE_SUCCESS(rv, rv);
123  }
124 
125  nsCOMPtr<nsIMutableArray> array =
126  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
127  NS_ENSURE_SUCCESS(rv, rv);
128 
129  PRUint32 count;
130  count = mMarshalls.EnumerateRead(sbDeviceManager::EnumerateIntoArray,
131  array.get());
132 
133  // we can't trust the count returned from EnumerateRead because that won't
134  // tell us about erroring on the last element
135  rv = array->GetLength(&count);
136  NS_ENSURE_SUCCESS(rv, rv);
137  if (count < mMarshalls.Count()) {
138  return NS_ERROR_FAILURE;
139  }
140 
141  return CallQueryInterface(array, aMarshalls);
142 }
143 
144 /* sbIDeviceMarshall sbIDeviceManager::getMarshallByID (in nsIDPtr aIDPtr); */
145 NS_IMETHODIMP sbDeviceManager::GetMarshallByID(const nsID * aIDPtr,
146  sbIDeviceMarshall **_retval)
147 {
148  NS_ENSURE_ARG_POINTER(_retval);
149  NS_ENSURE_ARG_POINTER(aIDPtr);
150 
151  if (!mMonitor) {
152  // when EM_NO_RESTART is set, we don't see the appropriate app startup
153  // attempt to manually initialize.
154  nsresult rv = Init();
155  NS_ENSURE_SUCCESS(rv, rv);
156  }
157 
158  PRBool succeded = mMarshalls.Get(*aIDPtr, _retval);
159  return succeded ? NS_OK : NS_ERROR_NOT_AVAILABLE;
160 }
161 
162 /* void sbIDeviceManager::updateDevices (); */
163 NS_IMETHODIMP sbDeviceManager::UpdateDevices()
164 {
165  nsCOMPtr<nsIArray> controllers;
166  nsresult rv = this->GetControllers(getter_AddRefs(controllers));
167  NS_ENSURE_SUCCESS(rv, rv);
168 
169  PRUint32 length;
170  rv = controllers->GetLength(&length);
171  NS_ENSURE_SUCCESS(rv, rv);
172 
173  for (PRUint32 i = 0; i < length; ++i) {
174  nsCOMPtr<sbIDeviceController> controller;
175  rv = controllers->QueryElementAt(i, NS_GET_IID(sbIDeviceController),
176  getter_AddRefs(controller));
177  NS_ENSURE_SUCCESS(rv, rv);
178  rv = controller->ConnectDevices();
179  NS_ENSURE_SUCCESS(rv, rv);
180  }
181 
182  return NS_OK;
183 }
184 
185 /* sbIDeviceEvent createEvent (in unsigned long aType,
186  [optional] in nsIVariant aData,
187  [optional] in nsISupports aOrigin); */
188 NS_IMETHODIMP sbDeviceManager::CreateEvent(PRUint32 aType,
189  nsIVariant *aData,
190  nsISupports *aOrigin,
191  PRUint32 aDeviceState,
192  PRUint32 aDeviceSubState,
193  sbIDeviceEvent **_retval)
194 {
195  return sbDeviceEvent::CreateEvent(aType,
196  aData,
197  aOrigin,
198  aDeviceState,
199  aDeviceSubState,
200  _retval);
201 }
202 
203 /* sbIDeviceEventBeforeAddedData createBeforeAddedData(in sbIDevice aDevice */
204 NS_IMETHODIMP sbDeviceManager::CreateBeforeAddedData(
205  sbIDevice *aDevice,
207 {
209  aData);
210 }
211 
212 /* sbIDevice sbIDeviceManager::getDeviceForItem(in sbIMediaItem aItem); */
213 NS_IMETHODIMP sbDeviceManager::GetDeviceForItem(sbIMediaItem* aMediaItem,
214  sbIDevice** _retval)
215 {
216  NS_ENSURE_ARG_POINTER(aMediaItem);
217  NS_ENSURE_ARG_POINTER(_retval);
218 
219  nsresult rv;
220 
221  // get the list of devices
222  nsCOMPtr<nsIArray> deviceList;
223  rv = GetDevices(getter_AddRefs(deviceList));
224  NS_ENSURE_SUCCESS(rv, rv);
225 
226  // check each device for item
227  PRUint32 deviceCount;
228  rv = deviceList->GetLength(&deviceCount);
229  NS_ENSURE_SUCCESS(rv, rv);
230  for (PRUint32 i = 0; i < deviceCount; i++) {
231  // get the next device
232  nsCOMPtr<sbIDevice> device = do_QueryElementAt(deviceList, i, &rv);
233  NS_ENSURE_SUCCESS(rv, rv);
234 
235  // try getting the device library for the item. if one is found, the item
236  // belongs to the device
237  nsCOMPtr<sbIDeviceLibrary> library;
239  aMediaItem,
240  getter_AddRefs(library));
241  if (NS_SUCCEEDED(rv)) {
242  device.swap(*_retval);
243  return NS_OK;
244  }
245  }
246 
247  *_retval = nsnull;
248 
249  return NS_OK;
250 }
251 
252 /* readonly attribute nsIArray sbIDeviceControllerRegistrar::controllers; */
253 NS_IMETHODIMP sbDeviceManager::GetControllers(nsIArray * *aControllers)
254 {
255  NS_ENSURE_ARG_POINTER(aControllers);
256 
257  nsresult rv;
258 
259  if (!mMonitor) {
260  // when EM_NO_RESTART is set, we don't see the appropriate app startup
261  // attempt to manually initialize.
262  rv = Init();
263  NS_ENSURE_SUCCESS(rv, rv);
264  }
265 
266  nsCOMPtr<nsIMutableArray> array =
267  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
268  NS_ENSURE_SUCCESS(rv, rv);
269 
270  PRUint32 count;
271  count = mControllers.EnumerateRead(sbDeviceManager::EnumerateIntoArray,
272  array.get());
273 
274  // we can't trust the count returned from EnumerateRead because that won't
275  // tell us about erroring on the last element
276  rv = array->GetLength(&count);
277  NS_ENSURE_SUCCESS(rv, rv);
278  if (count < mControllers.Count()) {
279  return NS_ERROR_FAILURE;
280  }
281 
282  return CallQueryInterface(array, aControllers);
283 }
284 
285 /* void sbIDeviceControllerRegistrar::registerController (
286  in sbIDeviceController aController); */
287 NS_IMETHODIMP sbDeviceManager::RegisterController(sbIDeviceController *aController)
288 {
289  NS_ENSURE_ARG_POINTER(aController);
290 
291  nsresult rv;
292 
293  if (!mMonitor) {
294  // when EM_NO_RESTART is set, we don't see the appropriate app startup
295  // attempt to manually initialize.
296  rv = Init();
297  NS_ENSURE_SUCCESS(rv, rv);
298  }
299 
300  nsID* id;
301  rv = aController->GetId(&id);
302  NS_ENSURE_SUCCESS(rv, rv);
303  NS_ENSURE_ARG_POINTER(id);
304 
305  PRBool succeeded = mControllers.Put(*id, aController);
306  NS_Free(id);
307  return succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
308 }
309 
310 /* void sbIDeviceControllerRegistrar::unregisterController (in sbIDeviceController aController); */
311 NS_IMETHODIMP sbDeviceManager::UnregisterController(sbIDeviceController *aController)
312 {
313  NS_ENSURE_ARG_POINTER(aController);
314 
315  nsresult rv;
316 
317  if (!mMonitor) {
318  // when EM_NO_RESTART is set, we don't see the appropriate app startup
319  // attempt to manually initialize.
320  rv = Init();
321  NS_ENSURE_SUCCESS(rv, rv);
322  }
323 
324  nsID* id;
325  rv = aController->GetId(&id);
326  NS_ENSURE_SUCCESS(rv, rv);
327  NS_ENSURE_ARG_POINTER(id);
328 
329  mControllers.Remove(*id);
330  NS_Free(id);
331  return NS_OK;
332 }
333 
334 /* sbIDeviceController sbIDeviceControllerRegistrar::getController (in nsIDPtr aControllerId); */
335 NS_IMETHODIMP sbDeviceManager::GetController(const nsID * aControllerId,
336  sbIDeviceController **_retval)
337 {
338  NS_ENSURE_ARG_POINTER(_retval);
339  NS_ENSURE_ARG_POINTER(aControllerId);
340 
341  if (!mMonitor) {
342  // when EM_NO_RESTART is set, we don't see the appropriate app startup
343  // attempt to manually initialize.
344  nsresult rv = Init();
345  NS_ENSURE_SUCCESS(rv, rv);
346  }
347 
348 
349  PRBool succeded = mControllers.Get(*aControllerId, _retval);
350  return succeded ? NS_OK : NS_ERROR_NOT_AVAILABLE;
351 }
352 
353 /* readonly attribute nsIArray sbIDeviceRegistrar::devices; */
354 NS_IMETHODIMP sbDeviceManager::GetDevices(nsIArray * *aDevices)
355 {
356  NS_ENSURE_ARG_POINTER(aDevices);
357 
358  nsresult rv;
359 
360  if (!mMonitor) {
361  // when EM_NO_RESTART is set, we don't see the appropriate app startup
362  // attempt to manually initialize.
363  rv = Init();
364  NS_ENSURE_SUCCESS(rv, rv);
365  }
366 
367  nsCOMPtr<nsIMutableArray> array =
368  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
369  NS_ENSURE_SUCCESS(rv, rv);
370 
371  PRUint32 count;
372  count = mDevices.EnumerateRead(sbDeviceManager::EnumerateIntoArray,
373  array.get());
374 
375  // we can't trust the count returned from EnumerateRead because that won't
376  // tell us about erroring on the last element
377  rv = array->GetLength(&count);
378  NS_ENSURE_SUCCESS(rv, rv);
379  if (count < mDevices.Count()) {
380  return NS_ERROR_FAILURE;
381  }
382 
383  return CallQueryInterface(array, aDevices);
384 }
385 
386 /* void sbIDeviceRegistrar::registerDevice (in sbIDevice aDevice); */
387 NS_IMETHODIMP sbDeviceManager::RegisterDevice(sbIDevice *aDevice)
388 {
389  NS_ENSURE_ARG_POINTER(aDevice);
390  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
391 
392  // prevent anybody from seeing a half-added device
393  nsAutoMonitor mon(mMonitor);
394 
395  nsresult rv;
396  nsID* id;
397  rv = aDevice->GetId(&id);
398  NS_ENSURE_SUCCESS(rv, rv);
399  NS_ENSURE_ARG_POINTER(id);
400 
401  PRBool succeeded = mDevices.Put(*id, aDevice);
402  NS_Free(id);
403  if (!succeeded) {
404  return NS_ERROR_OUT_OF_MEMORY;
405  }
406 
407  rv = aDevice->Connect();
408  if (NS_FAILED(rv)) {
409  // the device failed to connect, remove it from the hash
410  mDevices.Remove(*id);
411  NS_ENSURE_SUCCESS(rv, rv);
412  }
413  return NS_OK;
414 }
415 
416 /* void sbIDeviceRegistrar::unregisterDevice (in sbIDevice aDevice); */
417 NS_IMETHODIMP sbDeviceManager::UnregisterDevice(sbIDevice *aDevice)
418 {
419  NS_ENSURE_ARG_POINTER(aDevice);
420 
421  nsresult rv;
422 
423  if (!mMonitor) {
424  // when EM_NO_RESTART is set, we don't see the appropriate app startup
425  // attempt to manually initialize.
426  rv = Init();
427  NS_ENSURE_SUCCESS(rv, rv);
428  }
429 
430  nsID* id;
431  rv = aDevice->GetId(&id);
432  NS_ENSURE_SUCCESS(rv, rv);
433  NS_ENSURE_ARG_POINTER(id);
434 
435  mDevices.Remove(*id);
436  NS_Free(id);
437  return NS_OK;
438 }
439 
440 /* sbIDevice sbIDeviceRegistrar::getDevice (in nsIDPtr aDeviceId); */
441 NS_IMETHODIMP sbDeviceManager::GetDevice(const nsID * aDeviceId,
442  sbIDevice **_retval)
443 {
444  NS_ENSURE_ARG_POINTER(_retval);
445  NS_ENSURE_ARG_POINTER(aDeviceId);
446 
447  if (!mMonitor) {
448  // when EM_NO_RESTART is set, we don't see the appropriate app startup
449  // attempt to manually initialize.
450  nsresult rv = Init();
451  NS_ENSURE_SUCCESS(rv, rv);
452  }
453 
454  PRBool succeeded = mDevices.Get(*aDeviceId, _retval);
455  return succeeded ? NS_OK : NS_ERROR_NOT_AVAILABLE;
456 }
457 
458 /* void nsIObserver::observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
459 NS_IMETHODIMP sbDeviceManager::Observe(nsISupports *aSubject,
460  const char *aTopic,
461  const PRUnichar *aData)
462 {
463  nsresult rv;
464  if (!strcmp(aTopic, APPSTARTUP_CATEGORY)) {
465  // listen for profile startup and profile shutdown messages
466  nsCOMPtr<nsIObserverService> obsSvc =
467  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
468  NS_ENSURE_SUCCESS(rv, rv);
469 
470  nsCOMPtr<nsIObserver> observer =
471  do_QueryInterface(NS_ISUPPORTS_CAST(nsIObserver*, this), &rv);
472  NS_ENSURE_SUCCESS(rv, rv);
473 
474  rv = obsSvc->AddObserver(observer, NS_PROFILE_STARTUP_OBSERVER_ID, PR_FALSE);
475  NS_ENSURE_SUCCESS(rv, rv);
476 
477  rv = obsSvc->AddObserver(observer,
479  PR_FALSE);
480  NS_ENSURE_SUCCESS(rv, rv);
481 
482  rv = obsSvc->AddObserver(observer, NS_QUIT_APPLICATION_GRANTED_OBSERVER_ID, PR_FALSE);
483  NS_ENSURE_SUCCESS(rv, rv);
484 
485  rv = obsSvc->AddObserver(observer, NS_QUIT_APPLICATION_REQUESTED_OBSERVER_ID, PR_FALSE);
486  NS_ENSURE_SUCCESS(rv, rv);
487 
488  rv = obsSvc->AddObserver(observer, SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC, PR_FALSE);
489  NS_ENSURE_SUCCESS(rv, rv);
490 
491  rv = obsSvc->AddObserver(observer, NS_PROFILE_SHUTDOWN_OBSERVER_ID, PR_FALSE);
492  NS_ENSURE_SUCCESS(rv, rv);
493 
494  } else if (!strcmp(NS_PROFILE_STARTUP_OBSERVER_ID, aTopic)) {
495  // Called after the profile has been loaded, so prefs and such are available
496  rv = this->Init();
497  NS_ENSURE_SUCCESS(rv, rv);
498  } else if (!strcmp(SB_MAIN_LIBRARY_READY_OBSERVER_ID, aTopic)) {
499  // Called after the main Songbird window is presented in case device
500  // enumeration hangs.
502  NS_ENSURE_SUCCESS(rv, rv);
503  } else if (!strcmp(NS_QUIT_APPLICATION_REQUESTED_OBSERVER_ID, aTopic)) {
504  // Usually (but not always!) we'll get a quit-application-requested
505  // notification - if we do, use it to show a dialog to the user to let them
506  // cancel if device operations are in progress.
507  PRBool shouldQuit = PR_FALSE;
508  rv = this->QuitApplicationRequested(&shouldQuit);
509  NS_ENSURE_SUCCESS(rv, rv);
510 
511  if (!shouldQuit) {
512  nsCOMPtr<nsISupportsPRBool> stopShutdown =
513  do_QueryInterface(aSubject, &rv);
514  NS_ENSURE_SUCCESS(rv, rv);
515  rv = stopShutdown->SetData(PR_TRUE);
516  NS_ENSURE_SUCCESS(rv, rv);
517  }
518  } else if (!strcmp(NS_QUIT_APPLICATION_GRANTED_OBSERVER_ID, aTopic)) {
519  // Called when the request to shutdown has been granted.
520  // We show a dialog warning the user that this will abort device operations
521  // here (but they can't cancel the quit at this point).
522  // Due to Bug 9459 this will be called twice.
523  rv = this->QuitApplicationGranted();
524  NS_ENSURE_SUCCESS(rv, rv);
525 
526  // Remove the observer so we don't get called a second time, since we are
527  // shutting down anyways this should not cause problems.
528  nsCOMPtr<nsIObserverService> obsSvc =
529  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
530  NS_ENSURE_SUCCESS(rv, rv);
531 
532  nsCOMPtr<nsIObserver> observer =
533  do_QueryInterface(NS_ISUPPORTS_CAST(nsIObserver*, this), &rv);
534  NS_ENSURE_SUCCESS(rv, rv);
535 
536  rv = obsSvc->RemoveObserver(observer, NS_QUIT_APPLICATION_GRANTED_OBSERVER_ID);
537  NS_ENSURE_SUCCESS(rv, rv);
538 
539  } else if (!strcmp(SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC, aTopic)) {
540  // Called during shutdown, the profile is still available
541  rv = this->PrepareShutdown();
542  NS_ENSURE_SUCCESS(rv, rv);
543  } else if (!strcmp(NS_PROFILE_SHUTDOWN_OBSERVER_ID, aTopic)) {
544  // Called when near the end of shutdown, profile is just about to be gone
545  rv = this->FinalShutdown();
546  NS_ENSURE_SUCCESS(rv, rv);
547 
548  // remove all the observers
549  nsCOMPtr<nsIObserverService> obsSvc =
550  do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
551  NS_ENSURE_SUCCESS(rv, rv);
552 
553  nsCOMPtr<nsIObserver> observer =
554  do_QueryInterface(NS_ISUPPORTS_CAST(nsIObserver*, this), &rv);
555  NS_ENSURE_SUCCESS(rv, rv);
556 
557  rv = obsSvc->RemoveObserver(observer, NS_PROFILE_STARTUP_OBSERVER_ID);
558  NS_ENSURE_SUCCESS(rv, rv);
559 
560  rv = obsSvc->RemoveObserver(observer, SB_MAIN_LIBRARY_READY_OBSERVER_ID);
561  NS_ENSURE_SUCCESS(rv, rv);
562 
563  rv = obsSvc->RemoveObserver(observer, SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC);
564  NS_ENSURE_SUCCESS(rv, rv);
565 
566  rv = obsSvc->RemoveObserver(observer, NS_PROFILE_SHUTDOWN_OBSERVER_ID);
567  NS_ENSURE_SUCCESS(rv, rv);
568  }
569  return NS_OK;
570 }
571 
573 {
574  nsresult rv;
575 
576  NS_ENSURE_FALSE(mMonitor, NS_ERROR_ALREADY_INITIALIZED);
577 
578  mMonitor = nsAutoMonitor::NewMonitor(__FILE__);
579  NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
580 
581  nsAutoMonitor mon(mMonitor);
582 
583  // initialize the hashtables
584  PRBool succeeded;
585  succeeded = mControllers.Init();
586  NS_ENSURE_TRUE(succeeded, NS_ERROR_OUT_OF_MEMORY);
587 
588  succeeded = mDevices.Init();
589  NS_ENSURE_TRUE(succeeded, NS_ERROR_OUT_OF_MEMORY);
590 
591  succeeded = mMarshalls.Init();
592  NS_ENSURE_TRUE(succeeded, NS_ERROR_OUT_OF_MEMORY);
593 
594  // load the marshalls
595  nsCOMPtr<nsICategoryManager> catMgr =
596  do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
597  NS_ENSURE_SUCCESS(rv, rv);
598 
599  nsCOMPtr<nsISimpleEnumerator> enumerator;
600  rv = catMgr->EnumerateCategory("songbird-device-marshall",
601  getter_AddRefs(enumerator));
602  NS_ENSURE_SUCCESS(rv, rv);
603 
604  PRBool hasMore;
605  rv = enumerator->HasMoreElements(&hasMore);
606  NS_ENSURE_SUCCESS(rv, rv);
607 
608  while(hasMore) {
609  nsCOMPtr<nsISupports> supports;
610  rv = enumerator->GetNext(getter_AddRefs(supports));
611  NS_ENSURE_SUCCESS(rv, rv);
612 
613  nsCOMPtr<nsISupportsCString> data = do_QueryInterface(supports, &rv);
614  NS_ENSURE_SUCCESS(rv, rv);
615 
616  nsCString entryName;
617  rv = data->GetData(entryName);
618  NS_ENSURE_SUCCESS(rv, rv);
619 
620  char * contractId;
621  rv = catMgr->GetCategoryEntry("songbird-device-marshall", entryName.get(), &contractId);
622  NS_ENSURE_SUCCESS(rv, rv);
623 
624  nsCOMPtr<sbIDeviceMarshall> marshall =
625  do_CreateInstance(contractId, &rv);
626  NS_Free(contractId);
627  NS_ENSURE_SUCCESS(rv, rv);
628 
629  nsID* id;
630  rv = marshall->GetId(&id);
631  NS_ENSURE_SUCCESS(rv, rv);
632 
633  succeeded = mMarshalls.Put(*id, marshall);
634  NS_Free(id);
635  NS_ENSURE_TRUE(succeeded, NS_ERROR_OUT_OF_MEMORY);
636 
637  // have the marshall load the controllers
638  nsCOMPtr<sbIDeviceControllerRegistrar> registrar =
639  do_QueryInterface(NS_ISUPPORTS_CAST(sbIDeviceControllerRegistrar*, this), &rv);
640  NS_ENSURE_SUCCESS(rv, rv);
641 
642  rv = marshall->LoadControllers(registrar);
643  NS_ENSURE_SUCCESS(rv, rv);
644 
645  rv = enumerator->HasMoreElements(&hasMore);
646  NS_ENSURE_SUCCESS(rv, rv);
647  }
648 
649  // connect all the devices
650  //rv = this->UpdateDevices();
651  //NS_ENSURE_SUCCESS(rv, rv);
652 
653  // Indicate that the device manager services are ready.
654  nsCOMPtr<sbIServiceManager>
655  serviceManager = do_GetService(SB_SERVICE_MANAGER_CONTRACTID, &rv);
656  NS_ENSURE_SUCCESS(rv, rv);
657  rv = serviceManager->SetServiceReady(SONGBIRD_DEVICEMANAGER2_CONTRACTID,
658  PR_TRUE);
659  NS_ENSURE_SUCCESS(rv, rv);
660 
661  return NS_OK;
662 }
663 
664 nsresult sbDeviceManager::GetCanDisconnect(PRBool* aCanDisconnect)
665 {
666  NS_ENSURE_ARG_POINTER(aCanDisconnect);
667  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
668  nsAutoMonitor mon(mMonitor);
669 
670  nsresult rv;
671 
672  // get an array of our devices
673  nsCOMPtr<nsIArray> devices;
674  rv = GetDevices(getter_AddRefs(devices));
675  NS_ENSURE_SUCCESS(rv, rv);
676 
677  PRUint32 length;
678  rv = devices->GetLength(&length);
679  NS_ENSURE_SUCCESS(rv, rv);
680 
681  // for each of them, can we disconnect?
682  PRBool canDisconnect = PR_TRUE;
683  for (PRUint32 i = 0; i < length; ++i) {
684  nsCOMPtr<sbIDevice> device;
685  rv = devices->QueryElementAt(i, NS_GET_IID(sbIDevice),
686  getter_AddRefs(device));
687  NS_ENSURE_SUCCESS(rv, rv);
688 
689  rv = device->GetCanDisconnect(&canDisconnect);
690  NS_ENSURE_SUCCESS(rv, rv);
691 
692  if (!canDisconnect) {
693  break;
694  }
695  }
696 
697  *aCanDisconnect = canDisconnect;
698 
699  return NS_OK;
700 }
701 
703 {
704  nsresult rv;
705 
706  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
707  nsAutoMonitor mon(mMonitor);
708 
709  // Get the list of marshalls.
710  nsCOMPtr<nsIArray> marshalls;
711  rv = this->GetMarshalls(getter_AddRefs(marshalls));
712  NS_ENSURE_SUCCESS(rv, rv);
713 
714  // Begin monitoring for each marshall.
715  PRUint32 length;
716  rv = marshalls->GetLength(&length);
717  NS_ENSURE_SUCCESS(rv, rv);
718  for (PRUint32 i = 0; i < length; i++) {
719  // Get the next marshall.
720  nsCOMPtr<sbIDeviceMarshall> marshall;
721  rv = marshalls->QueryElementAt(i,
722  NS_GET_IID(sbIDeviceMarshall),
723  getter_AddRefs(marshall));
724  if (NS_FAILED(rv)) {
725  NS_WARNING("sbDeviceManager::BeginMarshallMonitoring: "
726  "marshall failed to QI to sbIDeviceMarshall");
727  continue;
728  }
729 
730  // Begin marshall monitoring, continue even if it fails
731  rv = marshall->BeginMonitoring();
732  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
733  "sbDeviceManager::BeginMarshallMonitoring: "
734  "BeginMonitoring failed");
735  }
736 
737  return NS_OK;
738 }
739 
740 nsresult sbDeviceManager::QuitApplicationRequested(PRBool *aShouldQuit)
741 {
742  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
743  nsAutoMonitor mon(mMonitor);
744 
745  nsresult rv;
746 
747  // There has been a request to shutdown, let's check with the devices and if
748  // they are busy then query the user if they wish to force them to quit
749  PRBool canDisconnect;
750  rv = GetCanDisconnect(&canDisconnect);
751  NS_ENSURE_SUCCESS(rv, rv);
752 
753  if (!canDisconnect) {
754  // one of our devices doesn't want to be disconnected
755  nsCOMPtr<nsIPromptService> prompter =
756  do_CreateInstance("@songbirdnest.com/Songbird/Prompter;1", &rv);
757  NS_ENSURE_SUCCESS(rv, rv);
758 
760  nsString title = bundle.Get("device.dialog.quitwhileactive.title");
761  nsString message = bundle.Get("device.dialog.quitwhileactive.message");
762  nsString label0 = bundle.Get("device.dialog.quitwhileactive.quit");
763  nsString label1 = bundle.Get("device.dialog.quitwhileactive.noquit");
764  PRUint32 buttonFlags = nsIPromptService::BUTTON_POS_0 *
765  nsIPromptService::BUTTON_TITLE_IS_STRING +
766  nsIPromptService::BUTTON_POS_1 *
767  nsIPromptService::BUTTON_TITLE_IS_STRING;
768  PRInt32 buttonPressed;
769 
770  rv = prompter->ConfirmEx
771  (nsnull,
772  title.get(),
773  message.get(),
774  buttonFlags,
775  label0.get(),
776  label1.get(),
777  nsnull,
778  nsnull,
779  nsnull,
780  &buttonPressed);
781  NS_ENSURE_SUCCESS(rv, rv);
782 
783  // Quit button is button zero.
784  *aShouldQuit = buttonPressed == 0;
785  }
786  else {
787  *aShouldQuit = PR_TRUE;
788  }
789 
790  mHasAllowedShutdown = *aShouldQuit;
791 
792  return NS_OK;
793 }
794 
795 
797 {
798  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
799  nsAutoMonitor mon(mMonitor);
800 
801  nsresult rv;
802 
803  if (!mHasAllowedShutdown) {
804  // Shutdown has been granted. Let's check with the devices and if
805  // they are busy then display a dialog that will let the user wait until
806  // it's done (but not abort the quit).
807  // Only do this if we _didn't_ show the cancelable dialog (see
808  // QuitApplicationRequested) - which would happen if the
809  // quit-application-requested notification wasn't sent for some reason.
810  PRBool canDisconnect;
811  rv = GetCanDisconnect(&canDisconnect);
812  NS_ENSURE_SUCCESS(rv, rv);
813 
814  if (!canDisconnect) {
815  // one of our devices doesn't want to be disconnected
816  nsCOMPtr<sbIPrompter> prompter =
817  do_CreateInstance("@songbirdnest.com/Songbird/Prompter;1", &rv);
818  NS_ENSURE_SUCCESS(rv, rv);
819 
820  // This will hold up a dialog, we do not continue
821  // until the dialog is closed, which will be closed automatically when
822  // the devices are no longer busy, or the user closes it.
823  nsCOMPtr<nsIDOMWindow> dialogWindow;
824  prompter->OpenDialog
825  (nsnull,
826  NS_LITERAL_STRING("chrome://songbird/content/xul/waitForCompletion.xul"),
827  NS_LITERAL_STRING("waitForCompletion"),
828  NS_LITERAL_STRING(""),
829  nsnull,
830  getter_AddRefs(dialogWindow));
831  }
832  }
833 
834  // Ok now we can shutdown
835  this->PrepareShutdown();
836 
837  return NS_OK;
838 }
839 
841 {
842  nsresult rv;
843 
844  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
845  nsAutoMonitor mon(mMonitor);
846 
847  // Indicate that the device manager services are no longer ready.
848  nsCOMPtr<sbIServiceManager>
849  serviceManager = do_GetService(SB_SERVICE_MANAGER_CONTRACTID, &rv);
850  NS_ENSURE_SUCCESS(rv, rv);
851  rv = serviceManager->SetServiceReady(SONGBIRD_DEVICEMANAGER2_CONTRACTID,
852  PR_FALSE);
853  NS_ENSURE_SUCCESS(rv, rv);
854 
855  // disconnect all the marshalls (i.e. stop watching for new devices)
856  nsCOMPtr<nsIArray> marshalls;
857  rv = this->GetMarshalls(getter_AddRefs(marshalls));
858  NS_ENSURE_SUCCESS(rv, rv);
859 
860  PRUint32 length;
861  rv = marshalls->GetLength(&length);
862  NS_ENSURE_SUCCESS(rv, rv);
863 
864  for (PRUint32 i = 0; i < length; ++i) {
865  nsCOMPtr<sbIDeviceMarshall> marshall;
866  rv = marshalls->QueryElementAt(i, NS_GET_IID(sbIDeviceMarshall),
867  getter_AddRefs(marshall));
868  NS_ENSURE_SUCCESS(rv, rv);
869  rv = marshall->StopMonitoring();
870  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
871  "StopMonitoring returned an error, monitoring of devices may not be completely stopped.");
872  }
873 
874  // ask the controllers to disconnect all devices
875  nsCOMPtr<nsIArray> controllers;
876  rv = this->GetControllers(getter_AddRefs(controllers));
877  NS_ENSURE_SUCCESS(rv, rv);
878 
879  rv = controllers->GetLength(&length);
880  NS_ENSURE_SUCCESS(rv, rv);
881 
882  for (PRUint32 i = 0; i < length; ++i) {
883  nsCOMPtr<sbIDeviceController> controller;
884  rv = controllers->QueryElementAt(i, NS_GET_IID(sbIDeviceController),
885  getter_AddRefs(controller));
886  if (NS_FAILED(rv)) {
887  NS_WARNING("Failed to disconnect device.");
888  continue;
889  }
890  rv = controller->DisconnectDevices();
891  if (NS_FAILED(rv))
892  NS_WARNING("Failed to disconnect device.");
893  }
894 
895  // Remove all devices.
896  rv = RemoveAllDevices();
897  if (NS_FAILED(rv))
898  NS_WARNING("Failed to remove all devices.");
899 
900  return NS_OK;
901 }
902 
904 {
905  nsresult rv;
906 
907  NS_ENSURE_TRUE(mMonitor, NS_ERROR_NOT_INITIALIZED);
908  nsAutoMonitor mon(mMonitor);
909 
910  // get rid of all our controllers
911  // ask the controllers to disconnect all devices
912  nsCOMPtr<nsIArray> controllers;
913  rv = this->GetControllers(getter_AddRefs(controllers));
914  NS_ENSURE_SUCCESS(rv, rv);
915 
916  PRUint32 length;
917  rv = controllers->GetLength(&length);
918  NS_ENSURE_SUCCESS(rv, rv);
919 
920  for (PRUint32 i = 0; i < length; ++i) {
921  nsCOMPtr<sbIDeviceController> controller;
922  rv = controllers->QueryElementAt(i, NS_GET_IID(sbIDeviceController),
923  getter_AddRefs(controller));
924  NS_ENSURE_SUCCESS(rv, rv);
925  rv = controller->ReleaseDevices();
926  NS_ENSURE_SUCCESS(rv, rv);
927  }
928 
929  mControllers.Clear();
930  mMarshalls.Clear();
931 
932  return NS_OK;
933 }
934 
936 {
937  nsresult rv;
938 
939  // Get the list of all devices.
940  nsCOMPtr<nsIArray> devices;
941  rv = this->GetDevices(getter_AddRefs(devices));
942  NS_ENSURE_SUCCESS(rv, rv);
943 
944  // Remove each device.
945  PRUint32 length;
946  rv = devices->GetLength(&length);
947  NS_ENSURE_SUCCESS(rv, rv);
948  for (PRInt32 i = length - 1; i >= 0; i--) {
949  // Get device.
950  nsCOMPtr<sbIDevice> device = do_QueryElementAt(devices, i, &rv);
951  NS_ENSURE_SUCCESS(rv, rv);
952 
953  // Get device controller.
954  nsCOMPtr<sbIDeviceController> controller;
955  nsID* controllerID = nsnull;
956  rv = device->GetControllerId(&controllerID);
957  NS_ENSURE_SUCCESS(rv, rv);
958  sbAutoNSMemPtr autoControllerID(controllerID);
959  rv = GetController(controllerID, getter_AddRefs(controller));
960  NS_ENSURE_SUCCESS(rv, rv);
961 
962  // Get device marshall.
963  nsCOMPtr<sbIDeviceMarshall> marshall;
964  nsID* marshallID = nsnull;
965  rv = controller->GetMarshallId(&marshallID);
966  NS_ENSURE_SUCCESS(rv, rv);
967  sbAutoNSMemPtr autoMarshallID(marshallID);
968  rv = GetMarshallByID(marshallID, getter_AddRefs(marshall));
969  NS_ENSURE_SUCCESS(rv, rv);
970 
971  // Remove device.
972  rv = marshall->RemoveDevice(device);
973  NS_ENSURE_SUCCESS(rv, rv);
974  }
975 
976  return NS_OK;
977 }
978 
#define NS_QUIT_APPLICATION_REQUESTED_OBSERVER_ID
static sbDeviceEvent * CreateEvent()
nsresult RemoveAllDevices()
#define SB_MAIN_LIBRARY_READY_OBSERVER_ID
return NS_OK
var registrar
menuItem id
Definition: FeedWriter.js:971
inArray array
function succeeded(ch, cx, status, data)
#define NS_QUIT_APPLICATION_GRANTED_OBSERVER_ID
nsresult QuitApplicationGranted()
nsString Get(const nsAString &aKey, const nsAString &aDefault=SBVoidString())
static nsresult GetDeviceLibraryForItem(sbIDevice *aDevice, sbIMediaItem *aItem, sbIDeviceLibrary **_retval)
NS_IMPL_THREADSAFE_CI(sbMediaListEnumeratorWrapper)
NS_IMPL_QUERY_INTERFACE7_CI(sbDeviceManager, sbIDeviceManager2, sbIDeviceControllerRegistrar, sbIDeviceRegistrar, sbIDeviceEventTarget, nsISupportsWeakReference, nsIClassInfo, nsIObserver) NS_IMPL_CI_INTERFACE_GETTER5(sbDeviceManager
PRMonitor * mMonitor
NS_IMPL_THREADSAFE_RELEASE(sbRequestItem)
NS_IMPL_THREADSAFE_ADDREF(sbRequestItem)
readonly attribute nsIArray marshalls
var bundle
var count
Definition: test_bug7406.js:32
[UNIMPLEMENTED UNTIL AFTER 0.3]
PRBool mHasAllowedShutdown
#define NS_PROFILE_SHUTDOWN_OBSERVER_ID
nsInterfaceHashtableMT< nsIDHashKey, sbIDeviceController > mControllers
nsInterfaceHashtableMT< nsIDHashKey, sbIDevice > mDevices
#define SB_SERVICE_MANAGER_CONTRACTID
GstMessage * message
Songbird String Bundle Definitions.
[UNIMPLEMENTED UNTIL AFTER 0.3]
nsresult BeginMarshallMonitoring()
StringArrayEnumerator prototype hasMore
static nsresult CreateEventBeforeAddedData(sbIDevice *aDevice, sbIDeviceEventBeforeAddedData **aBeforeAddedData)
sbIJobCancelable NS_DECL_CLASSINFO(sbGstreamerMediaInspector)
readonly attribute nsIArray devices
#define SONGBIRD_DEVICEMANAGER2_CONTRACTID
nsresult QuitApplicationRequested(PRBool *aShouldQuit)
NS_INTERFACE_MAP_END NS_IMPL_CI_INTERFACE_GETTER5(sbLocalDatabaseMediaItem, nsIClassInfo, nsISupportsWeakReference, nsIRequestObserver, sbILibraryResource, sbIMediaItem) sbLocalDatabaseMediaItem
nsInterfaceHashtableMT< nsIDHashKey, sbIDeviceMarshall > mMarshalls
Interface that defines a single item of media in the system.
nsresult PrepareShutdown()
[UNIMPLEMENTED UNTIL AFTER 0.3]
nsresult FinalShutdown()
observe data
Definition: FeedWriter.js:1329
_getSelectedPageStyle s i
let observer
readonly attribute boolean canDisconnect
#define NS_PROFILE_STARTUP_OBSERVER_ID
const SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC
_updateTextAndScrollDataForFrame aData
readonly attribute nsIArray controllers