Bug 854753 - [B2G][Audio] Implement SpeakerManager API. r=amarchesini, sr=jonas

This commit is contained in:
Randy Lin 2013-11-25 12:50:03 +13:00
parent 57a91dafdb
commit 19e505c45a
29 changed files with 946 additions and 5 deletions

View File

@ -18,4 +18,4 @@
# Modifying this file will now automatically clobber the buildbot machines \o/
#
Bug 921918 - need clobber for Windows.
Bug 854753 - need clobber for Windows for new webidl changes.

View File

@ -712,6 +712,7 @@ GK_ATOM(onerror, "onerror")
GK_ATOM(onfailed, "onfailed")
GK_ATOM(onfocus, "onfocus")
GK_ATOM(onfrequencychange, "onfrequencychange")
GK_ATOM(onspeakerforcedchange, "onspeakerforcedchange")
GK_ATOM(onget, "onget")
GK_ATOM(ongroupchange, "ongroupchange")
GK_ATOM(onhashchange, "onhashchange")

View File

@ -654,6 +654,11 @@ NON_IDL_EVENT(warning,
EventNameType_None,
NS_EVENT)
NON_IDL_EVENT(speakerforcedchange,
NS_SPEAKERMANAGER_SPEAKERFORCEDCHANGE,
EventNameType_None,
NS_EVENT)
// Events that only have on* attributes on XUL elements
NON_IDL_EVENT(text,
NS_TEXT_TEXT,

View File

@ -313,6 +313,11 @@ this.PermissionsTable = { geolocation: {
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
"speaker-control": {
app: DENY_ACTION,
privileged: ALLOW_ACTION,
certified: ALLOW_ACTION
},
};
/**

View File

@ -25,6 +25,7 @@
#include "nsJSUtils.h"
#include "nsCxPusher.h"
#include "nsIAudioManager.h"
#include "SpeakerManagerService.h"
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
#endif
@ -170,6 +171,12 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
UnregisterType(data->mType, data->mElementHidden,
CONTENT_PROCESS_ID_MAIN, data->mWithVideo);
}
#ifdef MOZ_WIDGET_GONK
bool active = AnyAudioChannelIsActive();
for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
mSpeakerManager[i]->SetAudioChannelActive(active);
}
#endif
}
void
@ -559,6 +566,19 @@ AudioChannelService::Notify(nsITimer* aTimer)
return NS_OK;
}
bool
AudioChannelService::AnyAudioChannelIsActive()
{
for (int i = AUDIO_CHANNEL_INT_LAST - 1;
i >= AUDIO_CHANNEL_INT_NORMAL; --i) {
if (!mChannelCounters[i].IsEmpty()) {
return true;
}
}
return false;
}
bool
AudioChannelService::ChannelsActiveWithHigherPriorityThan(
AudioChannelInternalType aType)

View File

@ -18,7 +18,9 @@
namespace mozilla {
namespace dom {
#ifdef MOZ_WIDGET_GONK
class SpeakerManagerService;
#endif
class AudioChannelService
: public nsIObserver
, public nsITimerCallback
@ -81,6 +83,21 @@ public:
virtual void SetDefaultVolumeControlChannel(AudioChannelType aType,
bool aHidden);
bool AnyAudioChannelIsActive();
#ifdef MOZ_WIDGET_GONK
void RegisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
{
if (!mSpeakerManager.Contains(aSpeakerManager)) {
mSpeakerManager.AppendElement(aSpeakerManager);
}
}
void UnregisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
{
mSpeakerManager.RemoveElement(aSpeakerManager);
}
#endif
protected:
void Notify();
@ -163,7 +180,9 @@ protected:
AudioChannelAgentData* aData, void *aUnused);
nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
#ifdef MOZ_WIDGET_GONK
nsTArray<SpeakerManagerService*> mSpeakerManager;
#endif
nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
AudioChannelType mCurrentHigherChannel;

View File

@ -17,6 +17,10 @@
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
#ifdef MOZ_WIDGET_GONK
#include "SpeakerManagerService.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::hal;
@ -120,6 +124,12 @@ AudioChannelServiceChild::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
if (obs) {
obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
}
#ifdef MOZ_WIDGET_GONK
bool active = AnyAudioChannelIsActive();
for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
mSpeakerManager[i]->SetAudioChannelActive(active);
}
#endif
}
void

View File

@ -802,6 +802,11 @@ DOMInterfaces = {
'nativeType': 'nsDOMAttributeMap',
},
'MozSpeakerManager': {
'nativeType': 'mozilla::dom::SpeakerManager',
'headerFile': 'SpeakerManager.h'
},
'MozPowerManager': {
'nativeType': 'mozilla::dom::PowerManager',
},

View File

@ -81,6 +81,7 @@
#if defined(MOZ_WIDGET_GONK)
#include "nsVolume.h"
#include "nsVolumeService.h"
#include "SpeakerManagerService.h"
#endif
#ifdef XP_WIN
@ -586,6 +587,20 @@ ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs)
return true;
}
bool
ContentChild::RecvSpeakerManagerNotify()
{
#ifdef MOZ_WIDGET_GONK
nsRefPtr<SpeakerManagerService> service =
SpeakerManagerService::GetSpeakerManagerService();
if (service) {
service->Notify();
}
return true;
#endif
return false;
}
static CancelableTask* sFirstIdleTask;
static void FirstIdle(void)

View File

@ -178,6 +178,8 @@ public:
virtual bool RecvSetOffline(const bool& offline);
virtual bool RecvSpeakerManagerNotify();
virtual bool RecvNotifyVisited(const URIParams& aURI);
// auto remove when alertfinished is received.
nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);

View File

@ -113,6 +113,7 @@
#ifdef MOZ_WIDGET_GONK
#include "nsIVolume.h"
#include "nsIVolumeService.h"
#include "SpeakerManagerService.h"
using namespace mozilla::system;
#endif
@ -2478,6 +2479,35 @@ ContentParent::RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor)
#endif
}
bool
ContentParent::RecvSpeakerManagerGetSpeakerStatus(bool* aValue)
{
#ifdef MOZ_WIDGET_GONK
*aValue = false;
nsRefPtr<SpeakerManagerService> service =
SpeakerManagerService::GetSpeakerManagerService();
if (service) {
*aValue = service->GetSpeakerStatus();
}
return true;
#endif
return false;
}
bool
ContentParent::RecvSpeakerManagerForceSpeaker(const bool& aEnable)
{
#ifdef MOZ_WIDGET_GONK
nsRefPtr<SpeakerManagerService> service =
SpeakerManagerService::GetSpeakerManagerService();
if (service) {
service->ForceSpeaker(aEnable, mChildID);
}
return true;
#endif
return false;
}
bool
ContentParent::RecvStartVisitedQuery(const URIParams& aURI)
{

View File

@ -474,6 +474,10 @@ private:
virtual bool RecvBroadcastVolume(const nsString& aVolumeName);
virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue);
virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable);
virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE;
virtual bool RecvNuwaReady() MOZ_OVERRIDE;

View File

@ -238,6 +238,8 @@ child:
*/
async AudioChannelNotify();
async SpeakerManagerNotify();
/**
* Do a memory info dump to a file in our temp directory.
*
@ -247,7 +249,6 @@ child:
async DumpMemoryInfoToTempDir(nsString identifier,
bool minimizeMemoryUsage,
bool dumpChildProcesses);
/**
* Dump this process's GC and CC logs.
*
@ -471,6 +472,11 @@ parent:
sync KeywordToURI(nsCString keyword)
returns (OptionalInputStreamParams postData, OptionalURIParams uri);
sync SpeakerManagerForceSpeaker(bool aEnable);
sync SpeakerManagerGetSpeakerStatus()
returns (bool value);
both:
AsyncMessage(nsString aMessage, ClonedMessageData aData,
CpowEntry[] aCpows, Principal aPrincipal);

View File

@ -81,7 +81,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
PARALLEL_DIRS += ['plugins/ipc/hangui']
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
PARALLEL_DIRS += ['wifi']
PARALLEL_DIRS += [
'speakermanager',
'wifi',
]
if CONFIG['MOZ_B2G_RIL']:
PARALLEL_DIRS += [

View File

@ -0,0 +1,219 @@
/* 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 "nsIDOMClassInfo.h"
#include "nsIDOMEventListener.h"
#include "SpeakerManagerService.h"
#include "nsIPermissionManager.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIDocShell.h"
#include "nsDOMEvent.h"
#include "AudioChannelService.h"
using namespace mozilla::dom;
NS_IMPL_QUERY_INTERFACE_INHERITED1(SpeakerManager, nsDOMEventTargetHelper,
nsIDOMEventListener)
NS_IMPL_ADDREF_INHERITED(SpeakerManager, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(SpeakerManager, nsDOMEventTargetHelper)
SpeakerManager::SpeakerManager()
: mForcespeaker(false)
, mVisible(false)
{
SetIsDOMBinding();
SpeakerManagerService *service =
SpeakerManagerService::GetSpeakerManagerService();
if (service) {
service->RegisterSpeakerManager(this);
}
}
SpeakerManager::~SpeakerManager()
{
SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService();
if (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::GetSpeakerManagerService();
if (service) {
return service->GetSpeakerStatus();
}
return false;
}
bool
SpeakerManager::Forcespeaker()
{
return mForcespeaker;
}
void
SpeakerManager::SetForcespeaker(bool aEnable)
{
SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService();
if (service) {
service->ForceSpeaker(aEnable, mVisible);
}
mForcespeaker = aEnable;
}
void
SpeakerManager::DispatchSimpleEvent(const nsAString& aStr)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
nsresult rv = CheckInnerWindowCorrectness();
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIDOMEvent> event;
rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create the error event!!!");
return;
}
rv = event->InitEvent(aStr, false, false);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to init the error event!!!");
return;
}
event->SetTrusted(true);
rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
if (NS_FAILED(rv)) {
NS_ERROR("Failed to dispatch the event!!!");
return;
}
}
void
SpeakerManager::Init(nsPIDOMWindow* aWindow)
{
BindToOwner(aWindow->IsOuterWindow() ?
aWindow->GetCurrentInnerWindow() : aWindow);
mVisible = !GetOwner()->IsBackground();
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
NS_ENSURE_TRUE_VOID(target);
target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this,
/* useCapture = */ true,
/* wantsUntrusted = */ false);
}
nsPIDOMWindow*
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<nsPIDOMWindow> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
if (!ownerWindow) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIPermissionManager> permMgr =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
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;
}
nsRefPtr<SpeakerManager> object = new SpeakerManager();
object->Init(ownerWindow);
return object.forget();
}
JSObject*
SpeakerManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return MozSpeakerManagerBinding::Wrap(aCx, aScope, this);
}
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::GetSpeakerManagerService();
if (service && 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) {
AudioChannelService* audioChannelService =
AudioChannelService::GetAudioChannelService();
if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) {
service->ForceSpeaker(false, mVisible);
}
}
return NS_OK;
}
void
SpeakerManager::SetAudioChannelActive(bool isActive)
{
if (!isActive && !mVisible) {
SpeakerManagerService *service =
SpeakerManagerService::GetSpeakerManagerService();
if (service) {
service->ForceSpeaker(false, mVisible);
}
}
}

