merge b2g-inbound to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 12 Aug 2013 11:30:31 +0200
changeset 155125 87c1796bc46c6677b6aba9c56cf3796baa4c9d82
parent 155118 f057fca0962763dcdb50286e34ec05c9f768818d (current diff)
parent 155124 c521aae575b5094a9ed59d6d5084fd6772d9acf6 (diff)
child 155126 9ab28833eda00abf3dd3099f2a21798691887c74
child 155133 80229aca54f9d27e6cf37963f93bcd5b54162f13
child 155154 caa22f2e3c773812bc35e8ce9911a66740d2f469
child 155182 e999ab01a7eb434c9906b8a220b349d8e40ac5ea
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
first release with
nightly linux32
87c1796bc46c / 26.0a1 / 20130812030209 / files
nightly linux64
87c1796bc46c / 26.0a1 / 20130812030209 / files
nightly mac
87c1796bc46c / 26.0a1 / 20130812030209 / files
nightly win32
87c1796bc46c / 26.0a1 / 20130812030209 / files
nightly win64
87c1796bc46c / 26.0a1 / 20130812030209 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge b2g-inbound to mozilla-central
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "0e3e30e489ff38cceb8b9cf9ee5caea5fb072457", 
+    "revision": "cc6b6bcc278e02d1e23c78cc41fcc21c074a82db", 
     "repo_path": "/integration/gaia-central"
 }
--- a/dom/settings/SettingsDB.jsm
+++ b/dom/settings/SettingsDB.jsm
@@ -3,16 +3,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/ObjectWrapper.jsm");
+
 this.EXPORTED_SYMBOLS = ["SettingsDB", "SETTINGSDB_NAME", "SETTINGSSTORE_NAME"];
 
 const DEBUG = false;
 function debug(s) {
   if (DEBUG) dump("-*- SettingsDB: " + s + "\n");
 }
 
 this.SETTINGSDB_NAME = "settings";
@@ -75,40 +78,128 @@ SettingsDB.prototype = {
     stream.close();
 
     objectStore.openCursor().onsuccess = function(event) {
       let cursor = event.target.result;
       if (cursor) {
         let value = cursor.value;
         if (value.settingName in settings) {
           if (DEBUG) debug("Upgrade " +settings[value.settingName]);
-          value.defaultValue = settings[value.settingName];
+          value.defaultValue = this.prepareValue(settings[value.settingName]);
           delete settings[value.settingName];
           if ("settingValue" in value) {
-            value.userValue = value.settingValue;
+            value.userValue = this.prepareValue(value.settingValue);
             delete value.settingValue;
           }
           cursor.update(value);
         } else if ("userValue" in value || "settingValue" in value) {
           value.defaultValue = undefined;
           if (aOldVersion == 1 && value.settingValue) {
-            value.userValue = value.settingValue;
+            value.userValue = this.prepareValue(value.settingValue);
             delete value.settingValue;
           }
           cursor.update(value);
         } else {
           cursor.delete();
         }
         cursor.continue();
       } else {
         for (let name in settings) {
-          if (DEBUG) debug("Set new:" + name +", " + settings[name]);
-          objectStore.add({ settingName: name, defaultValue: settings[name], userValue: undefined });
+          let value = this.prepareValue(settings[name]);
+          if (DEBUG) debug("Set new:" + name +", " + value);
+          objectStore.add({ settingName: name, defaultValue: value, userValue: undefined });
+        }
+      }
+    }.bind(this);
+  },
+
+  // If the value is a data: uri, convert it to a Blob.
+  convertDataURIToBlob: function(aValue) {
+    /* base64 to ArrayBuffer decoding, from
+       https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
+    */
+    function b64ToUint6 (nChr) {
+      return nChr > 64 && nChr < 91 ?
+          nChr - 65
+        : nChr > 96 && nChr < 123 ?
+          nChr - 71
+        : nChr > 47 && nChr < 58 ?
+          nChr + 4
+        : nChr === 43 ?
+          62
+        : nChr === 47 ?
+          63
+        :
+          0;
+    }
+
+    function base64DecToArr(sBase64, nBlocksSize) {
+      let sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""),
+          nInLen = sB64Enc.length,
+          nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize
+                                : nInLen * 3 + 1 >> 2,
+          taBytes = new Uint8Array(nOutLen);
+
+      for (let nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
+        nMod4 = nInIdx & 3;
+        nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
+        if (nMod4 === 3 || nInLen - nInIdx === 1) {
+          for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
+            taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
+          }
+          nUint24 = 0;
         }
       }
