/* -*- 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); }