View File

@ -0,0 +1,65 @@
/* 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 "nsDOMEventTargetHelper.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 MOZ_FINAL
: public nsDOMEventTargetHelper
, public nsIDOMEventListener
{
friend class SpeakerManagerService;
friend class SpeakerManagerServiceChild;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMEVENTLISTENER
public:
void Init(nsPIDOMWindow* aWindow);
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_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

View File

@ -0,0 +1,199 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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/Util.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::GetSpeakerManagerService()
{
MOZ_ASSERT(NS_IsMainThread());
if (XRE_GetProcessType() != GeckoProcessType_Default) {
return SpeakerManagerServiceChild::GetSpeakerManagerService();
}
// If we already exist, exit early
if (gSpeakerManagerService) {
return gSpeakerManagerService;
}
// Create new instance, register, return
nsRefPtr<SpeakerManagerService> service = new SpeakerManagerService();
NS_ENSURE_TRUE(service, nullptr);
gSpeakerManagerService = service;
return gSpeakerManagerService;
}
void
SpeakerManagerService::Shutdown()
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
return SpeakerManagerServiceChild::Shutdown();
}
if (gSpeakerManagerService) {
gSpeakerManagerService = nullptr;
}
}
NS_IMPL_ISUPPORTS1(SpeakerManagerService, nsIObserver)
void
SpeakerManagerService::ForceSpeaker(bool aEnable, uint64_t aChildId)
{
TuruOnSpeaker(aEnable);
if (aEnable) {
mSpeakerStatusSet.Put(aChildId);
}
Notify();
return;
}
void
SpeakerManagerService::ForceSpeaker(bool aEnable, bool aVisible)
{
// b2g main process without oop
TuruOnSpeaker(aEnable && aVisible);
mVisible = aVisible;
mOrgSpeakerStatus = aEnable;
Notify();
}
void
SpeakerManagerService::TuruOnSpeaker(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 PRUnichar* 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)) {
TuruOnSpeaker(false);
mSpeakerStatusSet.Remove(childID);
}
if (mOrgSpeakerStatus) {
TuruOnSpeaker(!mOrgSpeakerStatus);
mOrgSpeakerStatus = false;
}
} else {
NS_WARNING("ipc:content-shutdown message without childID property");
}
}
return NS_OK;
}
SpeakerManagerService::SpeakerManagerService()
: mOrgSpeakerStatus(false),
mVisible(false)
{
MOZ_COUNT_CTOR(SpeakerManagerService);
if (XRE_GetProcessType() == GeckoProcessType_Default) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, "ipc:content-shutdown", false);
}
}
AudioChannelService* audioChannelService =
AudioChannelService::GetAudioChannelService();
if (audioChannelService) {
audioChannelService->RegisterSpeakerManager(this);
}
}
SpeakerManagerService::~SpeakerManagerService()
{
MOZ_COUNT_DTOR(SpeakerManagerService);
AudioChannelService* audioChannelService =
AudioChannelService::GetAudioChannelService();
if (audioChannelService)
audioChannelService->UnregisterSpeakerManager(this);
}

View File

@ -0,0 +1,72 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 "nsAutoPtr.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
static SpeakerManagerService* GetSpeakerManagerService();
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);
}
/**
* Shutdown the singleton.
*/
static void Shutdown();
protected:
SpeakerManagerService();
virtual ~SpeakerManagerService();
// Notify to UA if device speaker status changed
virtual void Notify();
void TuruOnSpeaker(bool aEnable);
nsTArray<nsRefPtr<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

