Bug 1263799 - Support IPC for TV service between B2G and content processes. Part 1 - Refactor TV service and pass TV stream handle. r=schien draft
authorKevin Chen <kechen@mozilla.com>
Fri, 15 Apr 2016 13:40:34 +0800
changeset 357230 30ec37fdd8203dbdeb762182aecf51e4f28c2218
parent 356965 86730d0a82093d705e44f33a34973d28b269f1ea
child 357231 f3d5186c0c26e61e80d6485262c45564f8ac45cf
push id16736
push userbmo:kechen@mozilla.com
push dateThu, 28 Apr 2016 09:03:26 +0000
reviewersschien
bugs1263799
milestone49.0a1
Bug 1263799 - Support IPC for TV service between B2G and content processes. Part 1 - Refactor TV service and pass TV stream handle. r=schien MozReview-Commit-ID: 7vjmcxcjGQl
dom/tv/TVChannel.cpp
dom/tv/TVChannel.h
dom/tv/TVListeners.cpp
dom/tv/TVListeners.h
dom/tv/TVManager.cpp
dom/tv/TVManager.h
dom/tv/TVServiceCallbacks.cpp
dom/tv/TVServiceFactory.cpp
dom/tv/TVServiceFactory.h
dom/tv/TVServiceRunnables.h
dom/tv/TVSimulatorService.js
dom/tv/TVSource.cpp
dom/tv/TVSource.h
dom/tv/TVTuner.cpp
dom/tv/TVTuner.h
dom/tv/TVTypes.cpp
dom/tv/TVTypes.h
dom/tv/gonk/TVGonkService.cpp
dom/tv/gonk/TVGonkService.h
dom/tv/moz.build
dom/tv/nsITVService.idl
layout/build/nsLayoutModule.cpp
--- a/dom/tv/TVChannel.cpp
+++ b/dom/tv/TVChannel.cpp
@@ -12,18 +12,17 @@
 #include "mozilla/dom/TVUtils.h"
 #include "nsITVService.h"
 #include "nsServiceManagerUtils.h"
 #include "TVChannel.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(TVChannel, DOMEventTargetHelper,
-                                   mTVService, mSource)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(TVChannel, DOMEventTargetHelper, mSource)
 
 NS_IMPL_ADDREF_INHERITED(TVChannel, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TVChannel, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVChannel)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 TVChannel::TVChannel(nsPIDOMWindowInner* aWindow,
@@ -62,19 +61,16 @@ TVChannel::Init(nsITVChannelData* aData)
   aData->GetNetworkId(mNetworkId);
   aData->GetTransportStreamId(mTransportStreamId);
   aData->GetServiceId(mServiceId);
   aData->GetName(mName);
   aData->GetNumber(mNumber);
   aData->GetIsEmergency(&mIsEmergency);
   aData->GetIsFree(&mIsFree);
 
-  mTVService = TVServiceFactory::AutoCreateTVService();
-  NS_ENSURE_TRUE(mTVService, false);
-
   return true;
 }
 
 /* virtual */ JSObject*
 TVChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return TVChannelBinding::Wrap(aCx, this, aGivenProto);
 }
@@ -91,35 +87,36 @@ TVChannel::GetPrograms(const TVGetProgra
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
+
   RefPtr<TVTuner> tuner = mSource->Tuner();
   nsString tunerId;
   tuner->GetId(tunerId);
 
   uint64_t startTime = aOptions.mStartTime.WasPassed() ?
                        aOptions.mStartTime.Value() :
                        PR_Now();
   uint64_t endTime = aOptions.mDuration.WasPassed() ?
                      (startTime + aOptions.mDuration.Value()) :
                      std::numeric_limits<uint64_t>::max();
   nsCOMPtr<nsITVServiceCallback> callback =
     new TVServiceProgramGetterCallback(this, promise, false);
   nsresult rv =
-    mTVService->GetPrograms(tunerId,
-                            ToTVSourceTypeStr(mSource->Type()),
-                            mNumber,
-                            startTime,
-                            endTime,
-                            callback);
+    service->GetPrograms(tunerId, ToTVSourceTypeStr(mSource->Type()), mNumber,
+                         startTime, endTime, callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   return promise.forget();
 }
 
 void
@@ -183,30 +180,31 @@ TVChannel::GetCurrentProgram(ErrorResult
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
+
   RefPtr<TVTuner> tuner = mSource->Tuner();
   nsString tunerId;
   tuner->GetId(tunerId);
 
   // Get only one program from now on.
   nsCOMPtr<nsITVServiceCallback> callback =
     new TVServiceProgramGetterCallback(this, promise, true);
-  nsresult rv =
-    mTVService->GetPrograms(tunerId,
-                            ToTVSourceTypeStr(mSource->Type()),
-                            mNumber,
-                            PR_Now(),
-                            std::numeric_limits<uint64_t>::max(),
-                            callback);
+  nsresult rv = service->GetPrograms(
+    tunerId, ToTVSourceTypeStr(mSource->Type()), mNumber, PR_Now(),
+    std::numeric_limits<uint64_t>::max(), callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   return promise.forget();
 }
 
 } // namespace dom
--- a/dom/tv/TVChannel.h
+++ b/dom/tv/TVChannel.h
@@ -7,17 +7,16 @@
 #ifndef mozilla_dom_TVChannel_h
 #define mozilla_dom_TVChannel_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 // Include TVChannelBinding.h since enum TVChannelType can't be forward declared.
 #include "mozilla/dom/TVChannelBinding.h"
 
 class nsITVChannelData;
-class nsITVService;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class TVSource;
 
 class TVChannel final : public DOMEventTargetHelper
@@ -65,17 +64,16 @@ public:
 private:
   TVChannel(nsPIDOMWindowInner* aWindow,
             TVSource* aSource);
 
   ~TVChannel();
 
   bool Init(nsITVChannelData* aData);
 
-  nsCOMPtr<nsITVService> mTVService;
   RefPtr<TVSource> mSource;
   nsString mNetworkId;
   nsString mTransportStreamId;
   nsString mServiceId;
   TVChannelType mType;
   nsString mNumber;
   nsString mName;
   bool mIsEmergency;
--- a/dom/tv/TVListeners.cpp
+++ b/dom/tv/TVListeners.cpp
@@ -7,96 +7,131 @@
 #include "mozilla/dom/TVSource.h"
 #include "mozilla/dom/TVTuner.h"
 #include "mozilla/dom/TVUtils.h"
 #include "TVListeners.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION(TVSourceListener, mSources)
+NS_IMPL_CYCLE_COLLECTION_CLASS(TVSourceListener)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TVSourceListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TVSourceListener)
+  tmp->Shutdown();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TVSourceListener)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TVSourceListener)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TVSourceListener)
   NS_INTERFACE_MAP_ENTRY(nsITVSourceListener)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-void
-TVSourceListener::RegisterSource(TVSource* aSource)
+/* static */ already_AddRefed<TVSourceListener>
+TVSourceListener::Create(TVSource* aSource)
+{
+  RefPtr<TVSourceListener> listener = new TVSourceListener(aSource);
+  return (listener->Init()) ? listener.forget() : nullptr;
+}
+
+TVSourceListener::TVSourceListener(TVSource* aSource) : mSource(aSource)
+{
+  MOZ_ASSERT(mSource);
+}
+
+TVSourceListener::~TVSourceListener()
 {
-  mSources.AppendElement(aSource);
+  Shutdown();
+}
+
+bool
+TVSourceListener::Init()
+{
+  RefPtr<TVTuner> tuner = mSource->Tuner();
+  tuner->GetId(mTunerId);
+
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    return false;
+  }
+
+  nsresult rv = service->RegisterSourceListener(
+    mTunerId, ToTVSourceTypeStr(mSource->Type()), this);
+  return NS_WARN_IF(NS_FAILED(rv)) ? false : true;
 }
 
 void
-TVSourceListener::UnregisterSource(TVSource* aSource)
+TVSourceListener::Shutdown()
 {
-  for (uint32_t i = 0; i < mSources.Length(); i++) {
-    if (mSources[i] == aSource) {
-      mSources.RemoveElementsAt(i, 1);
-    }
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    return;
   }
+
+  nsresult rv = service->UnregisterSourceListener(
+    mTunerId, ToTVSourceTypeStr(mSource->Type()), this);
+  NS_WARN_IF(NS_FAILED(rv));
 }
 
 /* virtual */ NS_IMETHODIMP
 TVSourceListener::NotifyChannelScanned(const nsAString& aTunerId,
                                        const nsAString& aSourceType,
                                        nsITVChannelData* aChannelData)
 {
-  RefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
-  source->NotifyChannelScanned(aChannelData);
-  return NS_OK;
+  if (NS_WARN_IF(!IsMatched(aTunerId, aSourceType))) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return mSource->NotifyChannelScanned(aChannelData);
 }
 
 /* virtual */ NS_IMETHODIMP
 TVSourceListener::NotifyChannelScanComplete(const nsAString& aTunerId,
                                             const nsAString& aSourceType)
 {
-  RefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
-  source->NotifyChannelScanComplete();
-  return NS_OK;
+  if (NS_WARN_IF(!IsMatched(aTunerId, aSourceType))) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return mSource->NotifyChannelScanComplete();
 }
 
 /* virtual */ NS_IMETHODIMP
 TVSourceListener::NotifyChannelScanStopped(const nsAString& aTunerId,
                                            const nsAString& aSourceType)
 {
-  RefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
-  source->NotifyChannelScanStopped();
-  return NS_OK;
+  if (NS_WARN_IF(!IsMatched(aTunerId, aSourceType))) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return mSource->NotifyChannelScanStopped();
 }
 
 /* virtual */ NS_IMETHODIMP
 TVSourceListener::NotifyEITBroadcasted(const nsAString& aTunerId,
                                        const nsAString& aSourceType,
                                        nsITVChannelData* aChannelData,
                                        nsITVProgramData** aProgramDataList,
                                        const uint32_t aCount)
 {
-  RefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
-  source->NotifyEITBroadcasted(aChannelData, aProgramDataList, aCount);
-  return NS_OK;
+  if (NS_WARN_IF(!IsMatched(aTunerId, aSourceType))) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return mSource->NotifyEITBroadcasted(aChannelData, aProgramDataList, aCount);
 }
 
