Bug 1109592 - Only open connection to nfcd while NFC is switched on. r=allstars.chh
authorThomas Zimmermann <tdz@users.sourceforge.net>
Thu, 12 Mar 2015 07:43:00 -0400
changeset 233507 6c311023dcabbea2d89401f9a11dce3c1e4e8fd0
parent 233506 9379160419158cec5b8657620c8ebfa9fef44926
child 233508 226052175769bb2d401835741e45f10cf11bbed4
push id28414
push userryanvm@gmail.com
push dateFri, 13 Mar 2015 16:15:53 +0000
treeherdermozilla-central@1722c4635fac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersallstars.chh
bugs1109592
milestone39.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 1109592 - Only open connection to nfcd while NFC is switched on. r=allstars.chh With this patch Gecko only creates a connection to nfcd while NFC is switched on, and closes the connection after NFC has been switched off. While the connection is being opened, commands to nfcd are held in a waiting queue. Gecko flushes the queue after it connected successfully to nfcd. As a side effect of this patch, the NFC thread only exists while NFC is switched on.
dom/nfc/gonk/Nfc.js
dom/nfc/gonk/NfcService.cpp
--- a/dom/nfc/gonk/Nfc.js
+++ b/dom/nfc/gonk/Nfc.js
@@ -493,27 +493,16 @@ let SessionHelper = {
   },
 
   isP2PSession: function isP2PSession(id) {
     return (this.tokenMap[id] != null) && this.tokenMap[id].isP2P;
   }
 };
 
 function Nfc() {
-  debug("Starting Nfc Service");
-
-  let nfcService = Cc["@mozilla.org/nfc/service;1"].getService(Ci.nsINfcService);
-  if (!nfcService) {
-    debug("No nfc service component available!");
-    return;
-  }
-
-  nfcService.start(this);
-  this.nfcService = nfcService;
-
   gMessageManager.init(this);
 
   this.targetsByRequestId = {};
 }
 
 Nfc.prototype = {
 
   classID:   NFC_CID,
@@ -524,16 +513,49 @@ Nfc.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsINfcGonkEventListener]),
 
   rfState: NFC.NFC_RF_STATE_IDLE,
 
   nfcService: null,
 
   targetsByRequestId: null,
 
+  // temporary variables while NFC initialization is pending
+  pendingNfcService: null,
+  pendingMessageQueue: [],
+
+  /**
+   * Start NFC service
+   */
+  startNfcService: function startNfcService() {
+    debug("Starting Nfc Service");
+
+    let nfcService =
+      Cc["@mozilla.org/nfc/service;1"].getService(Ci.nsINfcService);
+    if (!nfcService) {
+      debug("No nfc service component available!");
+      return false;
+    }
+
+    nfcService.start(this);
+    this.pendingNfcService = nfcService;
+
+    return true;
+  },
+
+  /**
+   * Shutdown NFC service
+   */
+  shutdownNfcService : function shutdownNfcService() {
+    debug("Shutting down Nfc Service");
+
+    this.nfcService.shutdown();
+    this.nfcService = null;
+  },
+
   /**
    * Send arbitrary message to Nfc service.
    *
    * @param nfcMessageType
    *        A text message type.
    * @param message [optional]
    *        An optional message object to send.
    */
@@ -568,48 +590,51 @@ Nfc.prototype = {
       return null;
     }
     delete this.targetsByRequestId[requestId];
 
     return target;
   },
 
   /**
-   * Send Error response to content. This is used only
-   * in case of discovering an error in message received from
-   * content process.
+   * Send Error response to content process.
    *
    * @param message
    *        An nsIMessageListener's message parameter.
+   * @param errorMsg
+   *        A string with an error message.
    */
