AudioChannelAgent.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "AudioChannelAgent.h"
  6. #include "AudioChannelService.h"
  7. #include "mozilla/Preferences.h"
  8. #include "nsContentUtils.h"
  9. #include "nsIDocument.h"
  10. #include "nsIDOMWindow.h"
  11. #include "nsPIDOMWindow.h"
  12. #include "nsIURI.h"
  13. using namespace mozilla::dom;
  14. NS_IMPL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
  15. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioChannelAgent)
  16. tmp->Shutdown();
  17. NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
  18. NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
  19. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  20. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioChannelAgent)
  21. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
  22. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
  23. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  24. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgent)
  25. NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgent)
  26. NS_INTERFACE_MAP_ENTRY(nsISupports)
  27. NS_INTERFACE_MAP_END
  28. NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent)
  29. NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent)
  30. AudioChannelAgent::AudioChannelAgent()
  31. : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR)
  32. , mInnerWindowID(0)
  33. , mIsRegToService(false)
  34. {
  35. }
  36. AudioChannelAgent::~AudioChannelAgent()
  37. {
  38. Shutdown();
  39. }
  40. void
  41. AudioChannelAgent::Shutdown()
  42. {
  43. if (mIsRegToService) {
  44. NotifyStoppedPlaying();
  45. }
  46. }
  47. NS_IMETHODIMP AudioChannelAgent::GetAudioChannelType(int32_t *aAudioChannelType)
  48. {
  49. *aAudioChannelType = mAudioChannelType;
  50. return NS_OK;
  51. }
  52. NS_IMETHODIMP
  53. AudioChannelAgent::Init(mozIDOMWindow* aWindow, int32_t aChannelType,
  54. nsIAudioChannelAgentCallback *aCallback)
  55. {
  56. return InitInternal(nsPIDOMWindowInner::From(aWindow), aChannelType,
  57. aCallback, /* useWeakRef = */ false);
  58. }
  59. NS_IMETHODIMP
  60. AudioChannelAgent::InitWithWeakCallback(mozIDOMWindow* aWindow,
  61. int32_t aChannelType,
  62. nsIAudioChannelAgentCallback *aCallback)
  63. {
  64. return InitInternal(nsPIDOMWindowInner::From(aWindow), aChannelType,
  65. aCallback, /* useWeakRef = */ true);
  66. }
  67. nsresult
  68. AudioChannelAgent::FindCorrectWindow(nsPIDOMWindowInner* aWindow)
  69. {
  70. MOZ_ASSERT(aWindow->IsInnerWindow());
  71. mWindow = aWindow->GetScriptableTop();
  72. if (NS_WARN_IF(!mWindow)) {
  73. return NS_OK;
  74. }
  75. // From here we do an hack for nested iframes.
  76. // The system app doesn't have access to the nested iframe objects so it
  77. // cannot control the volume of the agents running in nested apps. What we do
  78. // here is to assign those Agents to the top scriptable window of the parent
  79. // iframe (what is controlled by the system app).
  80. // For doing this we go recursively back into the chain of windows until we
  81. // find apps that are not the system one.
  82. nsCOMPtr<nsPIDOMWindowOuter> outerParent = mWindow->GetParent();
  83. if (!outerParent || outerParent == mWindow) {
  84. return NS_OK;
  85. }
  86. nsCOMPtr<nsPIDOMWindowInner> parent = outerParent->GetCurrentInnerWindow();
  87. if (!parent) {
  88. return NS_OK;
  89. }
  90. nsCOMPtr<nsIDocument> doc = parent->GetExtantDoc();
  91. if (!doc) {
  92. return NS_OK;
  93. }
  94. if (nsContentUtils::IsChromeDoc(doc)) {
  95. return NS_OK;
  96. }
  97. nsAdoptingCString systemAppUrl =
  98. mozilla::Preferences::GetCString("b2g.system_startup_url");
  99. if (!systemAppUrl) {
  100. return NS_OK;
  101. }
  102. nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
  103. nsCOMPtr<nsIURI> uri;
  104. principal->GetURI(getter_AddRefs(uri));
  105. if (uri) {
  106. nsAutoCString spec;
  107. uri->GetSpec(spec);
  108. if (spec.Equals(systemAppUrl)) {
  109. return NS_OK;
  110. }
  111. }
  112. return FindCorrectWindow(parent);
  113. }
  114. nsresult
  115. AudioChannelAgent::InitInternal(nsPIDOMWindowInner* aWindow,
  116. int32_t aChannelType,
  117. nsIAudioChannelAgentCallback *aCallback,
  118. bool aUseWeakRef)
  119. {
  120. // We syncd the enum of channel type between nsIAudioChannelAgent.idl and
  121. // AudioChannelBinding.h the same.
  122. MOZ_ASSERT(int(AUDIO_AGENT_CHANNEL_NORMAL) == int(AudioChannel::Normal) &&
  123. int(AUDIO_AGENT_CHANNEL_CONTENT) == int(AudioChannel::Content) &&
  124. int(AUDIO_AGENT_CHANNEL_NOTIFICATION) == int(AudioChannel::Notification) &&
  125. int(AUDIO_AGENT_CHANNEL_ALARM) == int(AudioChannel::Alarm) &&
  126. int(AUDIO_AGENT_CHANNEL_TELEPHONY) == int(AudioChannel::Telephony) &&
  127. int(AUDIO_AGENT_CHANNEL_RINGER) == int(AudioChannel::Ringer) &&
  128. int(AUDIO_AGENT_CHANNEL_SYSTEM) == int(AudioChannel::System) &&
  129. int(AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION) == int(AudioChannel::Publicnotification),
  130. "Enum of channel on nsIAudioChannelAgent.idl should be the same with AudioChannelBinding.h");
  131. if (mAudioChannelType != AUDIO_AGENT_CHANNEL_ERROR ||
  132. aChannelType > AUDIO_AGENT_CHANNEL_SYSTEM ||
  133. aChannelType < AUDIO_AGENT_CHANNEL_NORMAL) {
  134. return NS_ERROR_FAILURE;
  135. }
  136. if (NS_WARN_IF(!aWindow)) {
  137. return NS_ERROR_FAILURE;
  138. }
  139. MOZ_ASSERT(aWindow->IsInnerWindow());
  140. mInnerWindowID = aWindow->WindowID();
  141. nsresult rv = FindCorrectWindow(aWindow);
  142. if (NS_WARN_IF(NS_FAILED(rv))) {
  143. return rv;
  144. }
  145. mAudioChannelType = aChannelType;
  146. if (aUseWeakRef) {
  147. mWeakCallback = do_GetWeakReference(aCallback);
  148. } else {
  149. mCallback = aCallback;
  150. }
  151. MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
  152. ("AudioChannelAgent, InitInternal, this = %p, type = %d, "
  153. "owner = %p, hasCallback = %d\n", this, mAudioChannelType,
  154. mWindow.get(), (!!mCallback || !!mWeakCallback)));
  155. return NS_OK;
  156. }
  157. NS_IMETHODIMP
  158. AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig,
  159. uint8_t aAudible)
  160. {
  161. if (NS_WARN_IF(!aConfig)) {
  162. return NS_ERROR_FAILURE;
  163. }
  164. RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
  165. if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
  166. service == nullptr || mIsRegToService) {
  167. return NS_ERROR_FAILURE;
  168. }
  169. MOZ_ASSERT(AudioChannelService::AudibleState::eNotAudible == 0 &&
  170. AudioChannelService::AudibleState::eMaybeAudible == 1 &&
  171. AudioChannelService::AudibleState::eAudible == 2);
  172. service->RegisterAudioChannelAgent(this,
  173. static_cast<AudioChannelService::AudibleState>(aAudible));
  174. AudioPlaybackConfig config = service->GetMediaConfig(mWindow,
  175. mAudioChannelType);
  176. MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
  177. ("AudioChannelAgent, NotifyStartedPlaying, this = %p, "
  178. "audible = %d, mute = %d, volume = %f, suspend = %d\n", this,
  179. aAudible, config.mMuted, config.mVolume, config.mSuspend));
  180. aConfig->SetConfig(config.mVolume, config.mMuted, config.mSuspend);
  181. mIsRegToService = true;
  182. return NS_OK;
  183. }
  184. NS_IMETHODIMP
  185. AudioChannelAgent::NotifyStoppedPlaying()
  186. {
  187. if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
  188. !mIsRegToService) {
  189. return NS_ERROR_FAILURE;
  190. }
  191. MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
  192. ("AudioChannelAgent, NotifyStoppedPlaying, this = %p\n", this));
  193. RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
  194. if (service) {
  195. service->UnregisterAudioChannelAgent(this);
  196. }
  197. mIsRegToService = false;
  198. return NS_OK;
  199. }
  200. NS_IMETHODIMP
  201. AudioChannelAgent::NotifyStartedAudible(uint8_t aAudible, uint32_t aReason)
  202. {
  203. MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
  204. ("AudioChannelAgent, NotifyStartedAudible, this = %p, "
  205. "audible = %d, reason = %d\n", this, aAudible, aReason));
  206. RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
  207. if (NS_WARN_IF(!service)) {
  208. return NS_ERROR_FAILURE;
  209. }
  210. service->AudioAudibleChanged(
  211. this,
  212. static_cast<AudioChannelService::AudibleState>(aAudible),
  213. static_cast<AudioChannelService::AudibleChangedReasons>(aReason));
  214. return NS_OK;
  215. }
  216. already_AddRefed<nsIAudioChannelAgentCallback>
  217. AudioChannelAgent::GetCallback()
  218. {
  219. nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback;
  220. if (!callback) {
  221. callback = do_QueryReferent(mWeakCallback);
  222. }
  223. return callback.forget();
  224. }
  225. void
  226. AudioChannelAgent::WindowVolumeChanged()
  227. {
  228. nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
  229. if (!callback) {
  230. return;
  231. }
  232. AudioPlaybackConfig config = GetMediaConfig();
  233. MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
  234. ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, "
  235. "volume = %f\n", this, config.mMuted, config.mVolume));
  236. callback->WindowVolumeChanged(config.mVolume, config.mMuted);
  237. }
  238. void
  239. AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend)
  240. {
  241. nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
  242. if (!callback) {
  243. return;
  244. }
  245. if (!IsDisposableSuspend(aSuspend)) {
  246. aSuspend = GetMediaConfig().mSuspend;
  247. }
  248. MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
  249. ("AudioChannelAgent, WindowSuspendChanged, this = %p, "
  250. "suspended = %d\n", this, aSuspend));
  251. callback->WindowSuspendChanged(aSuspend);
  252. }
  253. AudioPlaybackConfig
  254. AudioChannelAgent::GetMediaConfig()
  255. {
  256. RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
  257. AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED);
  258. if (service) {
  259. config = service->GetMediaConfig(mWindow, mAudioChannelType);
  260. }
  261. return config;
  262. }
  263. bool
  264. AudioChannelAgent::IsDisposableSuspend(nsSuspendedTypes aSuspend) const
  265. {
  266. return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
  267. aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
  268. }
  269. uint64_t
  270. AudioChannelAgent::WindowID() const
  271. {
  272. return mWindow ? mWindow->WindowID() : 0;
  273. }
  274. uint64_t
  275. AudioChannelAgent::InnerWindowID() const
  276. {
  277. return mInnerWindowID;
  278. }
  279. void
  280. AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID,
  281. bool aCapture)
  282. {
  283. if (aInnerWindowID != mInnerWindowID) {
  284. return;
  285. }
  286. nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
  287. if (!callback) {
  288. return;
  289. }
  290. MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
  291. ("AudioChannelAgent, WindowAudioCaptureChanged, this = %p, "
  292. "capture = %d\n", this, aCapture));
  293. callback->WindowAudioCaptureChanged(aCapture);
  294. }
  295. bool
  296. AudioChannelAgent::IsPlayingStarted() const
  297. {
  298. return mIsRegToService;
  299. }
  300. bool
  301. AudioChannelAgent::ShouldBlockMedia() const
  302. {
  303. return mWindow ?
  304. mWindow->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK : false;
  305. }