View File

@ -0,0 +1,113 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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/Util.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::GetSpeakerManagerService()
{
MOZ_ASSERT(NS_IsMainThread());
// If we already exist, exit early
if (gSpeakerManagerServiceChild) {
return gSpeakerManagerServiceChild;
}
// Create new instance, register, return
nsRefPtr<SpeakerManagerServiceChild> service = new SpeakerManagerServiceChild();
NS_ENSURE_TRUE(service, nullptr);
gSpeakerManagerServiceChild = service;
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());
AudioChannelService* audioChannelService = AudioChannelService::GetAudioChannelService();
if (audioChannelService) {
audioChannelService->RegisterSpeakerManager(this);
}
MOZ_COUNT_CTOR(SpeakerManagerServiceChild);
}
SpeakerManagerServiceChild::~SpeakerManagerServiceChild()
{
AudioChannelService* audioChannelService = AudioChannelService::GetAudioChannelService();
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"));
}
}

View File

@ -0,0 +1,37 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 "nsAutoPtr.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:
static SpeakerManagerService* GetSpeakerManagerService();
static void Shutdown();
virtual void ForceSpeaker(bool aEnable, bool aVisible) MOZ_OVERRIDE;
virtual bool GetSpeakerStatus() MOZ_OVERRIDE;
virtual void SetAudioChannelActive(bool aIsActive) MOZ_OVERRIDE;
virtual void Notify() MOZ_OVERRIDE;
protected:
SpeakerManagerServiceChild();
virtual ~SpeakerManagerServiceChild();
};
} // namespace dom
} // namespace mozilla
#endif