-  sendNfcErrorResponse: function sendNfcErrorResponse(message, errorCode) {
+  sendNfcErrorResponse: function sendNfcErrorResponse(message, errorMsg) {
     if (!message.target) {
       return;
     }
 
     let nfcMsgType = message.name + "Response";
-    message.data.errorMsg = this.getErrorMessage(errorCode);
+    message.data.errorMsg = errorMsg;
     message.target.sendAsyncMessage(nfcMsgType, message.data);
   },
 
-  getErrorMessage: function getErrorMessage(errorCode) {
-    return NFC.NFC_ERROR_MSG[errorCode];
-  },
-
   /**
    * Process the incoming message from the NFC Service.
    */
   onEvent: function onEvent(event) {
     let message = Cu.cloneInto(event, this);
     DEBUG && debug("Received message from NFC Service: " + JSON.stringify(message));
 
     message.type = message.rspType || message.ntfType;
     switch (message.type) {
       case NfcNotificationType.INITIALIZED:
-        // Do nothing.
+        this.nfcService = this.pendingNfcService;
+        // Send messages that have been queued up during initialization
+        // TODO: Bug 1141007: send error responses if the message
+        // indicates an error during initialization.
+        while (this.pendingMessageQueue.length) {
+          this.receiveMessage(this.pendingMessageQueue.shift());
+        }
+        this.pendingNfcService = null;
         break;
       case NfcNotificationType.TECH_DISCOVERED:
         // Update the upper layers with a session token (alias)
         message.sessionToken =
           SessionHelper.registerSession(message.sessionId, message.isP2P);
         // Do not expose the actual session to the content
         let sessionId = message.sessionId;
         delete message.sessionId;
@@ -643,16 +668,19 @@ Nfc.prototype = {
         break;
       case NfcResponseType.CHANGE_RF_STATE_RSP:
         this.sendNfcResponse(message);
 
         if (!message.errorMsg) {
           this.rfState = message.rfState;
           gMessageManager.onRFStateChanged(this.rfState);
         }
+        if (this.rfState == NFC.NFC_RF_STATE_IDLE) {
+          this.shutdownNfcService();
+        }
         break;
       case NfcResponseType.READ_NDEF_RSP: // Fall through.
       case NfcResponseType.WRITE_NDEF_RSP:
       case NfcResponseType.MAKE_READ_ONLY_RSP:
       case NfcResponseType.FORMAT_RSP:
       case NfcResponseType.TRANSCEIVE_RSP:
         this.sendNfcResponse(message);
         break;
@@ -680,24 +708,43 @@ Nfc.prototype = {
      */
     gSystemMessenger.broadcastMessage("nfc-hci-event-transaction", message);
   },
 
   /**
    * Process a message from the gMessageManager.
    */
   receiveMessage: function receiveMessage(message) {
-    // Return early if we don't need the NFC Service.
+    // Return early if we don't need the NFC Service. We won't start
+    // the NFC daemon here.
     switch (message.name) {
       case "NFC:QueryInfo":
         return {rfState: this.rfState};
       default:
         break;
     }
 
+    // Start NFC Service if necessary. Messages are held in a
+    // queue while initialization is being performed.
+    if (!this.nfcService) {
+      if ((message.name == "NFC:ChangeRFState") &&
+          (message.data.rfState != "idle") &&
+          !this.pendingNfcService) {
+        this.startNfcService(); // error handled in next branch
+      }
+      if (this.pendingNfcService) {
+        this.pendingMessageQueue.push(message);
+      } else {
+        this.sendNfcErrorResponse(message, "NotInitialize");
+      }
+      return;
+    }
+
+    // NFC Service is running and we have a message for it. This
+    // is the case during normal operation.
     if (message.name != "NFC:ChangeRFState") {
       // Update the current sessionId before sending to the NFC service.
       message.data.sessionId = SessionHelper.getId(message.data.sessionToken);
     }
 
     let command = CommandMsgTable[message.name];
     if (!command) {
       debug("Unknown message: " + message.name);
@@ -733,18 +780,24 @@ Nfc.prototype = {
    */
   observe: function(subject, topic, data) {
     if (topic != "profile-after-change") {
       debug("Should receive 'profile-after-change' only, received " + topic);
     }
   },
 
   shutdown: function shutdown() {
-    this.nfcService.shutdown();
-    this.nfcService = null;
+    // We shutdown before initialization has been completed. The
+    // pending messages will receive an error response.
+    while (this.pendingMessageQueue.length) {
+      this.sendNfcErrorResponse(this.pendingMessageQueue.shift(), "NotInitialize");
+    }
+    if (this.nfcService) {
+      this.shutdownNfcService();
+    }
   }
 };
 
 function NfcTechDiscoveredSysMsg(sessionToken, isP2P, records) {
   this.sessionToken = sessionToken;
   this.isP2P = isP2P;
   this.records = records;
 }
--- a/dom/nfc/gonk/NfcService.cpp
+++ b/dom/nfc/gonk/NfcService.cpp
@@ -259,17 +259,16 @@ public:
   }
 
 private:
   NfcMessageHandler* mHandler;
   nsAutoPtr<UnixSocketRawData> mData;
 };
 
 NfcService::NfcService()
-  : mConsumer(new NfcConsumer(this))
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!gNfcService);
 }
 
 NfcService::~NfcService()
 {
   MOZ_ASSERT(!gNfcService);
@@ -305,16 +304,18 @@ NfcService::Start(nsINfcGonkEventListene
     NS_WARNING("Can't create Nfc worker thread.");
     Shutdown();
     return NS_ERROR_FAILURE;
   }
 
   mListener = aListener;
   mHandler = new NfcMessageHandler();
 
+  mConsumer = new NfcConsumer(this);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 NfcService::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());