Bug 1216398 - support non-discoverable controlling device. r=junior,xeonchen.
authorShih-Chiang Chien <schien@mozilla.com>
Tue, 20 Oct 2015 16:47:56 +0800
changeset 304117 d340e3a25b9c06a5d52dccd62b87c27bfb3e3f4e
parent 304116 6705504e492456dc982d2f93d6bd7326928eb69a
child 304118 b2bd9c7a075d1469cb9511508ea3b554fac01d84
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjunior, xeonchen
bugs1216398
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1216398 - support non-discoverable controlling device. r=junior,xeonchen.
dom/presentation/interfaces/nsITCPPresentationServer.idl
dom/presentation/provider/MulticastDNSDeviceProvider.cpp
dom/presentation/provider/TCPPresentationServer.js
dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
dom/presentation/tests/xpcshell/test_tcp_control_channel.js
--- a/dom/presentation/interfaces/nsITCPPresentationServer.idl
+++ b/dom/presentation/interfaces/nsITCPPresentationServer.idl
@@ -44,44 +44,39 @@ interface nsITCPPresentationServerListen
                         in DOMString aUrl,
                         in DOMString aPresentationId,
                         in nsIPresentationControlChannel aControlChannel);
 };
 
 /**
  * TCP presentation server which can be used by discovery services.
  */
-[scriptable, uuid(494237ec-c567-41ab-afc9-82d26c4fe1dc)]
+[scriptable, uuid(55d6b605-2389-4aae-a8fe-60d4440540ea)]
 interface nsITCPPresentationServer: nsISupports
 {
   /**
-   * This method initializes a TCP presentation server.
-   * @param   aId
-   *          The unique Id for the device within the discovery scope. If aId
-   *          is null, empty string or opt-out, the TCP presentation server
-   *          should not work until the |id| is set appropriately.
+   * This method initialize server socket.
    * @param   aPort
    *          The port of the server socket.  Pass 0 or opt-out to indicate no
    *          preference, and a port will be selected automatically.
    * @throws  NS_ERROR_FAILURE if the server socket has been inited or the
    *          server socket can not be inited.
    */
-  void init([optional] in AUTF8String aId, [optional] in uint16_t aPort);
+  void startService([optional] in uint16_t aPort);
 
   /**
    * Request session to designated remote TCP device.
    * @param   aDeviceInfo
    *          The remtoe device info for establish connection.
    * @param   aUrl
    *          The URL requested to open by remote device.
    * @param   aPresentationId
    *          The Id for representing this session.
    * @returns The control channel for this session.
-   * @throws  NS_ERROR_FAILURE if the server socket has been inited or the
-   *          server socket can not be inited.
+   * @throws  NS_ERROR_FAILURE if the Id hasn't been inited.
    */
   nsIPresentationControlChannel requestSession(in nsITCPDeviceInfo aDeviceInfo,
                                                in DOMString aUrl,
                                                in DOMString aPresentationId);
 
   /**
    * Close server socket and call |listener.onClose(NS_OK)|
    */
@@ -89,21 +84,18 @@ interface nsITCPPresentationServer: nsIS
 
   /**
    * Get the listen port of the TCP socket, valid after |init|. 0 indicates
    * the server socket is not inited or closed.
    */
   readonly attribute uint16_t port;
 
   /**
-   * The id of the TCP presentation server. The setter should be use if the |id|
-   * is not set by the |init|. Moreover, if the |id| is not set by |init|, the
-   * TCP presentation server should not work until the |id| is set.
-   * @throws  NS_ERROR_FAILURE if the non-null id has been set by |init| or this
-   *          setter
+   * The id of the TCP presentation server. |requestSession| won't
+   * work until the |id| is set.
    */
   attribute AUTF8String id;
 
   /**
    * the listener for handling events of this TCP presentation server
    */
   attribute nsITCPPresentationServerListener listener;
 };
--- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
+++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
@@ -9,16 +9,20 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 
+#ifdef MOZ_WIDGET_ANDROID
+#include "nsIPropertyBag2.h"
+#endif // MOZ_WIDGET_ANDROID
+
 #define PREF_PRESENTATION_DISCOVERY "dom.presentation.discovery.enabled"
 #define PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS "dom.presentation.discovery.timeout_ms"
 #define PREF_PRESENTATION_DISCOVERABLE "dom.presentation.discoverable"
 #define PREF_PRESENTATION_DEVICE_NAME "dom.presentation.device.name"
 
 #define SERVICE_TYPE "_mozilla_papi._tcp."
 
 inline static PRLogModuleInfo*