-    };
+      return taBytes;
+    }
+
+    // Check if we have a data: uri, and if it's base64 encoded.
+    // data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...
+    if (typeof aValue == "string" && aValue.startsWith("data:")) {
+      try {
+        let uri = Services.io.newURI(aValue, null, null);
+        // XXX: that would be nice to reuse the c++ bits of the data:
+        // protocol handler instead.
+        let mimeType = "application/octet-stream";
+        let mimeDelim = aValue.indexOf(";");
+        if (mimeDelim !== -1) {
+          mimeType = aValue.substring(5, mimeDelim);
+        }
+        let start = aValue.indexOf(",") + 1;
+        let isBase64 = ((aValue.indexOf("base64") + 7) == start);
+        let payload = aValue.substring(start);
+
+        return new Blob([isBase64 ? base64DecToArr(payload) : payload],
+                        { type: mimeType });
+      } catch(e) {
+        dump(e);
+      }
+    }
+    return aValue
+  },
+
+  // Makes sure any property that is a data: uri gets converted to a Blob.
+  prepareValue: function(aObject) {
+    let kind = ObjectWrapper.getObjectKind(aObject);
+    if (kind == "array") {
+      let res = [];
+      aObject.forEach(function(aObj) {
+        res.push(this.prepareValue(aObj));
+      }, this);
+      return res;
+    } else if (kind == "file" || kind == "blob" || kind == "date") {
+      return aObject;
+    } else if (kind == "primitive") {
+      return this.convertDataURIToBlob(aObject);
+    }
+
+    // Fall-through, we now have a dictionary object.
+    for (let prop in aObject) {
+      aObject[prop] = this.prepareValue(aObject[prop]);
+    }
+    return aObject;
   },
 
   init: function init(aGlobal) {
     this.initDBHelper(SETTINGSDB_NAME, SETTINGSDB_VERSION,
                       [SETTINGSSTORE_NAME], aGlobal);
   }
 }
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -12,17 +12,17 @@ function debug(s) {
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/SettingsQueue.jsm");
 Cu.import("resource://gre/modules/SettingsDB.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/ObjectWrapper.jsm")
+Cu.import("resource://gre/modules/ObjectWrapper.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 function SettingsLock(aSettingsManager) {
   this._open = true;
   this._isBusy = false;
@@ -184,18 +184,20 @@ SettingsLock.prototype = {
   },
 
   _serializePreservingBinaries: function _serializePreservingBinaries(aObject) {
     // We need to serialize settings objects, otherwise they can change between
     // the set() call and the enqueued request being processed. We can't simply
     // parse(stringify(obj)) because that breaks things like Blobs, Files and
     // Dates, so we use stringify's replacer and parse's reviver parameters to
     // preserve binaries.
+    let manager = this._settingsManager;
     let binaries = Object.create(null);
     let stringified = JSON.stringify(aObject, function(key, value) {
+      value = manager._settingsDB.prepareValue(value);
       let kind = ObjectWrapper.getObjectKind(value);
       if (kind == "file" || kind == "blob" || kind == "date") {
         let uuid = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator)
                                                       .generateUUID().toString();
         binaries[uuid] = value;
         return uuid;
       }
       return value;
--- a/dom/settings/SettingsService.js
+++ b/dom/settings/SettingsService.js
@@ -160,17 +160,17 @@ SettingsServiceLock.prototype = {
 
   set: function set(aName, aValue, aCallback, aMessage) {
     debug("set: " + aName + ": " + JSON.stringify(aValue));
     if (aMessage === undefined)
       aMessage = null;
     this._requests.enqueue({ callback: aCallback,
                              intent: "set", 
                              name: aName, 
-                             value: aValue, 
+                             value: this._settingsService._settingsDB.prepareValue(aValue),
                              message: aMessage });
     this.createTransactionAndProcess();
   },
 
   classID : SETTINGSSERVICELOCK_CID,
   QueryInterface : XPCOMUtils.generateQI([nsISettingsServiceLock]),
 
   classInfo : XPCOMUtils.generateCI({ classID: SETTINGSSERVICELOCK_CID,
--- a/dom/settings/tests/Makefile.in
+++ b/dom/settings/tests/Makefile.in
@@ -11,16 +11,17 @@ relativesrcdir   = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_FILES = \
   test_settings_basics.html \
   test_settings_events.html \
   test_settings_onsettingchange.html \
   test_settings_blobs.html \
+  test_settings_data_uris.html \
   test_settings_navigator_object.html \
   $(NULL)
 
 MOCHITEST_CHROME_FILES = \
   test_settings_service.xul \
   test_settings_service.js \
   $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/settings/tests/test_settings_data_uris.html
@@ -0,0 +1,154 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806374
+-->
+<head>
+  <title>Test for Bug 806374 Settings API</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821630">Mozilla Bug 821630</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript;version=1.7">
+
+"use strict";
+
+if (SpecialPowers.isMainProcess()) {
+  SpecialPowers.Cu.import("resource://gre/modules/SettingsChangeNotifier.jsm");
+}
+
+SpecialPowers.addPermission("settings-read", true, document);
+SpecialPowers.addPermission("settings-write", true, document);
+
+function onUnwantedSuccess() {
+  ok(false, "onUnwantedSuccess: shouldn't get here");
+}
+
+function onFailure() {
+  return function(s) {
+    if (s) {
+      ok(false, "in on Failure! - " + s);
+    } else {
+      ok(false, "in on Failure!");
+    }
+  }
+}
+
+let mozSettings = window.navigator.mozSettings;
+let req;
+
+// A simple data URI that will be converted to a blob.
+let dataURI = "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;";
+
+function checkBlob(blob) {
+  try {
+    let url = URL.createObjectURL(blob);
+    ok(true, "Valid blob");
+  } catch (e) {
+    ok(false, "Valid blob");
+  }
+}
+
+let steps = [
+  function() {
+    let lock = mozSettings.createLock();
+    req = lock.clear();
+    req.onsuccess = next;
+    req.onerror = onFailure("Deleting database");
+  },
+  function() {
+    function obs(e) {
+      checkBlob(e.settingValue);
+      mozSettings.removeObserver("test1", obs);
+      next();
+    }
+    mozSettings.addObserver("test1", obs);
+    next();
+  },
+  function() {
+    // next is called by the observer above
+    let req = mozSettings.createLock().set({"test1": dataURI});
+    req.onerror = onFailure("Saving blob");
+  },
+  function() {
+    let req = mozSettings.createLock().get("test1");
+    req.onsuccess = function(event) {
+      checkBlob(event.target.result["test1"]);
+      next();
+    };
+    req.onerror = onFailure("Getting blob");
+  },
+  function() {
+    let req = mozSettings.createLock().set({"test2": [1, 2, dataURI, 4]});
+    req.onsuccess = next;
+    req.onerror = onFailure("Saving array");
+  },
+  function() {
+    let req = mozSettings.createLock().get("test2");
+    req.onsuccess = function(event) {
+      let val = event.target.result["test2"];
+      ok(Array.isArray(val), "Result is an array");
+      ok(val[0] == 1 && val[1] == 2 && val[3] == 4, "Primitives are preserved");
+      checkBlob(val[2]);
+      next();
+    };
+    req.onerror = onFailure("Getting array");
+  },
+  function() {
+    let req = mozSettings.createLock().set({"test3": {foo: "bar", baz: {number: 1, arr: [dataURI]}}});
+    req.onsuccess = next();
+    req.onerror = onFailure("Saving object");
+  },
+  function() {
+    let req = mozSettings.createLock().get("test3");
+    req.onsuccess = function(event) {
+      let val = event.target.result["test3"];
+      ok(typeof(val) == "object", "Result is an object");
+      ok("foo" in val && typeof(val.foo) == "string", "String property preserved");
+      ok("baz" in val && typeof(val.baz) == "object", "Object property preserved");
+      let baz = val.baz;
+      ok("number" in baz && baz.number == 1, "Primite inside object preserved");
+      ok("arr" in baz && Array.isArray(baz.arr), "Array inside object is preserved");
+      checkBlob(baz.arr[0]);
+      next();
+    };
+    req.onerror = onFailure("Getting object");
+  },
+  function() {
+    let req = mozSettings.createLock().clear();
+    req.onsuccess = function() {
+      next();
+    };
+    req.onerror = onFailure("Deleting database");
+  },
+  function () {
+    ok(true, "all done!\n");
+    SimpleTest.finish();
+  }
+];
+
+function next() {
+  try {
+    let step = steps.shift();
+    if (step) {
+      step();
+    }
+  } catch(ex) {
+    ok(false, "Caught exception", ex);
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(next);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -687,16 +687,18 @@ function RadioInterface(options) {
                      relSignalStrength: null},
   };
 
   this.voicemailInfo = {
     number: null,
     displayName: null
   };
 
+  this.operatorInfo = {};
+
   // Read the 'ril.radio.disabled' setting in order to start with a known
   // value at boot time.
   let lock = gSettingsService.createLock();
   lock.get("ril.radio.disabled", this);
 
   // Read preferred network type from the setting DB.
   lock.get("ril.radio.preferredNetworkType", this);
 
@@ -742,16 +744,29 @@ RadioInterface.prototype = {
                                          Ci.nsIObserver,
                                          Ci.nsISettingsServiceCallback]),
 
   debug: function debug(s) {
     dump("-*- RadioInterface[" + this.clientId + "]: " + s + "\n");
   },
 
   /**
+   * A utility function to copy objects. The srcInfo may contain
+   * 'rilMessageType', should ignore it.
+   */
+  updateInfo: function updateInfo(srcInfo, destInfo) {
+    for (let key in srcInfo) {
+      if (key === 'rilMessageType') {
+        continue;
+      }
+      destInfo[key] = srcInfo[key];
+    }
+  },
+
+  /**
    * Process a message from the content process.
    */
   receiveMessage: function receiveMessage(msg) {
     switch (msg.name) {
       case "RIL:GetRilContext":
         // This message is sync.
         return this.rilContext;
       case "RIL:EnumerateCalls":
@@ -1138,28 +1153,25 @@ RadioInterface.prototype = {
   updateNetworkInfo: function updateNetworkInfo(message) {
     let voiceMessage = message[RIL.NETWORK_INFO_VOICE_REGISTRATION_STATE];
     let dataMessage = message[RIL.NETWORK_INFO_DATA_REGISTRATION_STATE];
     let operatorMessage = message[RIL.NETWORK_INFO_OPERATOR];
     let selectionMessage = message[RIL.NETWORK_INFO_NETWORK_SELECTION_MODE];
 
     // Batch the *InfoChanged messages together
     if (voiceMessage) {
-      voiceMessage.batch = true;
-      this.updateVoiceConnection(voiceMessage);
+      this.updateVoiceConnection(voiceMessage, true);
     }
 
     if (dataMessage) {
-      dataMessage.batch = true;
-      this.updateDataConnection(dataMessage);
+      this.updateDataConnection(dataMessage, true);
     }
 
     if (operatorMessage) {
-      operatorMessage.batch = true;
-      this.handleOperatorChange(operatorMessage);
+      this.handleOperatorChange(operatorMessage, true);
     }
 
     let voice = this.rilContext.voice;
     let data = this.rilContext.data;
 
     this.checkRoamingBetweenOperators(voice);
     this.checkRoamingBetweenOperators(data);
 
@@ -1199,76 +1211,83 @@ RadioInterface.prototype = {
     let equalsShortName = shortName && (spn == shortName);
     let equalsMcc = iccInfo.mcc == operator.mcc;
 
     registration.roaming = registration.roaming &&
                            !(equalsMcc && (equalsLongName || equalsShortName));
   },
 
   /**
-   * Sends the RIL:VoiceInfoChanged message when the voice
-   * connection's state has changed.
+   * Handle data connection changes.
    *
-   * @param newInfo The new voice connection information. When newInfo.batch is true,
-   *                the RIL:VoiceInfoChanged message will not be sent.
+   * @param newInfo The new voice connection information.
+   * @param batch   When batch is true, the RIL:VoiceInfoChanged message will
+   *                not be sent.
    */
-  updateVoiceConnection: function updateVoiceConnection(newInfo) {
+  updateVoiceConnection: function updateVoiceConnection(newInfo, batch) {
     let voiceInfo = this.rilContext.voice;
     voiceInfo.state = newInfo.state;
     voiceInfo.connected = newInfo.connected;
     voiceInfo.roaming = newInfo.roaming;
     voiceInfo.emergencyCallsOnly = newInfo.emergencyCallsOnly;
     voiceInfo.type = newInfo.type;
 
     // Make sure we also reset the operator and signal strength information
     // if we drop off the network.
-    if (newInfo.regState !== RIL.NETWORK_CREG_STATE_REGISTERED_HOME &&
-        newInfo.regState !== RIL.NETWORK_CREG_STATE_REGISTERED_ROAMING) {
+    if (newInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
       voiceInfo.cell = null;
       voiceInfo.network = null;
       voiceInfo.signalStrength = null;
       voiceInfo.relSignalStrength = null;
     } else {
       voiceInfo.cell = newInfo.cell;
+      voiceInfo.network = this.operatorInfo;
     }
 
-    if (!newInfo.batch) {
+    if (!batch) {
       gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
                                                   this.clientId, voiceInfo);
     }
   },
 
-  updateDataConnection: function updateDataConnection(newInfo) {
+  /**
+   * Handle the data connection's state has changed.
+   *
+   * @param newInfo The new data connection information.
+   * @param batch   When batch is true, the RIL:DataInfoChanged message will
+   *                not be sent.
+   */
+  updateDataConnection: function updateDataConnection(newInfo, batch) {
     let dataInfo = this.rilContext.data;
     dataInfo.state = newInfo.state;
     dataInfo.roaming = newInfo.roaming;
     dataInfo.emergencyCallsOnly = newInfo.emergencyCallsOnly;
     dataInfo.type = newInfo.type;
     // For the data connection, the `connected` flag indicates whether
     // there's an active data call.
     let apnSetting = this.apnSettings.byType.default;
     dataInfo.connected = false;
     if (apnSetting) {
       dataInfo.connected = (this.getDataCallStateByType("default") ==
                             RIL.GECKO_NETWORK_STATE_CONNECTED);
     }
 
     // Make sure we also reset the operator and signal strength information
     // if we drop off the network.
-    if (newInfo.regState !== RIL.NETWORK_CREG_STATE_REGISTERED_HOME &&
-        newInfo.regState !== RIL.NETWORK_CREG_STATE_REGISTERED_ROAMING) {
+    if (newInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
       dataInfo.cell = null;
       dataInfo.network = null;
       dataInfo.signalStrength = null;
       dataInfo.relSignalStrength = null;
     } else {
       dataInfo.cell = newInfo.cell;
+      dataInfo.network = this.operatorInfo;
     }
 
-    if (!newInfo.batch) {
+    if (!batch) {
       gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
                                                   this.clientId, dataInfo);
     }
     this.updateRILNetworkInterface();
   },
 
   /**
    * Handle data errors
@@ -1366,39 +1385,47 @@ RadioInterface.prototype = {
   networkChanged: function networkChanged(srcNetwork, destNetwork) {
     return !destNetwork ||
       destNetwork.longName != srcNetwork.longName ||
       destNetwork.shortName != srcNetwork.shortName ||
       destNetwork.mnc != srcNetwork.mnc ||
       destNetwork.mcc != srcNetwork.mcc;
   },
 
-  handleOperatorChange: function handleOperatorChange(message) {
+  /**
+   * Handle operator information changes.
+   *
+   * @param message The new operator information.
+   * @param batch   When batch is true, the RIL:VoiceInfoChanged and
+   *                RIL:DataInfoChanged message will not be sent.
+   */
+  handleOperatorChange: function handleOperatorChange(message, batch) {
+    let operatorInfo = this.operatorInfo;
     let voice = this.rilContext.voice;
     let data = this.rilContext.data;
 
-    if (this.networkChanged(message, voice.network)) {
+    if (this.networkChanged(message, operatorInfo)) {
+      this.updateInfo(message, operatorInfo);
+
       // Update lastKnownNetwork
       if (message.mcc && message.mnc) {
         try {
           Services.prefs.setCharPref("ril.lastKnownNetwork",
                                      message.mcc + "-" + message.mnc);
         } catch (e) {}
       }
 
-      voice.network = message;
-      if (!message.batch) {
+      // If the voice is unregistered, no need to send RIL:VoiceInfoChanged.
+      if (voice.network && !batch) {
         gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
                                                     this.clientId, voice);
       }
-    }
-
-    if (this.networkChanged(message, data.network)) {
-      data.network = message;
-      if (!message.batch) {
+
+      // If the data is unregistered, no need to send RIL:DataInfoChanged.
+      if (data.network && !batch) {
         gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
                                                     this.clientId, data);
       }
     }
   },
 
   handleRadioStateChange: function handleRadioStateChange(message) {
     this._changingRadioPower = false;