Bug 1263799 - Support IPC for TV service between B2G and content processes. Part 2 - IPC. r?schien draft
authorKevin Chen <kechen@mozilla.com>
Fri, 18 Mar 2016 11:35:44 +0800
changeset 349684 ad93f8ff04ef24c1e5e9a08b7c2d74aaac9b6e46
parent 349683 3ca681de81c4be56c11c93a3a359fa06d0255023
child 350316 68edce28e61994a5a8ca8e00d051a421778cfd65
push id15156
push userbmo:kechen@mozilla.com
push dateTue, 12 Apr 2016 02:07:17 +0000
reviewersschien
bugs1263799
milestone48.0a1
Bug 1263799 - Support IPC for TV service between B2G and content processes. Part 2 - IPC. r?schien MozReview-Commit-ID: BOk8cVKUDvR
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/tv/TVServiceFactory.cpp
dom/tv/TVTypes.cpp
dom/tv/TVTypes.h
dom/tv/ipc/PTV.ipdl
dom/tv/ipc/PTVRequest.ipdl
dom/tv/ipc/PTVTypes.ipdlh
dom/tv/ipc/TVChild.cpp
dom/tv/ipc/TVChild.h
dom/tv/ipc/TVIPCHelper.cpp
dom/tv/ipc/TVIPCHelper.h
dom/tv/ipc/TVIPCService.cpp
dom/tv/ipc/TVIPCService.h
dom/tv/ipc/TVParent.cpp
dom/tv/ipc/TVParent.h
dom/tv/moz.build
dom/tv/nsITVService.idl
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -178,16 +178,17 @@
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/bluetooth/PBluetoothChild.h"
 #include "mozilla/dom/PFMRadioChild.h"
 #include "mozilla/dom/PPresentationChild.h"
 #include "mozilla/dom/PresentationIPCService.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/dom/PTVChild.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/PSpeechSynthesisChild.h"
 #endif
 
 #include "ProcessUtils.h"
 #include "URIUtils.h"
 #include "nsContentUtils.h"
@@ -1700,16 +1701,30 @@ ContentChild::RecvNotifyPresentationRece
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   NS_WARN_IF(!service);
 
   NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(aSessionId)));
 
   return true;
 }
 
+PTVChild*
+ContentChild::AllocPTVChild()
+{
+  NS_NOTREACHED("We should never be manually allocating PTVChild actors");
+  return nullptr;
+}
+
+bool
+ContentChild::DeallocPTVChild(PTVChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 bool
 ContentChild::RecvNotifyGMPsChanged()
 {
   GMPDecoderModule::UpdateUsableCodecs();
   return true;
 }
 
 PCrashReporterChild*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -370,16 +370,20 @@ public:
 
   virtual bool
   RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
                                          const nsString& aSessionId) override;
 
   virtual bool
   RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) override;
 
+  virtual PTVChild* AllocPTVChild() override;
+
+  virtual bool DeallocPTVChild(PTVChild* aActor) override;
+
   virtual bool RecvNotifyGMPsChanged() override;
 
   virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override;
 
   virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) override;
 
   virtual bool RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages,
                                   InfallibleTArray<SubstitutionMapping>&& resources,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -68,16 +68,17 @@
 #include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/Permissions.h"
 #include "mozilla/dom/PresentationParent.h"
 #include "mozilla/dom/PPresentationParent.h"
 #include "mozilla/dom/quota/QuotaManagerService.h"
 #include "mozilla/dom/telephony/TelephonyParent.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
+#include "mozilla/dom/TVParent.h"
 #include "mozilla/dom/voicemail/VoicemailParent.h"
 #include "mozilla/embedding/printingui/PrintingParent.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
@@ -4144,16 +4145,36 @@ ContentParent::DeallocPPresentationParen
 }
 
 bool
 ContentParent::RecvPPresentationConstructor(PPresentationParent* aActor)
 {
   return static_cast<PresentationParent*>(aActor)->Init();
 }
 
+PTVParent*
+ContentParent::AllocPTVParent()
+{
+  RefPtr<TVParent> actor = new TVParent();
+  return actor.forget().take();
+}
+
+bool
+ContentParent::DeallocPTVParent(PTVParent* aActor)
+{
+  RefPtr<TVParent> actor = dont_AddRef(static_cast<TVParent*>(aActor));
+  return true;
+}
+
+bool
+ContentParent::RecvPTVConstructor(PTVParent* aActor)
+{
+  return static_cast<TVParent*>(aActor)->Init();
+}
+
 PSpeechSynthesisParent*
 ContentParent::AllocPSpeechSynthesisParent()
 {
 #ifdef MOZ_WEBSPEECH
   return new mozilla::dom::SpeechSynthesisParent();
 #else
   return nullptr;
 #endif
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -853,16 +853,22 @@ private:
   virtual bool DeallocPFMRadioParent(PFMRadioParent* aActor) override;
 
   virtual PPresentationParent* AllocPPresentationParent() override;
 
   virtual bool DeallocPPresentationParent(PPresentationParent* aActor) override;
 
   virtual bool RecvPPresentationConstructor(PPresentationParent* aActor) override;
 
+  virtual PTVParent* AllocPTVParent() override;
+
+  virtual bool DeallocPTVParent(PTVParent* aActor) override;
+
+  virtual bool RecvPTVConstructor(PTVParent* aActor) override;
+
   virtual PSpeechSynthesisParent* AllocPSpeechSynthesisParent() override;
 
   virtual bool
   DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor) override;
 
   virtual bool
   RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor) override;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -51,16 +51,17 @@ include protocol PTelephony;
 include protocol PTestShell;
 include protocol PVoicemail;
 include protocol PJavaScript;
 include protocol PRemoteSpellcheckEngine;
 include protocol PWebBrowserPersistDocument;
 include protocol PWebrtcGlobal;
 include protocol PPresentation;
 include protocol PVRManager;
+include protocol PTV;
 include DOMTypes;
 include JavaScriptTypes;
 include InputStreamParams;
 include PTabContext;
 include URIParams;
 include PluginTypes;
 include ProtocolTypes;
 include PBackgroundSharedTypes;
@@ -500,16 +501,17 @@ prio(normal upto urgent) sync protocol P
     manages PTelephony;
     manages PTestShell;
     manages PVoicemail;
     manages PJavaScript;
     manages PRemoteSpellcheckEngine;
     manages PWebBrowserPersistDocument;
     manages PWebrtcGlobal;
     manages PPresentation;
+    manages PTV;
 
 both:
     // Depending on exactly how the new browser is being created, it might be
     // created from either the child or parent process!
     //
     // The child creates the PBrowser as part of
     // TabChild::BrowserFrameProvideWindow (which happens when the child's
     // content calls window.open()), and the parent creates the PBrowser as part
@@ -880,16 +882,18 @@ parent:
     async PBluetooth();
 
     async PFMRadio();
 
     async PWebrtcGlobal();
 
     async PPresentation();
 
+    async PTV();
+
     // Services remoting
 
     async StartVisitedQuery(URIParams uri);
     async VisitURI(URIParams uri, OptionalURIParams referrer, uint32_t flags);
     async SetURITitle(URIParams uri, nsString title);
 
     async LoadURIExternal(URIParams uri, PBrowser windowContext);
 