@@ -41,16 +45,28 @@ static const char* kObservedPrefs[] = {
   PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS,
   PREF_PRESENTATION_DISCOVERABLE,
   PREF_PRESENTATION_DEVICE_NAME,
   nullptr
 };
 
 namespace {
 
+#ifdef MOZ_WIDGET_ANDROID
+static void
+GetAndroidDeviceName(nsACString& aRetVal)
+{
+  nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
+  MOZ_ASSERT(infoService, "Could not find a system info service");
+
+  unused << NS_WARN_IF(NS_FAILED(infoService->GetPropertyAsACString(
+                                   NS_LITERAL_STRING("device"), aRetVal)));
+}
+#endif // MOZ_WIDGET_ANDROID
+
 class TCPDeviceInfo final : public nsITCPDeviceInfo
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITCPDEVICEINFO
 
   explicit TCPDeviceInfo(const nsACString& aId,
                          const nsACString& aAddress,
@@ -181,16 +197,26 @@ MulticastDNSDeviceProvider::Init()
 
   Preferences::AddStrongObservers(this, kObservedPrefs);
 
   mDiscoveryEnabled = Preferences::GetBool(PREF_PRESENTATION_DISCOVERY);
   mDiscveryTimeoutMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS);
   mDiscoverable = Preferences::GetBool(PREF_PRESENTATION_DISCOVERABLE);
   mServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME);
 
+#ifdef MOZ_WIDGET_ANDROID
+  // FIXME: Bug 1185806 - Provide a common device name setting.
+  if (mServiceName.IsEmpty()) {
+    GetAndroidDeviceName(mServiceName);
+    unused << Preferences::SetCString(PREF_PRESENTATION_DEVICE_NAME, mServiceName);
+  }
+#endif // MOZ_WIDGET_ANDROID
+
+  unused << mPresentationServer->SetId(mServiceName);
+
   if (mDiscoveryEnabled && NS_WARN_IF(NS_FAILED(rv = ForceDiscovery()))) {
     return rv;
   }
 
   if (mDiscoverable && NS_WARN_IF(NS_FAILED(rv = RegisterService()))) {
     return rv;
   }
 
@@ -236,17 +262,17 @@ MulticastDNSDeviceProvider::RegisterServ
   }
 
   MOZ_ASSERT(!mRegisterRequest);
 
   nsresult rv;
   if (NS_WARN_IF(NS_FAILED(rv = mPresentationServer->SetListener(mWrappedListener)))) {
     return rv;
   }
