From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001
From: "Matt A. Tobin" <mattatobin@localhost.localdomain>
Date: Fri, 2 Feb 2018 04:16:08 -0500
Subject: Add m-esr52 at 52.6.0

---
 dom/secureelement/DOMSecureElement.js              | 612 +++++++++++++++++++++
 dom/secureelement/DOMSecureElement.manifest        |  14 +
 dom/secureelement/SEUtils.jsm                      | 116 ++++
 dom/secureelement/gonk/ACEService.js               | 139 +++++
 dom/secureelement/gonk/ACEService.manifest         |   2 +
 dom/secureelement/gonk/GPAccessRulesManager.js     | 436 +++++++++++++++
 .../gonk/GPAccessRulesManager.manifest             |   2 +
 dom/secureelement/gonk/SecureElement.js            | 514 +++++++++++++++++
 dom/secureelement/gonk/SecureElement.manifest      |  18 +
 dom/secureelement/gonk/UiccConnector.js            | 360 ++++++++++++
 dom/secureelement/gonk/UiccConnector.manifest      |  17 +
 dom/secureelement/gonk/gp_consts.js                |  62 +++
 .../gonk/nsIAccessControlEnforcer.idl              |  32 ++
 dom/secureelement/gonk/nsIAccessRulesManager.idl   |  50 ++
 .../gonk/nsISecureElementConnector.idl             | 124 +++++
 dom/secureelement/gonk/se_consts.js                |  68 +++
 dom/secureelement/moz.build                        |  40 ++
 dom/secureelement/tests/unit/header_helper.js      |   9 +
 dom/secureelement/tests/unit/test_SEUtils.js       | 228 ++++++++
 dom/secureelement/tests/unit/xpcshell.ini          |   5 +
 20 files changed, 2848 insertions(+)
 create mode 100644 dom/secureelement/DOMSecureElement.js
 create mode 100644 dom/secureelement/DOMSecureElement.manifest
 create mode 100644 dom/secureelement/SEUtils.jsm
 create mode 100644 dom/secureelement/gonk/ACEService.js
 create mode 100644 dom/secureelement/gonk/ACEService.manifest
 create mode 100644 dom/secureelement/gonk/GPAccessRulesManager.js
 create mode 100644 dom/secureelement/gonk/GPAccessRulesManager.manifest
 create mode 100644 dom/secureelement/gonk/SecureElement.js
 create mode 100644 dom/secureelement/gonk/SecureElement.manifest
 create mode 100644 dom/secureelement/gonk/UiccConnector.js
 create mode 100644 dom/secureelement/gonk/UiccConnector.manifest
 create mode 100644 dom/secureelement/gonk/gp_consts.js
 create mode 100644 dom/secureelement/gonk/nsIAccessControlEnforcer.idl
 create mode 100644 dom/secureelement/gonk/nsIAccessRulesManager.idl
 create mode 100644 dom/secureelement/gonk/nsISecureElementConnector.idl
 create mode 100644 dom/secureelement/gonk/se_consts.js
 create mode 100644 dom/secureelement/moz.build
 create mode 100644 dom/secureelement/tests/unit/header_helper.js
 create mode 100644 dom/secureelement/tests/unit/test_SEUtils.js
 create mode 100644 dom/secureelement/tests/unit/xpcshell.ini

(limited to 'dom/secureelement')