View File

@ -0,0 +1,27 @@
# -*- Mode: python; c-basic-offset: 4; 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']
XPIDL_MODULE = 'dom_speakermanager'
EXPORTS += [
'SpeakerManager.h',
'SpeakerManagerService.h',
'SpeakerManagerServiceChild.h',
]
SOURCES += [
'SpeakerManager.cpp',
'SpeakerManagerService.cpp',
'SpeakerManagerServiceChild.cpp',
]
FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'gklayout'

View File

@ -0,0 +1,3 @@
[DEFAULT]
[test_speakermanager.html]

View File

@ -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, "mozAlarms on Firefox OS only.");
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPermissions(
[{ "type": "speaker-control", "allow": 1, "context": document }],
startTests);
</script>
</pre>
</body>
</html>

View File

@ -374,6 +374,7 @@ var interfaceNamesInGlobalScope =
"MozSmsFilter",
"MozSmsMessage",
"MozSmsSegmentInfo",
{name: "MozSpeakerManager", b2g: true},
{name: "MozStkCommandEvent", b2g: true, pref: "dom.icc.enabled"},
{name: "MozTimeManager", b2g: true},
{name: "MozVoicemail", b2g: true, pref: "dom.voicemail.enabled"},

View File

@ -0,0 +1,18 @@
/* 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/.
*/
/*
* Allow application can control acoustic sound output through speaker.
* Reference https://wiki.mozilla.org/WebAPI/SpeakerManager
*/
[Constructor()]
interface MozSpeakerManager : EventTarget {
/* query the speaker status */
readonly attribute boolean speakerforced;
/* force device device's acoustic sound output through speaker */
attribute boolean forcespeaker;
/* this event will be fired when device's speaker forced status change */
attribute EventHandler onspeakerforcedchange;
};