-  if (NS_WARN_IF(NS_FAILED(rv = mPresentationServer->Init(EmptyCString(), 0)))) {
+  if (NS_WARN_IF(NS_FAILED(rv = mPresentationServer->StartService(0)))) {
     return rv;
   }
   uint16_t servicePort;
   if (NS_WARN_IF(NS_FAILED(rv = mPresentationServer->GetPort(&servicePort)))) {
     return rv;
   }
 
   /**
@@ -903,17 +929,20 @@ MulticastDNSDeviceProvider::Observe(nsIS
   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY)) {
       OnDiscoveryChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERY));
     } else if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS)) {
       OnDiscoveryTimeoutChanged(Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS));
     } else if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERABLE)) {
       OnDiscoverableChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERABLE));
     } else if (data.EqualsLiteral(PREF_PRESENTATION_DEVICE_NAME)) {
-      OnServiceNameChanged(Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME));
+      nsAdoptingCString newServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME);
+      if (!mServiceName.Equals(newServiceName)) {
+        OnServiceNameChanged(newServiceName);
+      }
     }
   } else if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
     StopDiscovery(NS_OK);
   }
 
   return NS_OK;
 }
 
--- a/dom/presentation/provider/TCPPresentationServer.js
+++ b/dom/presentation/provider/TCPPresentationServer.js
@@ -29,95 +29,82 @@ function TCPPresentationServer() {
 TCPPresentationServer.prototype = {
   /**
    * If a user agent connects to this server, we create a control channel but
    * hand it to |TCPDevice.listener| when the initial information exchange
    * finishes. Therefore, we hold the control channels in this period.
    */
   _controlChannels: [],
 
-  init: function(aId, aPort) {
-    if (this._isInit()) {
+  startService: function(aPort) {
+    if (this._isServiceInit()) {
       DEBUG && log("TCPPresentationServer - server socket has been initialized");
       throw Cr.NS_ERROR_FAILURE;
     }
 
-    if (typeof aId === "undefined" || typeof aPort === "undefined") {
-      DEBUG && log("TCPPresentationServer - aId/aPort should not be undefined");
+    if (typeof aPort === "undefined") {
+      DEBUG && log("TCPPresentationServer - aPort should not be undefined");
       throw Cr.NS_ERROR_FAILURE;
     }
 
-    DEBUG && log("TCPPresentationServer - init id: " + aId + " port: " + aPort);
-
     /**
      * 0 or undefined indicates opt-out parameter, and a port will be selected
      * automatically.
      */
     let serverSocketPort = (aPort !== 0) ? aPort : -1;
 
     this._serverSocket = Cc["@mozilla.org/network/server-socket;1"]
                          .createInstance(Ci.nsIServerSocket);
 
     if (!this._serverSocket) {
       DEBUG && log("TCPPresentationServer - create server socket fail.");
       throw Cr.NS_ERROR_FAILURE;
     }
 
     try {
       this._serverSocket.init(serverSocketPort, false, -1);
+      this._serverSocket.asyncListen(this);
     } catch (e) {
       // NS_ERROR_SOCKET_ADDRESS_IN_USE
       DEBUG && log("TCPPresentationServer - init server socket fail: " + e);
       throw Cr.NS_ERROR_FAILURE;
     }
 
-    /**
-     * The setter may trigger |_serverSocket.asyncListen| if the |id| setting
-     * successes.
-     */
-    this.id = aId;
     this._port = this._serverSocket.port;
+
+    DEBUG && log("TCPPresentationServer - service start on port: " + aPort);
   },
 
   get id() {
     return this._id;
   },
 
   set id(aId) {
-    if (!aId || aId.length == 0 || aId === this._id) {
-      return;
-    } else if (this._id) {
-      throw Cr.NS_ERROR_FAILURE;
-    }
     this._id = aId;
-
-    if (this._serverSocket) {
-      this._serverSocket.asyncListen(this);
-    }
   },
 
   get port() {
     return this._port;
   },
 
   set listener(aListener) {
     this._listener = aListener;
   },
 
   get listener() {
     return this._listener;
   },
 
-  _isInit: function() {
-    return this._id !== null && this._serverSocket !== null;
+  _isServiceInit: function() {
+    return this._serverSocket !== null;
   },
 
   requestSession: function(aDeviceInfo, aUrl, aPresentationId) {
-    if (!this._isInit()) {
-      DEBUG && log("TCPPresentationServer - has not initialized; requestSession fails");
+    if (!this.id) {
+      DEBUG && log("TCPPresentationServer - Id has not initialized; requestSession fails");
       return null;
     }
     DEBUG && log("TCPPresentationServer - requestSession to " + aDeviceInfo.id
                  + ": " + aUrl + ", " + aPresentationId);
 
     let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
                 .getService(Ci.nsISocketTransportService)
 
@@ -137,18 +124,19 @@ TCPPresentationServer.prototype = {
                                  socketTransport,
                                  aDeviceInfo,
                                  aPresentationId,
                                  "sender",
                                  aUrl);
   },
 
   responseSession: function(aDeviceInfo, aSocketTransport) {
-    if (!this._isInit()) {
-      DEBUG && log("TCPPresentationServer - has not initialized; responseSession fails");
+    if (!this._isServiceInit()) {
+      DEBUG && log("TCPPresentationServer - should never receive remote " +
+                   "session request before server socket initialization");
       return null;
     }
     DEBUG && log("TCPPresentationServer - responseSession to "
                  + JSON.stringify(aDeviceInfo));
     return new TCPControlChannel(this,
                                  aSocketTransport,
                                  aDeviceInfo,
                                  null, // presentation ID
@@ -204,17 +192,16 @@ TCPPresentationServer.prototype = {
 
   close: function() {
     DEBUG && log("TCPPresentationServer - close");
     if (this._serverSocket) {
       DEBUG && log("TCPPresentationServer - close server socket");
       this._serverSocket.close();
       this._serverSocket = null;
     }
-    this._id = null;
     this._port = 0;
   },
 
   classID: Components.ID("{f4079b8b-ede5-4b90-a112-5b415a931deb}"),
   QueryInterface : XPCOMUtils.generateQI([Ci.nsIServerSocketListener,
                                           Ci.nsITCPPresentationServer]),
 };
 
--- a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
+++ b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
@@ -13,16 +13,17 @@ Cu.import("resource://gre/modules/XPCOMU
 const INFO_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1";
 const PROVIDER_CONTRACT_ID = "@mozilla.org/presentation-device/multicastdns-provider;1";
 const SD_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1";
 const UUID_CONTRACT_ID = "@mozilla.org/uuid-generator;1";
 const SERVER_CONTRACT_ID = "@mozilla.org/presentation-device/tcp-presentation-server;1";
 
 const PREF_DISCOVERY = "dom.presentation.discovery.enabled";
 const PREF_DISCOVERABLE = "dom.presentation.discoverable";
+const PREF_DEVICENAME= "dom.presentation.device.name";
 
 var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
 
 function MockFactory(aClass) {
   this._cls = aClass;
 }
 MockFactory.prototype = {
   createInstance: function(aOuter, aIID) {
@@ -376,19 +377,23 @@ function addDevice() {
   provider.listener = null;
   Assert.equal(listener.count(), 1);
 
   run_next_test();
 }
 
 function handleSessionRequest() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, true);
+  Services.prefs.setBoolPref(PREF_DISCOVERABLE, false);
 
   const testUrl = "http://example.com";
   const testPresentationId = "test-presentation-id";
+  const testDeviceName = "test-device-name";
+
+  Services.prefs.setCharPref(PREF_DEVICENAME, testDeviceName);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 "_mozilla_papi._tcp");
   let mockSDObj = {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
@@ -418,16 +423,17 @@ function handleSessionRequest() {
         deviceInfo: deviceInfo,
         url: url,
         presentationId: presentationId,
       };
       return {
         QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
       };
     },
+    id: "",
   };
 
   let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
   let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
   let listener = {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
                                            Ci.nsISupportsWeakReference]),
@@ -440,24 +446,26 @@ function handleSessionRequest() {
 
   let controlChannel = listener.device.establishControlChannel(testUrl, testPresentationId);
 
   Assert.equal(mockServerObj.request.deviceInfo.id, mockDevice.host);
   Assert.equal(mockServerObj.request.deviceInfo.address, mockDevice.host);
   Assert.equal(mockServerObj.request.deviceInfo.port, mockDevice.port);
   Assert.equal(mockServerObj.request.url, testUrl);
   Assert.equal(mockServerObj.request.presentationId, testPresentationId);
+  Assert.equal(mockServerObj.id, testDeviceName);
 
   provider.listener = null;
 
   run_next_test();
 }
 
 function handleOnSessionRequest() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, true);
+  Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 "_mozilla_papi._tcp");
   let mockSDObj = {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
@@ -477,17 +485,17 @@ function handleOnSessionRequest() {
                                               mockDevice.port,
                                               mockDevice.serviceName,
                                               mockDevice.serviceType));
     }
   };
 
   let mockServerObj = {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServer]),
-    init: function() {},
+    startService: function() {},
     sessionRequest: function() {},
     close: function() {},
     id: '',
     port: 0,
     listener: null,
   };
 
   let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
@@ -531,26 +539,29 @@ function handleOnSessionRequest() {
   Assert.equal(listener.request.presentationId, testPresentationId);
 
   provider.listener = null;
 
   run_next_test();
 }
 
 function handleOnSessionRequestFromUnknownDevice() {
+  Services.prefs.setBoolPref(PREF_DISCOVERY, false);
+  Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
+
   let mockSDObj = {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {},
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {}
   };
 
   let mockServerObj = {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServer]),
-    init: function() {},
+    startService: function() {},
     sessionRequest: function() {},
     close: function() {},
     id: '',
     port: 0,
     listener: null,
   };
 
   let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
@@ -672,17 +683,17 @@ function ignoreSelfDevice() {
                                               mockDevice.port,
                                               mockDevice.serviceName,
                                               mockDevice.serviceType));
     }
   };
 
   let mockServerObj = {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServer]),
