diff options
Diffstat (limited to 'dom/speakermanager')
-rw-r--r-- | dom/speakermanager/SpeakerManager.cpp | 217 | ||||
-rw-r--r-- | dom/speakermanager/SpeakerManager.h | 66 | ||||
-rw-r--r-- | dom/speakermanager/SpeakerManagerService.cpp | 224 | ||||
-rw-r--r-- | dom/speakermanager/SpeakerManagerService.h | 79 | ||||
-rw-r--r-- | dom/speakermanager/SpeakerManagerServiceChild.cpp | 121 | ||||
-rw-r--r-- | dom/speakermanager/SpeakerManagerServiceChild.h | 44 | ||||
-rw-r--r-- | dom/speakermanager/moz.build | 23 | ||||
-rw-r--r-- | dom/speakermanager/tests/mochitest.ini | 3 | ||||
-rw-r--r-- | dom/speakermanager/tests/test_speakermanager.html | 53 |
9 files changed, 830 insertions, 0 deletions
diff --git a/dom/speakermanager/SpeakerManager.cpp b/dom/speakermanager/SpeakerManager.cpp new file mode 100644 index 000000000..2b30fa72c --- /dev/null +++ b/dom/speakermanager/SpeakerManager.cpp @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SpeakerManager.h" + +#include "mozilla/Services.h" + +#include "mozilla/dom/Event.h" + +#include "AudioChannelService.h" +#include "nsIDocShell.h" +#include "nsIDOMClassInfo.h" +#include "nsIDOMEventListener.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIPermissionManager.h" +#include "SpeakerManagerService.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_QUERY_INTERFACE_INHERITED(SpeakerManager, DOMEventTargetHelper, + nsIDOMEventListener) +NS_IMPL_ADDREF_INHERITED(SpeakerManager, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(SpeakerManager, DOMEventTargetHelper) + +SpeakerManager::SpeakerManager() + : mForcespeaker(false) + , mVisible(false) +{ + SpeakerManagerService *service = + SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + service->RegisterSpeakerManager(this); +} + +SpeakerManager::~SpeakerManager() +{ + SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + + service->UnRegisterSpeakerManager(this); + nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner()); + NS_ENSURE_TRUE_VOID(target); + + target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), + this, + /* useCapture = */ true); +} + +bool +SpeakerManager::Speakerforced() +{ + // If a background app calls forcespeaker=true that doesn't change anything. + // 'speakerforced' remains false everywhere. + if (mForcespeaker && !mVisible) { + return false; + } + SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + return service->GetSpeakerStatus(); + +} + +bool +SpeakerManager::Forcespeaker() +{ + return mForcespeaker; +} + +void +SpeakerManager::SetForcespeaker(bool aEnable) +{ + SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + + service->ForceSpeaker(aEnable, mVisible); + mForcespeaker = aEnable; +} + +void +SpeakerManager::DispatchSimpleEvent(const nsAString& aStr) +{ + MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread"); + nsresult rv = CheckInnerWindowCorrectness(); + if (NS_FAILED(rv)) { + return; + } + + RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); + event->InitEvent(aStr, false, false); + event->SetTrusted(true); + + rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr); + if (NS_FAILED(rv)) { + NS_ERROR("Failed to dispatch the event!!!"); + return; + } +} + +void +SpeakerManager::Init(nsPIDOMWindowInner* aWindow) +{ + BindToOwner(aWindow); + + nsCOMPtr<nsIDocShell> docshell = GetOwner()->GetDocShell(); + NS_ENSURE_TRUE_VOID(docshell); + docshell->GetIsActive(&mVisible); + + nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner()); + NS_ENSURE_TRUE_VOID(target); + + target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), + this, + /* useCapture = */ true, + /* wantsUntrusted = */ false); +} + +nsPIDOMWindowInner* +SpeakerManager::GetParentObject() const +{ + return GetOwner(); +} + +/* static */ already_AddRefed<SpeakerManager> +SpeakerManager::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobal.GetAsSupports()); + if (!sgo) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports()); + if (!ownerWindow) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager(); + NS_ENSURE_TRUE(permMgr, nullptr); + + uint32_t permission = nsIPermissionManager::DENY_ACTION; + nsresult rv = + permMgr->TestPermissionFromWindow(ownerWindow, "speaker-control", + &permission); + NS_ENSURE_SUCCESS(rv, nullptr); + + if (permission != nsIPermissionManager::ALLOW_ACTION) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + RefPtr<SpeakerManager> object = new SpeakerManager(); + object->Init(ownerWindow); + return object.forget(); +} + +JSObject* +SpeakerManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return MozSpeakerManagerBinding::Wrap(aCx, this, aGivenProto); +} + +NS_IMETHODIMP +SpeakerManager::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString type; + aEvent->GetType(type); + + if (!type.EqualsLiteral("visibilitychange")) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner()); + NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE); + docshell->GetIsActive(&mVisible); + + // If an app that has called forcespeaker=true is switched + // from the background to the foreground 'speakerforced' + // switches to true in all apps. I.e. the app doesn't have to + // call forcespeaker=true again when it comes into foreground. + SpeakerManagerService *service = + SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + + if (mVisible && mForcespeaker) { + service->ForceSpeaker(mForcespeaker, mVisible); + } + // If an application that has called forcespeaker=true, but no audio is + // currently playing in the app itself, if application switch to + // the background, we switch 'speakerforced' to false. + if (!mVisible && mForcespeaker) { + RefPtr<AudioChannelService> audioChannelService = + AudioChannelService::GetOrCreate(); + if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) { + service->ForceSpeaker(false, mVisible); + } + } + return NS_OK; +} + +void +SpeakerManager::SetAudioChannelActive(bool isActive) +{ + if (mForcespeaker) { + SpeakerManagerService *service = + SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + service->ForceSpeaker(isActive, mVisible); + } +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/speakermanager/SpeakerManager.h b/dom/speakermanager/SpeakerManager.h new file mode 100644 index 000000000..bf084dfd9 --- /dev/null +++ b/dom/speakermanager/SpeakerManager.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SpeakerManager_h +#define mozilla_dom_SpeakerManager_h + +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/MozSpeakerManagerBinding.h" + +namespace mozilla { +namespace dom { +/* This class is used for UA to control devices's speaker status. + * After UA set the speaker status, the UA should handle the + * forcespeakerchange event and change the speaker status in UI. + * The device's speaker status would set back to normal when UA close the application. + */ +class SpeakerManager final + : public DOMEventTargetHelper + , public nsIDOMEventListener +{ + friend class SpeakerManagerService; + friend class SpeakerManagerServiceChild; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIDOMEVENTLISTENER + +public: + void Init(nsPIDOMWindowInner* aWindow); + + nsPIDOMWindowInner* GetParentObject() const; + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + /** + * WebIDL Interface + */ + // Get this api's force speaker setting. + bool Forcespeaker(); + // Force acoustic sound go through speaker. Don't force to speaker if application + // stay in the background and re-force when application + // go to foreground + void SetForcespeaker(bool aEnable); + // Get the device's speaker forced setting. + bool Speakerforced(); + + void SetAudioChannelActive(bool aIsActive); + IMPL_EVENT_HANDLER(speakerforcedchange) + + static already_AddRefed<SpeakerManager> + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + +protected: + SpeakerManager(); + ~SpeakerManager(); + void DispatchSimpleEvent(const nsAString& aStr); + // This api's force speaker setting + bool mForcespeaker; + bool mVisible; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SpeakerManager_h diff --git a/dom/speakermanager/SpeakerManagerService.cpp b/dom/speakermanager/SpeakerManagerService.cpp new file mode 100644 index 000000000..a444f7163 --- /dev/null +++ b/dom/speakermanager/SpeakerManagerService.cpp @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SpeakerManagerService.h" +#include "SpeakerManagerServiceChild.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/ContentParent.h" +#include "nsIPropertyBag2.h" +#include "nsThreadUtils.h" +#include "nsServiceManagerUtils.h" +#include "AudioChannelService.h" +#include <cutils/properties.h> + +#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1" +#include "nsIAudioManager.h" + +using namespace mozilla; +using namespace mozilla::dom; + +StaticRefPtr<SpeakerManagerService> gSpeakerManagerService; + +// static +SpeakerManagerService* +SpeakerManagerService::GetOrCreateSpeakerManagerService() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!XRE_IsParentProcess()) { + return SpeakerManagerServiceChild::GetOrCreateSpeakerManagerService(); + } + + // If we already exist, exit early + if (gSpeakerManagerService) { + return gSpeakerManagerService; + } + + // Create new instance, register, return + RefPtr<SpeakerManagerService> service = new SpeakerManagerService(); + + gSpeakerManagerService = service; + + return gSpeakerManagerService; +} + +SpeakerManagerService* +SpeakerManagerService::GetSpeakerManagerService() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!XRE_IsParentProcess()) { + return SpeakerManagerServiceChild::GetSpeakerManagerService(); + } + + return gSpeakerManagerService; +} + +void +SpeakerManagerService::Shutdown() +{ + if (!XRE_IsParentProcess()) { + return SpeakerManagerServiceChild::Shutdown(); + } + + if (gSpeakerManagerService) { + gSpeakerManagerService = nullptr; + } +} + +NS_IMPL_ISUPPORTS(SpeakerManagerService, nsIObserver) + +void +SpeakerManagerService::ForceSpeaker(bool aEnable, uint64_t aChildId) +{ + TurnOnSpeaker(aEnable); + if (aEnable) { + mSpeakerStatusSet.Put(aChildId); + } + Notify(); + return; +} + +void +SpeakerManagerService::ForceSpeaker(bool aEnable, bool aVisible) +{ + // b2g main process without oop + TurnOnSpeaker(aEnable && aVisible); + mVisible = aVisible; + mOrgSpeakerStatus = aEnable; + Notify(); +} + +void +SpeakerManagerService::TurnOnSpeaker(bool aOn) +{ + nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID); + NS_ENSURE_TRUE_VOID(audioManager); + int32_t phoneState; + audioManager->GetPhoneState(&phoneState); + int32_t forceuse = (phoneState == nsIAudioManager::PHONE_STATE_IN_CALL || + phoneState == nsIAudioManager::PHONE_STATE_IN_COMMUNICATION) + ? nsIAudioManager::USE_COMMUNICATION : nsIAudioManager::USE_MEDIA; + if (aOn) { + audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_SPEAKER); + } else { + audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_NONE); + } +} + +bool +SpeakerManagerService::GetSpeakerStatus() +{ + char propQemu[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", propQemu, ""); + if (!strncmp(propQemu, "1", 1)) { + return mOrgSpeakerStatus; + } + nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID); + NS_ENSURE_TRUE(audioManager, false); + int32_t usage; + audioManager->GetForceForUse(nsIAudioManager::USE_MEDIA, &usage); + return usage == nsIAudioManager::FORCE_SPEAKER; +} + +void +SpeakerManagerService::Notify() +{ + // Parent Notify to all the child processes. + nsTArray<ContentParent*> children; + ContentParent::GetAll(children); + for (uint32_t i = 0; i < children.Length(); i++) { + Unused << children[i]->SendSpeakerManagerNotify(); + } + + for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) { + mRegisteredSpeakerManagers[i]-> + DispatchSimpleEvent(NS_LITERAL_STRING("speakerforcedchange")); + } +} + +void +SpeakerManagerService::SetAudioChannelActive(bool aIsActive) +{ + if (!aIsActive && !mVisible) { + ForceSpeaker(!mOrgSpeakerStatus, mVisible); + } +} + +NS_IMETHODIMP +SpeakerManagerService::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (!strcmp(aTopic, "ipc:content-shutdown")) { + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); + if (!props) { + NS_WARNING("ipc:content-shutdown message without property bag as subject"); + return NS_OK; + } + + uint64_t childID = 0; + nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), + &childID); + if (NS_SUCCEEDED(rv)) { + // If the audio has paused by audiochannel, + // the enable flag should be false and don't need to handle. + if (mSpeakerStatusSet.Contains(childID)) { + TurnOnSpeaker(false); + mSpeakerStatusSet.Remove(childID); + } + if (mOrgSpeakerStatus) { + TurnOnSpeaker(!mOrgSpeakerStatus); + mOrgSpeakerStatus = false; + } + } else { + NS_WARNING("ipc:content-shutdown message without childID property"); + } + } else if (!strcmp(aTopic, "xpcom-will-shutdown")) { + // Note that we need to do this before xpcom-shutdown, since the + // AudioChannelService cannot be used past that point. + RefPtr<AudioChannelService> audioChannelService = + AudioChannelService::GetOrCreate(); + if (audioChannelService) { + audioChannelService->UnregisterSpeakerManager(this); + } + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(this, "ipc:content-shutdown"); + obs->RemoveObserver(this, "xpcom-will-shutdown"); + } + + Shutdown(); + } + return NS_OK; +} + +SpeakerManagerService::SpeakerManagerService() + : mOrgSpeakerStatus(false), + mVisible(false) +{ + MOZ_COUNT_CTOR(SpeakerManagerService); + if (XRE_IsParentProcess()) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(this, "ipc:content-shutdown", false); + obs->AddObserver(this, "xpcom-will-shutdown", false); + } + } + RefPtr<AudioChannelService> audioChannelService = + AudioChannelService::GetOrCreate(); + if (audioChannelService) { + audioChannelService->RegisterSpeakerManager(this); + } +} + +SpeakerManagerService::~SpeakerManagerService() +{ + MOZ_COUNT_DTOR(SpeakerManagerService); +} diff --git a/dom/speakermanager/SpeakerManagerService.h b/dom/speakermanager/SpeakerManagerService.h new file mode 100644 index 000000000..5f3506ae2 --- /dev/null +++ b/dom/speakermanager/SpeakerManagerService.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SpeakerManagerService_h__ +#define mozilla_dom_SpeakerManagerService_h__ + +#include "nsIObserver.h" +#include "nsTArray.h" +#include "SpeakerManager.h" +#include "nsIAudioManager.h" +#include "nsCheapSets.h" +#include "nsHashKeys.h" + +namespace mozilla { +namespace dom { + +class SpeakerManagerService : public nsIObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + /* + * Return null or instance which has been created. + */ + static SpeakerManagerService* GetSpeakerManagerService(); + /* + * Return SpeakerManagerService instance. + * If SpeakerManagerService is not exist, create and return new one. + */ + static SpeakerManagerService* GetOrCreateSpeakerManagerService(); + virtual void ForceSpeaker(bool aEnable, bool aVisible); + virtual bool GetSpeakerStatus(); + virtual void SetAudioChannelActive(bool aIsActive); + // Called by child + void ForceSpeaker(bool enable, uint64_t aChildid); + // Register the SpeakerManager to service for notify the speakerforcedchange event + void RegisterSpeakerManager(SpeakerManager* aSpeakerManager) + { + mRegisteredSpeakerManagers.AppendElement(aSpeakerManager); + } + void UnRegisterSpeakerManager(SpeakerManager* aSpeakerManager) + { + mRegisteredSpeakerManagers.RemoveElement(aSpeakerManager); + } + +protected: + SpeakerManagerService(); + + virtual ~SpeakerManagerService(); + // Notify to UA if device speaker status changed + virtual void Notify(); + + void TurnOnSpeaker(bool aEnable); + + /** + * Shutdown the singleton. + */ + static void Shutdown(); + + nsTArray<RefPtr<SpeakerManager> > mRegisteredSpeakerManagers; + // Set for remember all the child speaker status + nsCheapSet<nsUint64HashKey> mSpeakerStatusSet; + // The Speaker status assign by UA + bool mOrgSpeakerStatus; + + bool mVisible; + // This is needed for IPC communication between + // SpeakerManagerServiceChild and this class. + friend class ContentParent; + friend class ContentChild; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/dom/speakermanager/SpeakerManagerServiceChild.cpp b/dom/speakermanager/SpeakerManagerServiceChild.cpp new file mode 100644 index 000000000..6c1e30b78 --- /dev/null +++ b/dom/speakermanager/SpeakerManagerServiceChild.cpp @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SpeakerManagerServiceChild.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ContentParent.h" +#include "nsIObserverService.h" +#include "nsThreadUtils.h" +#include "AudioChannelService.h" +#include <cutils/properties.h> + +using namespace mozilla; +using namespace mozilla::dom; + +StaticRefPtr<SpeakerManagerServiceChild> gSpeakerManagerServiceChild; + +// static +SpeakerManagerService* +SpeakerManagerServiceChild::GetOrCreateSpeakerManagerService() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // If we already exist, exit early + if (gSpeakerManagerServiceChild) { + return gSpeakerManagerServiceChild; + } + + // Create new instance, register, return + RefPtr<SpeakerManagerServiceChild> service = new SpeakerManagerServiceChild(); + + gSpeakerManagerServiceChild = service; + + return gSpeakerManagerServiceChild; +} + +// static +SpeakerManagerService* +SpeakerManagerServiceChild::GetSpeakerManagerService() +{ + MOZ_ASSERT(NS_IsMainThread()); + + return gSpeakerManagerServiceChild; +} + +void +SpeakerManagerServiceChild::ForceSpeaker(bool aEnable, bool aVisible) +{ + mVisible = aVisible; + mOrgSpeakerStatus = aEnable; + ContentChild *cc = ContentChild::GetSingleton(); + if (cc) { + cc->SendSpeakerManagerForceSpeaker(aEnable && aVisible); + } +} + +bool +SpeakerManagerServiceChild::GetSpeakerStatus() +{ + ContentChild *cc = ContentChild::GetSingleton(); + bool status = false; + if (cc) { + cc->SendSpeakerManagerGetSpeakerStatus(&status); + } + char propQemu[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", propQemu, ""); + if (!strncmp(propQemu, "1", 1)) { + return mOrgSpeakerStatus; + } + return status; +} + +void +SpeakerManagerServiceChild::Shutdown() +{ + if (gSpeakerManagerServiceChild) { + gSpeakerManagerServiceChild = nullptr; + } +} + +void +SpeakerManagerServiceChild::SetAudioChannelActive(bool aIsActive) +{ + // Content process and switch to background with no audio and speaker forced. + // Then disable speaker + for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) { + mRegisteredSpeakerManagers[i]->SetAudioChannelActive(aIsActive); + } +} + +SpeakerManagerServiceChild::SpeakerManagerServiceChild() +{ + MOZ_ASSERT(NS_IsMainThread()); + RefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate(); + if (audioChannelService) { + audioChannelService->RegisterSpeakerManager(this); + } + MOZ_COUNT_CTOR(SpeakerManagerServiceChild); +} + +SpeakerManagerServiceChild::~SpeakerManagerServiceChild() +{ + RefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate(); + if (audioChannelService) { + audioChannelService->UnregisterSpeakerManager(this); + } + MOZ_COUNT_DTOR(SpeakerManagerServiceChild); +} + +void +SpeakerManagerServiceChild::Notify() +{ + for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) { + mRegisteredSpeakerManagers[i]->DispatchSimpleEvent(NS_LITERAL_STRING("speakerforcedchange")); + } +} diff --git a/dom/speakermanager/SpeakerManagerServiceChild.h b/dom/speakermanager/SpeakerManagerServiceChild.h new file mode 100644 index 000000000..5eb1c6afa --- /dev/null +++ b/dom/speakermanager/SpeakerManagerServiceChild.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SpeakerManagerServicechild_h__ +#define mozilla_dom_SpeakerManagerServicechild_h__ + +#include "nsISupports.h" +#include "SpeakerManagerService.h" + +namespace mozilla { +namespace dom { +/* This class is used to do the IPC to enable/disable speaker status + Also handle the application speaker competition problem +*/ +class SpeakerManagerServiceChild : public SpeakerManagerService +{ +public: + /* + * Return null or instance which has been created. + */ + static SpeakerManagerService* GetSpeakerManagerService(); + /* + * Return SpeakerManagerServiceChild instance. + * If SpeakerManagerServiceChild is not exist, create and return new one. + */ + static SpeakerManagerService* GetOrCreateSpeakerManagerService(); + static void Shutdown(); + virtual void ForceSpeaker(bool aEnable, bool aVisible) override; + virtual bool GetSpeakerStatus() override; + virtual void SetAudioChannelActive(bool aIsActive) override; + virtual void Notify() override; +protected: + SpeakerManagerServiceChild(); + virtual ~SpeakerManagerServiceChild(); +}; + +} // namespace dom +} // namespace mozilla + +#endif + diff --git a/dom/speakermanager/moz.build b/dom/speakermanager/moz.build new file mode 100644 index 000000000..22ed849fb --- /dev/null +++ b/dom/speakermanager/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] + +EXPORTS += [ + 'SpeakerManager.h', + 'SpeakerManagerService.h', + 'SpeakerManagerServiceChild.h', +] + +UNIFIED_SOURCES += [ + 'SpeakerManager.cpp', + 'SpeakerManagerService.cpp', + 'SpeakerManagerServiceChild.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' diff --git a/dom/speakermanager/tests/mochitest.ini b/dom/speakermanager/tests/mochitest.ini new file mode 100644 index 000000000..bb272adc0 --- /dev/null +++ b/dom/speakermanager/tests/mochitest.ini @@ -0,0 +1,3 @@ +[DEFAULT] + +[test_speakermanager.html] diff --git a/dom/speakermanager/tests/test_speakermanager.html b/dom/speakermanager/tests/test_speakermanager.html new file mode 100644 index 000000000..509df5e84 --- /dev/null +++ b/dom/speakermanager/tests/test_speakermanager.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test MozSpeakerManager API</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"> + <script type="application/javascript"> + + "use strict"; + + function testObject() { + var mgr = new MozSpeakerManager(); + var spkforced = false; + mgr.onspeakerforcedchange = function() { + if (spkforced) { + is(mgr.speakerforced, true, 'speaker should be true'); + spkforced = false; + mgr.forcespeaker = false; + } else { + is(mgr.speakerforced, false, 'speaker should be false'); + SimpleTest.finish(); + } + } + spkforced = true; + mgr.forcespeaker = true; + } + + function startTests() { + // Currently applicable only on FxOS + if (navigator.userAgent.indexOf("Mobile") != -1 && + navigator.appVersion.indexOf("Android") == -1) { + testObject(); + } else { + ok(true, "MozSpeakerManager on Firefox OS only."); + SimpleTest.finish(); + } + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPermissions( + [{ "type": "speaker-control", "allow": 1, "context": document }], + startTests); + + </script> +</pre> +</body> +</html> |