View File

@ -532,6 +532,7 @@ if CONFIG['MOZ_NFC']:
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
WEBIDL_FILES += [
'MozSpeakerManager.webidl',
'MozWifiConnectionInfoEvent.webidl',
'MozWifiStatusChangeEvent.webidl',
]

View File

@ -78,6 +78,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \
-I$(topsrcdir)/dom/audiochannel \
-I$(topsrcdir)/dom/telephony \
-I$(topsrcdir)/dom/media \
-I$(topsrcdir)/dom/speakermanager \
-I. \
-I$(topsrcdir)/editor/libeditor/base \
-I$(topsrcdir)/editor/libeditor/text \
@ -100,6 +101,7 @@ endif
ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
LOCAL_INCLUDES += -I$(topsrcdir)/dom/system/gonk
LOCAL_INCLUDES += -I$(topsrcdir)/dom/speakermanager
endif #}
ifdef MOZ_B2G_FM #{

View File

@ -100,6 +100,7 @@
#ifdef MOZ_WIDGET_GONK
#include "nsVolumeService.h"
#include "SpeakerManagerService.h"
using namespace mozilla::system;
#endif
@ -372,6 +373,7 @@ nsLayoutStatics::Shutdown()
#ifdef MOZ_WIDGET_GONK
nsVolumeService::Shutdown();
SpeakerManagerService::Shutdown();
#endif
#ifdef MOZ_WEBSPEECH

View File

@ -460,6 +460,10 @@ enum nsEventStructType
#define NS_MEDIARECORDER_WARNING (NS_MEDIARECORDER_EVENT_START + 2)
#define NS_MEDIARECORDER_STOP (NS_MEDIARECORDER_EVENT_START + 3)
// SpeakerManager events
#define NS_SPEAKERMANAGER_EVENT_START 5800
#define NS_SPEAKERMANAGER_SPEAKERFORCEDCHANGE (NS_SPEAKERMANAGER_EVENT_START + 1)
#ifdef MOZ_GAMEPAD
// Gamepad input events
#define NS_GAMEPAD_START 6000