--- a/dom/tv/TVServiceFactory.cpp
+++ b/dom/tv/TVServiceFactory.cpp
@@ -1,14 +1,15 @@
 /* -*- 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 "ipc/TVIPCService.h"
 #include "nsITVService.h"
 #include "nsITVSimulatorService.h"
 #include "nsServiceManagerUtils.h"
 #include "TVServiceFactory.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "gonk/TVGonkService.h"
 #endif
@@ -16,16 +17,21 @@
 namespace mozilla {
 namespace dom {
 
 /* static */ already_AddRefed<nsITVService>
 TVServiceFactory::AutoCreateTVService()
 {
   nsCOMPtr<nsITVService> service;
 
+  if (GeckoProcessType_Default != XRE_GetProcessType()) {
+    service = new TVIPCService();
+    return service.forget();
+  }
+
 #ifdef MOZ_WIDGET_GONK
   service = new TVGonkService();
 #endif
 
   if (service == nullptr) {
     // Fallback to TV simulator service, especially for TV simulator on WebIDE.
     service = do_CreateInstance(TV_SIMULATOR_SERVICE_CONTRACTID);
   }
--- a/dom/tv/TVTypes.cpp
+++ b/dom/tv/TVTypes.cpp
@@ -76,18 +76,32 @@ TVTunerData::GetSupportedSourceTypes(uin
     sourceTypes[i] = NS_strdup(mSupportedSourceTypes[i]);
   }
 
   *aSourceTypes = sourceTypes;
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
-TVTunerData::SetSupportedSourceTypes(uint32_t aCount,
-                                     const char** aSourceTypes)
+TVTunerData::GetSupportedSourceTypesByString(uint32_t* aCount,
+                                             nsTArray<nsCString>& aSourceTypes)
+{
+  *aCount = mCount;
+
+  for (uint32_t i = 0; i < mCount; i++) {
+    nsCString sourceType;
+    sourceType.AssignASCII(mSupportedSourceTypes[i]);
+    aSourceTypes.AppendElement(sourceType);
+  }
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVTunerData::SetSupportedSourceTypes(uint32_t aCount, const char** aSourceTypes)
 {
   if (aCount == 0) {
     return NS_ERROR_INVALID_ARG;
   }
   NS_ENSURE_ARG_POINTER(aSourceTypes);
 
   for (uint32_t i = 0; i < aCount; i++) {
     if (TVSourceType::EndGuard_ == ToTVSourceType(aSourceTypes[i])) {
@@ -106,16 +120,47 @@ TVTunerData::SetSupportedSourceTypes(uin
                           nullptr;
   for (uint32_t i = 0; i < mCount; i++) {
     mSupportedSourceTypes[i] = NS_strdup(aSourceTypes[i]);
   }
 
   return NS_OK;
 }
 
+/* virtual */ NS_IMETHODIMP
+TVTunerData::SetSupportedSourceTypesByString(
+  uint32_t aCount, const nsTArray<nsCString>& aSourceTypes)
+{
+  if (aCount == 0) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  for (uint32_t i = 0; i < aCount; i++) {
+    if (TVSourceType::EndGuard_ ==
+        ToTVSourceType(NS_ConvertUTF8toUTF16(aSourceTypes[i]))) {
+
+      return NS_ERROR_INVALID_ARG;
+    }
+  }
+
+  if (mSupportedSourceTypes) {
+    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mSupportedSourceTypes);
+  }
+
+  mCount = aCount;
+
+  mSupportedSourceTypes =
+    (mCount > 0) ? static_cast<char**>(moz_xmalloc(mCount * sizeof(char*)))
+                 : nullptr;
+  for (uint32_t i = 0; i < mCount; i++) {
+    mSupportedSourceTypes[i] = NS_strdup(aSourceTypes[i].get());
+  }
+
+  return NS_OK;
+}
 
 /*
  * Implementation of TVChannelData
  */
 
 NS_IMPL_ISUPPORTS(TVChannelData, nsITVChannelData)
 
 TVChannelData::TVChannelData()
@@ -400,18 +445,32 @@ TVProgramData::GetAudioLanguages(uint32_
     languages[i] = NS_strdup(mAudioLanguages[i]);
   }
 
   *aLanguages = languages;
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
-TVProgramData::SetAudioLanguages(uint32_t aCount,
-                                 const char** aLanguages)
+TVProgramData::GetAudioLanguagesByString(uint32_t* aCount,
+                                         nsTArray<nsCString>& aLanguages)
+{
+  *aCount = mAudioLanguageCount;
+
+  for (uint32_t i = 0; i < mAudioLanguageCount; i++) {
+    nsCString languages;
+    languages.AssignASCII(mAudioLanguages[i]);
+    aLanguages.AppendElement(languages);
+  }
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVProgramData::SetAudioLanguages(uint32_t aCount, const char** aLanguages)
 {
   if (aCount > 0) {
     NS_ENSURE_ARG_POINTER(aLanguages);
   }
 
   if (mAudioLanguages) {
     NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mAudioLanguageCount, mAudioLanguages);
   }
@@ -424,56 +483,108 @@ TVProgramData::SetAudioLanguages(uint32_
   for (uint32_t i = 0; i < mAudioLanguageCount; i++) {
     mAudioLanguages[i] = NS_strdup(aLanguages[i]);
   }
 
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
-TVProgramData::GetSubtitleLanguages(uint32_t* aCount,
-                                    char*** aLanguages)
+TVProgramData::SetAudioLanguagesByString(uint32_t aCount,
+                                         const nsTArray<nsCString>& aLanguages)
+{
+  if (mAudioLanguages) {
+    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mAudioLanguageCount, mAudioLanguages);
+  }
+
+  mAudioLanguageCount = aCount;
+
+  mAudioLanguages =
+    (mAudioLanguageCount > 0)
+      ? static_cast<char**>(moz_xmalloc(mAudioLanguageCount * sizeof(char*)))
+      : nullptr;
+  for (uint32_t i = 0; i < mAudioLanguageCount; i++) {
+    mAudioLanguages[i] = NS_strdup(aLanguages[i].get());
+  }
+
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVProgramData::GetSubtitleLanguages(uint32_t* aCount, char*** aLanguages)
 {
   *aCount = mSubtitleLanguageCount;
 
   char** languages = (mSubtitleLanguageCount > 0) ?
                      static_cast<char **>(moz_xmalloc(mSubtitleLanguageCount * sizeof(char*))) :
                      nullptr;
   for (uint32_t i = 0; i < mSubtitleLanguageCount; i++) {
     languages[i] = NS_strdup(mSubtitleLanguages[i]);
   }
 
   *aLanguages = languages;
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
-TVProgramData::SetSubtitleLanguages(uint32_t aCount,
-                                    const char** aLanguages)
+TVProgramData::GetSubtitleLanguagesByString(uint32_t* aCount,
+                                            nsTArray<nsCString>& aLanguages)
 {
-  if (aCount > 0) {
-    NS_ENSURE_ARG_POINTER(aLanguages);
+  *aCount = mSubtitleLanguageCount;
+
+  for (uint32_t i = 0; i < mSubtitleLanguageCount; i++) {
+    nsCString languages;
+    languages.AssignASCII(mSubtitleLanguages[i]);
+    aLanguages.AppendElement(languages);
   }
 
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVProgramData::SetSubtitleLanguages(uint32_t aCount, const char** aLanguages)
+{
   if (mSubtitleLanguages) {
     NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mSubtitleLanguageCount, mSubtitleLanguages);
   }
 
   mSubtitleLanguageCount = aCount;
 
   mSubtitleLanguages = (mSubtitleLanguageCount > 0) ?
                        static_cast<char **>(moz_xmalloc(mSubtitleLanguageCount * sizeof(char*))) :
                        nullptr;
   for (uint32_t i = 0; i < mSubtitleLanguageCount; i++) {
     mSubtitleLanguages[i] = NS_strdup(aLanguages[i]);
   }
 
   return NS_OK;
 }
 
+/* virtual */ NS_IMETHODIMP
+TVProgramData::SetSubtitleLanguagesByString(
+  uint32_t aCount, const nsTArray<nsCString>& aLanguages)
+{
+  if (mSubtitleLanguages) {
+    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mSubtitleLanguageCount,
+                                          mSubtitleLanguages);
+  }
+
+  mSubtitleLanguageCount = aCount;
+
+  mSubtitleLanguages =
+    (mSubtitleLanguageCount > 0)
+      ? static_cast<char**>(moz_xmalloc(mSubtitleLanguageCount * sizeof(char*)))
+      : nullptr;
+  for (uint32_t i = 0; i < mSubtitleLanguageCount; i++) {
+    mSubtitleLanguages[i] = NS_strdup(aLanguages[i].get());
+  }
+
+  return NS_OK;
+}
+
 /*
  * Implementation of TVGonkNativeHandleData
  */
 
 NS_IMPL_ISUPPORTS(TVGonkNativeHandleData, nsITVGonkNativeHandleData)
 
 TVGonkNativeHandleData::TVGonkNativeHandleData() {}
 
--- a/dom/tv/TVTypes.h
+++ b/dom/tv/TVTypes.h
@@ -5,16 +5,17 @@
  * 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"
+#include "mozilla/dom/PTVTypes.h"
 
 namespace mozilla {
 namespace dom {
 
 typedef Tuple<nsString, nsString, nsCOMPtr<nsITVSourceListener>>
 TVSourceListenerTuple;
 
 using mozilla::layers::GonkNativeHandle;
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/PTV.ipdl
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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 protocol PContent;
+include protocol PTVRequest;
+include PTVTypes;
+
+namespace mozilla {
+namespace dom {
+
+struct TVGetTunersRequest {
+};
+
+struct TVSetSourceRequest {
+  nsString tunerId;
+  nsString sourceType;
+};
+
+struct TVStartScanningChannelsRequest {
+  nsString tunerId;
+  nsString sourceType;
+};
+
+struct TVStopScanningChannelsRequest {
+  nsString tunerId;
+  nsString sourceType;
+};
+
+struct TVClearScannedChannelsCacheRequest {
+};
+
+struct TVSetChannelRequest {
+  nsString tunerId;
+  nsString sourceType;
+  nsString channelNumber;
+};
+
+struct TVGetChannelsRequest {
+  nsString tunerId;
+  nsString sourceType;
+};
+
+struct TVGetProgramsRequest {
+  nsString tunerId;
+  nsString sourceType;
+  nsString channelNumber;
+  uint64_t startTime;
+  uint64_t endTime;
+};
+
+union TVIPCRequest {
+  TVGetTunersRequest;
+  TVSetSourceRequest;
+  TVStartScanningChannelsRequest;
+  TVStopScanningChannelsRequest;
+  TVClearScannedChannelsCacheRequest;
+  TVSetChannelRequest;
+  TVGetChannelsRequest;
+  TVGetProgramsRequest;
+};
+
+protocol PTV {
+  manager PContent;
+  manages PTVRequest;
+
+child:
+  async NotifyChannelScanned(nsString tunerId, nsString sourceType,
+                             TVIPCChannelData channelData);
+  async NotifyChannelScanComplete(nsString tunerId, nsString sourceType);
+  async NotifyChannelScanStopped(nsString tunerId, nsString sourceType);
+  async NotifyEITBroadcasted(nsString tunerId, nsString sourceType,
+                             TVIPCChannelData channelData,
+                             TVIPCProgramData[] programData);
+
+parent:
+  async __delete__();
+
+  async PTVRequest(TVIPCRequest aRequest);
+
+  async RegisterSourceHandler(nsString tunerId, nsString sourceType);
+  async UnregisterSourceHandler(nsString tunerId, nsString sourceType);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/PTVRequest.ipdl
@@ -0,0 +1,60 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PTV;
+include PTVTypes;
+
+namespace mozilla {
+namespace dom {
+
+
+struct TVSuccessResponse {
+};
+
+struct TVErrorResponse {
+  uint16_t errorCode;
+};
+
+struct TVGetTunersResponse {
+  TVIPCTunerData[] tuners;
+};
+
+struct TVSetSourceResponse {
+  TVIPCGonkNativeHandleData streamHandle;
+};
+
+struct TVSetChannelResponse {
+  TVIPCChannelData channel;
+};
+
+struct TVGetChannelsResponse {
+  TVIPCChannelData[] channels;
+};
+
+struct TVGetProgramsResponse {
+  TVIPCProgramData[] programs;
+};
+
+union TVIPCResponse {
+  TVSuccessResponse;
+  TVErrorResponse;
+  TVGetTunersResponse;
+  TVSetSourceResponse;
+  TVSetChannelResponse;
+  TVGetChannelsResponse;
+  TVGetProgramsResponse;
+};
+
+protocol PTVRequest
+{
+  manager PTV;
+
+child:
+  async __delete__(TVIPCResponse aResponse);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/PTVTypes.ipdlh
@@ -0,0 +1,42 @@
+using struct mozilla::layers::GonkNativeHandle from "mozilla/layers/GonkNativeHandleUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+struct TVIPCTunerData {
+  nsString id;
+  uint16_t streamType;
+  uint32_t sourceTypeCount;
+  nsCString[] sourceTypes;
+};
+
+struct TVIPCChannelData {
+  nsCString networkId;
+  nsCString transportStreamId;
+  nsCString serviceId;
+  nsCString type;
+  nsCString number;
+  nsCString name;
+  bool isEmergency;
+  bool isFree;
+};
+
+struct TVIPCProgramData {
+  nsCString eventId;
+  nsCString title;
+  uint64_t startTime;
+  uint64_t duration;
+  nsCString description;
+  nsCString rating;
+  uint32_t audioLanguageCount;
+  nsCString[] audioLanguages;
+  uint32_t subtitleLanguageCount;
+  nsCString[] subtitleLanguages;
+};
+
+struct TVIPCGonkNativeHandleData {
+  GonkNativeHandle handle;
+};
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/TVChild.cpp
@@ -0,0 +1,323 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMutableArray.h"
+#include "TVChild.h"
+#include "TVTuner.h"
+#include "TVIPCService.h"
+#include "TVIPCHelper.h"
+#include "TVServiceRunnables.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+/*
+ * Implementation of TVChild
+ */
+
+TVChild::TVChild(TVIPCService* aService)
+  : mActorDestroyed(false), mService(aService)
+{
+  MOZ_ASSERT(mService);
+
+  MOZ_COUNT_CTOR(TVChild);
+}
+
+TVChild::~TVChild()
+{
+  MOZ_COUNT_DTOR(TVChild);
+
+  if (!mActorDestroyed) {
+    Send__delete__(this);
+  }
+  mService = nullptr;
+}
+
+/* virtual */ void
+TVChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mActorDestroyed = true;
+  mService = nullptr;
+}
+
+/* virtual */ PTVRequestChild*
+TVChild::AllocPTVRequestChild(const TVIPCRequest& aRequest)
+{
+  NS_NOTREACHED(
+    "We should never be manually allocating PTVRequestChild actors");
+  return nullptr;
+}
+
+/* virtual */ bool
+TVChild::DeallocPTVRequestChild(PTVRequestChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+/* virtual */ bool
+TVChild::RecvNotifyChannelScanned(const nsString& aTunerId,
+                                  const nsString& aSourceType,
+                                  const TVIPCChannelData& aChannelData)
+{
+  if (mService) {
+    nsCOMPtr<nsITVChannelData> channelData;
+    channelData = do_CreateInstance(TV_CHANNEL_DATA_CONTRACTID);
+    UnpackTVIPCChannelData(channelData, aChannelData);
+    NS_WARN_IF(NS_FAILED(
+      mService->NotifyChannelScanned(aTunerId, aSourceType, channelData)));
+  }
+  return true;
+}
+
+/* virtual */ bool
+TVChild::RecvNotifyChannelScanComplete(const nsString& aTunerId,
+                                       const nsString& aSourceType)
+{
+  if (mService) {
+    NS_WARN_IF(
+      NS_FAILED(mService->NotifyChannelScanComplete(aTunerId, aSourceType)));
+  }
+  return true;
+}
+
+/* virtual */ bool
+TVChild::RecvNotifyChannelScanStopped(const nsString& aTunerId,
+                                      const nsString& aSourceType)
+{
+  if (mService) {
+    NS_WARN_IF(
+      NS_FAILED(mService->NotifyChannelScanStopped(aTunerId, aSourceType)));
+  }
+  return true;
+}
+
+/* virtual */ bool
+TVChild::RecvNotifyEITBroadcasted(const nsString& aTunerId,
+                                  const nsString& aSourceType,
+                                  const TVIPCChannelData& aChannelData,
+                                  nsTArray<TVIPCProgramData>&& aProgramDataList)
+{
+  if (mService) {
+    nsCOMPtr<nsITVChannelData> channelData;
+    channelData = do_CreateInstance(TV_CHANNEL_DATA_CONTRACTID);
+    UnpackTVIPCChannelData(channelData, aChannelData);
+
+    uint32_t count = aProgramDataList.Length();
+    nsTArray<nsITVProgramData*> programDataList(count);
+    for (uint32_t i = 0; i < count; i++) {
+      nsCOMPtr<nsITVProgramData> programData;
+      programData = do_CreateInstance(TV_PROGRAM_DATA_CONTRACTID);
+      UnpackTVIPCProgramData(programData, aProgramDataList[i]);
+      programDataList.AppendElement(programData);
+    }
+
+    nsresult rv = mService->NotifyEITBroadcasted(
+      aTunerId, aSourceType, channelData, programDataList.Elements(), count);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+  return true;
+}
+
+/*
+ * Implementation of TVRequestChild
+ */
+
+TVRequestChild::TVRequestChild(nsITVServiceCallback* aCallback)
+  : mActorDestroyed(false), mCallback(aCallback)
+{
+  MOZ_COUNT_CTOR(TVRequestChild);
+}
+
+TVRequestChild::~TVRequestChild()
+{
+  MOZ_COUNT_DTOR(TVRequestChild);
+
+  mCallback = nullptr;
+}
+
+/* virtual */ void
+TVRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mActorDestroyed = true;
+  mCallback = nullptr;
+}
+
+/* virtual */ bool
+TVRequestChild::Recv__delete__(const TVIPCResponse& aResponse)
+{
+  if (mActorDestroyed) {
+    return true;
+  }
+
+  if (!mCallback) {
+    return true;
+  }
+
+  nsresult rv = NS_ERROR_FAILURE;
+  switch (aResponse.type()) {
+    case TVIPCResponse::TTVSuccessResponse:
+      rv = DoResponse(aResponse.get_TVSuccessResponse());
+      break;
+    case TVIPCResponse::TTVErrorResponse:
+      rv = DoResponse(aResponse.get_TVErrorResponse());
+      break;
+    case TVIPCResponse::TTVGetTunersResponse:
+      rv = DoResponse(aResponse.get_TVGetTunersResponse());
+      break;
+    case TVIPCResponse::TTVSetSourceResponse:
+      rv = DoResponse(aResponse.get_TVSetSourceResponse());
+      break;
+    case TVIPCResponse::TTVSetChannelResponse:
+      rv = DoResponse(aResponse.get_TVSetChannelResponse());
+      break;
+    case TVIPCResponse::TTVGetChannelsResponse:
+      rv = DoResponse(aResponse.get_TVGetChannelsResponse());
+      break;
+    case TVIPCResponse::TTVGetProgramsResponse:
+      rv = DoResponse(aResponse.get_TVGetProgramsResponse());
+      break;
+    default:
+      MOZ_CRASH("Unknown TVIPCResponse type");
+  }
+
+  return NS_WARN_IF(NS_FAILED(rv)) ? false : true;
+}
+
+nsresult
+TVRequestChild::DoResponse(const TVSuccessResponse& aResponse)
+{
+  if (mCallback) {
+    return mCallback->NotifySuccess(nullptr);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+TVRequestChild::DoResponse(const TVErrorResponse& aResponse)
+{
+  if (mCallback) {
+    return mCallback->NotifyError(aResponse.errorCode());
+  }
+
+  return NS_OK;
+}
+
+nsresult
+TVRequestChild::DoResponse(const TVGetTunersResponse& aResponse)
+{
+  MOZ_ASSERT(mCallback);
+
+  nsCOMPtr<nsIMutableArray> tunerDataList =
+    do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!tunerDataList) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  const nsTArray<TVIPCTunerData>& tuners = aResponse.tuners();
+  for (uint32_t i = 0; i < tuners.Length(); i++) {
+    nsCOMPtr<nsITVTunerData> tunerData;
+    tunerData = do_CreateInstance(TV_TUNER_DATA_CONTRACTID);
+    UnpackTVIPCTunerData(tunerData, tuners[i]);
+    tunerDataList->AppendElement(tunerData, false);
+  }
+
+  nsCOMPtr<nsIRunnable> runnable =
+    new TVServiceNotifyRunnable(mCallback, tunerDataList);
+  return NS_DispatchToCurrentThread(runnable);
+}
+
+nsresult
+TVRequestChild::DoResponse(const TVSetSourceResponse& aResponse)
+{
+  MOZ_ASSERT(mCallback);
+
+  nsCOMPtr<nsIMutableArray> handleDataList =
+    do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!handleDataList) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsCOMPtr<nsITVGonkNativeHandleData> handleData;
+  handleData = do_CreateInstance(TV_GONK_NATIVE_HANDLE_DATA_CONTRACTID);
+  UnpackTVIPCGonkNativeHandleData(handleData, aResponse.streamHandle());
+  handleDataList->AppendElement(handleData, false);
+
+  nsCOMPtr<nsIRunnable> runnable =
+    new TVServiceNotifyRunnable(mCallback, handleDataList);
+  return NS_DispatchToCurrentThread(runnable);
+}
+
+nsresult
+TVRequestChild::DoResponse(const TVSetChannelResponse& aResponse)
+{
+  MOZ_ASSERT(mCallback);
+
+  nsCOMPtr<nsIMutableArray> channelDataList =
+    do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!channelDataList) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsCOMPtr<nsITVChannelData> channelData;
+  channelData = do_CreateInstance(TV_CHANNEL_DATA_CONTRACTID);
+  UnpackTVIPCChannelData(channelData, aResponse.channel());
+  channelDataList->AppendElement(channelData, false);
+
+  nsCOMPtr<nsIRunnable> runnable =
+    new TVServiceNotifyRunnable(mCallback, channelDataList);
+  return NS_DispatchToCurrentThread(runnable);
+}
+
+nsresult
+TVRequestChild::DoResponse(const TVGetChannelsResponse& aResponse)
+{
+  MOZ_ASSERT(mCallback);
+
+  nsCOMPtr<nsIMutableArray> channelDataList =
+    do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!channelDataList) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  const nsTArray<TVIPCChannelData>& channels = aResponse.channels();
+  for (uint32_t i = 0; i < channels.Length(); i++) {
+    nsCOMPtr<nsITVChannelData> channelData;
+    channelData = do_CreateInstance(TV_CHANNEL_DATA_CONTRACTID);
+    UnpackTVIPCChannelData(channelData, channels[i]);
+
+    channelDataList->AppendElement(channelData, false);
+  }
+
+  nsCOMPtr<nsIRunnable> runnable =
+    new TVServiceNotifyRunnable(mCallback, channelDataList);
+  return NS_DispatchToCurrentThread(runnable);
+}
+
+nsresult
+TVRequestChild::DoResponse(const TVGetProgramsResponse& aResponse)
+{
+  MOZ_ASSERT(mCallback);
+
+  nsCOMPtr<nsIMutableArray> programDataList =
+    do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!programDataList) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  const nsTArray<TVIPCProgramData>& programs = aResponse.programs();
+  for (uint32_t i = 0; i < programs.Length(); i++) {
+    nsCOMPtr<nsITVProgramData> programData;
+    programData = do_CreateInstance(TV_PROGRAM_DATA_CONTRACTID);
+    UnpackTVIPCProgramData(programData, programs[i]);
+    programDataList->AppendElement(programData, false);
+  }
+
+  nsCOMPtr<nsIRunnable> runnable =
+    new TVServiceNotifyRunnable(mCallback, programDataList);
+  return NS_DispatchToCurrentThread(runnable);
+}
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/TVChild.h
@@ -0,0 +1,88 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TVChild_h
+#define mozilla_dom_TVChild_h
+
+#include "mozilla/dom/PTVChild.h"
+#include "mozilla/dom/PTVRequestChild.h"
+#include "mozilla/dom/PTVTypes.h"
+
+class nsITVServiceCallback;
+
+namespace mozilla {
+namespace dom {
+
+class TVIPCService;
+
+class TVChild final : public PTVChild
+{
+public:
+  explicit TVChild(TVIPCService* aService);
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual PTVRequestChild* AllocPTVRequestChild(const TVIPCRequest& aRequest)
+    override;
+
+  virtual bool DeallocPTVRequestChild(PTVRequestChild* aActor) override;
+
+  virtual bool RecvNotifyChannelScanned(
+    const nsString& aTunerId, const nsString& aSourceType,
+    const TVIPCChannelData& aChannelData) override;
+
+  virtual bool RecvNotifyChannelScanComplete(
+    const nsString& aTunerId, const nsString& aSourceType) override;
+
+  virtual bool RecvNotifyChannelScanStopped(
+    const nsString& aTunerId, const nsString& aSourceType) override;
+
+  virtual bool RecvNotifyEITBroadcasted(
+    const nsString& aTunerId, const nsString& aSourceType,
+    const TVIPCChannelData& aChannelData,
+    nsTArray<TVIPCProgramData>&& aProgramDataList) override;
+
+private:
+  ~TVChild();
+
+  bool mActorDestroyed;
+  RefPtr<TVIPCService> mService;
+};
+
+class TVRequestChild final : public PTVRequestChild
+{
+public:
+  explicit TVRequestChild(nsITVServiceCallback* aCallback);
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual bool Recv__delete__(const TVIPCResponse& aResponse) override;
+
+private:
+  virtual ~TVRequestChild();
+
+  nsresult DoResponse(const TVSuccessResponse& aResponse);
+
+  nsresult DoResponse(const TVErrorResponse& aResponse);
+
+  nsresult DoResponse(const TVGetTunersResponse& aResponse);
+
+  nsresult DoResponse(const TVSetSourceResponse& aResponse);
+
+  nsresult DoResponse(const TVSetChannelResponse& aResponse);
+
+  nsresult DoResponse(const TVGetChannelsResponse& aResponse);
+
+  nsresult DoResponse(const TVGetProgramsResponse& aResponse);
+
+  bool mActorDestroyed;
+  nsCOMPtr<nsITVServiceCallback> mCallback;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVChild_h
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/TVIPCHelper.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TVIPCHelper.h"
+#include "nsITVService.h"
+#include "mozilla/dom/PTVTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * TVTunerData Pack/Unpack.
+ */
+void
+PackTVIPCTunerData(nsITVTunerData* aTunerData, TVIPCTunerData& aIPCTunerData)
+{
+  nsAutoString id;
+  uint16_t streamType;
+  uint32_t sourceTypeCount;
+  nsTArray<nsCString> sourceTypes;
+
+  if (aTunerData == nullptr) {
+    return;
+  }
+
+  aTunerData->GetId(aIPCTunerData.id());
+  aTunerData->GetStreamType(&streamType);
+  aIPCTunerData.streamType() = streamType;
+  aTunerData->GetSupportedSourceTypesByString(&sourceTypeCount, sourceTypes);
+  aIPCTunerData.sourceTypeCount() = sourceTypeCount;
+  aIPCTunerData.sourceTypes() = sourceTypes;
+  return;
+}
+
+void
+UnpackTVIPCTunerData(nsITVTunerData* aTunerData,
+                     const TVIPCTunerData& aIPCTunerData)
+{
+  if (aTunerData == nullptr) {
+    return;
+  }
+
+  aTunerData->SetId(aIPCTunerData.id());
+  aTunerData->SetStreamType(aIPCTunerData.streamType());
+  aTunerData->SetSupportedSourceTypesByString(aIPCTunerData.sourceTypeCount(),
+                                              aIPCTunerData.sourceTypes());
+  return;
+}
+
+/*
+ * TVChannelData Pack/Unpack.
+ */
+void
+PackTVIPCChannelData(nsITVChannelData* aChannelData,
+                     TVIPCChannelData& aIPCChannelData)
+{
+  nsString networkId;
+  nsString transportStreamId;
+  nsString serviceId;
+  nsString type;
+  nsString number;
+  nsString name;
+
+  if (aChannelData == nullptr) {
+    return;
+  }
+
+  aChannelData->GetNetworkId(networkId);
+  aIPCChannelData.networkId() = NS_ConvertUTF16toUTF8(networkId);
+  aChannelData->GetTransportStreamId(transportStreamId);
+  aIPCChannelData.transportStreamId() =
+    NS_ConvertUTF16toUTF8(transportStreamId);
+  aChannelData->GetServiceId(serviceId);
+  aIPCChannelData.serviceId() = NS_ConvertUTF16toUTF8(serviceId);
+  aChannelData->GetType(type);
+  aIPCChannelData.type() = NS_ConvertUTF16toUTF8(type);
+  aChannelData->GetNumber(number);
+  aIPCChannelData.number() = NS_ConvertUTF16toUTF8(number);
+  aChannelData->GetName(name);
+  aIPCChannelData.name() = NS_ConvertUTF16toUTF8(name);
+  aChannelData->GetIsEmergency(&aIPCChannelData.isEmergency());
+  aChannelData->GetIsFree(&aIPCChannelData.isFree());
+  return;
+}
+
+void
+UnpackTVIPCChannelData(nsITVChannelData* aChannelData,
+                       const TVIPCChannelData& aIPCChannelData)
+{
+  if (aChannelData == nullptr) {
+    return;
+  }
+
+  aChannelData->SetNetworkId(
+    NS_ConvertUTF8toUTF16(aIPCChannelData.networkId()));
+  aChannelData->SetTransportStreamId(
+    NS_ConvertUTF8toUTF16(aIPCChannelData.transportStreamId()));
+  aChannelData->SetServiceId(
+    NS_ConvertUTF8toUTF16(aIPCChannelData.serviceId()));
+  aChannelData->SetType(NS_ConvertUTF8toUTF16(aIPCChannelData.type()));
+  aChannelData->SetNumber(NS_ConvertUTF8toUTF16(aIPCChannelData.number()));
+  aChannelData->SetName(NS_ConvertUTF8toUTF16(aIPCChannelData.name()));
+  aChannelData->SetIsEmergency(aIPCChannelData.isEmergency());
+  aChannelData->SetIsFree(aIPCChannelData.isFree());
+
+  return;
+}
+
+/*
+ * TVProgramData Pack/Unpack.
+ */
+void
+PackTVIPCProgramData(nsITVProgramData* aProgramData,
+                     TVIPCProgramData& aIPCProgramData)
+{
+  nsString eventId;
+  nsString title;
+  uint64_t startTime;
+  uint64_t duration;
+  nsString description;
+  nsString rating;
+  uint32_t audioLanguageCount;
+  nsTArray<nsCString> audioLanguages;
+  uint32_t subtitleLanguageCount;
+  nsTArray<nsCString> subtitleLanguages;
+
+  if (aProgramData == nullptr) {
+    return;
+  }
+
+  aProgramData->GetEventId(eventId);
+  aIPCProgramData.eventId() = NS_ConvertUTF16toUTF8(eventId);
+
+  aProgramData->GetTitle(title);
+  aIPCProgramData.title() = NS_ConvertUTF16toUTF8(title);
+
+  aProgramData->GetStartTime(&startTime);
+  aIPCProgramData.startTime() = startTime;
+
+  aProgramData->GetDuration(&duration);
+  aIPCProgramData.duration() = duration;
+
+  aProgramData->GetDescription(description);
+  aIPCProgramData.description() = NS_ConvertUTF16toUTF8(description);
+
+  aProgramData->GetRating(rating);
+  aIPCProgramData.rating() = NS_ConvertUTF16toUTF8(rating);
+
+  aProgramData->GetAudioLanguagesByString(&audioLanguageCount, audioLanguages);
+  aIPCProgramData.audioLanguageCount() = audioLanguageCount;
+  aIPCProgramData.audioLanguages() = audioLanguages;
+
+  aProgramData->GetSubtitleLanguagesByString(&subtitleLanguageCount,
+                                             subtitleLanguages);
+  aIPCProgramData.subtitleLanguageCount() = subtitleLanguageCount;
+  aIPCProgramData.subtitleLanguages() = subtitleLanguages;
+
+  return;
+}
+
+void
+UnpackTVIPCProgramData(nsITVProgramData* aProgramData,
+                       const TVIPCProgramData& aIPCProgramData)
+{
+  if (aProgramData == nullptr) {
+    return;
+  }
+
+  aProgramData->SetEventId(NS_ConvertUTF8toUTF16(aIPCProgramData.eventId()));
+  aProgramData->SetTitle(NS_ConvertUTF8toUTF16(aIPCProgramData.title()));
+  aProgramData->SetStartTime(aIPCProgramData.startTime());
+  aProgramData->SetDuration(aIPCProgramData.duration());
+  aProgramData->SetDescription(
+    NS_ConvertUTF8toUTF16(aIPCProgramData.description()));
+  aProgramData->SetRating(NS_ConvertUTF8toUTF16(aIPCProgramData.rating()));
+  aProgramData->SetAudioLanguagesByString(aIPCProgramData.audioLanguageCount(),
+                                          aIPCProgramData.audioLanguages());
+  aProgramData->SetSubtitleLanguagesByString(
+    aIPCProgramData.subtitleLanguageCount(),
+    aIPCProgramData.subtitleLanguages());
+  return;
+}
+
+/**
+ * nsITVGonkNativeHandleData Serialize/De-serialize.
+ */
+void
+PackTVIPCGonkNativeHandleData(nsITVGonkNativeHandleData* aNativeHandleData,
+                              TVIPCGonkNativeHandleData& aIPCNativeHandleData)
+{
+  if (aNativeHandleData == nullptr) {
+    return;
+  }
+
+  aNativeHandleData->SetHandle(aIPCNativeHandleData.handle());
+  return;
+}
+
+void
+UnpackTVIPCGonkNativeHandleData(
+  nsITVGonkNativeHandleData* aNativeHandleData,
+  const TVIPCGonkNativeHandleData& aIPCNativeHandleData)
+{
+  if (aNativeHandleData == nullptr) {
+    return;
+  }
+
+  aNativeHandleData->SetHandle(
+    const_cast<GonkNativeHandle&>(aIPCNativeHandleData.handle()));
+  return;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/TVIPCHelper.h
@@ -0,0 +1,63 @@
+/* -*- 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_TVIPCHelper_h
+#define mozilla_dom_TVIPCHelper_h
+
+#include "nsITVService.h"
+#include "mozilla/dom/PTVTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * TVTunerData Pack/Unpack.
+ */
+void
+PackTVIPCTunerData(nsITVTunerData* aTunerData, TVIPCTunerData& aIPCTunerData);
+
+void
+UnpackTVIPCTunerData(nsITVTunerData* aTunerData,
+                     const TVIPCTunerData& aIPCTunerData);
+
+/*
+ * TVChannelData Pack/Unpack.
+ */
+void
+PackTVIPCChannelData(nsITVChannelData* aChannelData,
+                     TVIPCChannelData& aIPCChannelData);
+
+void
+UnpackTVIPCChannelData(nsITVChannelData* aChannelData,
+                       const TVIPCChannelData& aIPCChannelData);
+
+/*
+ * TVProgramData Pack/Unpack.
+ */
+void
+PackTVIPCProgramData(nsITVProgramData* aProgramData,
+                     TVIPCProgramData& aIPCProgramData);
+
+void
+UnpackTVIPCProgramData(nsITVProgramData* aProgramData,
+                       const TVIPCProgramData& aIPCProgramData);
+
+/**
+ * nsITVGonkNativeHandleData Serialize/De-serialize.
+ */
+void
+PackTVIPCGonkNativeHandleData(nsITVGonkNativeHandleData* aNativeHandleData,
+                              TVIPCGonkNativeHandleData& aIPCNativeHandleData);
+
+void
+UnpackTVIPCGonkNativeHandleData(
+  nsITVGonkNativeHandleData* aNativeHandleData,
+  const TVIPCGonkNativeHandleData& aIPCNativeHandleData);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVIPCHelper_h
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/TVIPCService.cpp
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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/ContentChild.h"
+#include "TVChild.h"
+#include "TVIPCService.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace
+{
+
+TVChild* sTVChild;
+
+} // anonymous
+
+NS_IMPL_ISUPPORTS(TVIPCService, nsITVService)
+
+TVIPCService::TVIPCService()
+{
+  ContentChild* contentChild = ContentChild::GetSingleton();
+  if (NS_WARN_IF(!contentChild)) {
+    return;
+  }
+  sTVChild = new TVChild(this);
+  NS_WARN_IF(!contentChild->SendPTVConstructor(sTVChild));
+}
+
+/* virtual */
+TVIPCService::~TVIPCService() { sTVChild = nullptr; }
+
+NS_IMETHODIMP
+TVIPCService::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));
+
+  if (sTVChild) {
+    NS_WARN_IF(!sTVChild->SendRegisterSourceHandler(nsAutoString(aTunerId),
+                                                    nsAutoString(aSourceType)));
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TVIPCService::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;
+    }
+  }
+
+  if (sTVChild) {
+    NS_WARN_IF(!sTVChild->SendUnregisterSourceHandler(
+                  nsAutoString(aTunerId), nsAutoString(aSourceType)));
+  }
+
+  return NS_OK;
+}
+
+void
+TVIPCService::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
+TVIPCService::GetTuners(nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aCallback) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return SendRequest(aCallback, TVGetTunersRequest());
+}
+
+/* virtual */ NS_IMETHODIMP
+TVIPCService::SetSource(const nsAString& aTunerId, const nsAString& aSourceType,
+                        nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aCallback) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return SendRequest(aCallback, TVSetSourceRequest(nsAutoString(aTunerId),
+                                                   nsAutoString(aSourceType)));
+}
+
+/* virtual */ NS_IMETHODIMP
+TVIPCService::StartScanningChannels(const nsAString& aTunerId,
+                                    const nsAString& aSourceType,
+                                    nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aCallback) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return SendRequest(aCallback,
+                     TVStartScanningChannelsRequest(nsAutoString(aTunerId),
+                                                    nsAutoString(aSourceType)));
+}
+
+/* virtual */ NS_IMETHODIMP
+TVIPCService::StopScanningChannels(const nsAString& aTunerId,
+                                   const nsAString& aSourceType,
+                                   nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aCallback) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return SendRequest(aCallback,
+                     TVStopScanningChannelsRequest(nsAutoString(aTunerId),
+                                                   nsAutoString(aSourceType)));
+}
+
+/* virtual */ NS_IMETHODIMP
+TVIPCService::ClearScannedChannelsCache()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return SendRequest(nullptr, TVClearScannedChannelsCacheRequest());
+}
+
+/* virtual */ NS_IMETHODIMP
+TVIPCService::SetChannel(const nsAString& aTunerId,
+                         const nsAString& aSourceType,
+                         const nsAString& aChannelNumber,
+                         nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aCallback) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return SendRequest(aCallback,
+                     TVSetChannelRequest(nsAutoString(aTunerId),
+                                         nsAutoString(aSourceType),
+                                         nsAutoString(aChannelNumber)));
+}
+
+/* virtual */ NS_IMETHODIMP
+TVIPCService::GetChannels(const nsAString& aTunerId,
+                          const nsAString& aSourceType,
+                          nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aCallback) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return SendRequest(
+    aCallback,
+    TVGetChannelsRequest(nsAutoString(aTunerId), nsAutoString(aSourceType)));
+}
+
+/* virtual */ NS_IMETHODIMP
+TVIPCService::GetPrograms(const nsAString& aTunerId,
+                          const nsAString& aSourceType,
+                          const nsAString& aChannelNumber, uint64_t startTime,
+                          uint64_t endTime, nsITVServiceCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aCallback) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return SendRequest(aCallback,
+                     TVGetProgramsRequest(nsAutoString(aTunerId),
+                                          nsAutoString(aSourceType),
+                                          nsAutoString(aChannelNumber),
+                                          startTime, endTime));
+}
+
+nsresult
+TVIPCService::SendRequest(nsITVServiceCallback* aCallback,
+                          const TVIPCRequest& aRequest)
+{
+  if (sTVChild) {
+    TVRequestChild* actor = new TVRequestChild(aCallback);
+    NS_WARN_IF(!sTVChild->SendPTVRequestConstructor(actor, aRequest));
+  }
+  return NS_OK;
+}
+
+nsresult
+TVIPCService::NotifyChannelScanned(const nsAString& aTunerId,
+                                   const nsAString& aSourceType,
+                                   nsITVChannelData* aChannelData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsTArray<nsCOMPtr<nsITVSourceListener>> listeners;
+  GetSourceListeners(aTunerId, aSourceType, listeners);
+  for (uint32_t i = 0; i < listeners.Length(); i++) {
+    nsresult rv =
+      listeners[i]->NotifyChannelScanned(aTunerId, aSourceType, aChannelData);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
+  return NS_OK;
+}
+
+nsresult
+TVIPCService::NotifyChannelScanComplete(const nsAString& aTunerId,
+                                        const nsAString& aSourceType)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsTArray<nsCOMPtr<nsITVSourceListener>> listeners;
+  GetSourceListeners(aTunerId, aSourceType, listeners);
+  for (uint32_t i = 0; i < listeners.Length(); i++) {
+    nsresult rv =
+      listeners[i]->NotifyChannelScanComplete(aTunerId, aSourceType);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
+  return NS_OK;
+}
+
+nsresult
+TVIPCService::NotifyChannelScanStopped(const nsAString& aTunerId,
+                                       const nsAString& aSourceType)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsTArray<nsCOMPtr<nsITVSourceListener>> listeners;
+  GetSourceListeners(aTunerId, aSourceType, listeners);
+  for (uint32_t i = 0; i < listeners.Length(); i++) {
+    nsresult rv = listeners[i]->NotifyChannelScanStopped(aTunerId, aSourceType);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
+  return NS_OK;
+}
+
+nsresult
+TVIPCService::NotifyEITBroadcasted(const nsAString& aTunerId,
+                                   const nsAString& aSourceType,
+                                   nsITVChannelData* aChannelData,
+                                   nsITVProgramData** aProgramDataList,
+                                   uint32_t aCount)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsTArray<nsCOMPtr<nsITVSourceListener>> listeners;
+  GetSourceListeners(aTunerId, aSourceType, listeners);
+  for (uint32_t i = 0; i < listeners.Length(); i++) {
+    nsresult rv = listeners[i]->NotifyEITBroadcasted(
+      aTunerId, aSourceType, aChannelData, aProgramDataList, aCount);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/TVIPCService.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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_TVIPCService_h
+#define mozilla_dom_TVIPCService_h
+
+#include "mozilla/Tuple.h"
+#include "nsITVService.h"
+#include "nsTArray.h"
+#include "TVTypes.h"
+
+namespace mozilla
+{
+namespace dom
+{
+
+class TVIPCRequest;
+
+class TVIPCService final : public nsITVService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITVSERVICE
+
+  TVIPCService();
+
+  nsresult NotifyChannelScanned(const nsAString& aTunerId,
+                                const nsAString& aSourceType,
+                                nsITVChannelData* aChannelData);
+
+  nsresult NotifyChannelScanComplete(const nsAString& aTunerId,
+                                     const nsAString& aSourceType);
+
+  nsresult NotifyChannelScanStopped(const nsAString& aTunerId,
+                                    const nsAString& aSourceType);
+
+  nsresult NotifyEITBroadcasted(const nsAString& aTunerId,
+                                const nsAString& aSourceType,
+                                nsITVChannelData* aChannelData,
+                                nsITVProgramData** aProgramDataList,
+                                uint32_t aCount);
+
+private:
+  virtual ~TVIPCService();
+
+  void GetSourceListeners(
+    const nsAString& aTunerId, const nsAString& aSourceType,
+    nsTArray<nsCOMPtr<nsITVSourceListener>>& aListeners) const;
+
+  nsresult SendRequest(nsITVServiceCallback* aCallback,
+                       const TVIPCRequest& aRequest);
+
+  nsTArray<UniquePtr<TVSourceListenerTuple>> mSourceListenerTuples;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVIPCService_h
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/TVParent.cpp
@@ -0,0 +1,600 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TVParent.h"
+#include "mozilla/unused.h"
+#include "TVTypes.h"
+#include "TVIPCHelper.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * Implementation of TVParent
+ */
+
+NS_IMPL_ISUPPORTS(TVParent, nsITVSourceListener)
+
+TVParent::TVParent() {}
+
+TVParent::~TVParent() {}
+
+bool
+TVParent::Init()
+{
+  MOZ_ASSERT(!mService);
+  mService = do_GetService(TV_SERVICE_CONTRACTID);
+  return NS_WARN_IF(!mService) ? false : true;
+}
+
+/* virtual */ void
+TVParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mService = nullptr;
+}
+
+/* virtual */ bool
+TVParent::RecvPTVRequestConstructor(PTVRequestParent* aActor,
+                                    const TVIPCRequest& aRequest)
+{
+  TVRequestParent* actor = static_cast<TVRequestParent*>(aActor);
+
+  nsresult rv = NS_ERROR_FAILURE;
+  switch (aRequest.type()) {
+    case TVIPCRequest::TTVGetTunersRequest:
+      rv = actor->DoRequest(aRequest.get_TVGetTunersRequest());
+      break;
+    case TVIPCRequest::TTVSetSourceRequest:
+      rv = actor->DoRequest(aRequest.get_TVSetSourceRequest());
+      break;
+    case TVIPCRequest::TTVStartScanningChannelsRequest:
+      rv = actor->DoRequest(aRequest.get_TVStartScanningChannelsRequest());
+      break;
+    case TVIPCRequest::TTVStopScanningChannelsRequest:
+      rv = actor->DoRequest(aRequest.get_TVStopScanningChannelsRequest());
+      break;
+    case TVIPCRequest::TTVClearScannedChannelsCacheRequest:
+      rv = actor->DoRequest(aRequest.get_TVClearScannedChannelsCacheRequest());
+      break;
+    case TVIPCRequest::TTVSetChannelRequest:
+      rv = actor->DoRequest(aRequest.get_TVSetChannelRequest());
+      break;
+    case TVIPCRequest::TTVGetChannelsRequest:
+      rv = actor->DoRequest(aRequest.get_TVGetChannelsRequest());
+      break;
+    case TVIPCRequest::TTVGetProgramsRequest:
+      rv = actor->DoRequest(aRequest.get_TVGetProgramsRequest());
+      break;
+    default:
+      MOZ_CRASH("Unknown TVIPCRequest type");
+  }
+
+  return !NS_WARN_IF(NS_FAILED(rv));
+}
+
+/* virtual */ PTVRequestParent*
+TVParent::AllocPTVRequestParent(const TVIPCRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  RefPtr<TVRequestParent> actor = new TVRequestParent(mService);
+  return actor.forget().take();
+}
+
+/* virtual */ bool
+TVParent::DeallocPTVRequestParent(PTVRequestParent* aActor)
+{
+  RefPtr<TVRequestParent> actor =
+    dont_AddRef(static_cast<TVRequestParent*>(aActor));
+  return true;
+}
+
+/* virtual */ bool
+TVParent::RecvRegisterSourceHandler(const nsString& aTunerId,
+                                    const nsString& aSourceType)
+{
+  MOZ_ASSERT(mService);
+  NS_WARN_IF(
+    NS_FAILED(mService->RegisterSourceListener(aTunerId, aSourceType, this)));
+  return true;
+}
+
+/* virtual */ bool
+TVParent::RecvUnregisterSourceHandler(const nsString& aTunerId,
+                                      const nsString& aSourceType)
+{
+  MOZ_ASSERT(mService);
+  NS_WARN_IF(
+    NS_FAILED(mService->UnregisterSourceListener(aTunerId, aSourceType, this)));
+  return true;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVParent::NotifyChannelScanned(const nsAString& aTunerId,
+                               const nsAString& aSourceType,
+                               nsITVChannelData* aChannelData)
+{
+  TVIPCChannelData channelData;
+  PackTVIPCChannelData(aChannelData, channelData);
+  if (NS_WARN_IF(!SendNotifyChannelScanned(nsAutoString(aTunerId),
+                                           nsAutoString(aSourceType),
+                                           channelData))) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVParent::NotifyChannelScanComplete(const nsAString& aTunerId,
+                                    const nsAString& aSourceType)
+{
+  if (NS_WARN_IF(!SendNotifyChannelScanComplete(nsAutoString(aTunerId),
+                                                nsAutoString(aSourceType)))) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVParent::NotifyChannelScanStopped(const nsAString& aTunerId,
+                                   const nsAString& aSourceType)
+{
+  if (NS_WARN_IF(!SendNotifyChannelScanStopped(nsAutoString(aTunerId),
+                                               nsAutoString(aSourceType)))) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVParent::NotifyEITBroadcasted(const nsAString& aTunerId,
+                               const nsAString& aSourceType,
+                               nsITVChannelData* aChannelData,
+                               nsITVProgramData** aProgramDataList,
+                               uint32_t aCount)
+{
+  TVIPCChannelData ipcChannelData;
+  nsTArray<TVIPCProgramData> programDataList;
+
+  PackTVIPCChannelData(aChannelData, ipcChannelData);
+  for (uint32_t idx = 0; idx < aCount; idx++) {
+    TVIPCProgramData ipcProgramData;
+    PackTVIPCProgramData(aProgramDataList[idx], ipcProgramData);
+    programDataList.AppendElement(ipcProgramData);
+  }
+
+  if (NS_WARN_IF(!SendNotifyEITBroadcasted(nsAutoString(aTunerId),
+                                           nsAutoString(aSourceType),
+                                           ipcChannelData, programDataList))) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+/*
+ * Implementation of TVRequestBaseCallback
+ */
+
+class TVRequestBaseCallback : public nsITVServiceCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITVSERVICECALLBACK
+
+  explicit TVRequestBaseCallback(TVRequestParent* aRequestParent)
+    : mRequestParent(aRequestParent)
+  {
+    MOZ_ASSERT(mRequestParent);
+  }
+
+protected:
+  virtual ~TVRequestBaseCallback() {}
+
+  RefPtr<TVRequestParent> mRequestParent;
+};
+
+NS_IMPL_ISUPPORTS(TVRequestBaseCallback, nsITVServiceCallback)
+
+/* virtual */ NS_IMETHODIMP
+TVRequestBaseCallback::NotifySuccess(nsIArray* aDataList)
+{
+  if (NS_WARN_IF(aDataList)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsresult rv;
+  rv = mRequestParent->SendResponse(TVSuccessResponse());
+  mRequestParent = nullptr;
+
+  return rv;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVRequestBaseCallback::NotifyError(uint16_t aErrorCode)
+{
+  nsresult rv;
+  rv = mRequestParent->SendResponse(TVErrorResponse(aErrorCode));
+  mRequestParent = nullptr;
+
+  return rv;
+}
+
+/*
+ * Implementation of TVRequestTunerGetterCallback
+ */
+
+class TVRequestTunerGetterCallback final : public TVRequestBaseCallback
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  explicit TVRequestTunerGetterCallback(TVRequestParent* aRequestParent)
+    : TVRequestBaseCallback(aRequestParent)
+  {
+  }
+
+  NS_IMETHOD NotifySuccess(nsIArray* aDataList) override;
+
+private:
+  ~TVRequestTunerGetterCallback() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(TVRequestTunerGetterCallback,
+                             TVRequestBaseCallback)
+
+/* virtual */ NS_IMETHODIMP
+TVRequestTunerGetterCallback::NotifySuccess(nsIArray* aDataList)
+{
+  if (NS_WARN_IF(!aDataList)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  uint32_t length;
+  nsresult rv = aDataList->GetLength(&length);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsTArray<TVIPCTunerData> tuners(length);
+  for (uint32_t i = 0; i < length; i++) {
+    nsCOMPtr<nsITVTunerData> tunerData = do_QueryElementAt(aDataList, i);
+    if (NS_WARN_IF(!tunerData)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    TVIPCTunerData ipcTunerData;
+    PackTVIPCTunerData(tunerData, ipcTunerData);
+
+    tuners.AppendElement(ipcTunerData);
+  }
+
+  rv = mRequestParent->SendResponse(TVGetTunersResponse(tuners));
+  mRequestParent = nullptr;
+
+  return rv;
+}
+
+/*
+ * Implementation of TVRequestSourceSetterCallback
+ */
+
+class TVRequestSourceSetterCallback final : public TVRequestBaseCallback
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  explicit TVRequestSourceSetterCallback(TVRequestParent* aRequestParent)
+    : TVRequestBaseCallback(aRequestParent)
+  {
+  }
+
+  NS_IMETHOD NotifySuccess(nsIArray* aDataList) override;
+
+private:
+  ~TVRequestSourceSetterCallback() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(TVRequestSourceSetterCallback,
+                             TVRequestBaseCallback)
+
+/* virtual */ NS_IMETHODIMP
+TVRequestSourceSetterCallback::NotifySuccess(nsIArray* aDataList)
+{
+  if (NS_WARN_IF(!aDataList)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  uint32_t length;
+  nsresult rv = aDataList->GetLength(&length);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (NS_WARN_IF(length != 1)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<nsITVGonkNativeHandleData> handleData =
+    do_QueryElementAt(aDataList, 0);
+  if (NS_WARN_IF(!handleData)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  TVIPCGonkNativeHandleData ipcHandleData;
+  PackTVIPCGonkNativeHandleData(handleData, ipcHandleData);
+  rv = mRequestParent->SendResponse(TVSetSourceResponse(ipcHandleData));
+  mRequestParent = nullptr;
+
+  return rv;
+}
+
+/*
+ * Implementation of TVRequestChannelSetterCallback
+ */
+
+class TVRequestChannelSetterCallback final : public TVRequestBaseCallback
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  explicit TVRequestChannelSetterCallback(TVRequestParent* aRequestParent)
+    : TVRequestBaseCallback(aRequestParent)
+  {
+  }
+
+  NS_IMETHOD NotifySuccess(nsIArray* aDataList) override;
+
+private:
+  ~TVRequestChannelSetterCallback() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(TVRequestChannelSetterCallback,
+                             TVRequestBaseCallback)
+
+/* virtual */ NS_IMETHODIMP
+TVRequestChannelSetterCallback::NotifySuccess(nsIArray* aDataList)
+{
+  if (NS_WARN_IF(!aDataList)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  uint32_t length;
+  nsresult rv = aDataList->GetLength(&length);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (NS_WARN_IF(length != 1)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<nsITVChannelData> channelData = do_QueryElementAt(aDataList, 0);
+  if (NS_WARN_IF(!channelData)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  TVIPCChannelData ipcChannelData;
+
+  PackTVIPCChannelData(channelData, ipcChannelData);
+  rv = mRequestParent->SendResponse(TVSetChannelResponse(ipcChannelData));
+  mRequestParent = nullptr;
+
+  return rv;
+}
+
+/*
+ * Implementation of TVRequestChannelGetterCallback
+ */
+
+class TVRequestChannelGetterCallback final : public TVRequestBaseCallback
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  explicit TVRequestChannelGetterCallback(TVRequestParent* aRequestParent)
+    : TVRequestBaseCallback(aRequestParent)
+  {
+  }
+
+  NS_IMETHOD NotifySuccess(nsIArray* aDataList) override;
+
+private:
+  ~TVRequestChannelGetterCallback() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(TVRequestChannelGetterCallback,
+                             TVRequestBaseCallback)
+
+/* virtual */ NS_IMETHODIMP
+TVRequestChannelGetterCallback::NotifySuccess(nsIArray* aDataList)
+{
+  if (NS_WARN_IF(!aDataList)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  uint32_t length;
+  nsresult rv = aDataList->GetLength(&length);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsTArray<TVIPCChannelData> channels(length);
+  for (uint32_t i = 0; i < length; i++) {
+    nsCOMPtr<nsITVChannelData> channelData = do_QueryElementAt(aDataList, i);
+    TVIPCChannelData ipcChannelData;
+
+    if (NS_WARN_IF(!channelData)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    PackTVIPCChannelData(channelData, ipcChannelData);
+    channels.AppendElement(ipcChannelData);
+  }
+
+  rv = mRequestParent->SendResponse(TVGetChannelsResponse(channels));
+  mRequestParent = nullptr;
+
+  return rv;
+}
+
+/*
+ * Implementation of TVRequestProgramGetterCallback
+ */
+
+class TVRequestProgramGetterCallback final : public TVRequestBaseCallback
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  explicit TVRequestProgramGetterCallback(TVRequestParent* aRequestParent)
+    : TVRequestBaseCallback(aRequestParent)
+  {
+  }
+
+  NS_IMETHOD NotifySuccess(nsIArray* aDataList) override;
+
+private:
+  ~TVRequestProgramGetterCallback() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(TVRequestProgramGetterCallback,
+                             TVRequestBaseCallback)
+
+/* virtual */ NS_IMETHODIMP
+TVRequestProgramGetterCallback::NotifySuccess(nsIArray* aDataList)
+{
+  if (NS_WARN_IF(!aDataList)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  uint32_t length;
+  nsresult rv = aDataList->GetLength(&length);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsTArray<TVIPCProgramData> programs(length);
+  for (uint32_t i = 0; i < length; i++) {
+    nsCOMPtr<nsITVProgramData> programData = do_QueryElementAt(aDataList, i);
+    if (NS_WARN_IF(!programData)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    TVIPCProgramData ipcProgramData;
+    PackTVIPCProgramData(programData, ipcProgramData);
+    programs.AppendElement(ipcProgramData);
+  }
+
+  rv = mRequestParent->SendResponse(TVGetProgramsResponse(programs));
+  mRequestParent = nullptr;
+
+  return rv;
+}
+
+/*
+ * Implementation of TVRequestParent
+ */
+
+NS_IMPL_ISUPPORTS0(TVRequestParent)
+
+TVRequestParent::TVRequestParent(nsITVService* aService) : mService(aService)
+{
+  MOZ_COUNT_CTOR(TVRequestParent);
+}
+
+TVRequestParent::~TVRequestParent()
+{
+  MOZ_COUNT_DTOR(TVRequestParent);
+
+  mService = nullptr;
+}
+
+/* virtual */ void
+TVRequestParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mService = nullptr;
+}
+
+nsresult
+TVRequestParent::DoRequest(const TVGetTunersRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  return mService->GetTuners(new TVRequestTunerGetterCallback(this));
+}
+
+nsresult
+TVRequestParent::DoRequest(const TVSetSourceRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  return mService->SetSource(aRequest.tunerId(), aRequest.sourceType(),
+                             new TVRequestSourceSetterCallback(this));
+}
+
+nsresult
+TVRequestParent::DoRequest(const TVStartScanningChannelsRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  return mService->StartScanningChannels(
+    aRequest.tunerId(), aRequest.sourceType(), new TVRequestBaseCallback(this));
+}
+
+nsresult
+TVRequestParent::DoRequest(const TVStopScanningChannelsRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  return mService->StopScanningChannels(
+    aRequest.tunerId(), aRequest.sourceType(), new TVRequestBaseCallback(this));
+}
+
+nsresult
+TVRequestParent::DoRequest(const TVClearScannedChannelsCacheRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+
+  nsresult rv = mService->ClearScannedChannelsCache();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return SendResponse(
+      TVErrorResponse(nsITVServiceCallback::TV_ERROR_FAILURE));
+  }
+
+  return SendResponse(TVSuccessResponse());
+}
+
+nsresult
+TVRequestParent::DoRequest(const TVSetChannelRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  return mService->SetChannel(aRequest.tunerId(), aRequest.sourceType(),
+                              aRequest.channelNumber(),
+                              new TVRequestChannelSetterCallback(this));
+}
+
+nsresult
+TVRequestParent::DoRequest(const TVGetChannelsRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  return mService->GetChannels(aRequest.tunerId(), aRequest.sourceType(),
+                               new TVRequestChannelGetterCallback(this));
+}
+
+nsresult
+TVRequestParent::DoRequest(const TVGetProgramsRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  return mService->GetPrograms(aRequest.tunerId(), aRequest.sourceType(),
+                               aRequest.channelNumber(), aRequest.startTime(),
+                               aRequest.endTime(),
+                               new TVRequestProgramGetterCallback(this));
+}
+
+nsresult
+TVRequestParent::SendResponse(const TVIPCResponse& aResponse)
+{
+  if (NS_WARN_IF(!Send__delete__(this, aResponse))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/ipc/TVParent.h
@@ -0,0 +1,88 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TVParent_h__
+#define mozilla_dom_TVParent_h__
+
+#include "mozilla/dom/PTVParent.h"
+#include "mozilla/dom/PTVRequestParent.h"
+#include "mozilla/dom/PTVTypes.h"
+#include "nsITVService.h"
+
+namespace mozilla {
+namespace dom {
+
+class TVParent final : public PTVParent, public nsITVSourceListener
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSITVSOURCELISTENER
+
+  TVParent();
+
+  bool Init();
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual bool RecvPTVRequestConstructor(PTVRequestParent* aActor,
+                                         const TVIPCRequest& aRequest) override;
+
+  virtual PTVRequestParent*
+  AllocPTVRequestParent(const TVIPCRequest& aRequest) override;
+
+  virtual bool DeallocPTVRequestParent(PTVRequestParent* aActor) override;
+
+  virtual bool RecvRegisterSourceHandler(const nsString& aTunerId,
+                                         const nsString& aSourceType) override;
+
+  virtual bool RecvUnregisterSourceHandler(
+    const nsString& aTunerId, const nsString& aSourceType) override;
+
+private:
+  virtual ~TVParent();
+
+  nsCOMPtr<nsITVService> mService;
+};
+
+class TVRequestParent final : public PTVRequestParent, public nsISupports
+{
+  friend class TVParent;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  explicit TVRequestParent(nsITVService* aService);
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  nsresult SendResponse(const TVIPCResponse& aResponse);
+
+private:
+  virtual ~TVRequestParent();
+
+  nsresult DoRequest(const TVGetTunersRequest& aRequest);
+
+  nsresult DoRequest(const TVSetSourceRequest& aRequest);
+
+  nsresult DoRequest(const TVStartScanningChannelsRequest& aRequest);
+
+  nsresult DoRequest(const TVStopScanningChannelsRequest& aRequest);
+
+  nsresult DoRequest(const TVClearScannedChannelsCacheRequest& aRequest);
+
+  nsresult DoRequest(const TVSetChannelRequest& aRequest);
+
+  nsresult DoRequest(const TVGetChannelsRequest& aRequest);
+
+  nsresult DoRequest(const TVGetProgramsRequest& aRequest);
+
+  nsCOMPtr<nsITVService> mService;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVParent_h__
--- a/dom/tv/moz.build
+++ b/dom/tv/moz.build
@@ -1,29 +1,37 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
+    'ipc/TVChild.h',
+    'ipc/TVIPCHelper.h',
+    'ipc/TVIPCService.h',
+    'ipc/TVParent.h',
     'TVChannel.h',
     'TVListeners.h',
     'TVManager.h',
     'TVProgram.h',
     'TVServiceCallbacks.h',
     'TVServiceFactory.h',
     'TVServiceRunnables.h',
     'TVSource.h',
     'TVTuner.h',
     'TVTypes.h',
     'TVUtils.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ipc/TVChild.cpp',
+    'ipc/TVIPCHelper.cpp',
+    'ipc/TVIPCService.cpp',
+    'ipc/TVParent.cpp',
     'TVChannel.cpp',
     'TVListeners.cpp',
     'TVManager.cpp',
     'TVProgram.cpp',
     'TVServiceCallbacks.cpp',
     'TVServiceFactory.cpp',
     'TVSource.cpp',
     'TVTuner.cpp',
@@ -35,16 +43,22 @@ XPIDL_SOURCES += [
     'nsITVSimulatorService.idl',
 ]
 
 EXTRA_COMPONENTS += [
     'TVSimulatorService.js',
     'TVSimulatorService.manifest',
 ]
 
+IPDL_SOURCES += [
+    'ipc/PTV.ipdl',
+    'ipc/PTVRequest.ipdl',
+    'ipc/PTVTypes.ipdlh'
+]
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     UNIFIED_SOURCES += [
         'gonk/TVGonkService.cpp',
     ];
 
 XPIDL_MODULE = 'dom_tv'
 
 MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
--- a/dom/tv/nsITVService.idl
+++ b/dom/tv/nsITVService.idl
@@ -2,22 +2,27 @@
  * 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 "nsISupports.idl"
 
 interface nsIArray;
 
 %{C++
+#include "nsTArray.h"
+
 #define TV_TUNER_DATA_CID \
   { 0x1f36be28, 0xf9fe, 0x2dc3, { 0xbf, 0x2a, 0x17, 0x97, 0x93, 0x40, 0xff, 0xe1 } }
 #define TV_TUNER_DATA_CONTRACTID \
   "@mozilla.org/tv/tvtunerdata;1"
 %}
 
+[ref] native CStringArray(nsTArray<nsCString>);
+[ref] native ConstCStringArray(const nsTArray<nsCString>);
+
 /**
  * XPCOM component which acts as the container for tuner data.
  *
  * NOTE: Use do_CreateInstance() to create the Gecko provided implementation,
  * and then uses the setter functions to adjust the properties of the object
  * before passing it.
  */
 [scriptable, builtinclass, uuid(c6d39e86-022b-4db5-b0df-602abfbeac69)]
@@ -40,25 +45,31 @@ interface nsITVTunerData : nsISupports
    * http://seanyhlin.github.io/TV-Manager-API/ for available values.
    *
    * @param count       The number of supported source types.
    * @param sourceTypes An array of supported source types.
    */
   void getSupportedSourceTypes([optional] out unsigned long count,
                                [retval, array, size_is(count)] out string sourceTypes);
 
+  void getSupportedSourceTypesByString([optional] out unsigned long count,
+                                       out CStringArray sourceTypes);
+
   /**
    * Set the supported source types of the tuner. Please refer to
    * http://seanyhlin.github.io/TV-Manager-API/ for available values.
    *
    * @param count       The number of supported source types.
    * @param sourceTypes An array of supported source types.
    */
   void setSupportedSourceTypes(in unsigned long count,
                                [array, size_is(count)] in string sourceTypes);
+
+  void setSupportedSourceTypesByString(in unsigned long count,
+                                       in ConstCStringArray sourceTypes);
 };
 
 %{C++
 #define TV_CHANNEL_DATA_CID \
   { 0xdafe6881, 0x0964, 0xdb5b, { 0x59, 0xc6, 0x20, 0x0b, 0xa6, 0x59, 0xe6, 0x68 } }
 #define TV_CHANNEL_DATA_CONTRACTID \
   "@mozilla.org/tv/tvchanneldata;1"
 %}
@@ -112,45 +123,59 @@ interface nsITVProgramData : nsISupports
    * http://seanyhlin.github.io/TV-Manager-API/ for available values.
    *
    * @param count     The number of languages.
    * @param languages An array of languages.
    */
   void getAudioLanguages([optional] out unsigned long count,
                          [retval, array, size_is(count)] out string languages);
 
+  void getAudioLanguagesByString([optional] out unsigned long count,
+                                 out CStringArray languages);
+
+
   /**
    * Set the audio languages of the program. Please refer to
    * http://seanyhlin.github.io/TV-Manager-API/ for available values.
    *
    * @param count     The number of languages.
    * @param languages An array of languages.
    */
   void setAudioLanguages(in unsigned long count,
                          [array, size_is(count)] in string languages);
 
+  void setAudioLanguagesByString(in unsigned long count,
+                                 in ConstCStringArray languages);
+
   /**
    * Get the subtitle languages of the program. Please refer to
    * http://seanyhlin.github.io/TV-Manager-API/ for available values.
    *
    * @param count     The number of languages.
    * @param languages An array of languages.
    */
   void getSubtitleLanguages([optional] out unsigned long count,
                             [retval, array, size_is(count)] out string languages);
 
+  void getSubtitleLanguagesByString([optional] out unsigned long count,
+                                    out CStringArray languages);
+
   /**
    * Set the subtitle languages of the program. Please refer to
    * http://seanyhlin.github.io/TV-Manager-API/ for available values.
    *
    * @param count     The number of languages.
    * @param languages An array of languages.
    */
   void setSubtitleLanguages(in unsigned long count,
                             [array, size_is(count)] in string languages);
+
+  void setSubtitleLanguagesByString(in unsigned long count,
+                                    in ConstCStringArray languages);
+
 };
 
 [scriptable, builtinclass, uuid(47746633-1b77-4df4-9424-d315bde3d455)]
 interface nsITVSourceListener : nsISupports
 {
   /**
    * Called when a channel is detected through scanning (after
    * |nsITVService::startScanningChannels()| is invoked and probably before