-already_AddRefed<TVSource>
-TVSourceListener::GetSource(const nsAString& aTunerId,
+bool
+TVSourceListener::IsMatched(const nsAString& aTunerId,
                             const nsAString& aSourceType)
 {
-  for (uint32_t i = 0; i < mSources.Length(); i++) {
-    nsString tunerId;
-    RefPtr<TVTuner> tuner = mSources[i]->Tuner();
-    tuner->GetId(tunerId);
-
-    nsString sourceType = ToTVSourceTypeStr(mSources[i]->Type());
-
-    if (aTunerId.Equals(tunerId) && aSourceType.Equals(sourceType)) {
-      RefPtr<TVSource> source = mSources[i];
-      return source.forget();
-    }
-  }
-
-  return nullptr;
+  return aTunerId.Equals(mTunerId) &&
+         ToTVSourceType(aSourceType) == mSource->Type();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/tv/TVListeners.h
+++ b/dom/tv/TVListeners.h
@@ -2,40 +2,54 @@
 /* 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_TVListeners_h
 #define mozilla_dom_TVListeners_h
 
-#include "mozilla/dom/TVSource.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsITVService.h"
-#include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 
+class TVSource;
+
+/*
+ * Instead of making |TVSource| class implement |TVSource| (WebIDL) and
+ * |nsITVSourceListener| (XPCOM) at the same time, having an individual class
+ * for |nsITVSourceListener| (XPCOM) interface would help the JS context
+ * recognize |nsITVSourceListener| instances (when it comes to use them in
+ * |TVSimulatorService.js|.)
+ */
 class TVSourceListener final : public nsITVSourceListener
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(TVSourceListener)
   NS_DECL_NSITVSOURCELISTENER
 
-  void RegisterSource(TVSource* aSource);
-
-  void UnregisterSource(TVSource* aSource);
+  static already_AddRefed<TVSourceListener> Create(TVSource* aSource);
 
 private:
-  ~TVSourceListener() {}
+  explicit TVSourceListener(TVSource* aSource);
+
+  ~TVSourceListener();
+
+  bool Init();
+
+  void Shutdown();
 
-  already_AddRefed<TVSource> GetSource(const nsAString& aTunerId,
-                                       const nsAString& aSourceType);
+  bool IsMatched(const nsAString& aTunerId, const nsAString& aSourceType);
+
+  RefPtr<TVSource> mSource;
 
-  nsTArray<RefPtr<TVSource>> mSources;
+  // Store the tuner ID for |UnregisterSourceListener| call in |Shutdown| since
+  // |mSource->Tuner()| may not exist at that moment.
+  nsString mTunerId;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TVListeners_h
--- a/dom/tv/TVManager.cpp
+++ b/dom/tv/TVManager.cpp
@@ -11,18 +11,18 @@
 #include "mozilla/dom/TVTuner.h"
 #include "nsITVService.h"
 #include "nsServiceManagerUtils.h"
 #include "TVManager.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(TVManager, DOMEventTargetHelper, mTVService,
-                                   mTuners, mPendingGetTunersPromises)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(TVManager, DOMEventTargetHelper, mTuners,
+                                   mPendingGetTunersPromises)
 
 NS_IMPL_ADDREF_INHERITED(TVManager, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TVManager, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVManager)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 TVManager::TVManager(nsPIDOMWindowInner* aWindow)
@@ -40,21 +40,23 @@ TVManager::Create(nsPIDOMWindowInner* aW
 {
   RefPtr<TVManager> manager = new TVManager(aWindow);
   return (manager->Init()) ? manager.forget() : nullptr;
 }
 
 bool
 TVManager::Init()
 {
-  mTVService = TVServiceFactory::AutoCreateTVService();
-  NS_ENSURE_TRUE(mTVService, false);
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    return false;
+  }
 
   nsCOMPtr<nsITVServiceCallback> callback = new TVServiceTunerGetterCallback(this);
-  nsresult rv = mTVService->GetTuners(callback);
+  nsresult rv = service->GetTuners(callback);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 /* virtual */ JSObject*
 TVManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
--- a/dom/tv/TVManager.h
+++ b/dom/tv/TVManager.h
@@ -4,18 +4,16 @@
  * 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_TVManager_h
 #define mozilla_dom_TVManager_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 
-class nsITVService;
-
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class TVTuner;
 
 class TVManager final : public DOMEventTargetHelper
 {
@@ -41,17 +39,16 @@ public:
 
 private:
   explicit TVManager(nsPIDOMWindowInner* aWindow);
 
   ~TVManager();
 
   bool Init();
 
-  nsCOMPtr<nsITVService> mTVService;
   nsTArray<RefPtr<TVTuner>> mTuners;
   bool mIsReady;
   nsTArray<RefPtr<Promise>> mPendingGetTunersPromises;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/tv/TVServiceCallbacks.cpp
+++ b/dom/tv/TVServiceCallbacks.cpp
@@ -42,23 +42,41 @@ TVServiceSourceSetterCallback::TVService
 
 TVServiceSourceSetterCallback::~TVServiceSourceSetterCallback()
 {
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceSourceSetterCallback::NotifySuccess(nsIArray* aDataList)
 {
-  // |aDataList| is expected to be null for setter callbacks.
-  if (aDataList) {
+  // |aDataList| is expected to be with only one element.
+  if (!aDataList) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsresult rv = mTuner->SetCurrentSource(mSourceType);
+  uint32_t length;
+  nsresult rv = aDataList->GetLength(&length);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return rv;
+  }
+  if (length != 1) {
+    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<nsITVGonkNativeHandleData> handleData =
+    do_QueryElementAt(aDataList, 0);
+  if (NS_WARN_IF(!handleData)) {
+    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return rv;
+  }
+
+  rv = mTuner->SetCurrentSource(mSourceType, handleData);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mPromise->MaybeReject(rv);
     return rv;
   }
 
   mPromise->MaybeResolve(JS::UndefinedHandleValue);
   return NS_OK;
 }
--- a/dom/tv/TVServiceFactory.cpp
+++ b/dom/tv/TVServiceFactory.cpp
@@ -1,40 +1,38 @@
 /* -*- 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 "mozilla/dom/TVListeners.h"
-#include "mozilla/Preferences.h"
+#include "TVServiceFactory.h"
+
+#ifdef MOZ_WIDGET_GONK
+#include "gonk/TVGonkService.h"
+#endif
 #include "nsITVService.h"
 #include "nsITVSimulatorService.h"
 #include "nsServiceManagerUtils.h"
-#include "TVServiceFactory.h"
+
 
 namespace mozilla {
 namespace dom {
 
 /* static */ already_AddRefed<nsITVService>
 TVServiceFactory::AutoCreateTVService()
 {
-  nsresult rv = NS_OK;
-  nsCOMPtr<nsITVService> service = do_CreateInstance(TV_SERVICE_CONTRACTID);
-  if (!service) {
-    // Fallback to the TV Simulator Service
-    service = do_CreateInstance(TV_SIMULATOR_SERVICE_CONTRACTID, &rv);
+  nsCOMPtr<nsITVService> service;
 
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return nullptr;
-    }
-  }
+#ifdef MOZ_WIDGET_GONK
+  service = new TVGonkService();
+#endif
 
-  rv = service->SetSourceListener(new TVSourceListener());
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
+  if (!service) {
+    // Fallback to TV simulator service, especially for TV simulator on WebIDE.
+    service = do_CreateInstance(TV_SIMULATOR_SERVICE_CONTRACTID);
   }
 
   return service.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/tv/TVServiceFactory.h
+++ b/dom/tv/TVServiceFactory.h
@@ -4,16 +4,19 @@
  * 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_TVServiceFactory_h
 #define mozilla_dom_TVServiceFactory_h
 
 #include "nsCOMPtr.h"
 
+#define TV_SERVICE_CID \
+  { 0x60fb3c53, 0x017f, 0x4340, { 0x91, 0x1b, 0xd5, 0x5c, 0x31, 0x28, 0x88, 0xb6 } }
+
 class nsITVService;
 
 namespace mozilla {
 namespace dom {
 
 class TVServiceFactory
 {
 public:
--- a/dom/tv/TVServiceRunnables.h
+++ b/dom/tv/TVServiceRunnables.h
@@ -27,17 +27,19 @@ class TVServiceNotifyRunnable final : pu
 {
 public:
   TVServiceNotifyRunnable(nsITVServiceCallback* aCallback,
                           nsIArray* aDataList,
                           uint16_t aErrorCode = nsITVServiceCallback::TV_ERROR_OK)
     : mCallback(aCallback)
     , mDataList(aDataList)
     , mErrorCode(aErrorCode)
-  {}
+  {
+    MOZ_ASSERT(mCallback);
+  }
 
   NS_IMETHOD Run()
   {
     if (mErrorCode == nsITVServiceCallback::TV_ERROR_OK) {
       return mCallback->NotifySuccess(mDataList);
     } else {
       return mCallback->NotifyError(mErrorCode);
     }
--- a/dom/tv/TVSimulatorService.js
+++ b/dom/tv/TVSimulatorService.js
@@ -9,35 +9,45 @@ function debug(aMsg) {
 
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cr = Components.returnCode;
 
 Cu.importGlobalProperties(["File"]);
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const TV_SIMULATOR_DUMMY_DIRECTORY   = "dummy";
 const TV_SIMULATOR_DUMMY_FILE        = "settings.json";
 
 // See http://seanyhlin.github.io/TV-Manager-API/#idl-def-TVSourceType
 const TV_SOURCE_TYPES = ["dvb-t","dvb-t2","dvb-c","dvb-c2","dvb-s",
                          "dvb-s2","dvb-h","dvb-sh","atsc","atsc-m/h",
                          "isdb-t","isdb-tb","isdb-s","isdb-c","1seg",
                          "dtmb","cmmb","t-dmb","s-dmb"];
 function containInvalidSourceType(aElement, aIndex, aArray) {
   return !TV_SOURCE_TYPES.includes(aElement);
 }
 
 // See http://seanyhlin.github.io/TV-Manager-API/#idl-def-TVChannelType
 const TV_CHANNEL_TYPES = ["tv","radio","data"];
 
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
 function TVSimulatorService() {
+  // This is used for saving the registered source listeners. The object literal
+  // is defined as below:
+  // {
+  //   "tunerId1": {
+  //     "sourceType1": [ /* source listeners */ ],
+  //     "sourceType2": [ /* source listeners */ ],
+  //     ...
+  //   },
+  //   ...
+  // }
+  this._sourceListeners = {};
   this._internalTuners = null;
   this._scanCompleteTimer = null;
   this._scanningWrapTunerData = null;
   this._init();
 }
 
 TVSimulatorService.prototype = {
   classID: Components.ID("{94b065ad-d45a-436a-b394-6dabc3cf110f}"),
@@ -192,239 +202,274 @@ TVSimulatorService.prototype = {
         }));
         this._internalTuners.set(
                 this._getTunerMapKey(tuner.id, sourceData.type),
                 wrapTunerData);
       }
     }
   },
 
-  getTuners: function TVSimGetTuners(aCallback) {
+  registerSourceListener: function(aTunerId, aSourceType, aListener) {
+    let tunerSourceListeners = this._sourceListeners[aTunerId];
+    if (!tunerSourceListeners) {
+      tunerSourceListeners = this._sourceListeners[aTunerId] = {};
+    }
+
+    let listeners = tunerSourceListeners[aSourceType];
+    if (!listeners) {
+      listeners = tunerSourceListeners[aSourceType] = [];
+    }
+
+    if (listeners.indexOf(aListener) < 0) {
+      listeners.push(aListener);
+    }
+  },
+
+  unregisterSourceListener: function(aTunerId, aSourceType, aListener) {
+    let tunerSourceListeners = this._sourceListeners[aTunerId];
+    if (!tunerSourceListeners) {
+      return;
+    }
+
+    let listeners = tunerSourceListeners[aSourceType];
+    if (!listeners) {
+      return;
+    }
+
+    let index = listeners.indexOf(aListener);
+    if (index < 0) {
+      return;
+    }
+
+    listeners.splice(index, 1);
+  },
+
+  _getSourceListeners: function(aTunerId, aSourceType) {
+    let tunerSourceListeners = this._sourceListeners[aTunerId];
+    if (!tunerSourceListeners) {
+      return [];
+    }
+
+    let listeners = tunerSourceListeners[aSourceType];
+    if (!listeners) {
+      return [];
+    }
+
+    return listeners;
+  },
+
+  getTuners: function(aCallback) {
     if (!aCallback) {
       debug("aCallback is null\n");
-      return Cr.NS_ERROR_INVALID_ARG;
+      throw Cr.NS_ERROR_INVALID_ARG;
     }
 
     let tuners = Cc["@mozilla.org/array;1"]
                    .createInstance(Ci.nsIMutableArray);
 
     for (let [k,wrapTunerData] of this._internalTuners) {
       tuners.appendElement(wrapTunerData.tuner, false);
     }
 
-    return aCallback.notifySuccess(tuners);
+    aCallback.notifySuccess(tuners);
   },
 
-  setSource: function TVSimSetSource(aTunerId, aSourceType, aCallback) {
+  setSource: function(aTunerId, aSourceType, aCallback) {
     if (!aCallback) {
       debug("aCallback is null\n");
-      return NS_ERROR_INVALID_ARG;
+      throw NS_ERROR_INVALID_ARG;
     }
 
     let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
     if (!wrapTunerData) {
-      return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      return;
     }
-    return aCallback.notifySuccess(null);
+
+    let streamHandle = Cc["@mozilla.org/tv/tvgonknativehandledata;1"]
+                         .createInstance(Ci.nsITVGonkNativeHandleData);
+    let streamHandles = Cc["@mozilla.org/array;1"]
+                          .createInstance(Ci.nsIMutableArray);
+    streamHandles.appendElement(streamHandle, false);
+
+    aCallback.notifySuccess(streamHandles);
   },
 
-  startScanningChannels: function TVSimStartScanningChannels(aTunerId, aSourceType, aCallback) {
+  startScanningChannels: function(aTunerId, aSourceType, aCallback) {
     if (!aCallback) {
       debug("aCallback is null\n");
-      return Cr.NS_ERROR_INVALID_ARG;
+      throw Cr.NS_ERROR_INVALID_ARG;
+    }
+
+    if (this._scanningWrapTunerData) {
+      aCallback.notifyError(Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+      return;
     }
 
     let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
     if (!wrapTunerData || !wrapTunerData.channels) {
-      return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
-    }
-
-    if (this._scanningWrapTunerData) {
-      return aCallback.notifyError(Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+      aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      return;
     }
 
     this._scanningWrapTunerData = wrapTunerData;
 
     aCallback.notifySuccess(null);
 
     for (let [key, wrapChannelData] of wrapTunerData.channels) {
-      this._sourceListener.notifyChannelScanned(
-                                        wrapTunerData.tuner.id,
-                                        wrapTunerData.sourceType,
-                                        wrapChannelData.channel);
+      for (let listener of this._getSourceListeners(aTunerId, aSourceType)) {
+        listener.notifyChannelScanned(aTunerId, aSourceType, wrapChannelData.channel);
+      }
     }
 
     this._scanCompleteTimer = Cc["@mozilla.org/timer;1"]
                                 .createInstance(Ci.nsITimer);
-    let rv = this._scanCompleteTimer.initWithCallback(this, 10,
-                                                      Ci.nsITimer.TYPE_ONE_SHOT);
-    return Cr.NS_OK;
+    this._scanCompleteTimer.initWithCallback(this, 10,
+                                             Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
-  notify: function TVSimTimerCallback(aTimer) {
+  notify: function(aTimer) {
     if (!this._scanningWrapTunerData) {
       return;
     }
 
     this._scanCompleteTimer = null;
-    let notifyResult = this._sourceListener.notifyChannelScanComplete(
-                                                  this._scanningWrapTunerData.tuner.id,
-                                                  this._scanningWrapTunerData.sourceType);
+
+    let tunerId = this._scanningWrapTunerData.tuner.id;
+    let sourceType = this._scanningWrapTunerData.sourceType;
+    let notifyResult = Cr.NS_OK;
+    for (let listener of this._getSourceListeners(tunerId, sourceType)) {
+      notifyResult = listener.notifyChannelScanComplete(tunerId, sourceType);
+    }
     this._scanningWrapTunerData = null;
     return notifyResult;
   },
 
-  stopScanningChannels: function TVSimStopScanningChannels(aTunerId, aSourceType, aCallback) {
+  stopScanningChannels: function(aTunerId, aSourceType, aCallback) {
     if (!aCallback) {
       debug("aCallback is null\n");
-      return Cr.NS_ERROR_INVALID_ARG;
+      throw Cr.NS_ERROR_INVALID_ARG;
     }
 
-    if (!this._scanningWrapTunerData) {
-      return aCallback.notifyError(Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+    if (!this._scanningWrapTunerData ||
+        aTunerId != this._scanningWrapTunerData.tuner.id ||
+        aSourceType != this._scanningWrapTunerData.sourceType) {
+      aCallback.notifyError(Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+      return;
     }
 
-    let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
-    if (!wrapTunerData) {
-      return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
-    }
+    this._scanningWrapTunerData = null;
 
     if (this._scanCompleteTimer) {
       this._scanCompleteTimer.cancel();
       this._scanCompleteTimer = null;
     }
 
-    if (wrapTunerData.tuner.id === this._scanningWrapTunerData.tuner.id &&
-        wrapTunerData.sourceType === this._scanningWrapTunerData.sourceType) {
-      this._scanningWrapTunerData = null;
-      this._sourceListener.notifyChannelScanStopped(
-                                 wrapTunerData.tuner.id,
-                                 wrapTunerData.sourceType);
+    for (let listener of this._getSourceListeners(aTunerId, aSourceType)) {
+      listener.notifyChannelScanStopped(aTunerId, aSourceType);
     }
 
-    return aCallback.notifySuccess(null);
+    aCallback.notifySuccess(null);
   },
 
-  clearScannedChannelsCache: function TVSimClearScannedChannelsCache(aTunerId, aSourceType, aCallback) {
-    // Doesn't support for this method.
-    return Cr.NS_OK;
+  clearScannedChannelsCache: function(aTunerId, aSourceType, aCallback) {
+    // Simulator service doesn't support channel cache, so there's nothing to do here.
   },
 
-  setChannel: function TVSimSetChannel(aTunerId, aSourceType, aChannelNumber, aCallback) {
+  setChannel: function(aTunerId, aSourceType, aChannelNumber, aCallback) {
     if (!aCallback) {
       debug("aCallback is null\n");
-      return Cr.NS_ERROR_INVALID_ARG;
+      throw Cr.NS_ERROR_INVALID_ARG;
     }
 
     let channel = Cc["@mozilla.org/array;1"]
                     .createInstance(Ci.nsIMutableArray);
 
     let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
     if (!wrapTunerData || !wrapTunerData.channels) {
-      return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      return;
     }
 
     let wrapChannelData = wrapTunerData.channels.get(aChannelNumber);
     if (!wrapChannelData) {
-      return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      return;
     }
 
     channel.appendElement(wrapChannelData.channel, false);
-    return aCallback.notifySuccess(channel);
-
+    aCallback.notifySuccess(channel);
   },
 
-  getChannels: function TVSimGetChannels(aTunerId, aSourceType, aCallback) {
+  getChannels: function(aTunerId, aSourceType, aCallback) {
     if (!aCallback) {
       debug("aCallback is null\n");
-      return Cr.NS_ERROR_INVALID_ARG;
+      throw Cr.NS_ERROR_INVALID_ARG;
     }
 
     let channelArray = Cc["@mozilla.org/array;1"]
                          .createInstance(Ci.nsIMutableArray);
 
     let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
     if (!wrapTunerData || !wrapTunerData.channels) {
-      return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      return;
     }
 
     for (let [key, wrapChannelData] of wrapTunerData.channels) {
       channelArray.appendElement(wrapChannelData.channel, false);
     }
 
-    return aCallback.notifySuccess(channelArray);
+    aCallback.notifySuccess(channelArray);
   },
 
-  getPrograms: function TVSimGetPrograms(aTunerId, aSourceType, aChannelNumber, aStartTime, aEndTime, aCallback) {
+  getPrograms: function(aTunerId, aSourceType, aChannelNumber, aStartTime, aEndTime, aCallback) {
     if (!aCallback) {
       debug("aCallback is null\n");
-      return Cr.NS_ERROR_INVALID_ARG;
+      throw Cr.NS_ERROR_INVALID_ARG;
     }
     let programArray = Cc["@mozilla.org/array;1"]
                          .createInstance(Ci.nsIMutableArray);
 
     let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
     if (!wrapTunerData || !wrapTunerData.channels) {
-      return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
+      return;
     }
 
     let wrapChannelData = wrapTunerData.channels.get(aChannelNumber);
     if (!wrapChannelData || !wrapChannelData.programs) {
-      return Cr.NS_ERROR_INVALID_ARG;
+      throw Cr.NS_ERROR_INVALID_ARG;
     }
 
     for (let program of wrapChannelData.programs) {
       programArray.appendElement(program, false);
     }
 
-    return aCallback.notifySuccess(programArray);
-
+    aCallback.notifySuccess(programArray);
   },
 
-  getOverlayId: function TVSimGetOverlayId(aTunerId, aCallback) {
-    if (!aCallback) {
-      debug("aCallback is null\n");
-      return Cr.NS_ERROR_INVALID_ARG;
-    }
-
-    // TVSimulatorService does not use this parameter.
-    overlayIds = Cc["@mozilla.org/array;1"]
-                  .createInstance(Ci.nsIMutableArray);
-    return aCallback.notifySuccess(overlayIds);
-  },
-
-  set sourceListener(aListener) {
-    this._sourceListener = aListener;
-  },
-
-  get sourceListener() {
-      return this._sourceListener;
-  },
-
-  getSimulatorVideoBlobURL: function TVSimGetSimulatorVideoBlob(aTunerId,
-                                                                aSourceType,
-                                                                aChannelNumber,
-                                                                aWin) {
+  getSimulatorVideoBlobURL: function(aTunerId, aSourceType, aChannelNumber, aWin) {
     let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
     if (!wrapTunerData || !wrapTunerData.channels) {
       return "";
     }
 
     let wrapChannelData = wrapTunerData.channels.get(aChannelNumber);
     if (!wrapChannelData || !wrapChannelData.videoFilePath) {
       return "";
     }
 
     let videoFile = new File(this._getFilePath(wrapChannelData.videoFilePath));
     let videoBlobURL = aWin.URL.createObjectURL(videoFile);
 
     return videoBlobURL;
   },
 
-  _getDummyData : function TVSimGetDummyData() {
+  _getDummyData : function() {
     // Load the setting file from local JSON file.
     // Synchrhronous File Reading.
     let file = Cc["@mozilla.org/file/local;1"]
                  .createInstance(Ci.nsILocalFile);
 
     let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
                     .createInstance(Ci.nsIFileInputStream);
     let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
@@ -449,42 +494,42 @@ TVSimulatorService.prototype = {
       throw e;
     } finally {
       cstream.close();
     }
 
     return settingsStr;
   },
 
-  _getTunerMapKey: function TVSimGetTunerMapKey(aTunerId, aSourceType) {
+  _getTunerMapKey: function(aTunerId, aSourceType) {
     return JSON.stringify({'tunerId': aTunerId, 'sourceType': aSourceType});
   },
 
-  _getWrapTunerData: function TVSimGetWrapTunerData(aTunerId, aSourceType) {
+  _getWrapTunerData: function(aTunerId, aSourceType) {
     if (!this._internalTuners || this._internalTuners.size <= 0) {
       return null;
     }
     return this._internalTuners.get(this._getTunerMapKey(aTunerId, aSourceType));
   },
 
-  _getFilePath: function TVSimGetFilePathFromDummyDirectory(fileName) {
+  _getFilePath: function(fileName) {
     let dsFile = Cc["@mozilla.org/file/directory_service;1"]
                    .getService(Ci.nsIProperties)
                    .get("ProfD", Ci.nsIFile);
     dsFile.append(TV_SIMULATOR_DUMMY_DIRECTORY);
     dsFile.append(fileName);
 
     return dsFile.path;
   },
 
-  _validateSettings: function TVSimValidateSettings(aSettingsObject) {
+  _validateSettings: function(aSettingsObject) {
     return this._validateTuners(aSettingsObject.tuners);
   },
 
-  _validateTuners: function TVSimValidateTuners(aTunersObject) {
+  _validateTuners: function(aTunersObject) {
     let tunerIds = new Array();
     for (let tuner of aTunersObject) {
       if (!tuner.id ||
           !tuner.supportedType ||
           !tuner.supportedType.length ||
           tuner.supportedType.some(containInvalidSourceType) ||
           tunerIds.includes(tuner.id)) {
         debug("invalid tuner data.");
@@ -494,32 +539,32 @@ TVSimulatorService.prototype = {
 
       if (!this._validateSources(tuner.sources)) {
         return false;
       }
     }
     return true;
   },
 
-  _validateSources: function TVSimValidateSources(aSourcesObject) {
+  _validateSources: function(aSourcesObject) {
     for (let source of aSourcesObject) {
       if (!source.type ||
           !TV_SOURCE_TYPES.includes(source.type)) {
         debug("invalid source data.");
         return false;
       }
 
       if (!this._validateChannels(source.channels)) {
         return false;
       }
     }
     return true;
   },
 
-  _validateChannels: function TVSimValidateChannels(aChannelsObject) {
+  _validateChannels: function(aChannelsObject) {
     let channelNumbers = new Array();
     for (let channel of aChannelsObject) {
       if (!channel.networkId ||
           !channel.transportStreamId ||
           !channel.serviceId ||
           !channel.type ||
           !TV_CHANNEL_TYPES.includes(channel.type) ||
           !channel.number ||
@@ -532,17 +577,17 @@ TVSimulatorService.prototype = {
 
       if (!this._validatePrograms(channel.programs)) {
         return false;
       }
     }
     return true;
   },
 
-  _validatePrograms: function TVSimValidatePrograms(aProgramsObject) {
+  _validatePrograms: function(aProgramsObject) {
     let eventIds = new Array();
     for (let program of aProgramsObject) {
       if (!program.eventId ||
           eventIds.includes(program.eventId) ||
           !program.title ||
           !program.startTime ||
           !program.duration) {
         debug("invalid program data.");
--- a/dom/tv/TVSource.cpp
+++ b/dom/tv/TVSource.cpp
@@ -16,32 +16,18 @@
 #include "mozilla/dom/TVUtils.h"
 #include "nsITVService.h"
 #include "nsServiceManagerUtils.h"
 #include "TVSource.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(TVSource)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TVSource,
-                                                  DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTVService)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTuner)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentChannel)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TVSource,
-                                                DOMEventTargetHelper)
-  tmp->Shutdown();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTVService)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTuner)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentChannel)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED(TVSource, DOMEventTargetHelper, mTuner,
+                                   mCurrentChannel, mSourceListener)
 
 NS_IMPL_ADDREF_INHERITED(TVSource, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TVSource, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVSource)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 TVSource::TVSource(nsPIDOMWindowInner* aWindow,
@@ -52,57 +38,38 @@ TVSource::TVSource(nsPIDOMWindowInner* a
   , mType(aType)
   , mIsScanning(false)
 {
   MOZ_ASSERT(mTuner);
 }
 
 TVSource::~TVSource()
 {
-  Shutdown();
 }
 
 /* static */ already_AddRefed<TVSource>
 TVSource::Create(nsPIDOMWindowInner* aWindow,
                  TVSourceType aType,
                  TVTuner* aTuner)
 {
   RefPtr<TVSource> source = new TVSource(aWindow, aType, aTuner);
   return (source->Init()) ? source.forget() : nullptr;
 }
 
 bool
 TVSource::Init()
 {
-  mTVService = TVServiceFactory::AutoCreateTVService();
-  NS_ENSURE_TRUE(mTVService, false);
-
-  nsCOMPtr<nsITVSourceListener> sourceListener;
-  mTVService->GetSourceListener(getter_AddRefs(sourceListener));
-  NS_ENSURE_TRUE(sourceListener, false);
-  (static_cast<TVSourceListener*>(sourceListener.get()))->RegisterSource(this);
+  mSourceListener = TVSourceListener::Create(this);
+  if (NS_WARN_IF(!mSourceListener)) {
+    return false;
+  }
 
   return true;
 }
 
-void
-TVSource::Shutdown()
-{
-  if (!mTVService) {
-    return;
-  }
-
-  nsCOMPtr<nsITVSourceListener> sourceListener;
-  mTVService->GetSourceListener(getter_AddRefs(sourceListener));
-  if (!sourceListener) {
-    return;
-  }
-  (static_cast<TVSourceListener*>(sourceListener.get()))->UnregisterSource(this);
-}
-
 /* virtual */ JSObject*
 TVSource::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return TVSourceBinding::Wrap(aCx, this, aGivenProto);
 }
 
 nsresult
 TVSource::SetCurrentChannel(nsITVChannelData* aChannelData)
@@ -175,23 +142,29 @@ TVSource::GetChannels(ErrorResult& aRv)
   }
 
   // The operation is prohibited when the source is scanning channels.
   if (mIsScanning) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return promise.forget();
+  }
+
   nsString tunerId;
   mTuner->GetId(tunerId);
 
   nsCOMPtr<nsITVServiceCallback> callback =
     new TVServiceChannelGetterCallback(this, promise);
   nsresult rv =
-    mTVService->GetChannels(tunerId, ToTVSourceTypeStr(mType), callback);
+    service->GetChannels(tunerId, ToTVSourceTypeStr(mType), callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
@@ -207,23 +180,29 @@ TVSource::SetCurrentChannel(const nsAStr
   }
 
   // The operation is prohibited when the source is scanning channels.
   if (mIsScanning) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return promise.forget();
+  }
+
   nsString tunerId;
   mTuner->GetId(tunerId);
 
   nsCOMPtr<nsITVServiceCallback> callback =
     new TVServiceChannelSetterCallback(this, promise, aChannelNumber);
-  nsresult rv =
-    mTVService->SetChannel(tunerId, ToTVSourceTypeStr(mType), aChannelNumber, callback);
+  nsresult rv = service->SetChannel(tunerId, ToTVSourceTypeStr(mType),
+                                    aChannelNumber, callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
@@ -233,42 +212,48 @@ TVSource::StartScanning(const TVStartSca
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  nsString tunerId;
-  mTuner->GetId(tunerId);
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return promise.forget();
+  }
 
   bool isRescanned = aOptions.mIsRescanned.WasPassed() &&
                      aOptions.mIsRescanned.Value();
 
   if (isRescanned) {
-    nsresult rv = mTVService->ClearScannedChannelsCache();
+    nsresult rv = service->ClearScannedChannelsCache();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
       return promise.forget();
     }
 
     rv = DispatchScanningStateChangedEvent(TVScanningState::Cleared, nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
       return promise.forget();
     }
   }
 
+  nsString tunerId;
+  mTuner->GetId(tunerId);
+
   // |SetIsScanning(bool)| should be called once |notifySuccess()| of this
   // callback is invoked.
   nsCOMPtr<nsITVServiceCallback> callback =
     new TVServiceChannelScanCallback(this, promise, true);
   nsresult rv =
-    mTVService->StartScanningChannels(tunerId, ToTVSourceTypeStr(mType), callback);
+    service->StartScanningChannels(tunerId, ToTVSourceTypeStr(mType), callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
@@ -277,25 +262,31 @@ TVSource::StopScanning(ErrorResult& aRv)
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return promise.forget();
+  }
+
   nsString tunerId;
   mTuner->GetId(tunerId);
 
   // |SetIsScanning(bool)| should be called once |notifySuccess()| of this
   // callback is invoked.
   nsCOMPtr<nsITVServiceCallback> callback =
     new TVServiceChannelScanCallback(this, promise, false);
   nsresult rv =
-    mTVService->StopScanningChannels(tunerId, ToTVSourceTypeStr(mType), callback);
+    service->StopScanningChannels(tunerId, ToTVSourceTypeStr(mType), callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   return promise.forget();
 }
 
 already_AddRefed<TVTuner>
--- a/dom/tv/TVSource.h
+++ b/dom/tv/TVSource.h
@@ -11,17 +11,17 @@
 // Include TVScanningStateChangedEventBinding.h since enum TVScanningState can't
 // be forward declared.
 #include "mozilla/dom/TVScanningStateChangedEventBinding.h"
 // Include TVSourceBinding.h since enum TVSourceType can't be forward declared.
 #include "mozilla/dom/TVSourceBinding.h"
 
 class nsITVChannelData;
 class nsITVProgramData;
-class nsITVService;
+class nsITVSourceListener;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class TVChannel;
 class TVProgram;
 class TVTuner;
@@ -87,28 +87,26 @@ private:
   TVSource(nsPIDOMWindowInner* aWindow,
            TVSourceType aType,
            TVTuner* aTuner);
 
   ~TVSource();
 
   bool Init();
 
-  void Shutdown();
-
   nsresult DispatchCurrentChannelChangedEvent(TVChannel* aChannel);
 
   nsresult DispatchScanningStateChangedEvent(TVScanningState aState,
                                              TVChannel* aChannel);
 
   nsresult DispatchEITBroadcastedEvent(const Sequence<OwningNonNull<TVProgram>>& aPrograms);
 
-  nsCOMPtr<nsITVService> mTVService;
   RefPtr<TVTuner> mTuner;
   RefPtr<TVChannel> mCurrentChannel;
+  nsCOMPtr<nsITVSourceListener> mSourceListener;
   TVSourceType mType;
   bool mIsScanning;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TVSource_h
--- a/dom/tv/TVTuner.cpp
+++ b/dom/tv/TVTuner.cpp
@@ -1,33 +1,34 @@
 /* -*- 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 "TVTuner.h"
+
 #include "DOMMediaStream.h"
+#include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TVCurrentSourceChangedEvent.h"
 #include "mozilla/dom/TVServiceCallbacks.h"
 #include "mozilla/dom/TVServiceFactory.h"
 #include "mozilla/dom/TVSource.h"
 #include "mozilla/dom/TVUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsITVService.h"
 #include "nsITVSimulatorService.h"
 #include "nsServiceManagerUtils.h"
-#include "TVTuner.h"
-#include "mozilla/dom/HTMLVideoElement.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(TVTuner, DOMEventTargetHelper,
-                                   mTVService, mStream, mCurrentSource, mSources)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(TVTuner, DOMEventTargetHelper, mStream,
+                                   mCurrentSource, mSources)
 
 NS_IMPL_ADDREF_INHERITED(TVTuner, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TVTuner, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVTuner)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 TVTuner::TVTuner(nsPIDOMWindowInner* aWindow)
@@ -76,35 +77,32 @@ TVTuner::Init(nsITVTunerData* aData)
       continue;
     }
 
     mSupportedSourceTypes.AppendElement(sourceType);
     mSources.AppendElement(source);
   }
   NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, supportedSourceTypes);
 
-  mTVService = TVServiceFactory::AutoCreateTVService();
-  NS_ENSURE_TRUE(mTVService, false);
-
   rv = aData->GetStreamType(&mStreamType);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 /* virtual */ JSObject*
 TVTuner::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return TVTunerBinding::Wrap(aCx, this, aGivenProto);
 }
 
 nsresult
-TVTuner::SetCurrentSource(TVSourceType aSourceType)
+TVTuner::SetCurrentSource(TVSourceType aSourceType,
+                          nsITVGonkNativeHandleData* aHandleData)
 {
-  ErrorResult error;
   if (mCurrentSource) {
     if (aSourceType == mCurrentSource->Type()) {
       // No actual change.
       return NS_OK;
     }
 
     // No need to stay tuned for non-current sources.
     nsresult rv = mCurrentSource->UnsetCurrentChannel();
@@ -115,20 +113,17 @@ TVTuner::SetCurrentSource(TVSourceType a
 
   for (uint32_t i = 0; i < mSources.Length(); i++) {
     if (aSourceType == mSources[i]->Type()) {
       mCurrentSource = mSources[i];
       break;
     }
   }
 
-  nsresult rv = InitMediaStream();
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+  mStream = CreateHwMediaStream(aHandleData);
 
   return DispatchCurrentSourceChangedEvent(mCurrentSource);
 }
 
 nsresult
 TVTuner::DispatchTVEvent(nsIDOMEvent* aEvent)
 {
   return DispatchTrustedEvent(aEvent);
@@ -163,21 +158,29 @@ TVTuner::SetCurrentSource(const TVSource
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // |SetCurrentSource(const TVSourceType)| will be called once |notifySuccess|
-  // of the callback is invoked.
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
+
+  // For TV simulator, |SetCurrentSource(TVSourceType)| will be called once
+  // |notifySuccess| of the callback is invoked. On the other hand, for gonk TV,
+  // |SetCurrentSource(TVSourceType, const GonkNativeHandle&)| will be called
+  // once |notifyStreamHandle| of the callback is invoked.
   nsCOMPtr<nsITVServiceCallback> callback =
     new TVServiceSourceSetterCallback(this, promise, aSourceType);
-  nsresult rv = mTVService->SetSource(mId, ToTVSourceTypeStr(aSourceType), callback);
+  nsresult rv =
+    service->SetSource(mId, ToTVSourceTypeStr(aSourceType), callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   return promise.forget();
 }
 
 void
@@ -198,32 +201,53 @@ TVTuner::GetStream() const
 {
   RefPtr<DOMMediaStream> stream = mStream;
   return stream.forget();
 }
 
 nsresult
 TVTuner::ReloadMediaStream()
 {
-  return InitMediaStream();
+  // Only TV simulator needs to reload the media stream (during channel changes).
+  if (NS_WARN_IF(nsITVTunerData::TV_STREAM_TYPE_SIMULATOR != mStreamType)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mStream = CreateSimulatedMediaStream();
+
+  return NS_OK;
 }
 
-nsresult
-TVTuner::InitMediaStream()
+already_AddRefed<DOMMediaStream>
+TVTuner::CreateHwMediaStream(nsITVGonkNativeHandleData* aHandleData)
 {
-  nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
-  RefPtr<DOMMediaStream> stream = nullptr;
+  RefPtr<DOMMediaStream> stream;
+
   if (mStreamType == nsITVTunerData::TV_STREAM_TYPE_HW) {
-    stream = DOMHwMediaStream::CreateHwStream(window);
+#ifdef MOZ_WIDGET_GONK
+    nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
+
+    layers::OverlayImage::SidebandStreamData imageData;
+    aHandleData->GetHandle(imageData.mStream);
+    window->GetInnerWidth(&imageData.mSize.width);
+    window->GetInnerHeight(&imageData.mSize.height);
+
+    RefPtr<layers::OverlayImage> overlayImage = new layers::OverlayImage();
+    overlayImage->SetData(imageData);
+    stream = DOMHwMediaStream::CreateHwStream(window, overlayImage);
+#endif
   } else if (mStreamType == nsITVTunerData::TV_STREAM_TYPE_SIMULATOR) {
     stream = CreateSimulatedMediaStream();
   }
 
-  mStream = stream.forget();
-  return NS_OK;
+  if (NS_WARN_IF(!stream)) {
+    return nullptr;
+  }
+
+  return stream.forget();
 }
 
 already_AddRefed<DOMMediaStream>
 TVTuner::CreateSimulatedMediaStream()
 {
   nsCOMPtr<nsPIDOMWindowInner> domWin = GetOwner();
   if (NS_WARN_IF(!domWin)) {
     return nullptr;
@@ -255,17 +279,20 @@ TVTuner::CreateSimulatedMediaStream()
     return nullptr;
   }
 
   mediaElement->SetLoop(true, error);
   if (NS_WARN_IF(error.Failed())) {
     return nullptr;
   }
 
-  nsCOMPtr<nsITVSimulatorService> simService(do_QueryInterface(mTVService));
+  nsCOMPtr<nsITVService> service = do_GetService(TV_SERVICE_CONTRACTID);
+  MOZ_ASSERT(service);
+
+  nsCOMPtr<nsITVSimulatorService> simService(do_QueryInterface(service));
   if (NS_WARN_IF(!simService)) {
     return nullptr;
   }
 
   if (NS_WARN_IF(!mCurrentSource)) {
     return nullptr;
   }
 
--- a/dom/tv/TVTuner.h
+++ b/dom/tv/TVTuner.h
@@ -8,17 +8,17 @@
 #define mozilla_dom_TVTuner_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 // Include TVTunerBinding.h since enum TVSourceType can't be forward declared.
 #include "mozilla/dom/TVTunerBinding.h"
 
 #define VIDEO_TAG NS_LITERAL_STRING("video")
 
-class nsITVService;
+class nsITVGonkNativeHandleData;
 class nsITVTunerData;
 
 namespace mozilla {
 
 class DOMMediaStream;
 
 namespace dom {
 
@@ -28,23 +28,25 @@ class TVSource;
 class TVTuner final : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TVTuner, DOMEventTargetHelper)
 
   static already_AddRefed<TVTuner> Create(nsPIDOMWindowInner* aWindow,
                                           nsITVTunerData* aData);
+
   nsresult NotifyImageSizeChanged(uint32_t aWidth, uint32_t aHeight);
 
   // WebIDL (internal functions)
 
   virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  nsresult SetCurrentSource(TVSourceType aSourceType);
+  nsresult SetCurrentSource(TVSourceType aSourceType,
+                            nsITVGonkNativeHandleData* aHandleData);
 
   nsresult DispatchTVEvent(nsIDOMEvent* aEvent);
 
   // WebIDL (public APIs)
 
   void GetSupportedSourceTypes(nsTArray<TVSourceType>& aSourceTypes,
                                ErrorResult& aRv) const;
 
@@ -67,21 +69,23 @@ private:
   explicit TVTuner(nsPIDOMWindowInner* aWindow);
 
   ~TVTuner();
 
   bool Init(nsITVTunerData* aData);
 
   nsresult InitMediaStream();
 
+  already_AddRefed<DOMMediaStream> CreateHwMediaStream(
+    nsITVGonkNativeHandleData* aHandleData);
+
   already_AddRefed<DOMMediaStream> CreateSimulatedMediaStream();
 
   nsresult DispatchCurrentSourceChangedEvent(TVSource* aSource);
 
-  nsCOMPtr<nsITVService> mTVService;
   RefPtr<DOMMediaStream> mStream;
   uint16_t mStreamType;
   RefPtr<TVSource> mCurrentSource;
   nsTArray<RefPtr<TVSource>> mSources;
   nsString mId;
   nsTArray<TVSourceType> mSupportedSourceTypes;
 };
 
--- a/dom/tv/TVTypes.cpp
+++ b/dom/tv/TVTypes.cpp
@@ -464,10 +464,34 @@ TVProgramData::SetSubtitleLanguages(uint
                        nullptr;
   for (uint32_t i = 0; i < mSubtitleLanguageCount; i++) {
     mSubtitleLanguages[i] = NS_strdup(aLanguages[i]);
   }
 
   return NS_OK;
 }
 
+/*
+ * Implementation of TVGonkNativeHandleData
+ */
+
+NS_IMPL_ISUPPORTS(TVGonkNativeHandleData, nsITVGonkNativeHandleData)
+
+TVGonkNativeHandleData::TVGonkNativeHandleData() {}
+
+TVGonkNativeHandleData::~TVGonkNativeHandleData() {}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkNativeHandleData::GetHandle(GonkNativeHandle& aHandle)
+{
+  aHandle = mHandle;
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkNativeHandleData::SetHandle(GonkNativeHandle& aHandle)
+{
+  mHandle = aHandle;
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/tv/TVTypes.h
+++ b/dom/tv/TVTypes.h
@@ -2,42 +2,49 @@
 /* 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_TVTypes_h
 #define mozilla_dom_TVTypes_h
 
+#include "mozilla/layers/GonkNativeHandle.h"
+#include "mozilla/Tuple.h"
 #include "nsITVService.h"
 
 namespace mozilla {
 namespace dom {
 
+typedef Tuple<nsString, nsString, nsCOMPtr<nsITVSourceListener>>
+TVSourceListenerTuple;
+
+using mozilla::layers::GonkNativeHandle;
+
 class TVTunerData final : public nsITVTunerData
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITVTUNERDATA
 
   TVTunerData();
 
 private:
   ~TVTunerData();
 
   nsString mId;
   char** mSupportedSourceTypes;
   uint32_t mCount;
   uint16_t mStreamType;
 };
 
 class TVChannelData final : public nsITVChannelData
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITVCHANNELDATA
 
   TVChannelData();
 
 private:
   ~TVChannelData();
 
   nsString mNetworkId;
@@ -48,17 +55,17 @@ private:
   nsString mName;
   bool mIsEmergency;
   bool mIsFree;
 };
 
 class TVProgramData final : public nsITVProgramData
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITVPROGRAMDATA
 
   TVProgramData();
 
 private:
   ~TVProgramData();
 
   nsString mEventId;
@@ -68,12 +75,26 @@ private:
   nsString mDescription;
   nsString mRating;
   char** mAudioLanguages;
   uint32_t mAudioLanguageCount;
   char** mSubtitleLanguages;
   uint32_t mSubtitleLanguageCount;
 };
 
+class TVGonkNativeHandleData final : public nsITVGonkNativeHandleData
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSITVGONKNATIVEHANDLEDATA
+
+  TVGonkNativeHandleData();
+
+private:
+  ~TVGonkNativeHandleData();
+
+  GonkNativeHandle mHandle;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TVTypes_h
new file mode 100644
--- /dev/null
+++ b/dom/tv/gonk/TVGonkService.cpp
@@ -0,0 +1,183 @@
+/* -*- 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 "TVGonkService.h"
+
+#include "mozilla/dom/TVServiceRunnables.h"
+#include "nsCOMPtr.h"
+#include "nsIMutableArray.h"
+#include "nsServiceManagerUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(TVGonkService, nsITVService)
+
+TVGonkService::TVGonkService() {}
+
+TVGonkService::~TVGonkService() {}
+
+NS_IMETHODIMP
+TVGonkService::RegisterSourceListener(const nsAString& aTunerId,
+                                      const nsAString& aSourceType,
+                                      nsITVSourceListener* aListener)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!aTunerId.IsEmpty());
+  MOZ_ASSERT(!aSourceType.IsEmpty());
+  MOZ_ASSERT(aListener);
+
+  mSourceListenerTuples.AppendElement(new TVSourceListenerTuple(
+    nsString(aTunerId), nsString(aSourceType), aListener));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TVGonkService::UnregisterSourceListener(const nsAString& aTunerId,
+                                        const nsAString& aSourceType,
+                                        nsITVSourceListener* aListener)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!aTunerId.IsEmpty());
+  MOZ_ASSERT(!aSourceType.IsEmpty());
+  MOZ_ASSERT(aListener);
+
+  for (uint32_t i = 0; i < mSourceListenerTuples.Length(); i++) {
+    const UniquePtr<TVSourceListenerTuple>& tuple = mSourceListenerTuples[i];
+    if (aTunerId.Equals(Get<0>(*tuple)) && aSourceType.Equals(Get<1>(*tuple)) &&
+        aListener == Get<2>(*tuple)) {
+      mSourceListenerTuples.RemoveElementAt(i);
+      break;
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+TVGonkService::GetSourceListeners(
+  const nsAString& aTunerId, const nsAString& aSourceType,
+  nsTArray<nsCOMPtr<nsITVSourceListener> >& aListeners) const
+{
+  aListeners.Clear();
+
+  for (uint32_t i = 0; i < mSourceListenerTuples.Length(); i++) {
+    const UniquePtr<TVSourceListenerTuple>& tuple = mSourceListenerTuples[i];
+    nsCOMPtr<nsITVSourceListener> listener = Get<2>(*tuple);
+    if (aTunerId.Equals(Get<0>(*tuple)) && aSourceType.Equals(Get<1>(*tuple))) {
+      aListeners.AppendElement(listener);
+      break;
+    }
+  }
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkService::GetTuners(nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(aCallback);
+
+  // TODO Bug 1229308 - Communicate with TV daemon process.
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkService::SetSource(const nsAString& aTunerId,
+                         const nsAString& aSourceType,
+                         nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(!aTunerId.IsEmpty());
+  MOZ_ASSERT(!aSourceType.IsEmpty());
+  MOZ_ASSERT(aCallback);
+
+  // TODO Bug 1229308 - Communicate with TV daemon process.
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkService::StartScanningChannels(const nsAString& aTunerId,
+                                     const nsAString& aSourceType,
+                                     nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(!aTunerId.IsEmpty());
+  MOZ_ASSERT(!aSourceType.IsEmpty());
+  MOZ_ASSERT(aCallback);
+
+  // TODO Bug 1229308 - Communicate with TV daemon process.
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkService::StopScanningChannels(const nsAString& aTunerId,
+                                    const nsAString& aSourceType,
+                                    nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(!aTunerId.IsEmpty());
+  MOZ_ASSERT(!aSourceType.IsEmpty());
+  MOZ_ASSERT(aCallback);
+
+  // TODO Bug 1229308 - Communicate with TV daemon process.
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkService::ClearScannedChannelsCache()
+{
+  // TODO Bug 1229308 - Communicate with TV daemon process.
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkService::SetChannel(const nsAString& aTunerId,
+                          const nsAString& aSourceType,
+                          const nsAString& aChannelNumber,
+                          nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(!aTunerId.IsEmpty());
+  MOZ_ASSERT(!aSourceType.IsEmpty());
+  MOZ_ASSERT(aCallback);
+
+  // TODO Bug 1229308 - Communicate with TV daemon process.
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkService::GetChannels(const nsAString& aTunerId,
+                           const nsAString& aSourceType,
+                           nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(!aTunerId.IsEmpty());
+  MOZ_ASSERT(!aSourceType.IsEmpty());
+  MOZ_ASSERT(aCallback);
+
+  // TODO Bug 1229308 - Communicate with TV daemon process.
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVGonkService::GetPrograms(const nsAString& aTunerId,
+                           const nsAString& aSourceType,
+                           const nsAString& aChannelNumber, uint64_t startTime,
+                           uint64_t endTime, nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(!aTunerId.IsEmpty());
+  MOZ_ASSERT(!aSourceType.IsEmpty());
+  MOZ_ASSERT(!aChannelNumber.IsEmpty());
+  MOZ_ASSERT(aCallback);
+
+  // TODO Bug 1229308 - Communicate with TV daemon process.
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/gonk/TVGonkService.h
@@ -0,0 +1,39 @@
+/* -*- 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_TVGonkService_h
+#define mozilla_dom_TVGonkService_h
+
+#include "nsCOMPtr.h"
+#include "nsITVService.h"
+#include "nsTArray.h"
+#include "TVTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+class TVGonkService final : public nsITVService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITVSERVICE
+
+  TVGonkService();
+
+private:
+  ~TVGonkService();
+
+  void GetSourceListeners(
+    const nsAString& aTunerId, const nsAString& aSourceType,
+    nsTArray<nsCOMPtr<nsITVSourceListener> >& aListeners) const;
+
+  nsTArray<UniquePtr<TVSourceListenerTuple>> mSourceListenerTuples;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVGonkService_h
--- a/dom/tv/moz.build
+++ b/dom/tv/moz.build
@@ -35,16 +35,21 @@ XPIDL_SOURCES += [
     'nsITVSimulatorService.idl',
 ]
 
 EXTRA_COMPONENTS += [
     'TVSimulatorService.js',
     'TVSimulatorService.manifest',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+    UNIFIED_SOURCES += [
+        'gonk/TVGonkService.cpp',
+    ];
+
 XPIDL_MODULE = 'dom_tv'
 
 MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/dom/tv/nsITVService.idl
+++ b/dom/tv/nsITVService.idl
@@ -203,16 +203,39 @@ interface nsITVSourceListener : nsISuppo
    */
   void notifyEITBroadcasted(in DOMString tunerId,
                             in DOMString sourceType,
                             in nsITVChannelData channelData,
                             [array, size_is(count)] in nsITVProgramData programDataList,
                             in unsigned long count);
 };
 
+%{C++
+#include "mozilla/layers/GonkNativeHandle.h"
+
+#define TV_GONK_NATIVE_HANDLE_DATA_CID \
+  { 0x1e72877a, 0xaacc, 0x425a, { 0xa3, 0x33, 0x85, 0xe1, 0xb2, 0x66, 0xcb, 0xb3 } }
+#define TV_GONK_NATIVE_HANDLE_DATA_CONTRACTID \
+  "@mozilla.org/tv/tvgonknativehandledata;1"
+%}
+
+[ref] native GonkNativeHandle(mozilla::layers::GonkNativeHandle);
+
+[scriptable, builtinclass, uuid(64acaf2e-3dc5-467a-962d-b4c2c920ec30)]
+interface nsITVGonkNativeHandleData : nsISupports
+{
+  /**
+   * A native handle associated with the TV stream.
+   *
+   * NOTE: It's only available for stream type |nsITVTunerData::TV_STREAM_TYPE_HW|.
+   */
+  [noscript]
+  attribute GonkNativeHandle handle;
+};
+
 [scriptable, builtinclass, uuid(01582a11-4707-455d-8d2a-2c8de8227dad)]
 interface nsITVServiceCallback : nsISupports
 {
   const unsigned short TV_ERROR_OK = 0;
   const unsigned short TV_ERROR_FAILURE = 1;
   const unsigned short TV_ERROR_INVALID_ARG = 2;
   const unsigned short TV_ERROR_NO_SIGNAL = 3;
   const unsigned short TV_ERROR_NOT_SUPPORTED = 4;
@@ -228,55 +251,58 @@ interface nsITVServiceCallback : nsISupp
    * Called when the operation succeeds.
    *
    * @param dataList A list of data.
    *                 An array of |nsITVTunerData| when used for |getTuners()|.
    *                 An array of |nsITVChannelData| ordered by the LCN (Logical
    *                 Channel Number) when used for |getChannels()|.
    *                 An array of |nsITVProgramData| ordered by the start time
    *                 for |getPrograms()|.
+   *                 An array of |nsITVGonkNativeHandleData| with only one
+   *                 element for |setSource()|.
    *                 An array of |nsITVChannelData| with only one element for
    *                 |setChannel()|.
-   *                 An array of |nsISupportsString| with only one element for
-   *                 |getOverlayId()|.
    *
    * NOTE: |nsIArray| is adopted to prevent this interface from being split into
    * multiple interfaces with different |notifySuccess|. Though the
    * implementation of TV service may need |nsIMutableArray| to fill in the
    * array, it doesn't seem necessary for other places to use the mutable one.
    */
   void notifySuccess([optional] in nsIArray dataList);
 };
 
 %{C++
 #define TV_SERVICE_CONTRACTID \
   "@mozilla.org/tv/tvservice;1"
 %}
 
-/**
- * XPCOM component which interacts with the underlying TV components.
- *
- * NOTE: The implementation is expected to be done by TV vendors to cooperate
- * with their own TV stack. The methods should not be called on any thread
- * except for the XPCOM main thread.
- *
- * NOTE: The callbacks passing in the methods must be called asynchronously. In
- * the implementation, actual runnable objects may need to be created and call
- * the callback off of the runnables, after the function returns.
- * TVServiceRunnables.h provides some ready-made runnables and could be used in
- * the following way.
- *
- * nsCOMPtr<nsIRunnable> runnable =
- *   new TVServiceNotifyRunnable(callback, dataList, optional errorCode);
- * return NS_DispatchToCurrentThread(runnable);
- */
-[scriptable, uuid(e52f93f1-6071-468b-a198-d8e6bc5ca348)]
+[scriptable, uuid(827f4e94-430a-47a6-954a-0c7470cfc5fe)]
 interface nsITVService : nsISupports
 {
-  attribute nsITVSourceListener sourceListener;
+  /**
+   * Register a source listener.
+   *
+   * @param tunerId    The ID of the tuner.
+   * @param sourceType The source type to be used.
+   * @param listener   The listener to register.
+   */
+  void registerSourceListener(in DOMString tunerId,
+                              in DOMString sourceType,
+                              in nsITVSourceListener listener);
+
+  /**
+   * Unregister a source listener.
+   *
+   * @param tunerId    The ID of the tuner.
+   * @param sourceType The source type to be used.
+   * @param listener   The listener to register.
+   */
+  void unregisterSourceListener(in DOMString tunerId,
+                                in DOMString sourceType,
+                                in nsITVSourceListener listener);
 
   /**
    * Get all tuners.
    *
    * @param callback   The callback object where either |notifyError| or
    *                   |notifySuccess|, which takes an array of
    *                   |nsITVTunerData|, is expected.
    */
@@ -373,21 +399,9 @@ interface nsITVService : nsISupports
    *                      expected.
    */
   void getPrograms(in DOMString tunerId,
                    in DOMString sourceType,
                    in DOMString channelNumber,
                    in unsigned long long startTime,
                    in unsigned long long endTime,
                    in nsITVServiceCallback callback);
-
-  /**
-   * Get the overlay ID of a given tuner.
-   *
-   * @param tunerId       The ID of the tuner.
-   * @param callback      The callback object where either |notifyError| or
-   *                      |notifySuccess|, which takes an array of
-   *                      |nsISupportsString| with only one element, is expected.
-   */
-  void getOverlayId(in DOMString tunerId,
-                    in nsITVServiceCallback callback);
 };
-
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -390,19 +390,22 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMediaManagerService,
                                          MediaManager::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileConnectionService,
                                          NS_CreateMobileConnectionService)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelephonyService,
                                          NS_CreateTelephonyService)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIVoicemailService,
                                          NS_CreateVoicemailService)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITVService,
+                                         TVServiceFactory::AutoCreateTVService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(TVTunerData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(TVChannelData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(TVProgramData)
+NS_GENERIC_FACTORY_CONSTRUCTOR(TVGonkNativeHandleData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PresentationDeviceManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(TextInputProcessor)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(FakeInputPortService,
                                          InputPortServiceFactory::CreateFakeInputPortService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(InputPortData)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPresentationService,
                                          NS_CreatePresentationService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PresentationTCPSessionTransport)
@@ -856,19 +859,21 @@ NS_DEFINE_NAMED_CID(NS_POCKETSPHINX_SPEE
 #endif
 #ifdef MOZ_WEBSPEECH
 NS_DEFINE_NAMED_CID(NS_SYNTHVOICEREGISTRY_CID);
 #endif
 
 #ifdef ACCESSIBILITY
 NS_DEFINE_NAMED_CID(NS_ACCESSIBILITY_SERVICE_CID);
 #endif
+NS_DEFINE_NAMED_CID(TV_SERVICE_CID);
 NS_DEFINE_NAMED_CID(TV_TUNER_DATA_CID);
 NS_DEFINE_NAMED_CID(TV_CHANNEL_DATA_CID);
 NS_DEFINE_NAMED_CID(TV_PROGRAM_DATA_CID);
+NS_DEFINE_NAMED_CID(TV_GONK_NATIVE_HANDLE_DATA_CID);
 
 NS_DEFINE_NAMED_CID(FAKE_INPUTPORT_SERVICE_CID);
 NS_DEFINE_NAMED_CID(INPUTPORT_DATA_CID);
 
 NS_DEFINE_NAMED_CID(GECKO_MEDIA_PLUGIN_SERVICE_CID);
 
 NS_DEFINE_NAMED_CID(PRESENTATION_SERVICE_CID);
 NS_DEFINE_NAMED_CID(PRESENTATION_DEVICE_MANAGER_CID);
@@ -1161,19 +1166,21 @@ static const mozilla::Module::CIDEntry k
   { &kNS_GAMEPAD_TEST_CID, false, nullptr, GamepadServiceTestConstructor },
 #endif
 #ifdef ACCESSIBILITY
   { &kNS_ACCESSIBILITY_SERVICE_CID, false, nullptr, CreateA11yService },
 #endif
   { &kTELEPHONY_SERVICE_CID, false, nullptr, nsITelephonyServiceConstructor },
   { &kNS_MOBILE_CONNECTION_SERVICE_CID, false, NULL, nsIMobileConnectionServiceConstructor },
   { &kNS_VOICEMAIL_SERVICE_CID, false, nullptr, nsIVoicemailServiceConstructor },
+  { &kTV_SERVICE_CID, false, nullptr, nsITVServiceConstructor },
   { &kTV_TUNER_DATA_CID, false, nullptr, TVTunerDataConstructor },
   { &kTV_CHANNEL_DATA_CID, false, nullptr, TVChannelDataConstructor },
   { &kTV_PROGRAM_DATA_CID, false, nullptr, TVProgramDataConstructor },
+  { &kTV_GONK_NATIVE_HANDLE_DATA_CID, false, nullptr, TVGonkNativeHandleDataConstructor },
   { &kPRESENTATION_SERVICE_CID, false, nullptr, nsIPresentationServiceConstructor },
   { &kPRESENTATION_DEVICE_MANAGER_CID, false, nullptr, PresentationDeviceManagerConstructor },
   { &kPRESENTATION_TCP_SESSION_TRANSPORT_CID, false, nullptr, PresentationTCPSessionTransportConstructor },
   { &kTEXT_INPUT_PROCESSOR_CID, false, nullptr, TextInputProcessorConstructor },
   { &kFAKE_INPUTPORT_SERVICE_CID, false, nullptr, FakeInputPortServiceConstructor },
   { &kINPUTPORT_DATA_CID, false, nullptr, InputPortDataConstructor },
 #ifdef MOZ_B2G
   { &kNS_HARDWARE_KEY_HANDLER_CID, false, nullptr, nsIHardwareKeyHandlerConstructor },
@@ -1327,19 +1334,21 @@ static const mozilla::Module::ContractID
   { NS_GAMEPAD_TEST_CONTRACTID, &kNS_GAMEPAD_TEST_CID },
 #endif
   { MEDIAMANAGERSERVICE_CONTRACTID, &kNS_MEDIAMANAGERSERVICE_CID },
 #ifdef ACCESSIBILITY
   { "@mozilla.org/accessibilityService;1", &kNS_ACCESSIBILITY_SERVICE_CID },
   { "@mozilla.org/accessibleRetrieval;1", &kNS_ACCESSIBILITY_SERVICE_CID },
 #endif
   { TELEPHONY_SERVICE_CONTRACTID, &kTELEPHONY_SERVICE_CID },
+  { TV_SERVICE_CONTRACTID, &kTV_SERVICE_CID },
   { TV_TUNER_DATA_CONTRACTID, &kTV_TUNER_DATA_CID },
   { TV_CHANNEL_DATA_CONTRACTID, &kTV_CHANNEL_DATA_CID },
   { TV_PROGRAM_DATA_CONTRACTID, &kTV_PROGRAM_DATA_CID },
+  { TV_GONK_NATIVE_HANDLE_DATA_CONTRACTID, &kTV_GONK_NATIVE_HANDLE_DATA_CID },
   { "@mozilla.org/gecko-media-plugin-service;1",  &kGECKO_MEDIA_PLUGIN_SERVICE_CID },
   { NS_MOBILE_CONNECTION_SERVICE_CONTRACTID, &kNS_MOBILE_CONNECTION_SERVICE_CID },
   { NS_VOICEMAIL_SERVICE_CONTRACTID, &kNS_VOICEMAIL_SERVICE_CID },
   { PRESENTATION_SERVICE_CONTRACTID, &kPRESENTATION_SERVICE_CID },
   { PRESENTATION_DEVICE_MANAGER_CONTRACTID, &kPRESENTATION_DEVICE_MANAGER_CID },
   { PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID, &kPRESENTATION_TCP_SESSION_TRANSPORT_CID },
   { "@mozilla.org/text-input-processor;1", &kTEXT_INPUT_PROCESSOR_CID },
   { FAKE_INPUTPORT_SERVICE_CONTRACTID, &kFAKE_INPUTPORT_SERVICE_CID },