Bug 1027787 - Unique device discovery names for b2g. r=paul
authorJ. Ryan Stinnett <jryans@gmail.com>
Thu, 17 Jul 2014 09:57:00 -0400
changeset 216921 c6f27c773336b2bef38a4d4e70af579ae9425ad5
parent 216920 14fe614bac3b6ff1e68b81d3ac1390d0628eb20b
child 216922 a1778d8e2e382f970154fe4bb81ffaf6c3ed7cf9
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaul
bugs1027787
milestone33.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 1027787 - Unique device discovery names for b2g. r=paul
toolkit/devtools/discovery/discovery.js
toolkit/devtools/discovery/tests/unit/test_discovery.js
toolkit/devtools/discovery/tests/unit/xpcshell.ini
--- a/toolkit/devtools/discovery/discovery.js
+++ b/toolkit/devtools/discovery/discovery.js
@@ -129,22 +129,121 @@ Transport.prototype = {
     }
     this.emit("message", object);
   },
 
   onStopListening: function() {}
 
 };
 
+/**
+ * Manages the local device's name.  The name can be generated in serveral
+ * platform-specific ways (see |_generate|).  The aim is for each device on the
+ * same local network to have a unique name.  If the Settings API is available,
+ * the name is saved there to persist across reboots.
+ */
+function LocalDevice() {
+  this._name = LocalDevice.UNKNOWN;
+  if ("@mozilla.org/settingsService;1" in Cc) {
+    this._settings =
+      Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService);
+    Services.obs.addObserver(this, "mozsettings-changed", false);
+  }
+  this._get(); // Trigger |_get| to load name eagerly
+}
+
+LocalDevice.SETTING = "devtools.discovery.device";
+LocalDevice.UNKNOWN = "unknown";
+
+LocalDevice.prototype = {
+
+  _get: function() {
+    if (!this._settings) {
+      // Without Settings API, just generate a name and stop, since the value
+      // can't be persisted.
+      this._generate();
+      return;
+    }
+    // Initial read of setting value
+    this._settings.createLock().get(LocalDevice.SETTING, {
+      handle: (_, name) => {
+        if (name && name !== LocalDevice.UNKNOWN) {
+          this._name = name;
+          log("Device: " + this._name);
+          return;
+        }
+        // No existing name saved, so generate one.
+        this._generate();
+      },
+      handleError: () => log("Failed to get device name setting")
+    });
+  },
+
+  /**
+   * Generate a new device name from various platform-specific properties.
+   * Triggers the |name| setter to persist if needed.
+   */
+  _generate: function() {
+    if (Services.appinfo.widgetToolkit == "gonk") {
+      // For Gonk devices, create one from the device name plus a little
+      // randomness.  The goal is just to distinguish devices in an office
+      // environment where many people may have the same device model for
+      // testing purposes (which would otherwise all report the same name).
+      let name = libcutils.property_get("ro.product.device");
+      // Pick a random number from [0, 2^32)
+      let randomID = Math.floor(Math.random() * Math.pow(2, 32));
+      // To hex and zero pad
+      randomID = ("00000000" + randomID.toString(16)).slice(-8);
+      this.name = name + "-" + randomID;
+    } else {
+      this.name = sysInfo.get("host");
+    }
+  },
+
+  /**
+   * Observe any changes that might be made via the Settings app
+   */
+  observe: function(subject, topic, data) {
+    if (topic !== "mozsettings-changed") {
+      return;
+    }
+    let setting = JSON.parse(data);
+    if (setting.key !== LocalDevice.SETTING) {
+      return;
+    }
+    this._name = setting.value;
+    log("Device: " + this._name);
+  },
+
+  get name() {
+    return this._name;
+  },
+
+  set name(name) {
+    if (!this._settings) {
+      this._name = name;
+      log("Device: " + this._name);
+      return;
+    }
+    // Persist to Settings API
+    // The new value will be seen and stored by the observer above
+    this._settings.createLock().set(LocalDevice.SETTING, name, {
+      handle: () => {},
+      handleError: () => log("Failed to set device name setting")
+    });
+  }
+
+};
+
 function Discovery() {
   EventEmitter.decorate(this);
 
   this.localServices = {};
   this.remoteServices = {};
-  this.device = { name: "unknown" };
+  this.device = new LocalDevice();
   this.replyTimeout = REPLY_TIMEOUT;
 
   // Defaulted to Transport, but can be altered by tests
   this._factories = { Transport: Transport };
 
   this._transports = {
     scan: null,
     update: null
@@ -153,18 +252,16 @@ function Discovery() {
     from: new Set()
   };
 
   this._onRemoteScan = this._onRemoteScan.bind(this);
   this._onRemoteUpdate = this._onRemoteUpdate.bind(this);
   this._purgeMissingDevices = this._purgeMissingDevices.bind(this);
 
   Services.obs.addObserver(this, "network-active-changed", false);
-
-  this._getSystemInfo();
 }
 
 Discovery.prototype = {
 
   /**
    * Add a new service offered by this device.
    * @param service string
    *        Name of the service
@@ -233,34 +330,16 @@ Discovery.prototype = {
 
   _waitForReplies: function() {
     clearTimeout(this._expectingReplies.timer);
     this._expectingReplies.from = new Set(this.getRemoteDevices());
     this._expectingReplies.timer =
       setTimeout(this._purgeMissingDevices, this.replyTimeout);
   },
 
-  /**
-   * Determine a unique name to identify the current device.
-   */
-  _getSystemInfo: function() {
-    // TODO Bug 1027787: Uniquify device name somehow?
-    try {
-      if (Services.appinfo.widgetToolkit == "gonk") {
-        this.device.name = libcutils.property_get("ro.product.device");
-      } else {
-        this.device.name = sysInfo.get("host");
-      }
-      log("Device: " + this.device.name);
-    } catch(e) {
-      log("Failed to get system info");
-      this.device.name = "unknown";
-    }
-  },
-
   get Transport() {
     return this._factories.Transport;
   },
 
   _startListeningForScan: function() {
     if (this._transports.scan) {
       return; // Already listening
     }
--- a/toolkit/devtools/discovery/tests/unit/test_discovery.js
+++ b/toolkit/devtools/discovery/tests/unit/test_discovery.js
@@ -64,17 +64,23 @@ TestTransport.prototype = {
   },
 
   onStopListening: function(socket, status) {}
 
 };
 
 // Use TestTransport instead of the usual Transport
 discovery._factories.Transport = TestTransport;
-discovery.device.name = "test-device";
+
+// Ignore name generation on b2g and force a fixed value
+Object.defineProperty(discovery.device, "name", {
+  get: function() {
+    return "test-device";
+  }
+});
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function*() {
   // At startup, no remote devices are known
   deepEqual(discovery.getRemoteDevicesWithService("devtools"), []);
--- a/toolkit/devtools/discovery/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/discovery/tests/unit/xpcshell.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 head =
 tail =
 
 [test_discovery.js]
+skip-if = toolkit == 'gonk' && debug # Debug doesn't like settings read