-    init: function() {},
+    startService: function() {},
     sessionRequest: function() {},
     close: function() {},
     id: '',
     port: 0,
     listener: null,
   };
 
   let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
--- a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
+++ b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
@@ -50,18 +50,18 @@ const OFFER_PORT = 123;
 
 // controller's presentation channel description
 const ANSWER_ADDRESS = '192.168.321.321';
 const ANSWER_PORT = 321;
 
 function loopOfferAnser() {
   tps = Cc["@mozilla.org/presentation-device/tcp-presentation-server;1"]
         .createInstance(Ci.nsITCPPresentationServer);
-  tps.init(null, PRESENTER_CONTROL_CHANNEL_PORT);
   tps.id = 'controllerID';
+  tps.startService(PRESENTER_CONTROL_CHANNEL_PORT);
 
   testPresentationServer();
 }
 
 
 function testPresentationServer() {
   let yayFuncs = makeJointSuccess(['controllerControlChannelClose',
                                    'presenterControlChannelClose']);
@@ -173,17 +173,17 @@ function setOffline() {
   }
 
   // Let the server socket be closed non-manually
   Services.io.offline = true;
 }
 
 function oneMoreLoop() {
   try {
-    tps.init('controllerID', PRESENTER_CONTROL_CHANNEL_PORT);
+    tps.startService(PRESENTER_CONTROL_CHANNEL_PORT);
     testPresentationServer();
   } catch (e) {
     Assert.ok(false, 'TCP presentation init fail:' + e);
     run_next_test();
   }
 }