diff --git a/dom/secureelement/DOMSecureElement.js b/dom/secureelement/DOMSecureElement.js
new file mode 100644
index 000000000..48f7053b5
--- /dev/null
+++ b/dom/secureelement/DOMSecureElement.js
@@ -0,0 +1,612 @@
+/* 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/. */
+
+/* Copyright © 2014, Deutsche Telekom, Inc. */
+
+"use strict";
+
+/* globals dump, Components, XPCOMUtils, DOMRequestIpcHelper, cpmm, SE  */
+
+const DEBUG = false;
+function debug(s) {
+  if (DEBUG) {
+    dump("-*- SecureElement DOM: " + s + "\n");
+  }
+}
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsISyncMessageSender");
+
+XPCOMUtils.defineLazyGetter(this, "SE", function() {
+  let obj = {};
+  Cu.import("resource://gre/modules/se_consts.js", obj);
+  return obj;
+});
+
+// Extend / Inherit from Error object
+function SEError(name, message) {
+  this.name = name || SE.ERROR_GENERIC;
+  this.message = message || "";
+}
+
+SEError.prototype = {
+  __proto__: Error.prototype,
+};
+
+function PromiseHelpersSubclass(win) {
+  this._window = win;
+}
+
+PromiseHelpersSubclass.prototype = {
+  __proto__: DOMRequestIpcHelper.prototype,
+
+  _window: null,
+
+  _context: [],
+
+  createSEPromise: function createSEPromise(callback, /* optional */ ctx) {
+    let ctxCallback = (resolverId) => {
+      if (ctx) {
+        this._context[resolverId] = ctx;
+      }
+
+      callback(resolverId);
+    };
+
+    return this.createPromiseWithId((aResolverId) => {
+      ctxCallback(aResolverId);
+    });
+  },
+
+  takePromise: function takePromise(resolverId) {
+    let resolver = this.takePromiseResolver(resolverId);
+    if (!resolver) {
+      return;
+    }
+
+    // Get the context associated with this resolverId
+    let context = this._context[resolverId];
+    delete this._context[resolverId];
+
+    return {resolver: resolver, context: context};
+  },
+
+  rejectWithSEError: function rejectWithSEError(name, message) {
+    let error = new SEError(name, message);
+    debug("rejectWithSEError - " + error.toString());
+
+    return this._window.Promise.reject(Cu.cloneInto(error, this._window));
+  }
+};
+
+// Helper wrapper class to do promises related chores
+var PromiseHelpers;
+
+/**
+ * Instance of 'SEReaderImpl' class is the connector to a secure element.
+ * A reader may or may not have a secure element present, since some
+ * secure elements are removable in nature (eg:- 'uicc'). These
+ * Readers can be physical devices or virtual devices.
+ */
+function SEReaderImpl() {}
+
+SEReaderImpl.prototype = {
+  _window: null,
+
+  _sessions: [],
+
+  type: null,
+  _isSEPresent: false,
+
+  classID: Components.ID("{1c7bdba3-cd35-4f8b-a546-55b3232457d5}"),
+  contractID: "@mozilla.org/secureelement/reader;1",
+  QueryInterface: XPCOMUtils.generateQI([]),
+
+  // Chrome-only function
+  onSessionClose: function onSessionClose(sessionCtx) {
+    let index = this._sessions.indexOf(sessionCtx);
+    if (index != -1) {
+      this._sessions.splice(index, 1);
+    }
+  },
+
+  initialize: function initialize(win, type, isPresent) {
+    this._window = win;
+    this.type = type;
+    this._isSEPresent = isPresent;
+  },
+
+  _checkPresence: function _checkPresence() {
+    if (!this._isSEPresent) {
+      throw new Error(SE.ERROR_NOTPRESENT);
+    }
+  },
+
+  openSession: function openSession() {
+    this._checkPresence();
+
+    return PromiseHelpers.createSEPromise((resolverId) => {
+      let sessionImpl = new SESessionImpl();
+      sessionImpl.initialize(this._window, this);
+      this._window.SESession._create(this._window, sessionImpl);
+      this._sessions.push(sessionImpl);
+      PromiseHelpers.takePromiseResolver(resolverId)
+                    .resolve(sessionImpl.__DOM_IMPL__);
+    });
+  },
+
+  closeAll: function closeAll() {
+    this._checkPresence();
+
+    return PromiseHelpers.createSEPromise((resolverId) => {
+      let promises = [];
+      for (let session of this._sessions) {
+        if (!session.isClosed) {
+          promises.push(session.closeAll());
+        }
+      }
+
+      let resolver = PromiseHelpers.takePromiseResolver(resolverId);
+      // Wait till all the promises are resolved
+      Promise.all(promises).then(() => {
+        this._sessions = [];
+        resolver.resolve();
+      }, (reason) => {
+        let error = new SEError(SE.ERROR_BADSTATE,
+          "Unable to close all channels associated with this reader");
+        resolver.reject(Cu.cloneInto(error, this._window));
+      });
+    });
+  },
+
+  updateSEPresence: function updateSEPresence(isSEPresent) {
+    if (!isSEPresent) {
+      this.invalidate();
+      return;
+    }
+
+    this._isSEPresent = isSEPresent;
+  },
+
+  invalidate: function invalidate() {
+    debug("Invalidating SE reader: " + this.type);
+    this._isSEPresent = false;
+    this._sessions.forEach(s => s.invalidate());
+    this._sessions = [];
+  },
+
+  get isSEPresent() {
+    return this._isSEPresent;
+  }
+};
+
+/**
+ * Instance of 'SESessionImpl' object represent a connection session
+ * to one of the secure elements available on the device.
+ * These objects can be used to get a communication channel with an application
+ * hosted by the Secure Element.
+ */
+function SESessionImpl() {}
+
+SESessionImpl.prototype = {
+  _window: null,
+
+  _channels: [],
+
+  _isClosed: false,
+
+  _reader: null,
+
+  classID: Components.ID("{2b1809f8-17bd-4947-abd7-bdef1498561c}"),
+  contractID: "@mozilla.org/secureelement/session;1",
+  QueryInterface: XPCOMUtils.generateQI([]),
+
+  // Chrome-only function
+  onChannelOpen: function onChannelOpen(channelCtx) {
+    this._channels.push(channelCtx);
+  },
+
+  // Chrome-only function
+  onChannelClose: function onChannelClose(channelCtx) {
+    let index = this._channels.indexOf(channelCtx);
+    if (index != -1) {
+      this._channels.splice(index, 1);
+    }
+  },
+
+  initialize: function initialize(win, readerCtx) {
+    this._window = win;
+    this._reader = readerCtx;
+  },
+
+  openLogicalChannel: function openLogicalChannel(aid) {
+    if (this._isClosed) {
+      return PromiseHelpers.rejectWithSEError(SE.ERROR_BADSTATE,
+             "Session Already Closed!");
+    }
+
+    let aidLen = aid ? aid.length : 0;
+    if (aidLen < SE.MIN_AID_LEN || aidLen > SE.MAX_AID_LEN) {
+      return PromiseHelpers.rejectWithSEError(SE.ERROR_ILLEGALPARAMETER,
+             "Invalid AID length - " + aidLen);
+    }
+
+    return PromiseHelpers.createSEPromise((resolverId) => {
+      /**
+       * @params for 'SE:OpenChannel'
+       *
+       * resolverId  : ID that identifies this IPC request.
+       * aid         : AID that identifies the applet on SecureElement
+       * type        : Reader type ('uicc' / 'eSE')
+       * appId       : Current appId obtained from 'Principal' obj
+       */
+      cpmm.sendAsyncMessage("SE:OpenChannel", {
+        resolverId: resolverId,
+        aid: aid,
+        type: this.reader.type,
+        appId: this._window.document.nodePrincipal.appId
+      });
+    }, this);
+  },
+
+  closeAll: function closeAll() {
+    if (this._isClosed) {
+      return PromiseHelpers.rejectWithSEError(SE.ERROR_BADSTATE,
+             "Session Already Closed!");
+    }
+
+    return PromiseHelpers.createSEPromise((resolverId) => {
+      let promises = [];
+      for (let channel of this._channels) {
+        if (!channel.isClosed) {
+          promises.push(channel.close());
+        }
+      }
+
+      let resolver = PromiseHelpers.takePromiseResolver(resolverId);
+      Promise.all(promises).then(() => {
+        this._isClosed = true;
+        this._channels = [];
+        // Notify parent of this session instance's closure, so that its
+        // instance entry can be removed from the parent as well.
+        this._reader.onSessionClose(this.__DOM_IMPL__);
+        resolver.resolve();
+      }, (reason) => {
+        resolver.reject(new Error(SE.ERROR_BADSTATE +
+          "Unable to close all channels associated with this session"));
+      });
+    });
+  },
+
+  invalidate: function invlidate() {
+    this._isClosed = true;
+    this._channels.forEach(ch => ch.invalidate());
+    this._channels = [];
+  },
+
+  get reader() {
+    return this._reader.__DOM_IMPL__;
+  },
+
+  get isClosed() {
+    return this._isClosed;
+  },
+};
+
+/**
+ * Instance of 'SEChannelImpl' object represent an ISO/IEC 7816-4 specification
+ * channel opened to a secure element. It can be either a logical channel
+ * or basic channel.
+ */
+function SEChannelImpl() {}
+
+SEChannelImpl.prototype = {
+  _window: null,
+
+  _channelToken: null,
+
+  _isClosed: false,
+
+  _session: null,
+
+  openResponse: [],
+
+  type: null,
+
+  classID: Components.ID("{181ebcf4-5164-4e28-99f2-877ec6fa83b9}"),
+  contractID: "@mozilla.org/secureelement/channel;1",
+  QueryInterface: XPCOMUtils.generateQI([]),
+
+  // Chrome-only function
+  onClose: function onClose() {
+    this._isClosed = true;
+    // Notify the parent
+    this._session.onChannelClose(this.__DOM_IMPL__);
+  },
+
+  initialize: function initialize(win, channelToken, isBasicChannel,
+                                  openResponse, sessionCtx) {
+    this._window = win;
+    // Update the 'channel token' that identifies and represents this
+    // instance of the object
+    this._channelToken = channelToken;
+    // Update 'session' obj
+    this._session = sessionCtx;
+    this.openResponse = Cu.cloneInto(new Uint8Array(openResponse), win);
+    this.type = isBasicChannel ? "basic" : "logical";
+  },
+
+  transmit: function transmit(command) {
+    // TODO remove this once it will be possible to have a non-optional dict
+    // in the WebIDL
+    if (!command) {
+      return PromiseHelpers.rejectWithSEError(SE.ERROR_ILLEGALPARAMETER,
+             "SECommand dict must be defined");
+    }
+
+    if (this._isClosed) {
+      return PromiseHelpers.rejectWithSEError(SE.ERROR_BADSTATE,
+             "Channel Already Closed!");
+    }
+
+    let dataLen = command.data ? command.data.length : 0;
+    if (dataLen > SE.MAX_APDU_LEN) {
+      return PromiseHelpers.rejectWithSEError(SE.ERROR_ILLEGALPARAMETER,
+             " Command data length exceeds max limit - 255. " +
+             " Extended APDU is not supported!");
+    }
+
+    if ((command.cla & 0x80 === 0) && ((command.cla & 0x60) !== 0x20)) {
+      if (command.ins === SE.INS_MANAGE_CHANNEL) {
+        return PromiseHelpers.rejectWithSEError(SE.ERROR_SECURITY,
+               "MANAGE CHANNEL command not permitted");
+      }
+      if ((command.ins === SE.INS_SELECT) && (command.p1 == 0x04)) {
+        // SELECT by DF Name (p1=04) is not allowed
+        return PromiseHelpers.rejectWithSEError(SE.ERROR_SECURITY,
+               "SELECT command not permitted");
+      }
+      debug("Attempting to transmit an ISO command");
+    } else {
+      debug("Attempting to transmit GlobalPlatform command");
+    }
+
+    return PromiseHelpers.createSEPromise((resolverId) => {
+      /**
+       * @params for 'SE:TransmitAPDU'
+       *
+       * resolverId  : Id that identifies this IPC request.
+       * apdu        : Object containing APDU data
+       * channelToken: Token that identifies the current channel over which
+                       'c-apdu' is being sent.
+       * appId       : Current appId obtained from 'Principal' obj
+       */
+      cpmm.sendAsyncMessage("SE:TransmitAPDU", {
+        resolverId: resolverId,
+        apdu: command,
+        channelToken: this._channelToken,
+        appId: this._window.document.nodePrincipal.appId
+      });
+    }, this);
+  },
+
+  close: function close() {
+    if (this._isClosed) {
+      return PromiseHelpers.rejectWithSEError(SE.ERROR_BADSTATE,
+             "Channel Already Closed!");
+    }
+
+    return PromiseHelpers.createSEPromise((resolverId) => {
+      /**
+       * @params for 'SE:CloseChannel'
+       *
+       * resolverId  : Id that identifies this IPC request.
+       * channelToken: Token that identifies the current channel over which
+                       'c-apdu' is being sent.
+       * appId       : Current appId obtained from 'Principal' obj
+       */
+      cpmm.sendAsyncMessage("SE:CloseChannel", {
+        resolverId: resolverId,
+        channelToken: this._channelToken,
+        appId: this._window.document.nodePrincipal.appId
+      });
+    }, this);
+  },
+
+  invalidate: function invalidate() {
+    this._isClosed = true;
+  },
+
+  get session() {
+    return this._session.__DOM_IMPL__;
+  },
+
+  get isClosed() {
+    return this._isClosed;
+  },
+};
+
+function SEResponseImpl() {}
+
+SEResponseImpl.prototype = {
+  sw1: 0x00,
+
+  sw2: 0x00,
+
+  data: null,
+
+  _channel: null,
+
+  classID: Components.ID("{58bc6c7b-686c-47cc-8867-578a6ed23f4e}"),
+  contractID: "@mozilla.org/secureelement/response;1",
+  QueryInterface: XPCOMUtils.generateQI([]),
+
+  initialize: function initialize(sw1, sw2, response, channelCtx) {
+    // Update the status bytes
+    this.sw1 = sw1;
+    this.sw2 = sw2;
+    this.data = response ? response.slice(0) : null;
+    // Update the channel obj
+    this._channel = channelCtx;
+  },
+
+  get channel() {
+    return this._channel.__DOM_IMPL__;
+  }
+};
+
+/**
+ * SEManagerImpl
+ */
+function SEManagerImpl() {}
+
+SEManagerImpl.prototype = {
+  __proto__: DOMRequestIpcHelper.prototype,
+
+  _window: null,
+
+  classID: Components.ID("{4a8b6ec0-4674-11e4-916c-0800200c9a66}"),
+  contractID: "@mozilla.org/secureelement/manager;1",
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsIDOMGlobalPropertyInitializer,
+    Ci.nsISupportsWeakReference,
+    Ci.nsIObserver
+  ]),
+
+  _readers: [],
+
+  init: function init(win) {
+    this._window = win;
+    PromiseHelpers = new PromiseHelpersSubclass(this._window);
+
+    // Add the messages to be listened to.
+    const messages = ["SE:GetSEReadersResolved",
+                      "SE:OpenChannelResolved",
+                      "SE:CloseChannelResolved",
+                      "SE:TransmitAPDUResolved",
+                      "SE:GetSEReadersRejected",
+                      "SE:OpenChannelRejected",
+                      "SE:CloseChannelRejected",
+                      "SE:TransmitAPDURejected",
+                      "SE:ReaderPresenceChanged"];
+
+    this.initDOMRequestHelper(win, messages);
+  },
+
+  // This function will be called from DOMRequestIPCHelper.
+  uninit: function uninit() {
+    // All requests that are still pending need to be invalidated
+    // because the context is no longer valid.
+    this.forEachPromiseResolver((k) => {
+      this.takePromiseResolver(k).reject("Window Context got destroyed!");
+    });
+    PromiseHelpers = null;
+    this._window = null;
+  },
+
+  getSEReaders: function getSEReaders() {
+    // invalidate previous readers on new request
+    if (this._readers.length) {
+      this._readers.forEach(r => r.invalidate());
+      this._readers = [];
+    }
+
+    return PromiseHelpers.createSEPromise((resolverId) => {
+      cpmm.sendAsyncMessage("SE:GetSEReaders", {
+        resolverId: resolverId,
+        appId: this._window.document.nodePrincipal.appId
+      });
+    });
+  },
+
+  receiveMessage: function receiveMessage(message) {
+    DEBUG && debug("Message received: " + JSON.stringify(message));
+
+    let result = message.data.result;
+    let resolver = null;
+    let context = null;
+
+    let promiseResolver = PromiseHelpers.takePromise(result.resolverId);
+    if (promiseResolver) {
+      resolver = promiseResolver.resolver;
+      // This 'context' is the instance that originated this IPC message.
+      context = promiseResolver.context;
+    }
+
+    switch (message.name) {
+      case "SE:GetSEReadersResolved":
+        let readers = new this._window.Array();
+        result.readers.forEach(reader => {
+          let readerImpl = new SEReaderImpl();
+          readerImpl.initialize(this._window, reader.type, reader.isPresent);
+          this._window.SEReader._create(this._window, readerImpl);
+
+          this._readers.push(readerImpl);
+          readers.push(readerImpl.__DOM_IMPL__);
+        });
+        resolver.resolve(readers);
+        break;
+      case "SE:OpenChannelResolved":
+        let channelImpl = new SEChannelImpl();
+        channelImpl.initialize(this._window,
+                               result.channelToken,
+                               result.isBasicChannel,
+                               result.openResponse,
+                               context);
+        this._window.SEChannel._create(this._window, channelImpl);
+        if (context) {
+          // Notify context's handler with SEChannel instance
+          context.onChannelOpen(channelImpl);
+        }
+        resolver.resolve(channelImpl.__DOM_IMPL__);
+        break;
+      case "SE:TransmitAPDUResolved":
+        let responseImpl = new SEResponseImpl();
+        responseImpl.initialize(result.sw1,
+                                result.sw2,
+                                result.response,
+                                context);
+        this._window.SEResponse._create(this._window, responseImpl);
+        resolver.resolve(responseImpl.__DOM_IMPL__);
+        break;
+      case "SE:CloseChannelResolved":
+        if (context) {
+          // Notify context's onClose handler
+          context.onClose();
+        }
+        resolver.resolve();
+        break;
+      case "SE:GetSEReadersRejected":
+      case "SE:OpenChannelRejected":
+      case "SE:CloseChannelRejected":
+      case "SE:TransmitAPDURejected":
+        let error = new SEError(result.error, result.reason);
+        resolver.reject(Cu.cloneInto(error, this._window));
+        break;
+      case "SE:ReaderPresenceChanged":
+        debug("Reader " + result.type + " present: " + result.isPresent);
+        let reader = this._readers.find(r => r.type === result.type);
+        if (reader) {
+          reader.updateSEPresence(result.isPresent);
+        }
+        break;
+      default:
+        debug("Could not find a handler for " + message.name);
+        resolver.reject(Cu.cloneInto(new SEError(), this._window));
+        break;
+    }
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
+  SEResponseImpl, SEChannelImpl, SESessionImpl, SEReaderImpl, SEManagerImpl
+]);
diff --git a/dom/secureelement/DOMSecureElement.manifest b/dom/secureelement/DOMSecureElement.manifest
new file mode 100644
index 000000000..858227496
--- /dev/null
+++ b/dom/secureelement/DOMSecureElement.manifest
@@ -0,0 +1,14 @@
+component {4a8b6ec0-4674-11e4-916c-0800200c9a66} DOMSecureElement.js
+contract @mozilla.org/secureelement/manager;1 {4a8b6ec0-4674-11e4-916c-0800200c9a66}
+
+component {1c7bdba3-cd35-4f8b-a546-55b3232457d5} DOMSecureElement.js
+contract @mozilla.org/secureelement/reader;1 {1c7bdba3-cd35-4f8b-a546-55b3232457d5}
+
+component {2b1809f8-17bd-4947-abd7-bdef1498561c} DOMSecureElement.js
+contract @mozilla.org/secureelement/session;1 {2b1809f8-17bd-4947-abd7-bdef1498561c}
+
+component {181ebcf4-5164-4e28-99f2-877ec6fa83b9} DOMSecureElement.js
+contract @mozilla.org/secureelement/channel;1 {181ebcf4-5164-4e28-99f2-877ec6fa83b9}
+
+component {58bc6c7b-686c-47cc-8867-578a6ed23f4e} DOMSecureElement.js
+contract @mozilla.org/secureelement/response;1 {58bc6c7b-686c-47cc-8867-578a6ed23f4e}
diff --git a/dom/secureelement/SEUtils.jsm b/dom/secureelement/SEUtils.jsm
new file mode 100644
index 000000000..d5980b19c
--- /dev/null
+++ b/dom/secureelement/SEUtils.jsm
@@ -0,0 +1,116 @@
+/* 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/. */
+
+/* Copyright © 2015, Deutsche Telekom, Inc. */
+
+"use strict";
+
+this.SEUtils = {
+  byteArrayToHexString: function byteArrayToHexString(array) {
+    let hexStr = "";
+
+    let len = array ? array.length : 0;
+    for (let i = 0; i < len; i++) {
+      let hex = (array[i] & 0xff).toString(16);
+      hex = (hex.length === 1) ? "0" + hex : hex;
+      hexStr += hex;
+    }
+
+    return hexStr.toUpperCase();
+  },
+
+  hexStringToByteArray: function hexStringToByteArray(hexStr) {
+    if (typeof hexStr !== "string" || hexStr.length % 2 !== 0) {
+      return [];
+    }
+
+    let array = [];
+    for (let i = 0, len = hexStr.length; i < len; i += 2) {
+      array.push(parseInt(hexStr.substr(i, 2), 16));
+    }
+
+    return array;
+  },
+
+  arraysEqual: function arraysEqual(a1, a2) {
+    if (!a1 || !a2) {
+      return false;
+    }
+
+    if (a1.length !== a2.length) {
+      return false;
+    }
+
+    for (let i = 0, len = a1.length; i < len; i++) {
+      if (a1[i] !== a2[i]) {
+        return false;
+      }
+    }
+
+    return true;
+  },
+
+  ensureIsArray: function ensureIsArray(obj) {
+    return Array.isArray(obj) ? obj : [obj];
+  },
+
+  /**
+   * parseTLV is intended primarily to be used to parse Global Platform Device
+   * Technology secure element access control data.
+   *
+   * The parsed result value is an internal format only.
+   *
+   * All tags will be treated as simple Tag Length Values (TLV), (i.e. with a
+   * plain value, not subject to further unpacking), unless those tags are
+   * listed in the containerTags array.
+   *
+   * @param bytes - byte array
+   * @param containerTags - byte array of tags
+   */
+  parseTLV: function parseTLV(bytes, containerTags) {
+    let result = {};
+
+    if (typeof bytes === "string") {
+      bytes = this.hexStringToByteArray(bytes);
+    }
+
+    if (!Array.isArray(bytes)) {
+      debug("Passed value is not an array nor a string.");
+      return null;
+    }
+
+    for (let pos = 0; pos < bytes.length; ) {
+      let tag = bytes[pos],
+          length = bytes[pos + 1],
+          value = bytes.slice(pos + 2, pos + 2 + length),
+          parsed = null;
+
+      // Support for 0xFF padded files (GPD 7.1.2)
+      if (tag === 0xFF) {
+        break;
+      }
+
+      if (containerTags.indexOf(tag) >= 0) {
+        parsed = this.parseTLV(value, containerTags);
+      } else {
+        parsed = value;
+      }
+
+      // Internal parsed format.
+      if (!result[tag]) {
+        result[tag] = parsed;
+      } else if (Array.isArray(result[tag])) {
+        result[tag].push(parsed);
+      } else {
+        result[tag] = [result[tag], parsed];
+      }
+
+      pos = pos + 2 + length;
+    }
+
+    return result;
+  }
+};
+
+this.EXPORTED_SYMBOLS = ["SEUtils"];
diff --git a/dom/secureelement/gonk/ACEService.js b/dom/secureelement/gonk/ACEService.js
new file mode 100644
index 000000000..b52ba5fab
--- /dev/null
+++ b/dom/secureelement/gonk/ACEService.js
@@ -0,0 +1,139 @@
+/* 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/. */
+
+/* Copyright © 2015, Deutsche Telekom, Inc. */
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
+                                  "resource://gre/modules/SEUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "SE", function() {
+  let obj = {};
+  Cu.import("resource://gre/modules/se_consts.js", obj);
+  return obj;
+});
+
+var DEBUG = SE.DEBUG_ACE;
+function debug(msg) {
+  if (DEBUG) {
+    dump("ACEservice: " + msg + "\n");
+  }
+}
+
+/**
+ * Implements decision making algorithm as described in GPD specification,
+ * mostly in 3.1, 3.2 and 4.2.3.
+ *
+ * TODO: Bug 1137533: Implement GPAccessRulesManager APDU filters
+ */
+function GPAccessDecision(rules, certHash, aid) {
+  this.rules = rules;
+  this.certHash = certHash;
+  this.aid = aid;
+}
+
+GPAccessDecision.prototype = {
+  isAccessAllowed: function isAccessAllowed() {
+    // GPD SE Access Control v1.1, 3.4.1, Table 3-2: (Conflict resolution)
+    // If a specific rule allows, all other non-specific access is denied.
+    // Conflicting specific rules will resolve to the first Allowed == "true"
+    // match. Given no specific rule, the global "All" rules will determine
+    // access. "Some", skips further processing if access Allowed == "true".
+    //
+    // Access must be decided before the SE connector openChannel, and the
+    // exchangeAPDU call.
+    //
+    // NOTE: This implementation may change with the introduction of APDU
+    //       filters.
+    let decision = this.rules.some(this._decideAppAccess.bind(this));
+    return decision;
+  },
+
+  _decideAppAccess: function _decideAppAccess(rule) {
+    let appMatched, appletMatched;
+
+    // GPD SE AC 4.2.3: Algorithm for Applying Rules
+    // Specific rule overrides global rule.
+    //
+    // DeviceAppID is the application hash, and the AID is SE Applet ID:
+    //
+    // GPD SE AC 4.2.3 A:
+    //   SearchRuleFor(DeviceAppID, AID)
+    // GPD SE AC 4.2.3 B: If no rule fits A:
+    //   SearchRuleFor(<AllDeviceApplications>, AID)
+    // GPD SE AC 4.2.3 C: If no rule fits A or B:
+    //   SearchRuleFor(DeviceAppID, <AllSEApplications>)
+    // GPD SE AC 4.2.3 D: If no rule fits A, B, or C:
+    //   SearchRuleFor(<AllDeviceApplications>, <AllSEApplications>)
+
+    // Device App
+    appMatched = Array.isArray(rule.application) ?
+      // GPD SE AC 4.2.3 A and 4.2.3 C (DeviceAppID rule)
+      this._appCertHashMatches(rule.application) :
+      // GPD SE AC 4.2.3 B and 4.2.3 D (All Device Applications)
+      rule.application === Ci.nsIAccessRulesManager.ALLOW_ALL;
+
+    if (!appMatched) {
+      return false; // bail out early.
+    }
+
+    // SE Applet
+    appletMatched = Array.isArray(rule.applet) ?
+      // GPD SE AC 4.2.3 A and 4.2.3 B (AID rule)
+      SEUtils.arraysEqual(rule.applet, this.aid) :
+      // GPD SE AC 4.2.3 C and 4.2.3 D (All AID)
+      rule.applet === Ci.nsIAccessRulesManager.ALL_APPLET;
+
+    return appletMatched;
+  },
+
+  _appCertHashMatches: function _appCertHashMatches(hashArray) {
+    if (!Array.isArray(hashArray)) {
+      return false;
+    }
+
+    return !!(hashArray.find((hash) => {
+      return SEUtils.arraysEqual(hash, this.certHash);
+    }));
+  }
+};
+
+function ACEService() {
+  this._rulesManagers = new Map();
+
+  this._rulesManagers.set(
+    SE.TYPE_UICC,
+    Cc["@mozilla.org/secureelement/access-control/rules-manager;1"]
+      .createInstance(Ci.nsIAccessRulesManager));
+}
+
+ACEService.prototype = {
+  _rulesManagers: null,
+
+  isAccessAllowed: function isAccessAllowed(localId, seType, aid) {
+    if(!Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps")) {
+      debug("Certified apps debug enabled, allowing access");
+      return Promise.resolve(true);
+    }
+
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  _getDevCertHashForApp: function getDevCertHashForApp(manifestURL) {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  classID: Components.ID("{882a7463-2ca7-4d61-a89a-10eb6fd70478}"),
+  contractID: "@mozilla.org/secureelement/access-control/ace;1",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessControlEnforcer])
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ACEService]);
+
diff --git a/dom/secureelement/gonk/ACEService.manifest b/dom/secureelement/gonk/ACEService.manifest
new file mode 100644
index 000000000..40949c83d
--- /dev/null
+++ b/dom/secureelement/gonk/ACEService.manifest
@@ -0,0 +1,2 @@
+component {882a7463-2ca7-4d61-a89a-10eb6fd70478} ACEService.js
+contract @mozilla.org/secureelement/access-control/ace;1 {882a7463-2ca7-4d61-a89a-10eb6fd70478}
\ No newline at end of file
diff --git a/dom/secureelement/gonk/GPAccessRulesManager.js b/dom/secureelement/gonk/GPAccessRulesManager.js
new file mode 100644
index 000000000..dce11ec09
--- /dev/null
+++ b/dom/secureelement/gonk/GPAccessRulesManager.js
@@ -0,0 +1,436 @@
+/* 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/. */
+
+/* Copyright © 2015, Deutsche Telekom, Inc. */
+
+"use strict";
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
+
+XPCOMUtils.defineLazyServiceGetter(this, "UiccConnector",
+                                   "@mozilla.org/secureelement/connector/uicc;1",
+                                   "nsISecureElementConnector");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
+                                  "resource://gre/modules/SEUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "SE", function() {
+  let obj = {};
+  Cu.import("resource://gre/modules/se_consts.js", obj);
+  return obj;
+});
+
+XPCOMUtils.defineLazyGetter(this, "GP", function() {
+  let obj = {};
+  Cu.import("resource://gre/modules/gp_consts.js", obj);
+  return obj;
+});
+
+var DEBUG = SE.DEBUG_ACE;
+function debug(msg) {
+  if (DEBUG) {
+    dump("-*- GPAccessRulesManager " + msg);
+  }
+}
+
+/**
+ * Based on [1] - "GlobalPlatform Device Technology
+ * Secure Element Access Control Version 1.0".
+ * GPAccessRulesManager reads and parses access rules from SE file system
+ * as defined in section #7 of [1]: "Structure of Access Rule Files (ARF)".
+ * Rules retrieval from ARA-M applet is not implmented due to lack of
+ * commercial implemenations of ARA-M.
+ * @todo Bug 1137537: Implement ARA-M support according to section #4 of [1]
+ */
+function GPAccessRulesManager() {}
+
+GPAccessRulesManager.prototype = {
+  // source [1] section 7.1.3 PKCS#15 Selection
+  PKCS_AID: "a000000063504b43532d3135",
+
+  // APDUs (ISO 7816-4) for accessing rules on SE file system
+  // see for more details: http://www.cardwerk.com/smartcards/
+  // smartcard_standard_ISO7816-4_6_basic_interindustry_commands.aspx
+  READ_BINARY:   [GP.CLA_SM, GP.INS_RB, GP.P1_RB, GP.P2_RB],
+  GET_RESPONSE:  [GP.CLA_SM, GP.INS_GR, GP.P1_GR, GP.P2_GR],
+  SELECT_BY_DF:  [GP.CLA_SM, GP.INS_SF, GP.P1_SF_DF, GP.P2_SF_FCP],
+
+  // Non-null if there is a channel open
+  channel: null,
+
+  // Refresh tag path in the acMain file as described in GPD spec,
+  // sections 7.1.5 and C.1.
+  REFRESH_TAG_PATH: [GP.TAG_SEQUENCE, GP.TAG_OCTETSTRING],
+  refreshTag: null,
+
+  // Contains rules as read from the SE
+  rules: [],
+
+  // Returns the latest rules. Results are cached.
+  getAccessRules: function getAccessRules() {
+    debug("getAccessRules");
+
+    return new Promise((resolve, reject) => {
+      this._readAccessRules(() => resolve(this.rules));
+    });
+  },
+
+  _readAccessRules: Task.async(function*(done) {
+    try {
+      yield this._openChannel(this.PKCS_AID);
+
+      let odf = yield this._readODF();
+      let dodf = yield this._readDODF(odf);
+
+      let acmf = yield this._readACMF(dodf);
+      let refreshTag = acmf[this.REFRESH_TAG_PATH[0]]
+                           [this.REFRESH_TAG_PATH[1]];
+
+      // Update cached rules based on refreshTag.
+      if (SEUtils.arraysEqual(this.refreshTag, refreshTag)) {
+        debug("_readAccessRules: refresh tag equals to the one saved.");
+        yield this._closeChannel();
+        return done();
+      }
+
+      this.refreshTag = refreshTag;
+      debug("_readAccessRules: refresh tag saved: " + this.refreshTag);
+
+      let acrf = yield this._readACRules(acmf);
+      let accf = yield this._readACConditions(acrf);
+      this.rules = yield this._parseRules(acrf, accf);
+
+      DEBUG && debug("_readAccessRules: " + JSON.stringify(this.rules, 0, 2));
+
+      yield this._closeChannel();
+      done();
+    } catch (error) {
+      debug("_readAccessRules: " + error);
+      this.rules = [];
+      yield this._closeChannel();
+      done();
+    }
+  }),
+
+  _openChannel: function _openChannel(aid) {
+    if (this.channel !== null) {
+      debug("_openChannel: Channel already opened, rejecting.");
+      return Promise.reject();
+    }
+
+    return new Promise((resolve, reject) => {
+      UiccConnector.openChannel(aid, {
+        notifyOpenChannelSuccess: (channel, openResponse) => {
+          debug("_openChannel/notifyOpenChannelSuccess: Channel " + channel +
+                " opened, open response: " + openResponse);
+          this.channel = channel;
+          resolve();
+        },
+        notifyError: (error) => {
+          debug("_openChannel/notifyError: failed to open channel, error: " +
+                error);
+          reject(error);
+        }
+      });
+    });
+  },
+
+  _closeChannel: function _closeChannel() {
+    if (this.channel === null) {
+      debug("_closeChannel: Channel not opened, rejecting.");
+      return Promise.reject();
+    }
+
+    return new Promise((resolve, reject) => {
+      UiccConnector.closeChannel(this.channel, {
+        notifyCloseChannelSuccess: () => {
+          debug("_closeChannel/notifyCloseChannelSuccess: chanel " +
+                this.channel + " closed");
+          this.channel = null;
+          resolve();
+        },
+        notifyError: (error) => {
+          debug("_closeChannel/notifyError: error closing channel, error" +
+                error);
+          reject(error);
+        }
+      });
+    });
+  },
+
+  _exchangeAPDU: function _exchangeAPDU(bytes) {
+    DEBUG && debug("apdu " + JSON.stringify(bytes));
+
+    let apdu = this._bytesToAPDU(bytes);
+    return new Promise((resolve, reject) => {
+      UiccConnector.exchangeAPDU(this.channel, apdu.cla,
+        apdu.ins, apdu.p1, apdu.p2, apdu.data, apdu.le,
+        {
+          notifyExchangeAPDUResponse: (sw1, sw2, data) => {
+            debug("APDU response is " + sw1.toString(16) + sw2.toString(16) +
+                  " data: " + data);
+
+            // 90 00 is "success"
+            if (sw1 !== 0x90 && sw2 !== 0x00) {
+              debug("rejecting APDU response");
+              reject(new Error("Response " + sw1 + "," + sw2));
+              return;
+            }
+
+            resolve(this._parseTLV(data));
+          },
+
+          notifyError: (error) => {
+            debug("_exchangeAPDU/notifyError " + error);
+            reject(error);
+          }
+        }
+      );
+    });
+  },
+
+  _readBinaryFile: function _readBinaryFile(selectResponse) {
+    DEBUG && debug("Select response: " + JSON.stringify(selectResponse));
+    // 0x80 tag parameter - get the elementary file (EF) length
+    // without structural information.
+    let fileLength = selectResponse[GP.TAG_FCP][0x80];
+
+    // If file is empty, no need to attempt to read it.
+    if (fileLength[0] === 0 && fileLength[1] === 0) {
+      return Promise.resolve(null);
+    }
+
+    // TODO READ BINARY with filelength not supported
+    // let readApdu = this.READ_BINARY.concat(fileLength);
+    return this._exchangeAPDU(this.READ_BINARY);
+  },
+
+  _selectAndRead: function _selectAndRead(df) {
+    return this._exchangeAPDU(this.SELECT_BY_DF.concat(df.length & 0xFF, df))
+           .then((resp) => this._readBinaryFile(resp));
+  },
+
+  _readODF: function _readODF() {
+    debug("_readODF");
+    return this._selectAndRead(GP.ODF_DF);
+  },
+
+  _readDODF: function _readDODF(odfFile) {
+    debug("_readDODF, ODF file: " + odfFile);
+
+    // Data Object Directory File (DODF) is used as an entry point to the
+    // Access Control data. It is specified in PKCS#15 section 6.7.6.
+    // DODF is referenced by the ODF file, which looks as follows:
+    //   A7 06
+    //      30 04
+    //         04 02 XY WZ
+    // where [0xXY, 0xWZ] is a DF of DODF file.
+    let DODF_DF = odfFile[GP.TAG_EF_ODF][GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
+    return this._selectAndRead(DODF_DF);
+  },
+
+  _readACMF: function _readACMF(dodfFile) {
+    debug("_readACMF, DODF file: " + dodfFile);
+
+    // ACMF file DF is referenced in DODF file, which looks like this:
+    //
+    //  A1 29
+    //     30 00
+    //     30 0F
+    //        0C 0D 47 50 20 53 45 20 41 63 63 20 43 74 6C
+    //     A1 14
+    //        30 12
+    //           06 0A 2A 86 48 86 FC 6B 81 48 01 01  <-- GPD registered OID
+    //           30 04
+    //              04 02 AB CD  <-- ACMF DF
+    //  A1 2B
+    //     30 00
+    //     30 0F
+    //        0C 0D 53 41 54 53 41 20 47 54 4F 20 31 2E 31
+    //     A1 16
+    //        30 14
+    //           06 0C 2B 06 01 04 01 2A 02 6E 03 01 01 01  <-- some other OID
+    //           30 04
+    //              04 02 XY WZ  <-- some other file's DF
+    //
+    // DODF file consists of DataTypes with oidDO entries. Entry with OID
+    // equal to "1.2.840.114283.200.1.1" ("2A 86 48 86 FC 6B 81 48 01 01")
+    // contains DF of the ACMF. In the file above, it means that ACMF DF
+    // equals to [0xAB, 0xCD], and not [0xXY, 0xWZ].
+    //
+    // Algorithm used to encode OID to an byte array:
+    //   http://www.snmpsharpnet.com/?p=153
+
+    let gpdOid = [0x2A,                 // 1.2
+                  0x86, 0x48,           // 840
+                  0x86, 0xFC, 0x6B,     // 114283
+                  0x81, 0x48,           // 129
+                  0x01,                 // 1
+                  0x01];                // 1
+
+    let records = SEUtils.ensureIsArray(dodfFile[GP.TAG_EXTERNALDO]);
+
+    // Look for the OID registered for GPD SE.
+    let gpdRecords = records.filter((record) => {
+      let oid = record[GP.TAG_EXTERNALDO][GP.TAG_SEQUENCE][GP.TAG_OID];
+      return SEUtils.arraysEqual(oid, gpdOid);
+    });
+
+    // [1] 7.1.5: "There shall be only one ACMF file per Secure Element.
+    // If a Secure Element contains several ACMF files, then the security shall
+    // be considered compromised and the Access Control enforcer shall forbid
+    // access to all (...) apps."
+    if (gpdRecords.length !== 1) {
+      return Promise.reject(new Error(gpdRecords.length + " ACMF files found"));
+    }
+
+    let ACMain_DF = gpdRecords[0][GP.TAG_EXTERNALDO][GP.TAG_SEQUENCE]
+                                 [GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
+    return this._selectAndRead(ACMain_DF);
+  },
+
+  _readACRules: function _readACRules(acMainFile) {
+    debug("_readACRules, ACMain file: " + acMainFile);
+
+    // ACMF looks like this:
+    //
+    //  30 10
+    //    04 08 XX XX XX XX XX XX XX XX
+    //    30 04
+    //       04 02 XY WZ
+    //
+    // where [XY, WZ] is a DF of ACRF, and XX XX XX XX XX XX XX XX is a refresh
+    // tag.
+
+    let ACRules_DF = acMainFile[GP.TAG_SEQUENCE][GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
+    return this._selectAndRead(ACRules_DF);
+  },
+
+  _readACConditions: function _readACConditions(acRulesFile) {
+    debug("_readACCondition, ACRules file: " + acRulesFile);
+
+    let acRules = SEUtils.ensureIsArray(acRulesFile[GP.TAG_SEQUENCE]);
+    if (acRules.length === 0) {
+      debug("No rules found in ACRules file.");
+      return Promise.reject(new Error("No rules found in ACRules file"));
+    }
+
+    // We first read all the condition files referenced in the ACRules file,
+    // because ACRules file might reference one ACCondition file more than
+    // once. Since reading it isn't exactly fast, we optimize here.
+    let acReadQueue = Promise.resolve({});
+
+    acRules.forEach((ruleEntry) => {
+      let df = ruleEntry[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
+
+      // Promise chain read condition entries:
+      let readAcCondition = (acConditionFiles) => {
+        if (acConditionFiles[df] !== undefined) {
+          debug("Skipping previously read acCondition df: " + df);
+          return acConditionFiles;
+        }
+
+        return this._selectAndRead(df)
+               .then((acConditionFileContents) => {
+                 acConditionFiles[df] = acConditionFileContents;
+                 return acConditionFiles;
+               });
+      }
+
+      acReadQueue = acReadQueue.then(readAcCondition);
+    });
+
+    return acReadQueue;
+  },
+
+  _parseRules: function _parseRules(acRulesFile, acConditionFiles) {
+    DEBUG && debug("_parseRules: acConditionFiles " + JSON.stringify(acConditionFiles));
+    let rules = [];
+
+    let acRules = SEUtils.ensureIsArray(acRulesFile[GP.TAG_SEQUENCE]);
+    acRules.forEach((ruleEntry) => {
+      DEBUG && debug("Parsing one rule: " + JSON.stringify(ruleEntry));
+      let rule = {};
+
+      // 0xA0 and 0x82 tags as per GPD spec sections C.1 - C.3. 0xA0 means
+      // that rule describes access to one SE applet only (and its AID is
+      // given). 0x82 means that rule describes acccess to all SE applets.
+      let oneApplet = ruleEntry[GP.TAG_GPD_AID];
+      let allApplets = ruleEntry[GP.TAG_GPD_ALL];
+
+      if (oneApplet) {
+        rule.applet = oneApplet[GP.TAG_OCTETSTRING];
+      } else if (allApplets) {
+        rule.applet = Ci.nsIAccessRulesManager.ALL_APPLET;
+      } else {
+        throw Error("Unknown applet definition");
+      }
+
+      let df = ruleEntry[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
+      let condition = acConditionFiles[df];
+      if (condition === null) {
+        rule.application = Ci.nsIAccessRulesManager.DENY_ALL;
+      } else if (condition[GP.TAG_SEQUENCE]) {
+        if (!Array.isArray(condition[GP.TAG_SEQUENCE]) &&
+            !condition[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING]) {
+          rule.application = Ci.nsIAccessRulesManager.ALLOW_ALL;
+        } else {
+          rule.application = SEUtils.ensureIsArray(condition[GP.TAG_SEQUENCE])
+                             .map((conditionEntry) => {
+            return conditionEntry[GP.TAG_OCTETSTRING];
+          });
+        }
+      } else {
+        throw Error("Unknown application definition");
+      }
+
+      DEBUG && debug("Rule parsed, adding to the list: " + JSON.stringify(rule));
+      rules.push(rule);
+    });
+
+    DEBUG && debug("All rules parsed, we have those in total: " + JSON.stringify(rules));
+    return rules;
+  },
+
+  _parseTLV: function _parseTLV(bytes) {
+    let containerTags = [
+      GP.TAG_SEQUENCE,
+      GP.TAG_FCP,
+      GP.TAG_GPD_AID,
+      GP.TAG_EXTERNALDO,
+      GP.TAG_INDIRECT,
+      GP.TAG_EF_ODF
+    ];
+    return SEUtils.parseTLV(bytes, containerTags);
+  },
+
+  // TODO consider removing if better format for storing
+  // APDU consts will be introduced
+  _bytesToAPDU: function _bytesToAPDU(arr) {
+    let apdu = {
+      cla: arr[0] & 0xFF,
+      ins: arr[1] & 0xFF,
+      p1: arr[2] & 0xFF,
+      p2: arr[3] & 0xFF,
+      p3: arr[4] & 0xFF,
+      le: 0
+    };
+
+    let data = (apdu.p3 > 0) ? (arr.slice(5)) : [];
+    apdu.data = (data.length) ? SEUtils.byteArrayToHexString(data) : null;
+    return apdu;
+  },
+
+  classID: Components.ID("{3e046b4b-9e66-439a-97e0-98a69f39f55f}"),
+  contractID: "@mozilla.org/secureelement/access-control/rules-manager;1",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessRulesManager])
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([GPAccessRulesManager]);
diff --git a/dom/secureelement/gonk/GPAccessRulesManager.manifest b/dom/secureelement/gonk/GPAccessRulesManager.manifest
new file mode 100644
index 000000000..2d7ea038b
--- /dev/null
+++ b/dom/secureelement/gonk/GPAccessRulesManager.manifest
@@ -0,0 +1,2 @@
+component {3e046b4b-9e66-439a-97e0-98a69f39f55f} GPAccessRulesManager.js
+contract @mozilla.org/secureelement/access-control/rules-manager;1 {3e046b4b-9e66-439a-97e0-98a69f39f55f}
diff --git a/dom/secureelement/gonk/SecureElement.js b/dom/secureelement/gonk/SecureElement.js
new file mode 100644
index 000000000..144c6d8d6
--- /dev/null
+++ b/dom/secureelement/gonk/SecureElement.js
@@ -0,0 +1,514 @@
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Copyright © 2014, Deutsche Telekom, Inc. */
+
+"use strict";
+
+/* globals dump, Components, XPCOMUtils, SE, Services, UiccConnector,
+   SEUtils, ppmm, gMap, UUIDGenerator */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
+
+XPCOMUtils.defineLazyGetter(this, "SE", () => {
+  let obj = {};
+  Cu.import("resource://gre/modules/se_consts.js", obj);
+  return obj;
+});
+
+// set to true in se_consts.js to see debug messages
+var DEBUG = SE.DEBUG_SE;
+function debug(s) {
+  if (DEBUG) {
+    dump("-*- SecureElement: " + s + "\n");
+  }
+}
+
+const SE_IPC_SECUREELEMENT_MSG_NAMES = [
+  "SE:GetSEReaders",
+  "SE:OpenChannel",
+  "SE:CloseChannel",
+  "SE:TransmitAPDU"
+];
+
+const SECUREELEMENTMANAGER_CONTRACTID =
+  "@mozilla.org/secureelement/parent-manager;1";
+const SECUREELEMENTMANAGER_CID =
+  Components.ID("{48f4e650-28d2-11e4-8c21-0800200c9a66}");
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
+
+XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
+                                   "@mozilla.org/parentprocessmessagemanager;1",
+                                   "nsIMessageBroadcaster");
+
+XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
+                                   "@mozilla.org/uuid-generator;1",
+                                   "nsIUUIDGenerator");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
+                                  "resource://gre/modules/SEUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "UiccConnector", () => {
+  let uiccClass = Cc["@mozilla.org/secureelement/connector/uicc;1"];
+  return uiccClass ? uiccClass.getService(Ci.nsISecureElementConnector) : null;
+});
+
+function getConnector(type) {
+  switch (type) {
+    case SE.TYPE_UICC:
+      return UiccConnector;
+    case SE.TYPE_ESE:
+    default:
+      debug("Unsupported SEConnector : " + type);
+      return null;
+  }
+}
+
+/**
+ * 'gMap' is a nested dictionary object that manages all the information
+ * pertaining to channels for a given application (appId). It manages the
+ * relationship between given application and its opened channels.
+ */
+XPCOMUtils.defineLazyGetter(this, "gMap", function() {
+  return {
+    // example structure of AppInfoMap
+    // {
+    //   "appId1": {
+    //     target: target1,
+    //     channels: {
+    //       "channelToken1": {
+    //         seType: "uicc",
+    //         aid: "aid1",
+    //         channelNumber: 1
+    //       },
+    //       "channelToken2": { ... }
+    //     }
+    //   },
+    //  "appId2": { ... }
+    // }
+    appInfoMap: {},
+
+    registerSecureElementTarget: function(appId, target) {
+      if (this.isAppIdRegistered(appId)) {
+        debug("AppId: " + appId + "already registered");
+        return;
+      }
+
+      this.appInfoMap[appId] = {
+        target: target,
+        channels: {}
+      };
+
+      debug("Registered a new SE target " + appId);
+    },
+
+    unregisterSecureElementTarget: function(target) {
+      let appId = Object.keys(this.appInfoMap).find((id) => {
+        return this.appInfoMap[id].target === target;
+      });
+
+      if (!appId) {
+        return;
+      }
+
+      debug("Unregistered SE Target for AppId: " + appId);
+      delete this.appInfoMap[appId];
+    },
+
+    isAppIdRegistered: function(appId) {
+      return this.appInfoMap[appId] !== undefined;
+    },
+
+    getChannelCountByAppIdType: function(appId, type) {
+      return Object.keys(this.appInfoMap[appId].channels)
+                   .reduce((cnt, ch) => ch.type === type ? ++cnt : cnt, 0);
+    },
+
+    // Add channel to the appId. Upon successfully adding the entry
+    // this function will return the 'token'
+    addChannel: function(appId, type, aid, channelNumber) {
+      let token = UUIDGenerator.generateUUID().toString();
+      this.appInfoMap[appId].channels[token] = {
+        seType: type,
+        aid: aid,
+        channelNumber: channelNumber
+      };
+      return token;
+    },
+
+    removeChannel: function(appId, channelToken) {
+      if (this.appInfoMap[appId].channels[channelToken]) {
+        debug("Deleting channel with token : " + channelToken);
+        delete this.appInfoMap[appId].channels[channelToken];
+      }
+    },
+
+    getChannel: function(appId, channelToken) {
+      if (!this.appInfoMap[appId].channels[channelToken]) {
+        return null;
+      }
+
+      return this.appInfoMap[appId].channels[channelToken];
+    },
+
+    getChannelsByTarget: function(target) {
+      let appId = Object.keys(this.appInfoMap).find((id) => {
+        return this.appInfoMap[id].target === target;
+      });
+
+      if (!appId) {
+        return [];
+      }
+
+      return Object.keys(this.appInfoMap[appId].channels)
+                   .map(token => this.appInfoMap[appId].channels[token]);
+    },
+
+    getTargets: function() {
+      return Object.keys(this.appInfoMap)
+                   .map(appId => this.appInfoMap[appId].target);
+    },
+  };
+});
+
+/**
+ * 'SecureElementManager' is the main object that handles IPC messages from
+ * child process. It interacts with other objects such as 'gMap' & 'Connector
+ * instances (UiccConnector, eSEConnector)' to perform various
+ * SE-related (open, close, transmit) operations.
+ * @TODO: Bug 1118097 Support slot based SE/reader names
+ * @TODO: Bug 1118101 Introduce SE type specific permissions
+ */
+function SecureElementManager() {
+  this._registerMessageListeners();
+  this._registerSEListeners();
+  Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+  this._acEnforcer =
+    Cc["@mozilla.org/secureelement/access-control/ace;1"]
+    .getService(Ci.nsIAccessControlEnforcer);
+}
+
+SecureElementManager.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsIMessageListener,
+    Ci.nsISEListener,
+    Ci.nsIObserver]),
+  classID: SECUREELEMENTMANAGER_CID,
+  classInfo: XPCOMUtils.generateCI({
+    classID:          SECUREELEMENTMANAGER_CID,
+    classDescription: "SecureElementManager",
+    interfaces:       [Ci.nsIMessageListener,
+                       Ci.nsISEListener,
+                       Ci.nsIObserver]
+  }),
+
+  // Stores information about supported SE types and their presence.
+  // key: secure element type, value: (Boolean) is present/accessible
+  _sePresence: {},
+
+  _acEnforcer: null,
+
+  _shutdown: function() {
+    this._acEnforcer = null;
+    this.secureelement = null;
+    Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+    this._unregisterMessageListeners();
+    this._unregisterSEListeners();
+  },
+
+  _registerMessageListeners: function() {
+    ppmm.addMessageListener("child-process-shutdown", this);
+    for (let msgname of SE_IPC_SECUREELEMENT_MSG_NAMES) {
+      ppmm.addMessageListener(msgname, this);
+    }
+  },
+
+  _unregisterMessageListeners: function() {
+    ppmm.removeMessageListener("child-process-shutdown", this);
+    for (let msgname of SE_IPC_SECUREELEMENT_MSG_NAMES) {
+      ppmm.removeMessageListener(msgname, this);
+    }
+    ppmm = null;
+  },
+
+  _registerSEListeners: function() {
+    let connector = getConnector(SE.TYPE_UICC);
+    if (!connector) {
+      return;
+    }
+
+    this._sePresence[SE.TYPE_UICC] = false;
+    connector.registerListener(this);
+  },
+
+  _unregisterSEListeners: function() {
+    Object.keys(this._sePresence).forEach((type) => {
+      let connector = getConnector(type);
+      if (connector) {
+        connector.unregisterListener(this);
+      }
+    });
+
+    this._sePresence = {};
+  },
+
+  notifySEPresenceChanged: function(type, isPresent) {
+    // we need to notify all targets, even those without open channels,
+    // app could've stored the reader without actually using it
+    debug("notifying DOM about SE state change");
+    this._sePresence[type] = isPresent;
+    gMap.getTargets().forEach(target => {
+      let result = { type: type, isPresent: isPresent };
+      target.sendAsyncMessage("SE:ReaderPresenceChanged", { result: result });
+    });
+  },
+
+  _canOpenChannel: function(appId, type) {
+    let opened = gMap.getChannelCountByAppIdType(appId, type);
+    let limit = SE.MAX_CHANNELS_ALLOWED_PER_SESSION;
+    // UICC basic channel is not accessible see comment in se_consts.js
+    limit = type === SE.TYPE_UICC ? limit - 1 : limit;
+    return opened < limit;
+  },
+
+  _handleOpenChannel: function(msg, callback) {
+    if (!this._canOpenChannel(msg.appId, msg.type)) {
+      debug("Max channels per session exceed");
+      callback({ error: SE.ERROR_GENERIC });
+      return;
+    }
+
+    let connector = getConnector(msg.type);
+    if (!connector) {
+      debug("No SE connector available");
+      callback({ error: SE.ERROR_NOTPRESENT });
+      return;
+    }
+
+    this._acEnforcer.isAccessAllowed(msg.appId, msg.type, msg.aid)
+    .then((allowed) => {
+      if (!allowed) {
+        callback({ error: SE.ERROR_SECURITY });
+        return;
+      }
+      connector.openChannel(SEUtils.byteArrayToHexString(msg.aid), {
+
+        notifyOpenChannelSuccess: (channelNumber, openResponse) => {
+          // Add the new 'channel' to the map upon success
+          let channelToken =
+            gMap.addChannel(msg.appId, msg.type, msg.aid, channelNumber);
+          if (channelToken) {
+            callback({
+              error: SE.ERROR_NONE,
+              channelToken: channelToken,
+              isBasicChannel: (channelNumber === SE.BASIC_CHANNEL),
+              openResponse: SEUtils.hexStringToByteArray(openResponse)
+            });
+          } else {
+            callback({ error: SE.ERROR_GENERIC });
+          }
+        },
+
+        notifyError: (reason) => {
+          debug("Failed to open the channel to AID : " +
+                SEUtils.byteArrayToHexString(msg.aid) +
+                ", Rejected with Reason : " + reason);
+          callback({ error: SE.ERROR_GENERIC, reason: reason, response: [] });
+        }
+      });
+    })
+    .catch((error) => {
+      debug("Failed to get info from accessControlEnforcer " + error);
+      callback({ error: SE.ERROR_SECURITY });
+    });
+  },
+
+  _handleTransmit: function(msg, callback) {
+    let channel = gMap.getChannel(msg.appId, msg.channelToken);
+    if (!channel) {
+      debug("Invalid token:" + msg.channelToken + ", appId: " + msg.appId);
+      callback({ error: SE.ERROR_GENERIC });
+      return;
+    }
+
+    let connector = getConnector(channel.seType);
+    if (!connector) {
+      debug("No SE connector available");
+      callback({ error: SE.ERROR_NOTPRESENT });
+      return;
+    }
+
+    // Bug 1137533 - ACE GPAccessRulesManager APDU filters
+    connector.exchangeAPDU(channel.channelNumber, msg.apdu.cla, msg.apdu.ins,
+                           msg.apdu.p1, msg.apdu.p2,
+                           SEUtils.byteArrayToHexString(msg.apdu.data),
+                           msg.apdu.le, {
+      notifyExchangeAPDUResponse: (sw1, sw2, response) => {
+        callback({
+          error: SE.ERROR_NONE,
+          sw1: sw1,
+          sw2: sw2,
+          response: SEUtils.hexStringToByteArray(response)
+        });
+      },
+
+      notifyError: (reason) => {
+        debug("Transmit failed, rejected with Reason : " + reason);
+        callback({ error: SE.ERROR_INVALIDAPPLICATION, reason: reason });
+      }
+    });
+  },
+
+  _handleCloseChannel: function(msg, callback) {
+    let channel = gMap.getChannel(msg.appId, msg.channelToken);
+    if (!channel) {
+      debug("Invalid token:" + msg.channelToken + ", appId:" + msg.appId);
+      callback({ error: SE.ERROR_GENERIC });
+      return;
+    }
+
+    let connector = getConnector(channel.seType);
+    if (!connector) {
+      debug("No SE connector available");
+      callback({ error: SE.ERROR_NOTPRESENT });
+      return;
+    }
+
+    connector.closeChannel(channel.channelNumber, {
+      notifyCloseChannelSuccess: () => {
+        gMap.removeChannel(msg.appId, msg.channelToken);
+        callback({ error: SE.ERROR_NONE });
+      },
+
+      notifyError: (reason) => {
+        debug("Failed to close channel with token: " + msg.channelToken +
+              ", reason: "+ reason);
+        callback({ error: SE.ERROR_BADSTATE, reason: reason });
+      }
+    });
+  },
+
+  _handleGetSEReadersRequest: function(msg, target, callback) {
+    gMap.registerSecureElementTarget(msg.appId, target);
+    let readers = Object.keys(this._sePresence).map(type => {
+      return { type: type, isPresent: this._sePresence[type] };
+    });
+    callback({ readers: readers, error: SE.ERROR_NONE });
+  },
+
+  _handleChildProcessShutdown: function(target) {
+    let channels = gMap.getChannelsByTarget(target);
+
+    let createCb = (seType, channelNumber) => {
+      return {
+        notifyCloseChannelSuccess: () => {
+          debug("closed " + seType + ", channel " + channelNumber);
+        },
+
+        notifyError: (reason) => {
+          debug("Failed to close  " + seType + " channel " +
+                channelNumber + ", reason: " + reason);
+        }
+      };
+    };
+
+    channels.forEach((channel) => {
+      let connector = getConnector(channel.seType);
+      if (!connector) {
+        return;
+      }
+
+      connector.closeChannel(channel.channelNumber,
+                             createCb(channel.seType, channel.channelNumber));
+    });
+
+    gMap.unregisterSecureElementTarget(target);
+  },
+
+  _sendSEResponse: function(msg, result) {
+    let promiseStatus = (result.error === SE.ERROR_NONE) ? "Resolved" : "Rejected";
+    result.resolverId = msg.data.resolverId;
+    msg.target.sendAsyncMessage(msg.name + promiseStatus, {result: result});
+  },
+
+  _isValidMessage: function(msg) {
+    let appIdValid = gMap.isAppIdRegistered(msg.data.appId);
+    return msg.name === "SE:GetSEReaders" ? true : appIdValid;
+  },
+
+  /**
+   * nsIMessageListener interface methods.
+   */
+
+  receiveMessage: function(msg) {
+    DEBUG && debug("Received '" + msg.name + "' message from content process" +
+                   ": " + JSON.stringify(msg.data));
+
+    if (msg.name === "child-process-shutdown") {
+      this._handleChildProcessShutdown(msg.target);
+      return null;
+    }
+
+    if (SE_IPC_SECUREELEMENT_MSG_NAMES.indexOf(msg.name) !== -1) {
+      if (!msg.target.assertPermission("secureelement-manage")) {
+        debug("SecureElement message " + msg.name + " from a content process " +
+              "with no 'secureelement-manage' privileges.");
+        return null;
+      }
+    } else {
+      debug("Ignoring unknown message type: " + msg.name);
+      return null;
+    }
+
+    let callback = (result) => this._sendSEResponse(msg, result);
+    if (!this._isValidMessage(msg)) {
+      debug("Message not valid");
+      callback({ error: SE.ERROR_GENERIC });
+      return null;
+    }
+
+    switch (msg.name) {
+      case "SE:GetSEReaders":
+        this._handleGetSEReadersRequest(msg.data, msg.target, callback);
+        break;
+      case "SE:OpenChannel":
+        this._handleOpenChannel(msg.data, callback);
+        break;
+      case "SE:CloseChannel":
+        this._handleCloseChannel(msg.data, callback);
+        break;
+      case "SE:TransmitAPDU":
+        this._handleTransmit(msg.data, callback);
+        break;
+    }
+    return null;
+  },
+
+  /**
+   * nsIObserver interface methods.
+   */
+
+  observe: function(subject, topic, data) {
+    if (topic === NS_XPCOM_SHUTDOWN_OBSERVER_ID) {
+      this._shutdown();
+    }
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SecureElementManager]);
diff --git a/dom/secureelement/gonk/SecureElement.manifest b/dom/secureelement/gonk/SecureElement.manifest
new file mode 100644
index 000000000..a76fcfc11
--- /dev/null
+++ b/dom/secureelement/gonk/SecureElement.manifest
@@ -0,0 +1,18 @@
+# Copyright 2012 Mozilla Foundation and Mozilla contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# SecureElementManager
+component {48f4e650-28d2-11e4-8c21-0800200c9a66} SecureElement.js
+contract @mozilla.org/secureelement/parent-manager;1 {48f4e650-28d2-11e4-8c21-0800200c9a66}
+category profile-after-change SecureElementManager @mozilla.org/secureelement/parent-manager;1
diff --git a/dom/secureelement/gonk/UiccConnector.js b/dom/secureelement/gonk/UiccConnector.js
new file mode 100644
index 000000000..517303de2
--- /dev/null
+++ b/dom/secureelement/gonk/UiccConnector.js
@@ -0,0 +1,360 @@
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Copyright © 2014, Deutsche Telekom, Inc. */
+
+"use strict";
+
+/* globals Components, XPCOMUtils, SE, dump, libcutils, Services,
+   iccService, SEUtils */
+
+const { interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
+
+XPCOMUtils.defineLazyGetter(this, "SE", function() {
+  let obj = {};
+  Cu.import("resource://gre/modules/se_consts.js", obj);
+  return obj;
+});
+
+// set to true in se_consts.js to see debug messages
+var DEBUG = SE.DEBUG_CONNECTOR;
+function debug(s) {
+  if (DEBUG) {
+    dump("-*- UiccConnector: " + s + "\n");
+  }
+}
+
+XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
+                                  "resource://gre/modules/SEUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "iccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
+
+const UICCCONNECTOR_CONTRACTID =
+  "@mozilla.org/secureelement/connector/uicc;1";
+const UICCCONNECTOR_CID =
+  Components.ID("{8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4}");
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
+
+// TODO: Bug 1118099  - Add multi-sim support.
+// In the Multi-sim, there is more than one client.
+// For now, use default clientID as 0. Ideally, SE parent process would like to
+// know which clients (uicc slot) are connected to CLF over SWP interface.
+const PREFERRED_UICC_CLIENTID =
+  libcutils.property_get("ro.moz.se.def_client_id", "0");
+
+/**
+ * 'UiccConnector' object is a wrapper over iccService's channel management
+ * related interfaces that implements nsISecureElementConnector interface.
+ */
+function UiccConnector() {
+  this._init();
+}
+
+UiccConnector.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISecureElementConnector,
+                                         Ci.nsIIccListener]),
+  classID: UICCCONNECTOR_CID,
+  classInfo: XPCOMUtils.generateCI({
+    classID: UICCCONNECTOR_CID,
+    contractID: UICCCONNECTOR_CONTRACTID,
+    classDescription: "UiccConnector",
+    interfaces: [Ci.nsISecureElementConnector,
+                 Ci.nsIIccListener,
+                 Ci.nsIObserver]
+  }),
+
+  _SEListeners: [],
+  _isPresent: false,
+
+  _init: function() {
+    Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+    let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
+    icc.registerListener(this);
+
+    // Update the state in order to avoid race condition.
+    // By this time, 'notifyCardStateChanged (with proper card state)'
+    // may have occurred already before this module initialization.
+    this._updatePresenceState();
+  },
+
+  _shutdown: function() {
+    Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+    let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
+    icc.unregisterListener(this);
+  },
+
+  _updatePresenceState: function() {
+    let uiccNotReadyStates = [
+      Ci.nsIIcc.CARD_STATE_UNKNOWN,
+      Ci.nsIIcc.CARD_STATE_ILLEGAL,
+      Ci.nsIIcc.CARD_STATE_PERSONALIZATION_IN_PROGRESS,
+      Ci.nsIIcc.CARD_STATE_PERMANENT_BLOCKED,
+      Ci.nsIIcc.CARD_STATE_UNDETECTED
+    ];
+
+    let cardState = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID).cardState;
+    let uiccPresent = cardState !== null &&
+                      uiccNotReadyStates.indexOf(cardState) == -1;
+
+    if (this._isPresent === uiccPresent) {
+      return;
+    }
+
+    debug("Uicc presence changed " + this._isPresent + " -> " + uiccPresent);
+    this._isPresent = uiccPresent;
+    this._SEListeners.forEach((listener) => {
+      listener.notifySEPresenceChanged(SE.TYPE_UICC, this._isPresent);
+    });
+  },
+
+  // See GP Spec, 11.1.4 Class Byte Coding
+  _setChannelToCLAByte: function(cla, channel) {
+    if (channel < SE.LOGICAL_CHANNEL_NUMBER_LIMIT) {
+      // b7 = 0 indicates the first interindustry class byte coding
+      cla = (cla & 0x9C) & 0xFF | channel;
+    } else if (channel < SE.SUPPLEMENTARY_LOGICAL_CHANNEL_NUMBER_LIMIT) {
+      // b7 = 1 indicates the further interindustry class byte coding
+      cla = (cla & 0xB0) & 0xFF | 0x40 | (channel - SE.LOGICAL_CHANNEL_NUMBER_LIMIT);
+    } else {
+      debug("Channel number must be within [0..19]");
+      return SE.ERROR_GENERIC;
+    }
+    return cla;
+  },
+
+  _doGetOpenResponse: function(channel, length, callback) {
+    // Le value is set. It means that this is a request for all available
+    // response bytes.
+    let cla = this._setChannelToCLAByte(SE.CLA_GET_RESPONSE, channel);
+    this.exchangeAPDU(channel, cla, SE.INS_GET_RESPONSE, 0x00, 0x00,
+                      null, length, {
+      notifyExchangeAPDUResponse: function(sw1, sw2, response) {
+        debug("GET Response : " + response);
+        if (callback) {
+          callback({
+            error: SE.ERROR_NONE,
+            sw1: sw1,
+            sw2: sw2,
+            response: response
+          });
+        }
+      },
+
+      notifyError: function(reason) {
+        debug("Failed to get open response: " +
+              ", Rejected with Reason : " + reason);
+        if (callback) {
+          callback({ error: SE.ERROR_INVALIDAPPLICATION, reason: reason });
+        }
+      }
+    });
+  },
+
+  _doIccExchangeAPDU: function(channel, cla, ins, p1, p2, p3,
+                               data, appendResp, callback) {
+    let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
+    icc.iccExchangeAPDU(channel, cla & 0xFC, ins, p1, p2, p3, data, {
+      notifyExchangeAPDUResponse: (sw1, sw2, response) => {
+        debug("sw1 : " + sw1 + ", sw2 : " + sw2 + ", response : " + response);
+
+        // According to ETSI TS 102 221 , Section 7.2.2.3.1,
+        // Enforce 'Procedure bytes' checks before notifying the callback.
+        // Note that 'Procedure bytes'are special cases.
+        // There is no need to handle '0x60' procedure byte as it implies
+        // no-action from SE stack perspective. This procedure byte is not
+        // notified to application layer.
+        if (sw1 === 0x6C) {
+          // Use the previous command header with length as second procedure
+          // byte (SW2) as received and repeat the procedure.
+
+          // Recursive! and Pass empty response '' as args, since '0x6C'
+          // procedure does not have to deal with appended responses.
+          this._doIccExchangeAPDU(channel, cla, ins, p1, p2,
+                                  sw2, data, "", callback);
+        } else if (sw1 === 0x61) {
+          // Since the terminal waited for a second procedure byte and
+          // received it (sw2), send a GET RESPONSE command header to the UICC
+          // with a maximum length of 'XX', where 'XX' is the value of the
+          // second procedure byte (SW2).
+
+          let claWithChannel = this._setChannelToCLAByte(SE.CLA_GET_RESPONSE,
+                                                         channel);
+
+          // Recursive, with GET RESPONSE bytes and '0x61' procedure IS interested
+          // in appended responses. Pass appended response and note that p3=sw2.
+          this._doIccExchangeAPDU(channel, claWithChannel, SE.INS_GET_RESPONSE,
+                                  0x00, 0x00, sw2, null,
+                                  (response ? response + appendResp : appendResp),
+                                  callback);
+        } else if (callback) {
+          callback.notifyExchangeAPDUResponse(sw1, sw2, response);
+        }
+      },
+
+      notifyError: (reason) => {
+        debug("Failed to trasmit C-APDU over the channel #  : " + channel +
+              ", Rejected with Reason : " + reason);
+        if (callback) {
+          callback.notifyError(reason);
+        }
+      }
+    });
+  },
+
+  /**
+   * nsISecureElementConnector interface methods.
+   */
+
+  /**
+   * Opens a channel on a default clientId
+   */
+  openChannel: function(aid, callback) {
+    if (!this._isPresent) {
+      callback.notifyError(SE.ERROR_NOTPRESENT);
+      return;
+    }
+
+    // TODO: Bug 1118106: Handle Resource management / leaks by persisting
+    // the newly opened channel in some persistent storage so that when this
+    // module gets restarted (say after opening a channel) in the event of
+    // some erroneous conditions such as gecko restart /, crash it can read
+    // the persistent storage to check if there are any held resources
+    // (opened channels) and close them.
+    let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
+    icc.iccOpenChannel(aid, {
+      notifyOpenChannelSuccess: (channel) => {
+        this._doGetOpenResponse(channel, 0x00, function(result) {
+          if (callback) {
+            callback.notifyOpenChannelSuccess(channel, result.response);
+          }
+        });
+      },
+
+      notifyError: (reason) => {
+        debug("Failed to open the channel to AID : " + aid +
+              ", Rejected with Reason : " + reason);
+        if (callback) {
+          callback.notifyError(reason);
+        }
+      }
+    });
+  },
+
+  /**
+   * Transmit the C-APDU (command) on default clientId.
+   */
+  exchangeAPDU: function(channel, cla, ins, p1, p2, data, le, callback) {
+    if (!this._isPresent) {
+      callback.notifyError(SE.ERROR_NOTPRESENT);
+      return;
+    }
+
+    if (data && data.length % 2 !== 0) {
+      callback.notifyError("Data should be a hex string with length % 2 === 0");
+      return;
+    }
+
+    cla = this._setChannelToCLAByte(cla, channel);
+    let lc = data ? data.length / 2 : 0;
+    let p3 = lc || le;
+
+    if (lc && (le !== -1)) {
+      data += SEUtils.byteArrayToHexString([le]);
+    }
+
+    // Pass empty response '' as args as we are not interested in appended
+    // responses yet!
+    debug("exchangeAPDU on Channel # " + channel);
+    this._doIccExchangeAPDU(channel, cla, ins, p1, p2, p3, data, "",
+                            callback);
+  },
+
+  /**
+   * Closes the channel on default clientId.
+   */
+  closeChannel: function(channel, callback) {
+    if (!this._isPresent) {
+      callback.notifyError(SE.ERROR_NOTPRESENT);
+      return;
+    }
+
+    let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
+    icc.iccCloseChannel(channel, {
+      notifyCloseChannelSuccess: function() {
+        debug("closeChannel successfully closed the channel # : " + channel);
+        if (callback) {
+          callback.notifyCloseChannelSuccess();
+        }
+      },
+
+      notifyError: function(reason) {
+        debug("Failed to close the channel #  : " + channel +
+              ", Rejected with Reason : " + reason);
+        if (callback) {
+          callback.notifyError(reason);
+        }
+      }
+    });
+  },
+
+  registerListener: function(listener) {
+    if (this._SEListeners.indexOf(listener) !== -1) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    this._SEListeners.push(listener);
+    // immediately notify listener about the current state
+    listener.notifySEPresenceChanged(SE.TYPE_UICC, this._isPresent);
+  },
+
+  unregisterListener: function(listener) {
+    let idx = this._SEListeners.indexOf(listener);
+    if (idx !== -1) {
+      this._SEListeners.splice(idx, 1);
+    }
+  },
+
+  /**
+   * nsIIccListener interface methods.
+   */
+  notifyStkCommand: function() {},
+
+  notifyStkSessionEnd: function() {},
+
+  notifyIccInfoChanged: function() {},
+
+  notifyCardStateChanged: function() {
+    debug("Card state changed, updating UICC presence.");
+    this._updatePresenceState();
+  },
+
+  /**
+   * nsIObserver interface methods.
+   */
+
+  observe: function(subject, topic, data) {
+    if (topic === NS_XPCOM_SHUTDOWN_OBSERVER_ID) {
+      this._shutdown();
+    }
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UiccConnector]);
diff --git a/dom/secureelement/gonk/UiccConnector.manifest b/dom/secureelement/gonk/UiccConnector.manifest
new file mode 100644
index 000000000..5ac8b3b7b
--- /dev/null
+++ b/dom/secureelement/gonk/UiccConnector.manifest
@@ -0,0 +1,17 @@
+# Copyright 2012 Mozilla Foundation and Mozilla contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# UiccConnector
+component {8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4} UiccConnector.js
+contract @mozilla.org/secureelement/connector/uicc;1 {8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4}
diff --git a/dom/secureelement/gonk/gp_consts.js b/dom/secureelement/gonk/gp_consts.js
new file mode 100644
index 000000000..7c3bc7165
--- /dev/null
+++ b/dom/secureelement/gonk/gp_consts.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+/* Copyright © 2015, Deutsche Telekom, Inc. */
+
+/* Object Directory File (ODF) is an elementary file which contain
+   pointers to other EFs. It is specified in PKCS#15 section 6.7. */
+this.ODF_DF = [0x50, 0x31];
+
+/* ISO 7816-4: secure messaging */
+this.CLA_SM = 0x00;
+
+/* ISO 7816-4, 5.4.1 table 11 */
+this.INS_SF = 0xA4; // select file
+this.INS_GR = 0xC0; // get response
+this.INS_RB = 0xB0; // read binary
+
+/* ISO 7816-4: select file, see 6.11.3, table 58 & 59 */
+this.P1_SF_DF = 0x00; // select DF
+this.P2_SF_FCP = 0x04; // return FCP
+
+/* ISO 7816-4: read binary, 6.1.3. P1 and P2 describe offset of the first byte
+   to be read. We always read the whole files at the moment. */
+this.P1_RB = 0x00;
+this.P2_RB = 0x00;
+
+/* ISO 7816-4: get response, 7.1.3 table 74,  P1-P2 '0000' (other values RFU) */
+this.P1_GR = 0x00;
+this.P2_GR = 0x00;
+
+/* ISO 7816-4: 5.1.5 File Control Information, Table 1. For FCP and FMD. */
+this.TAG_PROPRIETARY = 0x00;
+this.TAG_NON_TLV = 0x53;
+this.TAG_BER_TLV = 0x73;
+
+/* ASN.1 tags */
+this.TAG_SEQUENCE = 0x30;
+this.TAG_OCTETSTRING = 0x04;
+this.TAG_OID = 0x06; // Object Identifier
+
+/* ISO 7816-4: 5.1.5 File Control Information, Templates. */
+this.TAG_FCP = 0x62; // File control parameters template
+this.TAG_FMD = 0x64; // File management data template
+this.TAG_FCI = 0x6F; // File control information template
+
+/* EF_DIR tags */
+this.TAG_APPLTEMPLATE = 0x61;
+this.TAG_APPLIDENTIFIER = 0x4F;
+this.TAG_APPLLABEL = 0x50;
+this.TAG_APPLPATH = 0x51;
+
+this.TAG_GPD_ALL = 0x82; // EF-ACRules - GPD spec. "all applets"
+
+/* Generic TLVs that are parsed */
+this.TAG_GPD_AID = 0xA0; // AID in the EF-ACRules - GPD spec, "one applet"
+this.TAG_EXTERNALDO = 0xA1; // External data objects - PKCS#15
+this.TAG_INDIRECT = 0xA5; // Indirect value.
+this.TAG_EF_ODF = 0xA7; // Elemenetary File Object Directory File
+
+// Allow this file to be imported via Components.utils.import().
+this.EXPORTED_SYMBOLS = Object.keys(this);
diff --git a/dom/secureelement/gonk/nsIAccessControlEnforcer.idl b/dom/secureelement/gonk/nsIAccessControlEnforcer.idl
new file mode 100644
index 000000000..7ad1a97f6
--- /dev/null
+++ b/dom/secureelement/gonk/nsIAccessControlEnforcer.idl
@@ -0,0 +1,32 @@
+/* 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/. */
+
+/* Copyright © 2015, Deutsche Telekom, Inc. */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(4994a960-26d9-4d71-82dd-4505bd97bf2a)]
+interface nsIAccessControlEnforcer : nsISupports
+{
+  /**
+   * Determines whether application identified by its ID should be allowed
+   * to access Secure Element's applet identified by its AID. Decision
+   * is made according to the GPD specification.
+   *
+   * @param  localId
+   *         ID of an application accessing SE
+   * @param  seType
+   *         Type of the SE.
+   * @param  aid
+   *         AID of a SE applet
+   * @return Promise which is resolved to true if access should be allowed,
+   *         false otherwise, and rejected if the application contains
+   *         no developer certificate.
+   */
+  jsval isAccessAllowed(in unsigned long localId,
+                        in DOMString seType,
+                        in DOMString aid);
+};
diff --git a/dom/secureelement/gonk/nsIAccessRulesManager.idl b/dom/secureelement/gonk/nsIAccessRulesManager.idl
new file mode 100644
index 000000000..173f57c90
--- /dev/null
+++ b/dom/secureelement/gonk/nsIAccessRulesManager.idl
@@ -0,0 +1,50 @@
+/* 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/. */
+
+/* Copyright © 2015, Deutsche Telekom, Inc. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(7baedd2a-3189-4b03-b2a3-34016043b5e2)]
+interface nsIAccessRulesManager : nsISupports
+{
+  /* Wildcard: rule allows all applications to access an SE applet */
+  const unsigned short ALLOW_ALL = 1;
+  /* Wildcard: rule denies all applications to access an SE applet */
+  const unsigned short DENY_ALL = 2;
+  /* Wildcard: rule allows application(s) access to all SE applets */
+  const unsigned short ALL_APPLET = 3;
+
+  /**
+   * Initiates Access Rules Manager, this should perform the initial
+   * reading of rules from access rule source
+   * @return Promise which is resolved if init is successful or rejected
+   *         otherwise
+   */
+  jsval init();
+
+  /**
+   * Retrieves all access rules.
+   *
+   * Rules are stored in an array. Each rule contains the following properties:
+   *  - applet - describes an SE applet referenced by this rule. Might equal
+   *             to an applet AID (as a byte array), or to a wildcard "all"
+   *             meaning all applets.
+   *  - application - describes an application referenced by this rule. Might
+   *                  be an array of developer certificate hashes (each as
+   *                  a byte array) in which case it lists all applications
+   *                  allowed access. Alternatively, might equal to wildcard
+   *                  "allowed-all" or "denied-all".
+   *
+   * Example rule format:
+   *   [{ applet: ALL_APPLET,
+   *      application: [[0x01, 0x02, ..., 0x20],
+   *                    [0x20, 0x19, ...., 0x01]],
+   *    { applet: [0x00, 0x01, ..., 0x05],
+   *      application: ALLOW_ALL}}]
+   *
+   * @return Promise which resolves with Array containing parsed access rules
+   */
+  jsval getAccessRules();
+};
diff --git a/dom/secureelement/gonk/nsISecureElementConnector.idl b/dom/secureelement/gonk/nsISecureElementConnector.idl
new file mode 100644
index 000000000..92cc1eb2b
--- /dev/null
+++ b/dom/secureelement/gonk/nsISecureElementConnector.idl
@@ -0,0 +1,124 @@
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(1ff3f35a-1b6f-4e65-a89e-a363b8604cd7)]
+interface nsISEChannelCallback : nsISupports
+{
+  /**
+   * Callback function to notify on successfully opening a logical channel.
+   *
+   * @param channel
+   *        The Channel Number/Handle that is successfully opened.
+   * @param openResponse
+   *        Response from SE for OpenChannel operation.
+   */
+  void notifyOpenChannelSuccess(in long channel, in DOMString openResponse);
+
+  /**
+   * Callback function to notify on successfully closing the logical channel.
+   *
+   */
+  void notifyCloseChannelSuccess();
+
+  /**
+   * Callback function to notify the status of 'seExchangeAPDU' command.
+   *
+   * @param sw1
+   *        Response's First Status Byte
+   * @param sw2
+   *        Response's Second Status Byte
+   * @param data
+   *        Response's data
+   */
+  void notifyExchangeAPDUResponse(in octet sw1,
+                                  in octet sw2,
+                                  in DOMString data);
+
+  /**
+   * Callback function to notify error
+   *
+   * @param error
+   *        Error describing the reason for failure.
+   */
+  void notifyError(in DOMString error);
+};
+
+[scriptable, uuid(417f59ee-f582-45b9-9a4e-e9dcefecb4f7)]
+interface nsISEListener : nsISupports
+{
+  void notifySEPresenceChanged(in DOMString seType, in boolean isPresent);
+};
+
+[scriptable, uuid(3cef313a-1d01-432d-9cd2-6610a80911f3)]
+interface nsISecureElementConnector : nsISupports
+{
+   /**
+    * Open a logical communication channel with the specific secure element type
+    *
+    * @param aid
+    *        Application Identifier of the Card Applet on the secure element.
+    * @param callback
+    *        callback to notify the result of the operation.
+    */
+    void openChannel(in DOMString aid,
+                     in nsISEChannelCallback callback);
+
+   /**
+    * Exchanges APDU channel with the specific secure element type
+    *
+    * @param channel
+    *        Channel on which C-APDU to be transmitted.
+    * @param cla
+             Class Byte.
+    * @param ins
+             Instruction Byte
+    * @param p1
+             Reference parameter first byte
+    * @param p2
+             Reference parameter second byte
+    *        Refer to 3G TS 31.101 , 10.2 'Command APDU Structure' for all the cases.
+    * @param data
+             Sequence of C-APDU data octets
+    * @param le [optional]
+    *        le is the length of expected response. If the response is not expected,
+             it should be explicitly set to -1.
+    * @param callback
+    *        callback to notify the result of the operation.
+    */
+    void exchangeAPDU(in long channel,
+                      in octet cla,
+                      in octet ins,
+                      in octet p1,
+                      in octet p2,
+                      in DOMString data,
+                      in short le,
+                      in nsISEChannelCallback callback);
+
+   /**
+    * Closes the logical communication channel to the specific secure element type
+    *
+    * @param channel
+    *        Channel to be closed.
+    * @param callback
+    *        callback to notify the result of the operation.
+    */
+   void closeChannel(in long channel,
+                     in nsISEChannelCallback callback);
+
+   /**
+    * Register a Secure Element listener
+    *
+    * @param listener
+    */
+   void registerListener(in nsISEListener listener);
+
+   /**
+    * Unregister a Secure Element listener
+    *
+    * @param listener
+    */
+   void unregisterListener(in nsISEListener listener);
+};
diff --git a/dom/secureelement/gonk/se_consts.js b/dom/secureelement/gonk/se_consts.js
new file mode 100644
index 000000000..13489b7ae
--- /dev/null
+++ b/dom/secureelement/gonk/se_consts.js
@@ -0,0 +1,68 @@
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Copyright © 2014, Deutsche Telekom, Inc. */
+
+// Set to true to debug SecureElement (SE) stack
+this.DEBUG_ALL = false;
+
+// Set individually to debug specific layers
+this.DEBUG_CONNECTOR = DEBUG_ALL || false;
+this.DEBUG_ACE = DEBUG_ALL || false ;
+this.DEBUG_SE = DEBUG_ALL || false ;
+
+// Maximun logical channels per session.
+// For 'uicc' SE type this value is 3, as opening a basic channel' : 0
+// is not allowed for security reasons. In such scenarios, possible
+// supplementary logical channels available are : [1, 2, or 3].
+// However,Other SE types may support upto max 4 (including '0').
+this.MAX_CHANNELS_ALLOWED_PER_SESSION = 4;
+
+this.BASIC_CHANNEL = 0;
+
+// According GPCardSpec 2.2
+this.MAX_APDU_LEN = 255; // including APDU header
+
+// CLA (1 byte) + INS (1 byte) + P1 (1 byte) + P2 (1 byte)
+this.APDU_HEADER_LEN = 4;
+
+this.LOGICAL_CHANNEL_NUMBER_LIMIT = 4;
+this.SUPPLEMENTARY_LOGICAL_CHANNEL_NUMBER_LIMIT = 20;
+
+this.MIN_AID_LEN = 5;
+this.MAX_AID_LEN = 16;
+
+this.CLA_GET_RESPONSE = 0x00;
+
+this.INS_SELECT = 0xA4;
+this.INS_MANAGE_CHANNEL = 0x70;
+this.INS_GET_RESPONSE = 0xC0;
+
+// Match the following errors with SecureElement.webidl's SEError enum values
+this.ERROR_NONE               = "";
+this.ERROR_SECURITY           = "SESecurityError";
+this.ERROR_IO                 = "SEIoError";
+this.ERROR_BADSTATE           = "SEBadStateError";
+this.ERROR_INVALIDCHANNEL     = "SEInvalidChannelError";
+this.ERROR_INVALIDAPPLICATION = "SEInvalidApplicationError";
+this.ERROR_GENERIC            = "SEGenericError";
+this.ERROR_NOTPRESENT         = "SENotPresentError";
+this.ERROR_ILLEGALPARAMETER   = "SEIllegalParameterError";
+
+this.TYPE_UICC = "uicc";
+this.TYPE_ESE = "eSE";
+
+// Allow this file to be imported via Components.utils.import().
+this.EXPORTED_SYMBOLS = Object.keys(this);
diff --git a/dom/secureelement/moz.build b/dom/secureelement/moz.build
new file mode 100644
index 000000000..a2c87b014
--- /dev/null
+++ b/dom/secureelement/moz.build
@@ -0,0 +1,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/.
+#
+# Copyright © 2014 Deutsche Telekom, Inc.
+
+if CONFIG['MOZ_SECUREELEMENT']:
+    EXTRA_COMPONENTS += [
+        'DOMSecureElement.js',
+        'DOMSecureElement.manifest',
+    ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_SECUREELEMENT']:
+    EXTRA_COMPONENTS += [
+        'gonk/ACEService.js',
+        'gonk/ACEService.manifest',
+        'gonk/GPAccessRulesManager.js',
+        'gonk/GPAccessRulesManager.manifest',
+        'gonk/SecureElement.js',
+        'gonk/SecureElement.manifest',
+    ]
+    XPIDL_MODULE = 'dom_secureelement'
+    XPIDL_SOURCES += [
+        'gonk/nsIAccessControlEnforcer.idl',
+        'gonk/nsIAccessRulesManager.idl',
+        'gonk/nsISecureElementConnector.idl',
+    ]
+    EXTRA_JS_MODULES += [
+        'gonk/gp_consts.js',
+        'gonk/se_consts.js',
+        'SEUtils.jsm'
+    ]
+    XPCSHELL_TESTS_MANIFESTS += [
+        'tests/unit/xpcshell.ini'
+    ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
diff --git a/dom/secureelement/tests/unit/header_helper.js b/dom/secureelement/tests/unit/header_helper.js
new file mode 100644
index 000000000..ff275e858
--- /dev/null
+++ b/dom/secureelement/tests/unit/header_helper.js
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Strips spaces, and returns a byte array.
+ */
+function formatHexAndCreateByteArray(hexStr) {
+  return SEUtils.hexStringToByteArray(hexStr.replace(/\s+/g, ""));
+}
diff --git a/dom/secureelement/tests/unit/test_SEUtils.js b/dom/secureelement/tests/unit/test_SEUtils.js
new file mode 100644
index 000000000..c06eca0e1
--- /dev/null
+++ b/dom/secureelement/tests/unit/test_SEUtils.js
@@ -0,0 +1,228 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* globals run_next_test, add_test, ok, Components, SEUtils */
+/* exported run_test */
+
+Components.utils.import("resource://gre/modules/SEUtils.jsm");
+var GP = {};
+Components.utils.import("resource://gre/modules/gp_consts.js", GP);
+
+const VALID_HEX_STR = "0123456789ABCDEF";
+const VALID_BYTE_ARR = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF];
+
+// This set should be what the actual ACE uses.
+var containerTags = [
+  GP.TAG_SEQUENCE,
+  GP.TAG_FCP,
+  GP.TAG_GPD_AID,
+  GP.TAG_EXTERNALDO,
+  GP.TAG_INDIRECT,
+  GP.TAG_EF_ODF
+];
+
+function run_test() {
+  ok(!!SEUtils, "SEUtils should be available");
+  run_next_test();
+}
+
+add_test(function test_byteArrayToHexString() {
+  let hexStr = SEUtils.byteArrayToHexString(VALID_BYTE_ARR);
+  ok(hexStr === VALID_HEX_STR,
+     "should convert byte Array to uppercased hex string");
+
+  [[], null, undefined].forEach((input) => {
+    hexStr = SEUtils.byteArrayToHexString(input);
+    ok(hexStr === "", "invalid arg:" + input + " should return empty string");
+  });
+
+  run_next_test();
+});
+
+add_test(function test_hexStringToByteArray() {
+  let byteArr = SEUtils.hexStringToByteArray(VALID_HEX_STR);
+  ok(SEUtils.arraysEqual(byteArr, VALID_BYTE_ARR),
+     "should convert uppercased string to byte Array");
+
+  byteArr = SEUtils.hexStringToByteArray(VALID_HEX_STR.toLowerCase());
+  ok(SEUtils.arraysEqual(byteArr, VALID_BYTE_ARR),
+     "should convert lowercased string to byte Array");
+
+  ["", null, undefined, "123"].forEach((input) => {
+    byteArr = SEUtils.hexStringToByteArray(input);
+    ok(Array.isArray(byteArr) && byteArr.length === 0,
+       "invalid arg: " + input + " should be empty Array");
+  });
+
+  run_next_test();
+});
+
+add_test(function test_arraysEqual() {
+  ok(SEUtils.arraysEqual([1, 2, 3], [1, 2, 3]),
+     "should return true on equal Arrays");
+
+  [[1], [1, 2, 4], [3, 2, 1]].forEach((input) => {
+    ok(!SEUtils.arraysEqual([1, 2, 3], input),
+       "should return false when Arrays not equal");
+  });
+
+  [null, undefined].forEach((input) => {
+    ok(!SEUtils.arraysEqual([1, 2, 3], input),
+       "should return false when comparing Array with invalid argument");
+
+    ok(!SEUtils.arraysEqual(input, input),
+       "should return false when both args are invalid");
+  });
+
+  run_next_test();
+});
+
+add_test(function test_ensureIsArray() {
+  let obj = {a: "a"};
+  let targetArray = [obj];
+  let result = null;
+
+  result = SEUtils.ensureIsArray(obj);
+  ok(targetArray[0].a === result[0].a,
+     "should return true if array element contains the same value");
+  deepEqual(result, targetArray,
+            "result should be deeply equal to targetArray");
+
+  result = SEUtils.ensureIsArray(targetArray);
+  deepEqual(result, targetArray,
+            "ensureIsAray with an array should return same array value.");
+
+  run_next_test();
+});
+
+add_test(function test_parseTLV_empty() {
+  let containerTags = [];
+  let result = null;
+
+  // Base:
+  result = SEUtils.parseTLV([], []);
+  deepEqual({}, result,
+     "empty parse input should result in an " +
+     "empty object (internal SEUtils format only).");
+  run_next_test();
+});
+
+add_test(function test_parseTLV_selectResponse() {
+  let result = null;
+  let hexStr = "62 27 82 02 78 21 83 02 7F 50 A5 06 83 04 00 04 C1 DC 8A" +
+               "01 05 8B 06 2F 06 01 16 00 14 C6 06 90 01 00 83 01 01 81" +
+               "02 FF FF";
+
+  let expected = {
+    0x62: {
+      0x82: [0x78, 0x21],
+      0x83: [0x7F, 0x50],
+      0xA5: {
+        0x83: [0x00, 0x04, 0xC1, 0xDC]
+      },
+      0x8A: [0x05],
+      0x8B: [0x2F, 0x06, 0x01, 0x16, 0x00, 0x14],
+      0xC6: [0x90, 0x01, 0x00, 0x83, 0x01, 0x01],
+      0x81: [0xFF, 0xFF]
+    }
+  };
+
+  result = SEUtils.parseTLV(formatHexAndCreateByteArray(hexStr), containerTags);
+  deepEqual(result, expected,
+            "parsed real selectResponse should equal the expected rules");
+  run_next_test();
+});
+
+add_test(function test_parseTLV_DODF() {
+  let result = null;
+  let hexStr = "A1 29 30 00 30 0F 0C 0D 47 50 20 53 45 20 41 63 63 20 43" +
+               "74 6C A1 14 30 12 06 0A 2A 86 48 86 FC 6B 81 48 01 01 30" +
+               "04 04 02 43 00 A1 2B 30 00 30 0F 0C 0D 53 41 54 53 41 20" +
+               "47 54 4F 20 31 2E 31 A1 16 30 14 06 0C 2B 06 01 04 01 2A" +
+               "02 6E 03 01 01 01 30 04 04 02 45 31 FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF";
+
+  let expected = {
+    0xA1: [
+      {
+        0x30: [
+          {},
+          {
+            0x0C: [0x47, 0x50, 0x20, 0x53, 0x45, 0x20, 0x41, 0x63, 0x63,
+                   0x20, 0x43, 0x74, 0x6C]
+          }
+        ],
+        0xA1: {
+          0x30: {
+            0x06: [0x2A, 0x86, 0x48, 0x86, 0xFC, 0x6B, 0x81, 0x48, 0x01,
+                   0x01],
+            0x30: {
+              0x04: [0x43, 0x00]
+            }
+          }
+        }
+      },
+      {
+        0x30: [
+          {},
+          {
+            0x0C: [0x53, 0x41, 0x54, 0x53, 0x41, 0x20, 0x47, 0x54, 0x4F,
+                   0x20, 0x31, 0x2E, 0x31]
+          }
+        ],
+        0xA1: {
+          0x30: {
+            0x06: [0x2B, 0x06, 0x01, 0x04, 0x01, 0x2A, 0x02, 0x6E, 0x03,
+                   0x01, 0x01, 0x01],
+            0x30: {
+              0x04: [0x45, 0x31]
+            }
+          }
+        }
+      }
+    ]
+  };
+
+  result = SEUtils.parseTLV(formatHexAndCreateByteArray(hexStr), containerTags);
+  deepEqual(result, expected,
+            "Real Access Control Enforcer DODF file, with 0xFF padding. " +
+            "Should equal expected rules.");
+  run_next_test();
+});
+
+add_test(function test_parseTLV_acRules() {
+  let result = null;
+  let hexStr = "30 08 82 00 30 04 04 02 43 11 FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF" +
+               "FF FF FF FF FF FF FF FF FF";
+
+  let expected = {
+    0x30: {
+      0x82: [],
+      0x30: {
+        0x04: [0x43, 0x11]
+      }
+    }
+  };
+
+  result = SEUtils.parseTLV(formatHexAndCreateByteArray(hexStr), containerTags);
+  deepEqual(result, expected,
+            "Parsed Access Control Rules should equal the expected rules");
+  run_next_test();
+});
diff --git a/dom/secureelement/tests/unit/xpcshell.ini b/dom/secureelement/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..924fd581f
--- /dev/null
+++ b/dom/secureelement/tests/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head = header_helper.js
+tail =
+
+[test_SEUtils.js]
\ No newline at end of file
-- 
cgit v1.2.3