Apply smartcard-api.patch.

Change-Id: Iebb74aa0613469967af8510e6e86db0493e319e4
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..8e4f18c
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_AIDL_INCLUDES := packages/apps/SmartCardService/openmobileapi/src/org/simalliance/openmobileapi/service
+
+LOCAL_PACKAGE_NAME := SmartcardService
+LOCAL_CERTIFICATE := platform
+
+LOCAL_JAVA_LIBRARIES := core framework org.simalliance.openmobileapi
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..bc67321
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.simalliance.openmobileapi.service"
+      android:sharedUserId="org.simalliance.uid.openmobileapi"
+      android:versionCode="5" 
+      android:versionName="3.1.0">
+    
+    <permission android:label="SmartcardServicePermission label" 
+    			android:protectionLevel="dangerous" 
+    			android:name="org.simalliance.openmobileapi.SMARTCARD">
+   	</permission>
+    
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.NFC" />
+    <uses-permission android:name="com.android.nfc.permission.NFCEE_ADMIN" />
+    
+    <application android:label="SmartcardService">
+        
+        <uses-library android:name="org.simalliance.openmobileapi" android:required="true" />
+        
+        <service android:enabled="true"
+         		android:name="org.simalliance.openmobileapi.service.SmartcardService" 
+         		android:process=":remote"
+         		android:permission="org.simalliance.openmobileapi.SMARTCARD">
+            <intent-filter>
+                <action android:name="org.simalliance.openmobileapi.service.ISmartcardService" />
+            </intent-filter>
+        </service>
+        <receiver android:name="org.simalliance.openmobileapi.service.SmartcardServiceBootCompletedBroadcastReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest> 
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..f9dd75a
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,5 @@
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/app/SmartCardService.apk)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/SmartcardService_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/org.simalliance.openmobileapi.xml_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/JAVA_LIBRARIES/org.simalliance.openmobileapi_intermediates)
+
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..00ef47d
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,191 @@
+
+
+   Copyright 2010 Giesecke & Devrient GmbH for security package
+   Copyright (c) 2005-2008, The Android Open Source Project for all other source files
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/jni/Android.mk b/jni/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/jni/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/jni/assd/ASSDTerminal.cpp b/jni/assd/ASSDTerminal.cpp
new file mode 100644
index 0000000..698ad82
--- /dev/null
+++ b/jni/assd/ASSDTerminal.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "ASSDTerminal.h"
+
+#include <linux/ioctl.h>
+#include "assd.h"
+
+#include <stdlib.h>
+#include <fcntl.h>
+
+// #include <utils/Log.h>
+
+#define LOG_TAG "libassd"
+
+static int fd = -1;
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
+    return JNI_VERSION_1_6;
+}
+
+/*
+ * Class:     android_smartcard_terminals_ASSDTerminal
+ * Method:    Close
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_simalliance_openmobileapi_service_terminals_ASSDTerminal_Close
+(JNIEnv *env, jobject obj) {
+    if (fd >= 0)
+        close(fd);
+    fd = -1;
+}
+
+/*
+ * Class:     android_smartcard_terminals_ASSDTerminal
+ * Method:    Open
+ * Signature: (I)I
+ */
+JNIEXPORT jboolean JNICALL Java_org_simalliance_openmobileapi_service_terminals_ASSDTerminal_Open
+(JNIEnv *env, jobject obj) {
+    if (fd >= 0)
+        return false;
+
+    fd = open("/dev/assd", O_RDWR);
+    if (fd < 0)
+        return false;
+
+    if (ioctl(fd, ASSD_IOC_ENABLE)) {
+        close(fd);
+        fd = -1;
+        return false;
+    }
+
+    return true;
+}
+
+JNIEXPORT
+jboolean JNICALL Java_org_simalliance_openmobileapi_service_terminals_ASSDTerminal_IsPresent
+(JNIEnv *env, jobject obj) {
+    int result;
+    int f = fd;
+    if (fd < 0)
+        f = open("/dev/assd", O_RDWR);
+    if (f < 0)
+        return false;
+
+    result = ioctl(f, ASSD_IOC_PROBE);
+    if (fd < 0)
+        close(f);
+
+    if (result)
+        return false;
+
+    return true;
+}
+
+/*
+ * Class:     android_smartcard_terminals_ASSDTerminal
+ * Method:    Transmit
+ * Signature: (I[B)[B
+ */
+JNIEXPORT
+jbyteArray JNICALL Java_org_simalliance_openmobileapi_service_terminals_ASSDTerminal_Transmit
+(JNIEnv *env, jobject obj, jbyteArray jcommand) {
+    uint8_t* buf = NULL;
+    int resultLength;
+    jbyteArray result = NULL;
+    int commandLength = env->GetArrayLength(jcommand);
+    jbyte* command = env->GetByteArrayElements(jcommand, NULL);
+
+    if (command == NULL)
+        return NULL;
+    if ((fd < 0) || (commandLength < 1) || (commandLength > 510))
+        goto clean_and_return;
+
+    buf = (uint8_t*)malloc(512);
+    if (buf == NULL)
+        goto clean_and_return;
+
+    buf[0] = ((commandLength + 2) >> 8) & 0xff;
+    buf[1] = (commandLength + 2) & 0xff;
+    memcpy(&buf[2], command, commandLength);
+
+    if (ioctl(fd, ASSD_IOC_TRANSCEIVE, buf))
+        goto clean_and_return;
+
+    resultLength = ((buf[0] << 8) | buf[1]) - 2;
+    if ((resultLength < 1) || (resultLength > 510))
+        goto clean_and_return;
+
+    result = env->NewByteArray(resultLength);
+    if (result == NULL)
+        goto clean_and_return;
+
+    env->SetByteArrayRegion(result, 0, resultLength, (jbyte*)&buf[2]);
+
+clean_and_return:
+    if (buf != NULL)
+        free(buf);
+
+    env->ReleaseByteArrayElements(jcommand, command, JNI_ABORT);
+    return result;
+}
+
diff --git a/jni/assd/ASSDTerminal.h b/jni/assd/ASSDTerminal.h
new file mode 100644
index 0000000..f99d7c2
--- /dev/null
+++ b/jni/assd/ASSDTerminal.h
@@ -0,0 +1,45 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_simalliance_openmobileapi_service_terminals_ASSDTerminal */
+
+#ifndef _Included_org_simalliance_openmobileapi_service_terminals_ASSDTerminal
+#define _Included_org_simalliance_openmobileapi_service_terminals_ASSDTerminal
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_simalliance_openmobileapi_service_terminals_ASSDTerminal
+ * Method:    Close
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_simalliance_openmobileapi_service_terminals_ASSDTerminal_Close
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_simalliance_openmobileapi_service_terminals_ASSDTerminal
+ * Method:    Open
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_simalliance_openmobileapi_service_terminals_ASSDTerminal_Open
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_simalliance_openmobileapi_service_terminals_ASSDTerminal
+ * Method:    IsPresent
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_simalliance_openmobileapi_service_terminals_ASSDTerminal_IsPresent
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_simalliance_openmobileapi_service_terminals_ASSDTerminal
+ * Method:    Transmit
+ * Signature: ([B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_simalliance_openmobileapi_service_terminals_ASSDTerminal_Transmit
+  (JNIEnv *, jobject, jbyteArray);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/jni/assd/Android.mk b/jni/assd/Android.mk
new file mode 100644
index 0000000..72a530a
--- /dev/null
+++ b/jni/assd/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PRELINK_MODULE := false
+LOCAL_MODULE      := libassd
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES   := ASSDTerminal.cpp
+LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
+
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/jni/assd/assd.h b/jni/assd/assd.h
new file mode 100644
index 0000000..a6e9fb1
--- /dev/null
+++ b/jni/assd/assd.h
@@ -0,0 +1,45 @@
+
+#define ASSD_IOC_MAGIC	'A'
+
+/*
+ * Enable the ASSD capable card
+ *   - EBUSY - Function temporary not available
+ *   - ENODEV - No ASSD capable card is available
+ */
+#define ASSD_IOC_ENABLE	_IO(ASSD_IOC_MAGIC, 0)
+
+/*
+ * Transceive secure token to secure element
+ *   - EBUSY - Function temporary not available
+ *   - ENODEV - No ASSD capable card is available
+ *   - EINVAL - The secure token is invalid
+ *   - ETIMEDOUT - Timeout for communication with secure element reached
+ *   - EIO - Error in communication with secure element
+ */
+#define ASSD_IOC_TRANSCEIVE	_IOWR(ASSD_IOC_MAGIC, 1, char *)
+
+/*
+ * Probe if currently an ASSD capable card is available
+ *   - ENODEV - No ASSD capable card is available
+ */
+#define ASSD_IOC_PROBE	_IO(ASSD_IOC_MAGIC, 2)
+
+/*
+ * Wait until an ASSD capable card is available
+ *   - ENODEV - No ASSD capable card is available
+ *   - ETIMEOUT - The timeout was reached
+ */
+#define ASSD_IOC_WAIT	_IOW(ASSD_IOC_MAGIC, 3, int)
+
+/*
+ * Set timeout for communication with secure element
+ *   - EINVAL - The value is invalid
+ */
+#define ASSD_IOC_SET_TIMEOUT	_IOW(ASSD_IOC_MAGIC, 4, int)
+
+/*
+ * Get version information
+ *   - EFAULT - The output buffer is invalid
+ */
+#define ASSD_IOC_GET_VERSION	_IOR(ASSD_IOC_MAGIC, 5, char *)
+
diff --git a/openmobileapi/Android.mk b/openmobileapi/Android.mk
new file mode 100644
index 0000000..0cefe39
--- /dev/null
+++ b/openmobileapi/Android.mk
@@ -0,0 +1,36 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := org.simalliance.openmobileapi.xml
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+
+include $(BUILD_PREBUILT)
+
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += \
+      src/org/simalliance/openmobileapi/service/ISmartcardServiceCallback.aidl \
+      src/org/simalliance/openmobileapi/service/ISmartcardServiceReader.aidl \
+      src/org/simalliance/openmobileapi/service/ISmartcardServiceChannel.aidl \
+      src/org/simalliance/openmobileapi/service/ISmartcardServiceSession.aidl \
+      src/org/simalliance/openmobileapi/service/ISmartcardService.aidl
+
+LOCAL_AIDL_INCLUDES := packages/apps/SmartCardService/openmobileapi/src/org/simalliance/openmobileapi/service
+
+LOCAL_MODULE:= org.simalliance.openmobileapi
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_JAVA_LIBRARY)
+
+
+# put the classes.jar, with full class files instead of classes.dex inside, into the dist directory
+$(call dist-for-goals, droidcore, $(full_classes_jar):org.simalliance.openmobileapi.jar)
+
+
diff --git a/openmobileapi/org.simalliance.openmobileapi.xml b/openmobileapi/org.simalliance.openmobileapi.xml
new file mode 100644
index 0000000..643684d
--- /dev/null
+++ b/openmobileapi/org.simalliance.openmobileapi.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     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.
+-->
+
+<permissions>
+    <library name="org.simalliance.openmobileapi"
+            file="/system/framework/org.simalliance.openmobileapi.jar" />
+</permissions>
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/Channel.java b/openmobileapi/src/org/simalliance/openmobileapi/Channel.java
new file mode 100644
index 0000000..7fe5a32
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/Channel.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi;
+
+import java.io.IOException;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceChannel;
+import org.simalliance.openmobileapi.service.SmartcardError;
+
+import android.os.RemoteException;
+
+/**
+ * Instances of this class represent an ISO7816-4 channel opened to a secure
+ * element. It can be either a logical channel or the default channel. They can
+ * be used to send APDUs to the secure element. Channels are opened by calling
+ * the Session.openBasicChannel(byte[]) or Session.openLogicalChannel(byte[])
+ * methods.
+ * 
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v2.02</a>
+ */
+public class Channel {
+
+    private Session mSession;
+
+    private final ISmartcardServiceChannel mChannel;
+    private final SEService mService;
+    
+    private final Object mLock = new Object();
+
+    Channel(SEService service, Session session, ISmartcardServiceChannel channel) {
+    	mService = service;
+        mSession = session;
+        mChannel = channel;
+    }
+
+    /**
+     * Closes this channel to the Secure Element. If the method is called when the channel is already closed,
+     * this method will be ignored. The close() method shall wait for completion of any pending 
+     * transmit(byte[] command) before closing the channel.
+     */
+    public void close() {
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if (mChannel == null) {
+            throw new NullPointerException("channel must not be null");
+        }
+        SmartcardError error = new SmartcardError();
+        try {
+			mChannel.close(error);
+		} catch (RemoteException e) {
+		}
+        SEService.checkForException(error);
+    }
+
+    /**
+     * Tells if this channel is closed.
+     * 
+     * @return <code>true</code> if the channel is closed, <code>false</code> otherwise.
+     */
+    public boolean isClosed() {
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if (mChannel == null) {
+            throw new NullPointerException("channel must not be null");
+        }
+        try {
+			return mChannel.isClosed();
+		} catch (Exception e) {
+			throw new RuntimeException(e.getMessage());
+		}
+    }
+
+    /**
+     * Returns a boolean telling if this channel is the basic channel.
+     * 
+     * @return <code>true</code> if this channel is a basic channel. <code>false</code> if
+     *         this channel is a logical channel.
+     */
+    public boolean isBasicChannel() {
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if (mChannel == null) {
+            throw new NullPointerException("channel must not be null");
+        }
+        try {
+			return mChannel.isBasicChannel();
+		} catch (Exception e) {
+			throw new RuntimeException(e.getMessage());
+		}
+    }
+
+    /**
+     * Transmit an APDU command (as per ISO7816-4) to the secure element and
+     * wait for the response. The underlying layers might generate as much TPDUs
+     * as necessary to transport this APDU. The transport part is invisible from
+     * the application. <br>
+     * The system ensures the synchronization between all the concurrent calls
+     * to this method, and that only one APDU will be sent at a time,
+     * irrespective of the number of TPDUs that might be required to transport
+     * it to the SE. <br>
+     * The channel information in the class byte in the APDU will be completely
+     * ignored. The underlying system will add any required information to
+     * ensure the APDU is transported on this channel. There are restrictions on
+     * the set of commands that can be sent: <br>
+     * 
+     * <ul> 
+     * <li>MANAGE_CHANNEL commands are not allowed.</li> 
+     * <li>SELECT by DF Name (p1=04) are not allowed.</li>
+     * <li>CLA bytes with channel numbers are de-masked.</li> 
+     * </ul>
+     * 
+     * @param command the APDU command to be transmitted, as a byte array.
+     * 
+     * @return the response received, as a byte array.
+     * 
+     * @throws IOException if there is a communication problem to the reader or the Secure Element.
+     * @throws IllegalStateException if the channel is used after being closed.
+     * @throws IllegalArgumentException if the command byte array is less than 4 bytes long.
+     * @throws IllegalArgumentException if the length of the APDU is not coherent with the length of the command byte array.
+     * @throws SecurityException if the command is filtered by the security
+     *             policy
+     */
+    public byte[] transmit(byte[] command) throws IOException {
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if (mChannel == null) {
+            throw new NullPointerException("channel must not be null");
+        }
+
+        byte[] response;
+        synchronized( mLock ) {
+	        SmartcardError error = new SmartcardError();
+	        try {
+	            response = mChannel.transmit(command, error);
+	        } catch (Exception e) {
+	            throw new IOException(e.getMessage());
+	        }
+	        SEService.checkForException(error);
+        }
+        return response;
+    }
+    
+    /**
+     * Get the session that has opened this channel.
+     * 
+     * @return the session object this channel is bound to.
+     */
+    public Session getSession() {
+        return mSession;
+    }    
+    
+    /**
+     * Returns the data as received from the application select command inclusively the status word.
+     * The returned byte array contains the data bytes in the following order:
+     * [&lt;first data byte&gt;, ..., &lt;last data byte&gt;, &lt;sw1&gt;, &lt;sw2&gt;]
+     * @return The data as returned by the application select command inclusively the status word.
+     * Only the status word if the application select command has no returned data.
+     * Returns null if an application select command has not been performed or the selection response can not
+     * be retrieved by the reader implementation.
+     */
+    public byte[] getSelectResponse()
+    {
+       	if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if (mChannel == null) {
+            throw new NullPointerException("channel must not be null");
+        }
+        try {
+			if (mChannel.isClosed()) {
+			    throw new IllegalStateException("channel is closed");
+			}
+		} catch (Exception e1) {
+		    throw new RuntimeException(e1.getMessage());
+		}
+
+        byte[] response;
+        try {
+            response = mChannel.getSelectResponse();
+        } catch (Exception e) {
+		    throw new RuntimeException(e.getMessage());
+        }
+        
+        if(response != null && response.length == 0)
+    		response = null;
+    	return response;
+    }
+    
+    /**
+     * Performs a selection of the next Applet on this channel that matches to the partial AID specified   
+     * in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method.  
+     * This mechanism can be used by a device application to iterate through all Applets 
+     * matching to the same partial AID. 
+     * If selectNext() returns true a new Applet was successfully selected on this channel. 
+     * If no further Applet exists with matches to the partial AID this method returns false
+     * and the already selected Applet stays selected.
+     *
+     * @return <code>true</code> if new Applet was successfully selected.
+               <code>false</code> if no further Applet exists which matches the partial AID.
+     *
+     * @throws IOException if there is a communication problem to the reader or the Secure Element.
+     * @throws IllegalStateException if the channel is used after being closed or it is not connected.
+     * @throws SecurityException if the command is filtered by the security policy
+     */
+    public boolean selectNext() throws IOException {
+       	if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if (mChannel == null) {
+            throw new IllegalStateException("channel must not be null");
+        }
+        try {
+			if (mChannel.isClosed()) {
+			    throw new IllegalStateException("channel is closed");
+			}
+		} catch (Exception e1) {
+		    throw new RuntimeException(e1.getMessage());
+		}
+        
+        boolean response = false;
+        synchronized( mLock ) {
+	        SmartcardError error = new SmartcardError();
+	        try {
+	            response = mChannel.selectNext(error);
+	        } catch (Exception e) {
+	            throw new IOException(e.getMessage());
+	        }
+	        SEService.checkForException(error);
+        }
+        return response;
+    	
+    }
+    
+
+    // ******************************************************************
+    // package private methods
+    // ******************************************************************
+}
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/Reader.java b/openmobileapi/src/org/simalliance/openmobileapi/Reader.java
new file mode 100644
index 0000000..9253e54
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/Reader.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi;
+
+import java.io.IOException;
+import org.simalliance.openmobileapi.service.ISmartcardServiceReader;
+import org.simalliance.openmobileapi.service.ISmartcardServiceSession;
+import org.simalliance.openmobileapi.service.SmartcardError;
+import android.os.RemoteException;
+
+/**
+ * Instances of this class represent Secure Element Readers connected to this
+ * device. These Readers can be physical devices or virtual devices. They can be
+ * removable or not. They can contain Secure Element that can or cannot be
+ * removed.
+ * 
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v2.02</a>
+ */
+public class Reader {
+
+    private final String mName;
+    private final SEService mService;
+    private ISmartcardServiceReader mReader;
+    
+    private final Object mLock = new Object();
+
+
+    Reader(SEService service, String name ) {
+        mName = name;
+        mService = service;
+        mReader = null;
+        
+    }
+
+    /**
+     * Return the user-friendly name of this reader.
+     * <ul>
+	 * <li>If this reader is a SIM reader, then its name must start with the "SIM" prefix.</li>
+	 * <li>If the reader is a SD or micro SD reader, then its name must start with the "SD" prefix</li>
+	 * <li>If the reader is a embedded SE reader, then its name must start with the "eSE" prefix</li>
+	 * <ul>
+     * 
+     * @return name of this Reader
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Connects to a secure element in this reader. <br>
+     * This method prepares (initialises) the Secure Element for communication
+     * before the Session object is returned (e.g. powers the Secure Element by
+     * ICC ON if its not already on). There might be multiple sessions opened at
+     * the same time on the same reader. The system ensures the interleaving of
+     * APDUs between the respective sessions.
+     * 
+     * @throws IOException if something went wrong with the communicating to the
+     *             Secure Element or the reader.
+     * @return a Session object to be used to create Channels.
+     */
+    public Session openSession() throws IOException {
+
+    	if( mService == null || mService.isConnected() == false ){
+    		throw new IllegalStateException("service is not connected");
+    	}
+    	if( mReader == null ){
+    		try {
+    			mReader = mService.getReader(mName);
+    		} catch (Exception e) {
+    			throw new IOException("service reader cannot be accessed.");
+    		}
+    	}
+    	
+        synchronized (mLock) {
+        	SmartcardError error = new SmartcardError();
+        	ISmartcardServiceSession session;
+			try {
+				session = mReader.openSession(error);
+			} catch (RemoteException e) {
+				throw new IOException( e.getMessage() );
+			}
+        	SEService.checkForException(error);
+        	
+        	if( session == null ){
+        		throw new IOException( "service session is null." ); 
+        	}
+        	
+            return new Session(mService, session, this);
+        }
+    }
+
+    /**
+     * Check if a Secure Element is present in this reader.
+     * 
+     * @return <code>true</code> if the SE is present, <code>false</code> otherwise.
+     */
+    public boolean isSecureElementPresent() {
+    	if( mService == null || mService.isConnected() == false ){
+    		throw new IllegalStateException("service is not connected");
+    	}
+    	if( mReader == null ){
+    		try {
+    			mReader = mService.getReader(mName);
+    		} catch (Exception e) {
+    			throw new IllegalStateException("service reader cannot be accessed. " + e.getLocalizedMessage());
+    		}
+    	}
+
+    	SmartcardError error = new SmartcardError();
+    	boolean flag;
+		try {
+			flag = mReader.isSecureElementPresent(error);
+		} catch (RemoteException e) {
+			throw new RuntimeException(e.getMessage());
+		}
+    	SEService.checkForException(error);
+        return flag; 
+    }
+
+    /**
+     * Return the Secure Element service this reader is bound to.
+     * 
+     * @return the SEService object.
+     */
+    public SEService getSEService() {
+        return mService;
+    }
+
+    /**
+     * Close all the sessions opened on this reader. All the channels opened by
+     * all these sessions will be closed.
+     */
+    public void closeSessions() {
+    	if( mService == null || mService.isConnected() == false ){
+    		throw new IllegalStateException("service is not connected");
+    	}
+		if( mReader != null ) {
+	    	synchronized (mLock) {
+	        	SmartcardError error = new SmartcardError();
+	    		try {
+	    			mReader.closeSessions(error);
+	    		} catch (RemoteException e) {
+	    			throw new RuntimeException(e.getMessage());
+	    		}
+	        	SEService.checkForException(error);
+	        }
+    	}
+    }
+
+    // ******************************************************************
+    // package private methods
+    // ******************************************************************
+}
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/SEService.java b/openmobileapi/src/org/simalliance/openmobileapi/SEService.java
new file mode 100644
index 0000000..560c7b4
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/SEService.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi;
+
+import java.security.AccessControlException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import org.simalliance.openmobileapi.service.CardException;
+import org.simalliance.openmobileapi.service.ISmartcardService;
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.ISmartcardServiceReader;
+import org.simalliance.openmobileapi.service.SmartcardError;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * The SEService realises the communication to available Secure Elements on the
+ * device. This is the entry point of this API. It is used to connect to the
+ * infrastructure and get access to a list of Secure Element Readers.
+ * 
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v2.02</a>
+ */
+public class SEService {
+
+    private static final String SERVICE_TAG = "SEService";
+    
+    private final Object mLock = new Object();
+
+    /** The client context (e.g. activity). */
+    private final Context mContext;
+
+    /** The backend system. */
+    private volatile ISmartcardService mSmartcardService;
+
+    /**
+     * Class for interacting with the main interface of the backend.
+     */
+    private ServiceConnection mConnection;
+    
+    /**
+     * Collection of available readers
+     */
+    final private HashMap<String, Reader> mReaders = new HashMap<String,Reader>();
+
+    /**
+     * This implementation is used to receive callbacks from backend.
+     */
+    private final ISmartcardServiceCallback mCallback = new ISmartcardServiceCallback.Stub() {
+    };
+
+    /**
+     * Callback object that allows the notification of the caller if this
+     * SEService could be bound to the backend.
+     */
+    private CallBack mCallerCallback;
+
+    /**
+     * Interface to receive call-backs when the service is connected. If the
+     * target language and environment allows it, then this shall be an inner
+     * interface of the SEService class.
+     */
+    public interface CallBack {
+
+        /**
+         * Called by the framework when the service is connected.
+         * 
+         * @param service the connected service.
+         */
+        void serviceConnected(SEService service);
+    }
+
+    /**
+     * Establishes a new connection that can be used to connect to all the
+     * Secure Elements available in the system. The connection process can be
+     * quite long, so it happens in an asynchronous way. It is usable only if
+     * the specified listener is called or if isConnected() returns
+     * <code>true</code>. <br>
+     * The call-back object passed as a parameter will have its
+     * serviceConnected() method called when the connection actually happen.
+     * 
+     * @param context the context of the calling application. Cannot be
+     *            <code>null</code>.
+     * @param listener a SEService.CallBack object. Can be <code>null</code>.
+     */
+    public SEService(Context context, SEService.CallBack listener) {
+
+        if (context == null) {
+            throw new NullPointerException("context must not be null");
+        }
+        
+        mContext = context;
+        mCallerCallback = listener;
+
+        mConnection = new ServiceConnection() {
+
+            public synchronized void onServiceConnected(ComponentName className, IBinder service) {
+
+                mSmartcardService = ISmartcardService.Stub.asInterface(service);
+                if (mCallerCallback != null) {
+                    mCallerCallback.serviceConnected(SEService.this);
+                }
+                Log.v(SERVICE_TAG, "Service onServiceConnected");
+            }
+
+            public void onServiceDisconnected(ComponentName className) {
+                mSmartcardService = null;
+                Log.v(SERVICE_TAG, "Service onServiceDisconnected");
+            }
+        };
+
+        boolean bindingSuccessful = mContext.bindService(new Intent(ISmartcardService.class
+                .getName()), mConnection, Context.BIND_AUTO_CREATE);
+        if (bindingSuccessful) {
+            Log.v(SERVICE_TAG, "bindService successful");
+        }
+    }
+
+    /**
+     * Tells whether or not the service is connected.
+     * 
+     * @return <code>true</code> if the service is connected.
+     */
+    public boolean isConnected() {
+        if (mSmartcardService == null) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns the list of available Secure Element readers. More precisely it
+     * returns the list of readers that the calling application has the
+     * permission to connect to.
+     * 
+     * @return The readers list, as an array of Readers. If there are no readers the returned array is of length 0.
+     */
+    public Reader[] getReaders() {
+        if (mSmartcardService == null) {
+            throw new IllegalStateException("service not connected to system");
+        }
+
+        SmartcardError error = new SmartcardError();
+        String[] readerNames;
+        try {
+            readerNames = mSmartcardService.getReaders(error);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        mReaders.clear();
+        for (String readerName : readerNames) {
+			mReaders.put(readerName, new Reader( this, readerName ));
+        }
+        Collection<Reader> col = mReaders.values();
+        return col.toArray(new Reader[col.size()]);
+    }
+
+    /**
+     * Releases all Secure Elements resources allocated by this SEService. It is
+     * recommended to call this method in the termination method of the calling
+     * application (or part of this application) which is bound to this
+     * SEService.
+     * The SEService becomes invalid after calling shutdown().
+     */
+    public void shutdown() {
+        synchronized (mLock) {
+            if (mSmartcardService != null ) {
+            	Collection<Reader> col = mReaders.values();
+            	Iterator<Reader> iter = col.iterator();
+            	while( iter.hasNext() ) { 
+                    try {
+                    	Reader reader = iter.next();
+                        reader.closeSessions();
+                    } catch (Exception ignore) {
+                    }
+                }
+            }
+            try {
+                mContext.unbindService(mConnection);
+            } catch (IllegalArgumentException e) {
+                // Do nothing and fail silently since an error here indicates
+                // that binding never succeeded in the first place.
+            }
+            mSmartcardService = null;
+        }
+    }
+
+    // ******************************************************************
+    // package private methods
+    // ******************************************************************
+    
+    ISmartcardServiceReader getReader( String name ){
+    	
+        SmartcardError error = new SmartcardError();
+        ISmartcardServiceReader reader = null;
+        try {
+        	 reader = mSmartcardService.getReader(name, error);
+		} catch (RemoteException e) {
+			throw new RuntimeException(e.getMessage());
+		}
+        checkForException(error);
+        return reader;
+    }
+
+    static void checkForException(SmartcardError error) {
+        try {
+            error.throwException();
+        } catch (CardException exp) {
+            throw new IllegalStateException(exp.getMessage());
+        } catch (AccessControlException exp) {
+            throw new SecurityException(exp.getMessage());
+        }
+    }
+
+    ISmartcardServiceCallback getCallback() {
+		return mCallback;
+	}
+}
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/Session.java b/openmobileapi/src/org/simalliance/openmobileapi/Session.java
new file mode 100644
index 0000000..a174215
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/Session.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi;
+
+import java.io.IOException;
+import java.util.MissingResourceException;
+import java.util.NoSuchElementException;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceChannel;
+import org.simalliance.openmobileapi.service.ISmartcardServiceSession;
+import org.simalliance.openmobileapi.service.SmartcardError;
+
+import android.os.RemoteException;
+
+/**
+ * Instances of this class 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 in the secure element. This channel
+ * can be the basic channel or a logical channel.
+ * 
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v2.02</a>
+ */
+public class Session {
+
+	private final Object mLock = new Object();
+	private final SEService mService;
+    private final Reader mReader;
+    private final ISmartcardServiceSession mSession;
+
+    Session(SEService service, ISmartcardServiceSession session, Reader reader) {
+    	mService = service;
+        mReader = reader;
+        mSession = session;
+    }
+
+    /**
+     * Get the reader that provides this session.
+     * 
+     * @return The Reader object.
+     */
+    public Reader getReader() {
+        return mReader;
+    }
+
+    /**
+     * Get the Answer to Reset of this Secure Element. <br>
+     * The returned byte array can be null if the ATR for this Secure Element
+     * is not available.
+     * 
+     * @return the ATR as a byte array or null.
+     */
+    public byte[] getATR() {
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+    	if( mSession == null ){
+            throw new NullPointerException("service session is null");
+    	}
+        try {
+			return mSession.getAtr();
+		} catch (Exception e) {
+			return null;
+		}
+    }
+
+    /**
+     * Close the connection with the Secure Element. This will close any
+     * channels opened by this application with this Secure Element.
+     */
+    public void close() {
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if( mSession != null ){
+	        synchronized (mLock) {
+	        	SmartcardError error = new SmartcardError();
+	            try {
+					mSession.close(error);
+				} catch (RemoteException e) {
+					throw new RuntimeException(e.getMessage());
+				}
+	            SEService.checkForException(error);
+	        }
+        }
+    }
+
+    /**
+     * Tells if this session is closed.
+     * 
+     * @return <code>true</code> if the session is closed, false otherwise.
+     */
+    public boolean isClosed() {
+        try {
+        	if( mSession == null ){
+        		return true;
+        	}
+			return mSession.isClosed();
+		} catch (RemoteException e) {
+			throw new RuntimeException(e.getMessage());
+		}
+    }
+
+    /**
+     * Close any channel opened on this session.
+     * @throws IOException 
+     */
+    public void closeChannels() {
+
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+
+        if( mSession != null ){
+	        synchronized (mLock) {
+	        	SmartcardError error = new SmartcardError();
+	            try {
+					mSession.closeChannels(error);
+				} catch (RemoteException e) {
+					throw new RuntimeException(e.getMessage());
+				}
+	            SEService.checkForException(error);
+	        }
+        }
+    }
+    
+    
+    /**
+     * Get an access to the basic channel, as defined in the ISO/IEC 7816-4 specification (the one that has
+     * number 0). The obtained object is an instance of the Channel class.
+     * If the AID is null, which means no Applet is to be selected on this channel and the default Applet is
+     * used. If the AID is defined then the corresponding Applet is selected.
+     * Once this channel has been opened by a device application, it is considered as "locked" by this device
+     * application, and other calls to this method will return null, until the channel is closed. Some Secure
+     * Elements (like the UICC) might always keep the basic channel locked (i.e. return null to applications),
+     * to prevent access to the basic channel, while some other might return a channel object implementing
+     * some kind of filtering on the commands, restricting the set of accepted command to a smaller set.
+     * It is recommended for the UICC to reject the opening of the basic channel to a specific Applet, by
+     * always answering null to such a request.
+     * For other Secure Elements, the recommendation is to accept opening the basic channel on the default
+     * Applet until another Applet is selected on the basic channel. As there is no other way than a reset to
+     * select again the default Applet, the implementation of the transport API should guarantee that the
+     * openBasicChannel(null) command will return null until a reset occurs. If such a restriction is not
+     * possible, then openBasicChannel(null) should always return null and therefore prevent access to the
+     * default Applet on the basic channel.
+     * <p>
+     * 
+     * The optional select response data of an applet can be retrieved with byte[] getSelectResponse().
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a byte array, or null if no Applet is to be
+     * selected.
+     * @throws IOException if there is a communication problem to the reader or the Secure Element (e.g. if the SE is not responding).
+     * @throws IllegalStateException if the Secure Element session is used after being closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default application on this
+     *             session.
+     * 
+     * @throws NoSuchElementException if an Applet with the defined AID does not exist in the SE
+     *
+     * @return an instance of Channel if available or null.
+     */
+    public Channel openBasicChannel(byte[] aid) throws IOException {
+
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if( mSession == null ){
+    		throw new NullPointerException("service session is null");
+    	}
+        if (getReader() == null) {
+            throw new NullPointerException("reader must not be null");
+        }
+    	
+        synchronized (mLock) {
+            ISmartcardServiceChannel channel;
+            SmartcardError error = new SmartcardError();
+            try {
+                channel = mSession.openBasicChannelAid(
+                		aid,
+                        mService.getCallback(), 
+                        error);
+            } catch (Exception e) {
+                throw new IOException(e.getMessage());
+            }
+            SEService.checkForException(error);
+            error.clear();
+            boolean b = basicChannelInUse(error);
+            SEService.checkForException(error);
+            if ( b ) {
+            	return null;
+            }
+            error.clear();
+            b = channelCannotBeEstablished(error);            
+            SEService.checkForException(error);
+            if (b) {
+                return null;
+            }
+            if(aid == null || aid.length == 0)
+            {
+                error.clear();
+                b = isDefaultApplicationSelected(error);
+                SEService.checkForException(error);
+    	        if (!b) {
+    	            return null;
+    	        }
+            }
+            error.clear();
+            checkIfAppletAvailable(error);
+            SEService.checkForException(error);
+
+            if (channel == null)
+        	   return null;
+
+            return new Channel(mService, this, channel );
+        }
+    }
+
+    /**
+     * Open a logical channel with the Secure Element, selecting the Applet
+     * represented by the given AID. The AID can be null, which means no
+     * Applet is to be selected on this channel, the default Applet is
+     * used. It's up to the Secure Element to choose which logical channel will
+     * be used. 
+     * <p>
+     * The optional select response data of an applet can be retrieved with byte[] getSelectResponse().
+     * <p>
+     * A logical channel to an applet can be opened multiple times if the applet implements MultiSelectable.
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as
+     *            a byte array.
+     * @throws IOException if there is a communication problem to the reader or the Secure Element. (e.g. if the SE is
+	 *	not responding)
+     * @throws IllegalStateException if the Secure Element is used after being
+     *             closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default application on this
+     *             session.
+     * @throws NoSuchElementException if an Applet with the defined AID does not exist in the SE or a logical channel is already open to a non-multiselectable applet
+     * 
+     * @return an instance of Channel. Null if the Secure Element is unable to
+     *         provide a new logical channel.
+     */
+    public Channel openLogicalChannel(byte[] aid) throws IOException {
+
+        if (mService == null || mService.isConnected() == false) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        if( mSession == null ){
+    		throw new NullPointerException("service session is null");
+    	}
+        if (getReader() == null) {
+            throw new NullPointerException("reader must not be null");
+        }
+        synchronized (mLock) {
+            SmartcardError error = new SmartcardError();
+            ISmartcardServiceChannel channel;
+            try {
+                channel = mSession.openLogicalChannel(
+                		aid,
+                        mService.getCallback(),
+                        error);
+            } catch (Exception e) {
+                throw new IOException(e.getMessage());
+            }
+            SEService.checkForException(error);
+            error.clear();
+            boolean b = channelCannotBeEstablished(error);
+            SEService.checkForException(error);
+            if (b) {
+                return null;
+            }
+            error.clear();
+            checkIfAppletAvailable(error);
+            SEService.checkForException(error);
+
+           if (channel == null)
+              return null;
+
+            return new Channel(mService, this, channel);
+        }
+    }
+
+
+    // ******************************************************************
+    // package private methods
+    // ******************************************************************
+
+
+    private boolean isDefaultApplicationSelected(SmartcardError error) {
+        Exception exp = error.createException();
+        if (exp != null) {
+                String msg = exp.getMessage();
+                if (msg != null) {
+                    if (msg.contains("default application is not selected")) {
+                        return false;
+                    }
+                }
+        }
+        return true;
+    }
+    
+    private boolean basicChannelInUse(SmartcardError error) {
+        Exception exp = error.createException();
+        if (exp != null) {
+            String msg = exp.getMessage();
+            if (msg != null) {
+                if (msg.contains("basic channel in use")) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean channelCannotBeEstablished(SmartcardError error) {
+        Exception exp = error.createException();
+        if (exp != null) {
+            if (exp instanceof MissingResourceException) {
+                return true;
+            }
+            String msg = exp.getMessage();
+            if (msg != null) {
+                if (msg.contains("channel in use")) {
+                    return true;
+                }
+                if (msg.contains("open channel failed")) {
+                    return true;
+                }
+                if (msg.contains("out of channels")) {
+                    return true;
+                }
+                if (msg.contains("MANAGE CHANNEL")) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    private void checkIfAppletAvailable(SmartcardError error) throws NoSuchElementException {
+        Exception exp = error.createException();
+        if (exp != null) {
+	    	if(exp instanceof NoSuchElementException) {
+	    		throw new NoSuchElementException("Applet with the defined aid does not exist in the SE");
+	    	}
+        }
+    }
+    
+    
+}
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/service/CardException.java b/openmobileapi/src/org/simalliance/openmobileapi/service/CardException.java
new file mode 100644
index 0000000..112e108
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/service/CardException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+/**
+ * Generic exception of the smartcard system.
+ */
+public class CardException extends Exception {
+
+	private static final long serialVersionUID = -5298933800369298346L;
+
+    public CardException(String message) {
+        super(message);
+    }
+
+    public CardException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CardException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardService.aidl b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardService.aidl
new file mode 100644
index 0000000..5a91b1d
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardService.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.ISmartcardServiceReader;
+import org.simalliance.openmobileapi.service.SmartcardError;
+
+/**
+ * Smartcard service interface.
+ */
+interface ISmartcardService {
+
+    /**
+     * Returns the friendly names of available smart card readers.
+     */
+    String[] getReaders(out SmartcardError error);
+
+    /**
+     * Returns Smartcard Service reader object to the given name.
+     */
+    ISmartcardServiceReader getReader(String reader, out SmartcardError error);
+
+ 	/**
+     * Checks if the application defined by the package name is allowed to receive 
+     * NFC transaction events for the defined AID. 
+     */
+    boolean[] isNFCEventAllowed(String reader, in byte[] aid, in String[] packageNames, ISmartcardServiceCallback callback, out SmartcardError error);
+     
+}
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceCallback.aidl b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceCallback.aidl
new file mode 100644
index 0000000..bb896ba
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+/**
+ * Callback interface used by ISmartcardService to check if clients are alive.
+ */
+oneway interface ISmartcardServiceCallback {
+}
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceChannel.aidl b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceChannel.aidl
new file mode 100644
index 0000000..cdece89
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceChannel.aidl
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceSession;
+import org.simalliance.openmobileapi.service.SmartcardError;
+
+interface ISmartcardServiceChannel {
+
+    /**
+     * Closes the specified connection and frees internal resources.
+     * A logical channel will be closed.
+     */
+    void close(out SmartcardError error);
+
+    /**
+     * Tells if this channel is closed.
+     * 
+     * @return <code>true</code> if the channel is closed, <code>false</code> otherwise.
+     */
+    boolean isClosed();
+
+    /**
+     * Returns a boolean telling if this channel is the basic channel.
+     * 
+     * @return <code>true</code> if this channel is a basic channel. <code>false</code> if
+     *         this channel is a logical channel.
+     */
+    boolean isBasicChannel();
+    
+     /**
+     * Returns the data as received from the application select command inclusively the status word.
+     * The returned byte array contains the data bytes in the following order:
+     * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+     */
+    byte[] getSelectResponse();
+
+    /**
+     * Get the session that has opened this channel.
+     * 
+     * @return the session object this channel is bound to.
+     */
+    ISmartcardServiceSession getSession();
+
+    /**
+     * Transmits the specified command APDU and returns the response APDU.
+     * MANAGE channel commands are not supported.
+     * Selection of applets is not supported in logical channels.
+     */
+    byte[] transmit(in byte[] command, out SmartcardError error);
+
+    /**
+     * Performs a selection of the next Applet on this channel that matches to the partial AID specified   
+     * in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method.  
+     * This mechanism can be used by a device application to iterate through all Applets 
+     * matching to the same partial AID. 
+     * If selectNext() returns true a new Applet was successfully selected on this channel. 
+     * If no further Applet exists with matches to the partial AID this method returns false
+     * and the already selected Applet stays selected.
+     *
+     * @return <code>true</code> if new Applet was successfully selected.
+               <code>false</code> if no further Applet exists which matches the partial AID. 
+     */
+    boolean selectNext(out SmartcardError error);
+}
\ No newline at end of file
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceReader.aidl b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceReader.aidl
new file mode 100644
index 0000000..8f553de
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceReader.aidl
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceSession;
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.SmartcardError;
+
+interface ISmartcardServiceReader {
+	/**
+     * Return the user-friendly name of this reader.
+     * <ul>
+	 * <li>If this reader is a SIM reader, then its name must start with the "SIM" prefix.</li>
+	 * <li>If the reader is a SD or micro SD reader, then its name must start with the "SD" prefix</li>
+	 * <li>If the reader is a embedded SE reader, then its name must start with the "eSE" prefix</li>
+	 * <ul>
+     * 
+     * @return name of this Reader
+     */
+    String getName(out SmartcardError error);
+
+    /**
+     * Returns true if a card is present in the specified reader.
+     * Returns false if a card is not present in the specified reader.
+     */
+    boolean isSecureElementPresent(out SmartcardError error);
+
+
+    /**
+     * Connects to a secure element in this reader. <br>
+     * This method prepares (initialises) the Secure Element for communication
+     * before the Session object is returned (e.g. powers the Secure Element by
+     * ICC ON if its not already on). There might be multiple sessions opened at
+     * the same time on the same reader. The system ensures the interleaving of
+     * APDUs between the respective sessions.
+     * 
+     * @return a Session object to be used to create Channels.
+     */
+    ISmartcardServiceSession openSession(out SmartcardError error);
+    
+    /**
+     * Close all the sessions opened on this reader. All the channels opened by
+     * all these sessions will be closed.
+     */
+    void closeSessions(out SmartcardError error);
+    
+}
\ No newline at end of file
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceSession.aidl b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceSession.aidl
new file mode 100644
index 0000000..583b4af
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/service/ISmartcardServiceSession.aidl
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceChannel;
+import org.simalliance.openmobileapi.service.ISmartcardServiceReader;
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.SmartcardError;
+
+interface ISmartcardServiceSession {
+
+    /**
+     * Get the reader that provides this session.
+     * 
+     * @return The Reader object.
+     */
+    ISmartcardServiceReader getReader();
+
+   	/**
+	 * Returns the ATR of the connected card or null if the ATR is not available.
+	 */
+    byte[] getAtr();
+
+    /**
+     * Close the connection with the Secure Element. This will close any
+     * channels opened by this application with this Secure Element.
+     */
+    void close(out SmartcardError error);
+    
+    /**
+     * Close any channel opened on this session.
+     */
+    void closeChannels(out SmartcardError error);
+    
+    
+    /**
+     * Tells if this session is closed.
+     * 
+     * @return <code>true</code> if the session is closed, false otherwise.
+     */
+    boolean isClosed();
+
+    /**
+     * Opens a connection using the basic channel of the card in the
+     * specified reader and returns a channel handle.
+     * Logical channels cannot be opened with this connection.
+     * Use interface method openLogicalChannel() to open a logical channel.
+     */
+    ISmartcardServiceChannel openBasicChannel(ISmartcardServiceCallback callback, out SmartcardError error);
+
+    /**
+     * Opens a connection using the basic channel of the card in the
+     * specified reader and returns a channel handle. Selects the specified applet.
+     * Logical channels cannot be opened with this connection.
+     * Selection of other applets with this connection is not supported.
+     * Use interface method openLogicalChannel() to open a logical channel.
+     */
+    ISmartcardServiceChannel openBasicChannelAid(in byte[] aid, ISmartcardServiceCallback callback, out SmartcardError error);
+
+    /**
+     * Opens a connection using the next free logical channel of the card in the
+     * specified reader. Selects the specified applet.
+     * Selection of other applets with this connection is not supported.
+     */
+    ISmartcardServiceChannel openLogicalChannel(in byte[] aid, ISmartcardServiceCallback callback, out SmartcardError error);
+}
\ No newline at end of file
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/service/SmartcardError.aidl b/openmobileapi/src/org/simalliance/openmobileapi/service/SmartcardError.aidl
new file mode 100644
index 0000000..3aed68b
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/service/SmartcardError.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+package org.simalliance.openmobileapi.service;
+
+parcelable SmartcardError;
diff --git a/openmobileapi/src/org/simalliance/openmobileapi/service/SmartcardError.java b/openmobileapi/src/org/simalliance/openmobileapi/service/SmartcardError.java
new file mode 100644
index 0000000..5dc61c4
--- /dev/null
+++ b/openmobileapi/src/org/simalliance/openmobileapi/service/SmartcardError.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Smartcard service parameter class used to marshal exception information from
+ * the smartcard service to clients.
+ */
+public class SmartcardError implements Parcelable {
+    private String mClazz;
+
+    private String mMessage;
+
+    public static final Parcelable.Creator<SmartcardError> CREATOR = new Parcelable.Creator<SmartcardError>() {
+        public SmartcardError createFromParcel(Parcel in) {
+            return new SmartcardError(in);
+        }
+
+        public SmartcardError[] newArray(int size) {
+            return new SmartcardError[size];
+        }
+    };
+
+    /**
+     * Creates an empty smartcard error container.
+     */
+    public SmartcardError() {
+        this.mClazz = "";
+        this.mMessage = "";
+    }
+
+    private SmartcardError(Parcel in) {
+        mClazz = in.readString();
+        mMessage = in.readString();
+    }
+
+    /**
+     * Creates a smartcard error which creates the specified exception.
+     * 
+     * @param clazz the exception class. <code>null</code> to reset the error
+     *            information.
+     * @param message the exception message.
+     */
+    public SmartcardError(String clazz, String message) {
+        this.mClazz = (clazz == null) ? "" : clazz;
+        this.mMessage = (message == null) ? "" : message;
+    }
+
+    /**
+     * Clears the error.
+     */
+    public void clear() {
+        this.mClazz = "";
+        this.mMessage = "";
+    }
+
+    /**
+     * Creates the encoded exception. Returns <code>null</code> if empty. If the
+     * encoded exception is neither a RuntimeException nor a CardException, it
+     * is encapsulated in a RuntimeException.
+     * 
+     * @return the encoded exception or <code>null</code> if empty.
+     */
+    @SuppressWarnings({ "rawtypes" })
+    public Exception createException() {
+        try {
+            if (mClazz.length() == 0) {
+                return null;
+            }
+            if (mMessage.length() == 0) {
+                return (Exception) Class.forName(mClazz).newInstance();
+            }
+            Constructor constructor = Class.forName(mClazz).getConstructor(String.class);
+            return (Exception) constructor.newInstance(mMessage);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void readFromParcel(Parcel in) {
+        mClazz = in.readString();
+        mMessage = in.readString();
+    }
+
+    /**
+     * Sets the error information.
+     * 
+     * @param clazz the exception class. <code>null</code> to reset the error
+     *            information.
+     * @param message the exception message.
+     */
+    @SuppressWarnings({ "rawtypes" })
+    public void setError(Class clazz, String message) {
+        this.mClazz = (clazz == null) ? "" : clazz.getName();
+        this.mMessage = (message == null) ? "" : message;
+    }
+
+    /**
+     * Throws the encoded exception. Does not throw an exception if the
+     * container is empty. If the encoded exception is neither a
+     * RuntimeException nor a CardException, it is encapsulated in a
+     * RuntimeException.
+     * 
+     * @throws RuntimeException if the encoded exception is not a CardException.
+     * @throws CardException if a CardException is encoded.
+     */
+    public void throwException() throws CardException {
+        Exception e = createException();
+        if (e == null) {
+            return;
+        }
+        if (e instanceof CardException) {
+            throw (CardException) e;
+        }
+        if (e instanceof RuntimeException) {
+            throw (RuntimeException) e;
+        }
+        throw new RuntimeException(e);
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mClazz);
+        out.writeString(mMessage);
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/AddonTerminal.java b/src/org/simalliance/openmobileapi/service/AddonTerminal.java
new file mode 100644
index 0000000..0abe52c
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/AddonTerminal.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+
+public class AddonTerminal extends Terminal {
+
+    private Object mInstance = null;
+
+    private Method mIsCardPresent = null;
+
+    private Method mInternalConnect = null;
+
+    private Method mInternalDisconnect = null;
+
+    private Method mInternalTransmit = null;
+
+    private Method mInternalOpenLogicalChannel = null;
+
+    private Method mInternalOpenLogicalChannelAID = null;
+
+    private Method mInternalCloseLogicalChannel = null;
+
+    private Method mGetName = null;
+
+    private Method mGetAtr = null;
+    
+    private Method mGetSelectResponse = null;
+
+    public static String[] getPackageNames(Context context) {
+        List<String> packageNameList = new LinkedList<String>();
+        List<PackageInfo> pis = context.getPackageManager().getInstalledPackages(0);
+        for (PackageInfo p : pis) {
+            if (p.packageName.startsWith("org.simalliance.openmobileapi.service.terminals.")
+                    || p.packageName.startsWith("org.simalliance.openmobileapi.cts")) {
+                packageNameList.add(p.packageName);
+            }
+        }
+        String[] rstrings = new String[packageNameList.size()];
+        packageNameList.toArray(rstrings);
+        return rstrings;
+    }
+
+    public AddonTerminal(Context context, String packageName, String className) {
+        super("Addon", context);
+
+        try {
+            Context ctx = context.createPackageContext(packageName, Context.CONTEXT_IGNORE_SECURITY
+                    | Context.CONTEXT_INCLUDE_CODE);
+            ClassLoader cl = ctx.getClassLoader();
+            Class<?> cls = cl.loadClass(className);
+            mInstance = cls.getConstructor(new Class[] {
+                Context.class
+            }).newInstance(new Object[] {
+                context
+            });
+            if (mInstance != null) {
+                mGetAtr = mInstance.getClass().getDeclaredMethod("getAtr", (Class<?>[]) null);
+                mGetName = mInstance.getClass().getDeclaredMethod("getName", (Class<?>[]) null);
+                mIsCardPresent = mInstance.getClass().getDeclaredMethod("isCardPresent",
+                        (Class<?>[]) null);
+                mInternalConnect = mInstance.getClass().getDeclaredMethod("internalConnect",
+                        (Class<?>[]) null);
+                mInternalDisconnect = mInstance.getClass().getDeclaredMethod("internalDisconnect",
+                        (Class<?>[]) null);
+                mInternalTransmit = mInstance.getClass().getDeclaredMethod("internalTransmit",
+                        new Class[] {
+                            byte[].class
+                        });
+                mInternalOpenLogicalChannel = mInstance.getClass().getDeclaredMethod(
+                        "internalOpenLogicalChannel", (Class<?>[]) null);
+                mInternalOpenLogicalChannelAID = mInstance.getClass().getDeclaredMethod(
+                        "internalOpenLogicalChannel", new Class[] {
+                            byte[].class
+                        });
+                mInternalCloseLogicalChannel = mInstance.getClass().getDeclaredMethod(
+                        "internalCloseLogicalChannel", new Class[] {
+                            int.class
+                        });
+                mGetSelectResponse = mInstance.getClass().getDeclaredMethod("getSelectResponse",
+                        (Class<?>[]) null);
+            }
+        } catch (Exception e) {
+            throw new IllegalStateException("plugin internal error: " + e);
+        }
+    }
+
+    /**
+     * Returns the ATR of the connected card or null if the ATR is not
+     * available.
+     * 
+     * @return the ATR of the connected card or null if the ATR is not
+     *         available.
+     */
+    public byte[] getAtr() {
+        if (mGetAtr == null) {
+            throw new IllegalStateException("plugin error: Function String getAtr() not found");
+        }
+        try {
+            byte[] resp = (byte[]) mGetAtr.invoke(mInstance, (Object[]) null);
+            return resp;
+        } catch (Exception e) {
+            throw new IllegalStateException("plugin internal error: getAtr() execution: "
+                    + e.getCause());
+        }
+    }
+
+    public String getName() {
+        if (mGetName == null) {
+            throw new IllegalStateException("plugin error: Function String getName() not found");
+        }
+        try {
+            String s = (String) mGetName.invoke(mInstance, (Object[]) null);
+            return s;
+        } catch (Exception e) {
+            throw new IllegalStateException("plugin internal error: getName() execution: "
+                    + e.getCause());
+        }
+    }
+
+    public boolean isCardPresent() throws CardException {
+        if (mIsCardPresent == null) {
+            throw new IllegalStateException(
+                    "plugin error: Function String isCardPresent() not found");
+        }
+        try {
+            Boolean v = (Boolean) mIsCardPresent.invoke(mInstance, (Object[]) null);
+            return v.booleanValue();
+        } catch (Exception e) {
+            throw new CardException("plugin internal error: isCardPresent() execution: "
+                    + e.getCause());
+        }
+    }
+
+    protected void internalConnect() throws CardException {
+        if (mInternalConnect == null) {
+            throw new IllegalStateException(
+                    "plugin error: Function String internalConnect() not found");
+        }
+        try {
+            mInternalConnect.invoke(mInstance, (Object[]) null);
+            mIsConnected = true;
+        } catch (Exception e) {
+            throw new CardException("plugin internal error: internalConnect() execution: "
+                    + e.getCause());
+        }
+    }
+
+    protected void internalDisconnect() throws CardException {
+        if (mInternalDisconnect == null) {
+            throw new IllegalStateException(
+                    "plugin error: Function String internalDisconnect() not found");
+        }
+        try {
+            mInternalDisconnect.invoke(mInstance, (Object[]) null);
+            mIsConnected = false;
+        } catch (Exception e) {
+            throw new CardException("plugin internal error: internalDisconnect() execution");
+        }
+    }
+
+    protected byte[] internalTransmit(byte[] command) throws CardException {
+        if (mInternalTransmit == null) {
+            throw new IllegalStateException(
+                    "plugin error: Function String internalTransmit() not found");
+        }
+        try {
+            byte[] resp = (byte[]) mInternalTransmit.invoke(mInstance, new Object[] {
+                command
+            });
+            return resp;
+        } catch (Exception e) {
+            throw new CardException("plugin internal error: internalTransmit() execution: "
+                    + e.getCause());
+        }
+    }
+
+    protected int internalOpenLogicalChannel() throws Exception {
+        if (mInternalOpenLogicalChannel == null) {
+            throw new IllegalStateException(
+                    "plugin error: Function String internalOpenLogicalChannel() not found");
+        }
+        try {
+            Integer channel = (Integer) mInternalOpenLogicalChannel.invoke(mInstance,
+                    (Object[]) null);
+            return channel.intValue();
+        } catch (Exception e) {
+            throw (Exception) e.getCause();
+        }
+    }
+
+    protected int internalOpenLogicalChannel(byte[] aid) throws Exception {
+        if (mInternalOpenLogicalChannelAID == null) {
+            throw new IllegalStateException(
+                    "plugin error: Function internalOpenLogicalChannelAID() not found");
+        }
+        try {
+            Integer channel = (Integer) mInternalOpenLogicalChannelAID.invoke(mInstance,
+                    new Object[] {
+                        aid
+                    });
+            mSelectResponse = (byte[]) mGetSelectResponse.invoke(mInstance, (Object[]) null);
+            return channel.intValue();
+        } catch (Exception e) {
+            throw (Exception) e.getCause();
+        }
+    }
+
+    protected void internalCloseLogicalChannel(int channelNumber) throws CardException {
+        if (mInternalCloseLogicalChannel == null) {
+            throw new IllegalStateException(
+                    "plugin error: Function internalCloseLogicalChannel not found");
+        }
+        try {
+            mInternalCloseLogicalChannel.invoke(mInstance, new Object[] {
+                channelNumber
+            });
+        } catch (Exception e) {
+            throw new CardException(
+                    "plugin internal error: internalOpenLogicalChannel() execution: "
+                            + e.getCause());
+        }
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/CardException.java b/src/org/simalliance/openmobileapi/service/CardException.java
new file mode 100644
index 0000000..112e108
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/CardException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+/**
+ * Generic exception of the smartcard system.
+ */
+public class CardException extends Exception {
+
+	private static final long serialVersionUID = -5298933800369298346L;
+
+    public CardException(String message) {
+        super(message);
+    }
+
+    public CardException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CardException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/src/org/simalliance/openmobileapi/service/Channel.java b/src/org/simalliance/openmobileapi/service/Channel.java
new file mode 100644
index 0000000..5396b0e
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/Channel.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException; 
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.SmartcardService.SmartcardServiceSession;
+
+import android.util.Log;
+
+
+import java.security.AccessControlException;
+import java.util.NoSuchElementException;
+
+import org.simalliance.openmobileapi.service.security.AccessControlEnforcer;
+import org.simalliance.openmobileapi.service.security.ChannelAccess;
+
+
+/**
+ * Smartcard service base class for channel resources.
+ */
+class Channel implements IChannel, IBinder.DeathRecipient {
+
+    protected final int mChannelNumber;
+    
+    protected boolean mIsClosed;
+
+    protected long mHandle;
+
+    protected ISmartcardServiceSession mSession;
+    protected Terminal mTerminal;
+    
+    protected byte[] mSelectResponse;
+
+    protected final IBinder mBinder;
+
+
+    protected ChannelAccess mChannelAccess = null;
+    protected int mCallingPid = 0;
+
+
+    protected ISmartcardServiceCallback mCallback;
+
+    protected boolean mHasSelectedAid = false;
+    protected byte[] mAid = null;
+
+    Channel(SmartcardServiceSession session, Terminal terminal, int channelNumber, ISmartcardServiceCallback callback) {
+        this.mChannelNumber = channelNumber;
+        this.mSession = session;
+        this.mTerminal = terminal;
+        this.mCallback = callback;
+        this.mBinder = callback.asBinder();
+        this.mSelectResponse = terminal.getSelectResponse();
+        this.mIsClosed = false;
+        try {
+            mBinder.linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            Log.e(SmartcardService._TAG, "Failed to register client callback");
+        }
+    }
+
+    public void binderDied() {
+        // Close this channel if the client died.
+        try {
+            Log.e(SmartcardService._TAG, Thread.currentThread().getName()
+                    + " Client " + mBinder.toString() + " died");
+            close();
+        } catch (Exception ignore) {
+        }
+    }
+
+    public synchronized void close() throws CardException {
+
+        
+        Terminal terminal = getTerminal();
+        if( terminal == null ){
+			throw new IllegalStateException( "channel is not attached to a terminal");
+        }
+
+        if (isBasicChannel() && hasSelectedAid()) {
+            try {
+                Log.v(SmartcardService._TAG, "Close basic channel - Select with out AID ..."); 
+                terminal.select();
+            } catch (NoSuchElementException exp) {
+//#if defined(sec)
+                // Selection of the default application fails
+                try {
+                    Log.v(SmartcardService._TAG, "Close basic channel - Exception : " + exp.getLocalizedMessage()); 
+                	AccessControlEnforcer access = terminal.getAccessControlEnforcer();
+                	if( access != null ){
+                		terminal.select(AccessControlEnforcer.getDefaultAccessControlAid());
+                	}
+                } catch (NoSuchElementException exp2) {
+                    // Access Control Applet not available => Don't care
+                }
+//#endif
+            }
+        }
+        
+        try {
+            terminal.closeChannel(this);
+        	this.mIsClosed = true;
+        } finally {
+            mBinder.unlinkToDeath(this, 0);
+        }
+    }
+
+    public int getChannelNumber() {
+        return mChannelNumber;
+    }
+
+    /**
+     * Returns if this channel is a basic channel
+     * 
+     * @return true if this channel is a basic channel
+     */
+    public boolean isBasicChannel() {
+        return (mChannelNumber == 0) ? true : false;
+    }
+
+    public ISmartcardServiceCallback getCallback() {
+        return mCallback;
+    }
+
+    /**
+     * Returns the handle assigned to this channel.
+     * 
+     * @return the handle assigned to this channel.
+     */
+    long getHandle() {
+        return mHandle;
+    }
+
+    /**
+     * Returns the associated terminal.
+     * 
+     * @return the associated terminal.
+     */
+    public Terminal getTerminal() {
+        return mTerminal;
+    }
+
+    /**
+     * Assigns the channel handle.
+     * 
+     * @param handle the channel handle to be assigned.
+     */
+    void setHandle(long handle) {
+        this.mHandle = handle;
+    }
+
+	public byte[] transmit(byte[] command) throws CardException {
+
+		if( mChannelAccess == null ){
+			throw new AccessControlException( " Channel access not set.");
+		}
+        if (mChannelAccess.getCallingPid() !=  mCallingPid) {
+
+
+
+            throw new AccessControlException(" Wrong Caller PID. ");
+        }
+
+
+
+        checkCommand(command);
+
+
+        if (command.length < 4) {
+			throw new IllegalArgumentException(
+					" command must not be smaller than 4 bytes");
+		}
+		if (((command[0] & (byte) 0x80) == 0)
+				&& ((byte) (command[0] & (byte) 0x60) != (byte) 0x20)) {
+			// ISO command
+			if (command[1] == (byte) 0x70) {
+				throw new IllegalArgumentException(
+						"MANAGE CHANNEL command not allowed");
+			}
+			if ((command[1] == (byte) 0xA4) && (command[2] == (byte) 0x04)) {
+				throw new IllegalArgumentException("SELECT command not allowed");
+			}
+
+		} else {
+			// GlobalPlatform command
+		}
+
+		// set channel number bits
+		command[0] = setChannelToClassByte(command[0], mChannelNumber);
+
+		byte[] rsp = getTerminal().transmit(command, 2, 0, 0, null);
+
+		return rsp;
+	}
+
+	public boolean selectNext() throws CardException {
+
+		if( mChannelAccess == null ){
+			throw new AccessControlException( " Channel access not set.");
+		}
+        if (mChannelAccess.getCallingPid() !=  mCallingPid) {
+
+
+
+            throw new AccessControlException(" Wrong Caller PID. ");
+        }
+
+        
+        if( mAid == null || mAid.length == 0){
+        	throw new CardException(" no aid given");
+        }
+
+        mSelectResponse = null;
+        byte[] selectCommand = new byte[5 + mAid.length];
+        selectCommand[0] = 0x00;
+        selectCommand[1] = (byte) 0xA4;
+        selectCommand[2] = 0x04;
+        selectCommand[3] = 0x02; // next occurrence
+        selectCommand[4] = (byte)mAid.length;
+        System.arraycopy(mAid, 0, selectCommand, 5, mAid.length);
+        
+		// set channel number bits
+        selectCommand[0] = setChannelToClassByte(selectCommand[0], mChannelNumber);
+        
+        mSelectResponse = getTerminal().transmit(selectCommand, 2, 0, 0, "SELECT NEXT");
+
+
+        
+        int sw1 = mSelectResponse[mSelectResponse.length - 2] & 0xFF;
+        int sw2 = mSelectResponse[mSelectResponse.length - 1] & 0xFF;
+        int sw = (sw1 << 8) | sw2;
+       
+        if( (sw & 0xF000) == 0x9000 ){
+        	return true;
+        } else if( sw == 0x6a82 ) {
+        	return false;
+        } else {
+        	throw new CardException(" invalid action" );
+        }
+	}
+	
+	
+	/**
+	 * Returns a copy of the given CLA byte where the channel number bits are
+	 * set as specified by the given channel number
+	 * 
+	 * See GlobalPlatform Card Specification 2.2.0.7: 11.1.4 Class Byte Coding
+	 * 
+	 * @param cla
+	 *            the CLA byte. Won't be modified
+	 * @param channelNumber
+	 *            within [0..3] (for first interindustry class byte coding) or
+	 *            [4..19] (for further interindustry class byte coding)
+	 * @return the CLA byte with set channel number bits. The seventh bit
+	 *         indicating the used coding (first/further interindustry class
+	 *         byte coding) might be modified
+	 */
+	private byte setChannelToClassByte(byte cla, int channelNumber) {
+		if (channelNumber < 4) {
+			// b7 = 0 indicates the first interindustry class byte coding
+			cla = (byte) ((cla & 0xBC) | channelNumber);
+		} else if (channelNumber < 20) {
+			// b7 = 1 indicates the further interindustry class byte coding
+			boolean isSM = (cla & 0x0C) != 0;
+			cla = (byte) ((cla & 0xB0) | 0x40 | (channelNumber - 4));
+			if (isSM) cla |= 0x20;
+		} else {
+			throw new IllegalArgumentException(
+					"Channel number must be within [0..19]");
+		}
+		return cla;
+	}
+
+
+    public void setChannelAccess(ChannelAccess channelAccess) {
+        this.mChannelAccess = channelAccess;
+    }
+    
+    public ChannelAccess getChannelAccess(){
+    	return this.mChannelAccess;
+    }
+
+    public void setCallingPid( int pid) {
+
+
+
+    	mCallingPid = pid;
+    }
+
+    private void checkCommand( byte[] command ) {
+	    if( getTerminal().getAccessControlEnforcer() != null ) { 
+	    	// check command if it complies to the access rules.
+	    	// if not an exception is thrown
+	    	getTerminal().getAccessControlEnforcer().checkCommand(this, command);
+	    } else {
+	    	throw new AccessControlException( "FATAL: Access Controller not set for Terminal: " + getTerminal().getName());
+	    }
+    }
+
+
+    /**
+     * true if aid during open xxx channel coud be selected.
+     * false if aid could not be or was not selected.
+     * @return boolean.
+     */
+    public boolean hasSelectedAid() {
+        return mHasSelectedAid;
+    }
+
+    /**
+     * set selected aid flag and aid (may be null)
+     */
+    public void hasSelectedAid(boolean has, byte[] aid) {
+        mHasSelectedAid = has;
+        mAid = aid;
+    }
+    
+    /**
+     * Returns the data as received from the application select command inclusively the status word.
+     * The returned byte array contains the data bytes in the following order:
+     * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+     * @return The data as returned by the application select command inclusively the status word.
+     * @return Only the status word if the application select command has no returned data.
+     * @return null if an application select command has not been performed or the selection response can not
+     * be retrieved by the reader implementation.
+     */
+    public byte[] getSelectResponse()
+    {
+    	return mSelectResponse;
+    }
+    
+    boolean isClosed(){
+
+    	return mIsClosed;
+    }
+    
+    void setClosed(){
+
+    	mIsClosed = true;
+    }
+    
+    /**
+     * Implementation of the SmartcardService Channel interface according to OMAPI.
+     * 
+     * 
+     */
+    final class SmartcardServiceChannel extends ISmartcardServiceChannel.Stub {
+
+    	private final SmartcardServiceSession mSession;
+    	public SmartcardServiceChannel( SmartcardServiceSession session ){
+    		mSession = session;
+    	}
+    	
+		@Override
+		public void close(SmartcardError error) throws RemoteException {
+
+			SmartcardService.clearError(error);
+			try {
+				Channel.this.close();
+			} catch (Exception e) {
+				SmartcardService.setError(error, e);
+			} finally {
+				if( mSession != null )
+					mSession.removeChannel(Channel.this);
+			}
+		}
+		
+		public void setClosed(){
+			Channel.this.setClosed();
+		}
+
+		@Override
+		public boolean isClosed() throws RemoteException {
+			return Channel.this.isClosed();
+		}
+
+		@Override
+		public boolean isBasicChannel()
+				throws RemoteException {
+			return Channel.this.isBasicChannel();
+		}
+
+		@Override
+		public byte[] getSelectResponse()
+				throws RemoteException {
+			return Channel.this.getSelectResponse();
+		}
+
+		@Override
+		public ISmartcardServiceSession getSession()
+				throws RemoteException {
+			return Channel.this.mSession;
+		}
+
+		@Override
+		public byte[] transmit(byte[] command, SmartcardError error)
+				throws RemoteException {
+			SmartcardService.clearError(error);
+
+            try {
+                if (isClosed()) {
+                	SmartcardService.setError(error, IllegalStateException.class, "channel is closed");
+                	return null;
+                }
+                
+                if (command == null) {
+                	SmartcardService.setError(error, NullPointerException.class, "command must not be null");
+                    return null;
+                }
+                if (command.length < 4) {
+                	SmartcardService.setError(error, IllegalArgumentException.class,
+                            "command must have at least 4 bytes");
+                    return null;
+                }
+
+                Channel.this.setCallingPid(Binder.getCallingPid());
+
+
+                byte[] response = Channel.this.transmit(command);
+
+                return response;
+            } catch (Exception e) {
+                Log.v(SmartcardService._TAG, "transmit Exception: " + e.getMessage() + " (Command: " + Util.bytesToString(command) + ")");
+                SmartcardService.setError(error, e);
+                return null;
+            }
+		}
+		
+		@Override
+		public boolean selectNext(SmartcardError error)
+				throws RemoteException {
+			SmartcardService.clearError(error);
+
+            try {
+                if (isClosed()) {
+                	SmartcardService.setError(error, IllegalStateException.class, "channel is closed");
+                	return false;
+                }
+                
+
+                Channel.this.setCallingPid(Binder.getCallingPid());
+
+                boolean response = Channel.this.selectNext();
+                return response;
+            } catch (Exception e) {
+                SmartcardService.setError(error, e);
+                return false;
+            }
+		}  
+    };
+}
diff --git a/src/org/simalliance/openmobileapi/service/IChannel.java b/src/org/simalliance/openmobileapi/service/IChannel.java
new file mode 100644
index 0000000..06244fd
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/IChannel.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+
+import org.simalliance.openmobileapi.service.security.ChannelAccess;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+
+/**
+ * Smartcard service interface for channel resources.
+ */
+public interface IChannel {
+
+    /**
+     * Closes this channel.
+     * 
+     * @throws CardException if closing the channel failed.
+     */
+    void close() throws CardException;
+
+    /**
+     * Returns the channel number according to ISO 7816-4.
+     * 
+     * @return the channel number according to ISO 7816-4.
+     */
+    int getChannelNumber();
+
+    /**
+     * Returns if this channel is a basic channel
+     * 
+     * @return true if this channel is a basic channel
+     */
+    boolean isBasicChannel();
+
+    /**
+     * Returns the associated terminal.
+     * 
+     * @return the associated terminal.
+     */
+    ITerminal getTerminal();
+
+    /**
+     * Transmits the specified command APDU and returns the response APDU.
+     * MANAGE channel commands are not allowed. Applet selection commands are
+     * not allowed if this is a logical channel.
+     * 
+     * @param command the command APDU to be transmitted.
+     * @return the response APDU.
+     * @throws CardException if command transmission failed or the command is
+     *             not allowed.
+     */
+    byte[] transmit(byte[] command) throws CardException;
+
+
+    /**
+     * @param channelAccess
+     */
+    void setChannelAccess(ChannelAccess channelAccess);
+
+    /**
+     * @return channelAccess
+     */
+    ChannelAccess getChannelAccess();
+
+    /**
+     * Sets calling pid for validation in checkCommand.
+     * @param pid calling pid of the device application
+     */
+    void setCallingPid( int pid );
+
+
+    /**
+     * @return
+     */
+    ISmartcardServiceCallback getCallback();
+
+    /**
+     * true if aid during open xxx channel coud be selected.
+     * false if aid could not be or was not selected.
+     * @return 
+     */
+    boolean hasSelectedAid();
+
+    /**
+     * set selected aid flag and aid (may be null)
+     */
+    void hasSelectedAid(boolean has, byte[] aid);
+    
+    /**
+     * Returns the data as received from the application select command inclusively the status word.
+     * The returned byte array contains the data bytes in the following order:
+     * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+     * @return The data as returned by the application select command inclusively the status word.
+     * @return Only the status word if the application select command has no returned data.
+     * @return null if an application select command has not been performed or the selection response can not
+     * be retrieved by the reader implementation.
+     */
+    public byte[] getSelectResponse();
+}
diff --git a/src/org/simalliance/openmobileapi/service/ITerminal.java b/src/org/simalliance/openmobileapi/service/ITerminal.java
new file mode 100644
index 0000000..41ab5d0
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/ITerminal.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import java.io.PrintWriter;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+
+import org.simalliance.openmobileapi.service.SmartcardService.SmartcardServiceSession;
+import org.simalliance.openmobileapi.service.security.AccessControlEnforcer;
+import org.simalliance.openmobileapi.service.security.ChannelAccess;
+import android.content.pm.PackageManager;
+
+
+/**
+ * Smartcard service interface for terminal resources.
+ */
+public interface ITerminal {
+
+    /**
+     * Closes all open channels of this terminal.
+     */
+    void closeChannels();
+
+    /**
+     * Returns the channel for the specified handle or <code>null</code> if this
+     * handle is not registered.
+     * 
+     * @param hChannel the channel handle.
+     * @return the channel for the specified handle or <code>null</code> if this
+     *         handle is not registered.
+     */
+    IChannel getChannel(long hChannel);
+
+    /**
+     * Returns the reader name.
+     * 
+     * @return the reader name.
+     */
+    String getName();
+
+    /**
+     * Sends a select command on the basic channel. With this command the
+     * default application will be selected on the card. (e.g. CardManager)
+     * 
+     * @throw NoSuchElementException if the default applet couldn't be found or
+     *        selected
+     */
+    public void select();
+
+    /**
+     * Sends a select command on the basic channel.
+     * 
+     * @param aid the aid which should be selected
+     * @throw NoSuchElementException if the corresponding applet couldn't be
+     *        found
+     */
+    public void select(byte[] aid);
+
+    /**
+     * Opens the basic channel to the card.
+     * 
+     * @param callback the callback used to react on the death of the client.
+     * @return a handle for the basic channel.
+     * @throws CardException if opening the basic channel failed or the basic
+     *             channel is in use.
+     */
+    Channel openBasicChannel(SmartcardServiceSession session, ISmartcardServiceCallback callback) throws CardException;
+
+    /**
+     * Opens the basic channel to the card.
+     * 
+     * @param aid the AID of the applet to be selected.
+     * @param callback the callback used to react on the death of the client.
+     * @return a handle for the basic channel.
+     * @throws CardException if opening the basic channel failed or the basic
+     *             channel is in use.
+     */
+    Channel openBasicChannel(SmartcardServiceSession session, byte[] aid, ISmartcardServiceCallback callback) throws Exception;
+
+    /**
+     * Opens a logical channel to the card.
+     * 
+     * @param callback the callback used to react on the death of the client.
+     * @return a handle for the logical channel.
+     * @throws CardException if opening the logical channel failed.
+     */
+    Channel openLogicalChannel(SmartcardServiceSession session, ISmartcardServiceCallback callback) throws Exception;
+
+    /**
+     * Opens a logical channel to the card.
+     * 
+     * @param aid the AID of the applet to be selected.
+     * @param callback the callback used to react on the death of the client.
+     * @return a handle for the logical channel.
+     * @throws CardException if opening the logical channel failed.
+     */
+    Channel openLogicalChannel(SmartcardServiceSession session, byte[] aid, ISmartcardServiceCallback callback) throws Exception;
+
+    /**
+     * Returns <code>true</code> if a card is present; <code>false</code>
+     * otherwise.
+     * 
+     * @return <code>true</code> if a card is present; <code>false</code>
+     *         otherwise.
+     * @throws CardException if card presence information is not available.
+     */
+    boolean isCardPresent() throws CardException;
+
+    /**
+     * Returns <code>true</code> if terminal is connected <code>false</code>
+     * otherwise.
+     * 
+     * @return <code>true</code> if at least one terminal is connected.
+     */
+    public boolean isConnected();
+
+    /**
+     * Returns the ATR of the connected card or null if the ATR is not
+     * available.
+     * 
+     * @return the ATR of the connected card or null if the ATR is not
+     *         available.
+     */
+    public byte[] getAtr();
+    
+    /**
+     * Returns the data as received from the application select command inclusively the status word.
+     * The returned byte array contains the data bytes in the following order:
+     * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+     * @return The data as returned by the application select command inclusively the status word.
+     * @return Only the status word if the application select command has no returned data.
+     * @return null if an application select command has not been performed or the selection response can not
+     * be retrieved by the reader implementation.
+     */
+    public byte[] getSelectResponse();
+    
+    /**
+     * Exchanges APDU (SELECT, READ/WRITE) to the 
+     * given EF by File ID and file path via iccIO.
+     * 
+     * The given command is checked and might be rejected.
+     * 
+     * @param fileID
+     * @param filePath
+     * @param cmd
+     * @return
+     */
+    public byte[] simIOExchange(int fileID, String filePath, byte[] cmd) throws Exception;
+
+	
+
+    /**
+     * Set ups the Channel Access object for access control
+     * from the cached access rules
+     * for the given packageNames and the AID of the applet to be accessed.
+     * 
+     * @return ChannelAccess object containing the access flags/filter.
+     */
+    ChannelAccess setUpChannelAccess( PackageManager packageManager, byte[] aid, String packageName, ISmartcardServiceCallback callback);
+    
+    /**
+     * Set up the correct access control hander ARA (or ARF)
+     * and if indicated loads all accesses rules for the terminal.
+     * @param boolean flag if Access Rules should be loaded.
+     * @return true if rules have been successfully loaded
+      */
+    boolean initializeAccessControl( boolean loadAtStartup, ISmartcardServiceCallback callback);
+    
+    /**
+    * Reset the access control: should be called when the card is changed.
+    */
+    void resetAccessControl();
+    
+    
+    AccessControlEnforcer getAccessControlEnforcer();
+    	
+    
+    /**
+      * Dump the terminal state (for debugging purpose and crash tools)
+      * @param writer uses to print the dump
+      * @param prefix to be added before any new line (mainly used for indentation)
+      */
+    public void dump(PrintWriter writer, String prefix);    
+}
diff --git a/src/org/simalliance/openmobileapi/service/SmartcardError.java b/src/org/simalliance/openmobileapi/service/SmartcardError.java
new file mode 100644
index 0000000..5dc61c4
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/SmartcardError.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Smartcard service parameter class used to marshal exception information from
+ * the smartcard service to clients.
+ */
+public class SmartcardError implements Parcelable {
+    private String mClazz;
+
+    private String mMessage;
+
+    public static final Parcelable.Creator<SmartcardError> CREATOR = new Parcelable.Creator<SmartcardError>() {
+        public SmartcardError createFromParcel(Parcel in) {
+            return new SmartcardError(in);
+        }
+
+        public SmartcardError[] newArray(int size) {
+            return new SmartcardError[size];
+        }
+    };
+
+    /**
+     * Creates an empty smartcard error container.
+     */
+    public SmartcardError() {
+        this.mClazz = "";
+        this.mMessage = "";
+    }
+
+    private SmartcardError(Parcel in) {
+        mClazz = in.readString();
+        mMessage = in.readString();
+    }
+
+    /**
+     * Creates a smartcard error which creates the specified exception.
+     * 
+     * @param clazz the exception class. <code>null</code> to reset the error
+     *            information.
+     * @param message the exception message.
+     */
+    public SmartcardError(String clazz, String message) {
+        this.mClazz = (clazz == null) ? "" : clazz;
+        this.mMessage = (message == null) ? "" : message;
+    }
+
+    /**
+     * Clears the error.
+     */
+    public void clear() {
+        this.mClazz = "";
+        this.mMessage = "";
+    }
+
+    /**
+     * Creates the encoded exception. Returns <code>null</code> if empty. If the
+     * encoded exception is neither a RuntimeException nor a CardException, it
+     * is encapsulated in a RuntimeException.
+     * 
+     * @return the encoded exception or <code>null</code> if empty.
+     */
+    @SuppressWarnings({ "rawtypes" })
+    public Exception createException() {
+        try {
+            if (mClazz.length() == 0) {
+                return null;
+            }
+            if (mMessage.length() == 0) {
+                return (Exception) Class.forName(mClazz).newInstance();
+            }
+            Constructor constructor = Class.forName(mClazz).getConstructor(String.class);
+            return (Exception) constructor.newInstance(mMessage);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void readFromParcel(Parcel in) {
+        mClazz = in.readString();
+        mMessage = in.readString();
+    }
+
+    /**
+     * Sets the error information.
+     * 
+     * @param clazz the exception class. <code>null</code> to reset the error
+     *            information.
+     * @param message the exception message.
+     */
+    @SuppressWarnings({ "rawtypes" })
+    public void setError(Class clazz, String message) {
+        this.mClazz = (clazz == null) ? "" : clazz.getName();
+        this.mMessage = (message == null) ? "" : message;
+    }
+
+    /**
+     * Throws the encoded exception. Does not throw an exception if the
+     * container is empty. If the encoded exception is neither a
+     * RuntimeException nor a CardException, it is encapsulated in a
+     * RuntimeException.
+     * 
+     * @throws RuntimeException if the encoded exception is not a CardException.
+     * @throws CardException if a CardException is encoded.
+     */
+    public void throwException() throws CardException {
+        Exception e = createException();
+        if (e == null) {
+            return;
+        }
+        if (e instanceof CardException) {
+            throw (CardException) e;
+        }
+        if (e instanceof RuntimeException) {
+            throw (RuntimeException) e;
+        }
+        throw new RuntimeException(e);
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mClazz);
+        out.writeString(mMessage);
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/SmartcardService.java b/src/org/simalliance/openmobileapi/service/SmartcardService.java
new file mode 100644
index 0000000..058243d
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/SmartcardService.java
@@ -0,0 +1,986 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import dalvik.system.DexClassLoader;
+import dalvik.system.DexFile;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException; 
+
+import org.simalliance.openmobileapi.service.Channel;
+import org.simalliance.openmobileapi.service.Channel.SmartcardServiceChannel;
+import org.simalliance.openmobileapi.service.ISmartcardService;
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.Terminal.SmartcardServiceReader;
+
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+
+
+import org.simalliance.openmobileapi.service.security.AccessControlEnforcer;
+import org.simalliance.openmobileapi.service.security.ChannelAccess;
+
+
+/**
+ * The smartcard service is setup with privileges to access smart card hardware.
+ * The service enforces the permission
+ * 'org.simalliance.openmobileapi.service.permission.BIND'.
+ */
+public final class SmartcardService extends Service {
+
+    public static final String _TAG = "SmartcardService";
+    public static final String _UICC_TERMINAL = "SIM";
+    public static final String _eSE_TERMINAL = "eSE";
+    public static final String _SD_TERMINAL = "SD";
+    
+    static void clearError(SmartcardError error) {
+        if (error != null) {
+            error.clear();
+        }
+    }
+
+    @SuppressWarnings({ "rawtypes" })
+    static void setError(SmartcardError error, Class clazz, String message) {
+        if (error != null) {
+            error.setError(clazz, message);
+        }
+    }
+
+    static void setError(SmartcardError error, Exception e) {
+        if (error != null) {
+            error.setError(e.getClass(), e.getMessage());
+        }
+    }
+
+    /**
+     * For now this list is setup in onCreate(), not changed later and therefore
+     * not synchronized.
+     */
+    private Map<String, ITerminal> mTerminals = new TreeMap<String, ITerminal>();
+
+    /**
+     * For now this list is setup in onCreate(), not changed later and therefore
+     * not synchronized.
+     */
+    private Map<String, ITerminal> mAddOnTerminals = new TreeMap<String, ITerminal>();
+
+    /* Broadcast receivers */
+    private BroadcastReceiver mSimReceiver;
+    private BroadcastReceiver mNfcReceiver;
+    private BroadcastReceiver mMediaReceiver;
+    
+    /* Async task */
+    InitialiseTask mInitialiseTask;
+
+    /**
+     * ServiceHandler use to load rules from the terminal
+     */
+    private ServiceHandler mServiceHandler;
+    
+    
+    public SmartcardService() {
+        super();
+    }
+    
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.v(_TAG, Thread.currentThread().getName()
+                        + " smartcard service onBind");
+        if (ISmartcardService.class.getName().equals(intent.getAction())) {
+            return mSmartcardBinder;
+        }
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        Log.v(_TAG, Thread.currentThread().getName()
+                + " smartcard service onCreate");
+        
+        // Start up the thread running the service.  Note that we create a
+        // separate thread because the service normally runs in the process's
+        // main thread, which we don't want to block.  We also make it
+        // background priority so CPU-intensive work will not disrupt our UI.
+        HandlerThread thread = new HandlerThread("SmartCardServiceHandler");
+        thread.start();
+
+        // Get the HandlerThread's Looper and use it for our Handler
+        mServiceHandler = new ServiceHandler(thread.getLooper());
+        
+        createTerminals();
+        mInitialiseTask = new InitialiseTask();
+        mInitialiseTask.execute();
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.println("SMARTCARD SERVICE (dumpsys activity service org.simalliance.openmobileapi)");
+        writer.println();
+
+        String prefix = "  ";
+
+        if(!Build.IS_DEBUGGABLE) {
+            writer.println(prefix + "Your build is not debuggable!");
+            writer.println(prefix + "Smartcard service dump is only available for userdebug and eng build");
+        } else {
+            writer.println(prefix + "List of terminals:");
+            for (ITerminal terminal : mTerminals.values()) {
+               writer.println(prefix + "  " + terminal.getName());
+            }
+            writer.println();
+
+            writer.println(prefix + "List of add-on terminals:");
+            for (ITerminal terminal : mAddOnTerminals.values()) {
+               writer.println(prefix + "  " + terminal.getName());
+            }
+            writer.println();
+
+            for (ITerminal terminal : mTerminals.values()) {
+               terminal.dump(writer, prefix);
+            }
+            for (ITerminal terminal : mAddOnTerminals.values()) {
+               terminal.dump(writer, prefix);
+            }
+        }
+    }
+        
+    
+    private class InitialiseTask extends AsyncTask<Void, Void, Void> {
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+
+        }
+
+        @Override
+        protected Void doInBackground(Void... arg0) {
+
+            try {
+            	initializeAccessControl(null, null);
+            } catch( Exception e ){
+            	// do nothing since this is called were nobody can react.
+            }
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            super.onPostExecute(result);
+            Log.i(_TAG, "OnPostExecute()");
+            registerSimStateChangedEvent(getApplicationContext()) ;
+            registerAdapterStateChangedEvent(getApplicationContext());
+            registerMediaMountedEvent(getApplicationContext());
+            mInitialiseTask = null;
+        }
+    }    
+    
+    private void registerSimStateChangedEvent(Context context) {
+        Log.v(_TAG, "register SIM_STATE_CHANGED event");
+
+        IntentFilter intentFilter = new IntentFilter("android.intent.action.SIM_STATE_CHANGED");
+        mSimReceiver = new BroadcastReceiver() {
+    		@Override
+    		public void onReceive(Context context, Intent intent) {
+    			if("android.intent.action.SIM_STATE_CHANGED".equals(intent.getAction())) {
+                    final Bundle  extras    = intent.getExtras();
+                    final boolean simReady  = (extras != null) && "READY".equals(extras.getString("ss"));
+                    final boolean simLoaded = (extras != null) && "LOADED".equals(extras.getString("ss"));
+                    if( simReady ){
+                        Log.i(_TAG, "SIM is ready. Checking access rules for updates.");
+                        mServiceHandler.sendMessage(MSG_LOAD_UICC_RULES, 5);
+                    }
+                    else if( simLoaded){
+                        Log.i(_TAG, "SIM is loaded. Checking access rules for updates.");
+                        mServiceHandler.sendMessage(MSG_LOAD_UICC_RULES, 5);
+                    }
+    			}
+			}
+        };
+        
+        context.registerReceiver(mSimReceiver, intentFilter);
+    }    
+    private void unregisterSimStateChangedEvent(Context context) {
+    	if(mSimReceiver!= null) {
+            Log.v(_TAG, "unregister SIM_STATE_CHANGED event");
+            context.unregisterReceiver(mSimReceiver);
+            mSimReceiver = null;
+        }
+    }
+    
+
+    private void registerAdapterStateChangedEvent(Context context) {
+        Log.v(_TAG, "register ADAPTER_STATE_CHANGED event");
+
+        IntentFilter intentFilter = new IntentFilter("android.nfc.action.ADAPTER_STATE_CHANGED");
+        mNfcReceiver = new BroadcastReceiver() {
+    		@Override
+    		public void onReceive(Context context, Intent intent) {
+    		    final boolean nfcAdapterAction = intent.getAction().equals("android.nfc.action.ADAPTER_STATE_CHANGED");
+    		    final boolean nfcAdapterOn = nfcAdapterAction && intent.getIntExtra("android.nfc.extra.ADAPTER_STATE", 1) == 3; // is NFC Adapter turned on ?		
+    		    if( nfcAdapterOn){
+    		    	Log.i(_TAG, "NFC Adapter is ON. Checking access rules for updates.");
+    		    	mServiceHandler.sendMessage(MSG_LOAD_ESE_RULES, 5);
+		    	}
+    		}
+        };
+        context.registerReceiver(mNfcReceiver, intentFilter);
+    }    
+    
+    private void unregisterAdapterStateChangedEvent(Context context) {
+        if(mNfcReceiver!= null) {
+            Log.v(_TAG, "unregister ADAPTER_STATE_CHANGED event");
+            context.unregisterReceiver(mNfcReceiver);
+            mNfcReceiver = null;
+        }
+     }    
+    
+    private void registerMediaMountedEvent(Context context) {
+        Log.v(_TAG, "register MEDIA_MOUNTED event");
+
+        IntentFilter intentFilter = new IntentFilter("android.intent.action.MEDIA_MOUNTED");
+        mMediaReceiver = new BroadcastReceiver() {
+    		@Override
+    		public void onReceive(Context context, Intent intent) {
+    		    final boolean mediaMounted = intent.getAction().equals("android.intent.action.MEDIA_MOUNTED");
+    		    if( mediaMounted){
+    		    	Log.i(_TAG, "New Media is mounted. Checking access rules for updates.");
+    		    	 mServiceHandler.sendMessage(MSG_LOAD_SD_RULES, 5);
+		    	 }
+    		}
+        };
+        context.registerReceiver(mMediaReceiver, intentFilter);
+    }    
+    
+    private void unregisterMediaMountedEvent(Context context) {
+        if(mMediaReceiver != null) {
+            Log.v(_TAG, "unregister MEDIA_MOUNTED event");
+            context.unregisterReceiver(mMediaReceiver);
+            mMediaReceiver = null;
+        }
+     }
+    
+    /**
+     * Initalizes Access Control.
+     * At least the refresh tag is read and if it differs to the previous one (e.g. is null) the all
+     * access rules are read.
+     * 
+     * @param se
+     */
+    public boolean initializeAccessControl(String se, ISmartcardServiceCallback callback ) {
+    	return initializeAccessControl(false, se, callback);
+	}
+	
+	public synchronized boolean initializeAccessControl(boolean reset, String se, ISmartcardServiceCallback callback ) {
+		boolean result = true;
+    	Log.i(_TAG, "Initializing Access Control");
+    	
+    	if( callback == null ) {
+    		callback = new ISmartcardServiceCallback.Stub(){};
+    	}
+
+        Collection<ITerminal>col = mTerminals.values();
+        Iterator<ITerminal> iter = col.iterator();
+        while(iter.hasNext()){
+        	ITerminal terminal = iter.next();
+        	if( terminal == null ){
+
+        		continue;
+        	}
+
+        		if( se == null || terminal.getName().startsWith(se)) {
+        			boolean isCardPresent = false;
+                	try {
+                		isCardPresent = terminal.isCardPresent();
+        			} catch (CardException e) {
+        				isCardPresent = false;
+
+					}
+
+                	if(isCardPresent) {
+				    	Log.i(_TAG, "Initializing Access Control for " + terminal.getName());
+				    	if(reset) terminal.resetAccessControl();
+				    	result &= terminal.initializeAccessControl(true, callback);
+        			} else {
+    			    	Log.i(_TAG, "NOT initializing Access Control for " + terminal.getName() + " SE not present.");
+        			}
+        		}
+        }
+        col = this.mAddOnTerminals.values();
+        iter = col.iterator();
+        while(iter.hasNext()){
+        	ITerminal terminal = iter.next();
+        	if( terminal == null ){
+
+        		continue;
+        	}
+
+    		if( se == null || terminal.getName().startsWith(se)) {
+    			boolean isCardPresent = false;
+            	try {
+            		isCardPresent = terminal.isCardPresent();
+    			} catch (CardException e) {
+    				isCardPresent = false;
+
+				}
+
+            	if(isCardPresent) {
+			    	Log.i(_TAG, "Initializing Access Control for " + terminal.getName());
+			    	if(reset) terminal.resetAccessControl();
+			    	result &= terminal.initializeAccessControl(true, callback);
+    			} else {
+			    	Log.i(_TAG, "NOT initializing Access Control for " + terminal.getName() + " SE not present.");
+    			}
+    		}
+        }
+        return result;
+	}
+
+	public void onDestroy() {
+        Log.v(_TAG, " smartcard service onDestroy ...");
+        for (ITerminal terminal : mTerminals.values())
+            terminal.closeChannels();
+        for (ITerminal terminal : mAddOnTerminals.values())
+            terminal.closeChannels();
+
+        // Cancel the inialization background task if still running
+        if(mInitialiseTask != null) mInitialiseTask.cancel(true);
+        mInitialiseTask = null;
+
+        // Unregister all the broadcast receivers
+        unregisterSimStateChangedEvent(getApplicationContext()) ;
+        unregisterAdapterStateChangedEvent(getApplicationContext());
+        unregisterMediaMountedEvent(getApplicationContext());
+        
+        mServiceHandler = null;        
+        
+        Log.v(_TAG, Thread.currentThread().getName()
+                + " ... smartcard service onDestroy");
+
+	}
+
+	private ITerminal getTerminal(String reader, SmartcardError error) {
+        if (reader == null) {
+            setError(error, NullPointerException.class, "reader must not be null");
+            return null;
+        }
+        ITerminal terminal = mTerminals.get(reader);
+        if (terminal == null) {
+            terminal = mAddOnTerminals.get(reader);
+            if (terminal == null) {
+                setError(error, IllegalArgumentException.class, "unknown reader");
+            }
+        }
+        return terminal;
+    }
+
+    private String[] createTerminals() {
+        createBuildinTerminals();
+
+        Set<String> names = mTerminals.keySet();
+        ArrayList<String> list = new ArrayList<String>(names);
+        Collections.sort(list);
+        
+        // set UICC on the top
+        if(list.remove(_UICC_TERMINAL + " - UICC")) 
+        	list.add(0, _UICC_TERMINAL + " - UICC");
+
+        createAddonTerminals();
+        names = mAddOnTerminals.keySet();
+        for (String name : names) {
+            if (!list.contains(name)) {
+                list.add(name);
+            }
+        }
+        
+        return list.toArray(new String[list.size()]);
+    }
+
+    private String[] updateTerminals() {
+        Set<String> names = mTerminals.keySet();
+        ArrayList<String> list = new ArrayList<String>(names);
+        Collections.sort(list);
+        
+        // set UICC on the top
+        if(list.remove(_UICC_TERMINAL + " - UICC")) 
+        	list.add(0, _UICC_TERMINAL + " - UICC");
+        
+        updateAddonTerminals();
+        names = mAddOnTerminals.keySet();
+        for (String name : names) {
+            if (!list.contains(name)) {
+                list.add(name);
+            }
+        }
+        
+        return list.toArray(new String[list.size()]);
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+	private void createBuildinTerminals() {
+        Class[] types = new Class[] {
+            Context.class
+        };
+        Object[] args = new Object[] {
+            this
+        };
+        Object[] classes = getBuildinTerminalClasses();
+        for (Object clazzO : classes) {
+            try {
+                Class clazz = (Class) clazzO;
+                Constructor constr = clazz.getDeclaredConstructor(types);
+                ITerminal terminal = (ITerminal) constr.newInstance(args);
+                mTerminals.put(terminal.getName(), terminal);
+                Log.v(_TAG, Thread.currentThread().getName() + " adding "
+                        + terminal.getName());
+            } catch (Throwable t) {
+                Log.e(_TAG, Thread.currentThread().getName()
+                        + " CreateReaders Error: "
+                        + ((t.getMessage() != null) ? t.getMessage() : "unknown"));
+            }
+        }
+    }
+
+    private void createAddonTerminals() {
+        String[] packageNames = AddonTerminal.getPackageNames(this);
+        for (String packageName : packageNames) {
+            try {
+                String apkName = getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
+                DexFile dexFile = new DexFile(apkName);
+                Enumeration<String> classFileNames = dexFile.entries();
+                while (classFileNames.hasMoreElements()) {
+                    String className = classFileNames.nextElement();
+                    if (className.endsWith("Terminal")) {
+                        ITerminal terminal = new AddonTerminal(this, packageName, className);
+                        mAddOnTerminals.put(terminal.getName(), terminal);
+                        Log.v(_TAG, Thread.currentThread().getName() + " adding "
+                                + terminal.getName());
+                    }
+                }
+            } catch (Throwable t) {
+                Log.e(_TAG, Thread.currentThread().getName()
+                        + " CreateReaders Error: "
+                        + ((t.getMessage() != null) ? t.getMessage() : "unknown"));
+            }
+        }
+    }
+
+    private void updateAddonTerminals() {
+        Set<String> names = mAddOnTerminals.keySet();
+        ArrayList<String> namesToRemove = new ArrayList<String>();
+        for (String name : names) {
+            ITerminal terminal = mAddOnTerminals.get(name);
+            if (!terminal.isConnected()) {
+                namesToRemove.add(terminal.getName());
+            }
+        }
+        for (String name : namesToRemove) {
+            mAddOnTerminals.remove(name);
+        }
+
+        String[] packageNames = AddonTerminal.getPackageNames(this);
+        for (String packageName : packageNames) {
+            try {
+                String apkName = getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
+                DexFile dexFile = new DexFile(apkName);
+                Enumeration<String> classFileNames = dexFile.entries();
+                while (classFileNames.hasMoreElements()) {
+                    String className = classFileNames.nextElement();
+                    if (className.endsWith("Terminal")) {
+                        ITerminal terminal = new AddonTerminal(this, packageName, className);
+                        if (!mAddOnTerminals.containsKey(terminal.getName())) {
+                            mAddOnTerminals.put(terminal.getName(), terminal);
+                            Log.v(_TAG, Thread.currentThread().getName()
+                                    + " adding " + terminal.getName());
+                        }
+                    }
+                }
+
+            } catch (Throwable t) {
+                Log.e(_TAG, Thread.currentThread().getName()
+                        + " CreateReaders Error: "
+                        + ((t.getMessage() != null) ? t.getMessage() : "unknown"));
+            }
+        }
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+	private Object[] getBuildinTerminalClasses() {
+        ArrayList classes = new ArrayList();
+        try {
+            String packageName = "org.simalliance.openmobileapi.service";
+            String apkName = getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
+            DexClassLoader dexClassLoader = new DexClassLoader(apkName, getApplicationContext().getFilesDir().getAbsolutePath(), null, getClass()
+                    .getClassLoader());
+
+            Class terminalClass = Class.forName("org.simalliance.openmobileapi.service.Terminal", true, dexClassLoader);
+            if (terminalClass == null) {
+                return classes.toArray();
+            }
+
+            DexFile dexFile = new DexFile(apkName);
+            Enumeration<String> classFileNames = dexFile.entries();
+            while (classFileNames.hasMoreElements()) {
+                String className = classFileNames.nextElement();
+                Class clazz = Class.forName(className);
+                Class superClass = clazz.getSuperclass();
+                if (superClass != null && superClass.equals(terminalClass)
+                        && !className.equals("org.simalliance.openmobileapi.service.AddonTerminal")) {
+                    classes.add(clazz);
+                }
+            }
+        } catch (Throwable exp) {
+            // nothing to to
+        }
+        return classes.toArray();
+    }
+
+    /**
+     * Get package name from the user id.
+     *
+     * This shall fix the problem the issue that process name != package name
+     * due to anndroid:process attribute in manifest file.
+     *
+     * But this call is not really secure either since a uid can be shared between one
+     * and more apks
+     *
+     * @param uid
+     * @return The first package name associated with this uid.
+     */
+    public String getPackageNameFromCallingUid(int uid ){
+       PackageManager packageManager = getPackageManager();
+       if(packageManager != null){
+               String packageName[] = packageManager.getPackagesForUid(uid);
+               if( packageName != null && packageName.length > 0 ){
+                       return packageName[0];
+               }
+       }
+       throw new AccessControlException("Caller PackageName can not be determined");
+    }
+    
+    /**
+     * The smartcard service interface implementation.
+     */
+    private final ISmartcardService.Stub mSmartcardBinder = new ISmartcardService.Stub() {
+
+		@Override
+        public String[] getReaders(SmartcardError error) throws RemoteException {
+            clearError(error);
+            Log.v(_TAG, "getReaders()"); 
+            return updateTerminals();
+        }
+        
+		@Override
+		public ISmartcardServiceReader getReader(String reader,
+				SmartcardError error) throws RemoteException {
+        	clearError(error);
+			Terminal terminal = (Terminal)getTerminal(reader, error);
+			if( terminal != null ){
+				return terminal.new SmartcardServiceReader(SmartcardService.this);
+			}
+            setError(error, IllegalArgumentException.class, "invalid reader name");
+			return null;
+		}		
+        
+
+		@Override
+        public synchronized boolean[] isNFCEventAllowed(
+        		String reader, 
+        		byte[] aid,
+                String[] packageNames, 
+                ISmartcardServiceCallback callback, 
+                SmartcardError error) 
+                		throws RemoteException
+        {
+        	clearError(error);
+            try
+            {
+                if (callback == null) {
+                    setError(error, NullPointerException.class, "callback must not be null");
+                    return null;
+                }
+                ITerminal terminal = getTerminal(reader, error);
+                if (terminal == null) {
+                    return null;
+                }
+                if (aid == null || aid.length == 0) {
+                    aid = new byte[] {
+                            0x00, 0x00, 0x00, 0x00, 0x00
+                    };
+                }
+                if (aid.length < 5 || aid.length > 16) {
+                     setError(error, IllegalArgumentException.class, "AID out of range");
+                     return null;
+                }
+                if (packageNames == null || packageNames.length == 0) {
+                     setError(error, IllegalArgumentException.class, "process names not specified");
+                     return null;
+                }
+                AccessControlEnforcer ac = null;
+                if( terminal.getAccessControlEnforcer() == null ) {
+                	ac = new AccessControlEnforcer( terminal );
+                } else {
+                	ac = terminal.getAccessControlEnforcer();
+                }
+	            ac.setPackageManager(getPackageManager());
+	            ac.initialize(true, callback);
+	            return ac.isNFCEventAllowed(aid, packageNames, callback );
+            } catch (Exception e) {
+                setError(error, e);
+                Log.v(_TAG, "isNFCEventAllowed Exception: " + e.getMessage() );
+                return null;
+            }
+        }
+    };
+
+    /**
+     * The smartcard service interface implementation.
+     */
+    final class SmartcardServiceSession extends ISmartcardServiceSession.Stub {
+
+    	private final SmartcardServiceReader mReader;
+        /** List of open channels in use of by this client. */
+        private final Set<Channel> mChannels = new HashSet<Channel>();
+        
+        private final Object mLock = new Object();
+
+        private boolean mIsClosed;
+
+        private byte[] mAtr;
+    	
+    	public SmartcardServiceSession(SmartcardServiceReader reader){
+    		mReader = reader;
+    		mAtr = mReader.getAtr();
+    		mIsClosed = false;
+    	}
+    	
+		@Override
+		public ISmartcardServiceReader getReader() throws RemoteException {
+			return mReader;
+		}
+
+		@Override
+		public byte[] getAtr() throws RemoteException {
+			return mAtr;
+        }
+
+		@Override
+		public void close(SmartcardError error) throws RemoteException {
+            clearError(error);
+            if (mReader == null) {
+                return;
+            }
+            try {
+				mReader.closeSession(this);
+			} catch (CardException e) {
+				setError(error,e);
+			}
+		}
+
+		@Override
+		public void closeChannels(SmartcardError error) throws RemoteException {
+	        synchronized (mLock) {
+
+	        	Iterator<Channel>iter = mChannels.iterator();
+	        	try {
+		            while(iter.hasNext()) {
+		            	Channel channel = iter.next();
+		                if (channel != null && !channel.isClosed()) {
+		                    try {
+		                        channel.close();
+		                        // close changes indirectly mChannels, so we need a new iterator.
+		                        iter = mChannels.iterator();
+		                    } catch (Exception ignore) {
+		    	    	        Log.e(_TAG, "ServiceSession channel - close Exception " + ignore.getMessage());
+		                    }
+		                    channel.setClosed();
+		                }
+		            }
+		            mChannels.clear();
+	        	} catch( Exception e ) {
+	    	        Log.e(_TAG, "ServiceSession closeChannels Exception " + e.getMessage());
+	        	}
+	        }
+		}
+
+		@Override
+		public boolean isClosed() throws RemoteException {
+
+			return mIsClosed;
+		}
+
+		@Override
+		public ISmartcardServiceChannel openBasicChannel(
+				ISmartcardServiceCallback callback, SmartcardError error)
+				throws RemoteException {
+            return openBasicChannelAid( null, callback, error);
+		}
+
+		@Override
+		public ISmartcardServiceChannel openBasicChannelAid(byte[] aid,
+				ISmartcardServiceCallback callback, SmartcardError error)
+				throws RemoteException {
+            clearError(error);
+            if ( isClosed() ) {
+                setError( error, IllegalStateException.class, "session is closed");
+                return null;
+            }
+            if (callback == null) {
+                setError(error, NullPointerException.class, "callback must not be null");
+                return null;
+            }
+            if (mReader == null) {
+                setError(error, NullPointerException.class, "reader must not be null");
+                return null;
+            }
+
+            try {
+                boolean noAid = false;
+                if (aid == null || aid.length == 0) {
+                    aid = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00 };
+                    noAid = true;
+                }
+
+                if (aid.length < 5 || aid.length > 16) {
+                    setError(error, IllegalArgumentException.class, "AID out of range");
+                    return null;
+                }
+
+
+                String packageName = getPackageNameFromCallingUid( Binder.getCallingUid());
+                Log.v(_TAG, "Enable access control on basic channel for " + packageName);
+                ChannelAccess channelAccess = mReader.getTerminal().setUpChannelAccess(
+                		getPackageManager(), 
+                		aid,
+                		packageName, 
+                        callback );
+                Log.v(_TAG, "Access control successfully enabled.");
+                
+                channelAccess.setCallingPid(Binder.getCallingPid());
+
+
+
+                Log.v(_TAG, "OpenBasicChannel(AID)");
+                Channel channel = null;
+                if (noAid) {
+                    channel = mReader.getTerminal().openBasicChannel(this, callback);
+                } else {
+                    channel = mReader.getTerminal().openBasicChannel(this, aid, callback);
+                }
+
+                channel.setChannelAccess(channelAccess);
+
+                Log.v(_TAG, "Open basic channel success. Channel: " + channel.getChannelNumber() );
+                
+                SmartcardServiceChannel basicChannel = channel.new SmartcardServiceChannel(this);
+                mChannels.add(channel);
+                return basicChannel;
+                
+            } catch (Exception e) {
+                setError(error, e);
+                Log.v(_TAG, "OpenBasicChannel Exception: " + e.getMessage());
+                return null;
+            }
+		}
+
+		@Override
+		public ISmartcardServiceChannel openLogicalChannel(byte[] aid,
+				ISmartcardServiceCallback callback, SmartcardError error)
+				throws RemoteException {
+            clearError(error);
+
+            if ( isClosed() ) {
+                setError( error, IllegalStateException.class, "session is closed");
+                return null;
+            }
+
+            if (callback == null) {
+                setError(error, NullPointerException.class, "callback must not be null");
+                return null;
+            }
+            if (mReader == null) {
+                setError(error, NullPointerException.class, "reader must not be null");
+                return null;
+            }
+
+            try {
+                boolean noAid = false;
+                if (aid == null || aid.length == 0) {
+                    aid = new byte[] {
+                            0x00, 0x00, 0x00, 0x00, 0x00
+                    };
+                    noAid = true;
+                }
+
+                if (aid.length < 5 || aid.length > 16) {
+                    setError(error, IllegalArgumentException.class, "AID out of range");
+                    return null;
+                }
+
+
+                String packageName = getPackageNameFromCallingUid( Binder.getCallingUid());
+                Log.v(_TAG, "Enable access control on logical channel for " + packageName);
+                ChannelAccess channelAccess = mReader.getTerminal().setUpChannelAccess(
+                		getPackageManager(), 
+                		aid,
+                		packageName, 
+                        callback );
+                Log.v(_TAG, "Access control successfully enabled.");
+               channelAccess.setCallingPid(Binder.getCallingPid());
+
+
+                Log.v(_TAG, "OpenLogicalChannel");
+                Channel channel = null;
+                if (noAid) {
+                    channel = mReader.getTerminal().openLogicalChannel(this, callback);
+                } else {
+                    channel = mReader.getTerminal().openLogicalChannel(this, aid, callback);
+                }
+
+                channel.setChannelAccess(channelAccess);
+
+                Log.v(_TAG, "Open logical channel successfull. Channel: " + channel.getChannelNumber());
+                SmartcardServiceChannel logicalChannel = channel.new SmartcardServiceChannel(this);
+                mChannels.add(channel);
+                return logicalChannel;
+            } catch (Exception e) {
+                setError(error, e);
+                Log.v(_TAG, "OpenLogicalChannel Exception: " + e.getMessage());
+                return null;
+            }
+		}
+		
+		void setClosed(){
+			mIsClosed = true;
+
+		}
+    	
+	    /**
+	     * Closes the specified channel. <br>
+	     * After calling this method the session can not be used for the
+	     * communication with the secure element any more.
+	     * 
+	     * @param hChannel the channel handle obtained by an open channel command.
+	     */
+	    void removeChannel(Channel channel) {
+	        if (channel == null) {
+	        	return;
+	        }
+            mChannels.remove(channel);
+	    }
+    }
+    
+    /*
+     * Handler Thread used to load and initiate ChannelAccess condition
+     */
+    public final static int MSG_LOAD_UICC_RULES  = 1;
+    public final static int MSG_LOAD_ESE_RULES   = 2;
+    public final static int MSG_LOAD_SD_RULES    = 3;
+
+    public final static int NUMBER_OF_TRIALS      = 3;
+    public final static long WAIT_TIME            = 1000;
+
+    private final class ServiceHandler extends Handler {
+
+        @SuppressLint("HandlerLeak")
+        public ServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        public void sendMessage(int what, int nbTries) {
+           mServiceHandler.removeMessages(what);
+           Message newMsg = mServiceHandler.obtainMessage(what, nbTries, 0);
+           mServiceHandler.sendMessage(newMsg);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+           boolean result = true;
+
+           Log.i(_TAG, "Handle msg: what=" + msg.what + " nbTries=" + msg.arg1);
+
+           switch(msg.what) {
+           case MSG_LOAD_UICC_RULES:
+               try {
+                   result = initializeAccessControl(true, _UICC_TERMINAL, null );
+               } catch (Exception e) {
+                   Log.e(_TAG, "Got exception:" + e);
+               }
+               break;
+
+           case MSG_LOAD_ESE_RULES:
+               try {
+                   result = initializeAccessControl(true, _eSE_TERMINAL, null );
+               } catch (Exception e) {
+                   Log.e(_TAG, "Got exception:" + e);
+               }
+               break;
+
+           case MSG_LOAD_SD_RULES:
+               try {
+                   result = initializeAccessControl(true, _SD_TERMINAL, null );
+               } catch (Exception e) {
+                   Log.e(_TAG, "Got exception:" + e);
+               }
+               break;
+           }
+
+           if(!result && msg.arg1 > 0) {
+               // Try to re-post the message
+               Log.e(_TAG, "Fail to load rules: Let's try another time (" + msg.arg1 + " remaining attempt");
+               Message newMsg = mServiceHandler.obtainMessage(msg.what, msg.arg1 - 1, 0);
+               mServiceHandler.sendMessageDelayed(newMsg, WAIT_TIME);
+           }
+        }
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/SmartcardServiceBootCompletedBroadcastReceiver.java b/src/org/simalliance/openmobileapi/service/SmartcardServiceBootCompletedBroadcastReceiver.java
new file mode 100644
index 0000000..d136b06
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/SmartcardServiceBootCompletedBroadcastReceiver.java
@@ -0,0 +1,26 @@
+
+package org.simalliance.openmobileapi.service;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class SmartcardServiceBootCompletedBroadcastReceiver extends BroadcastReceiver {
+	public final static String _TAG = "SmartcardService";
+	public final static String _SCAPI_SERVICE = "org.simalliance.openmobileapi.service.SmartcardService";
+
+	@Override
+	public void onReceive(Context context, Intent intent) {
+	    final boolean bootCompleted = intent.getAction().equals("android.intent.action.BOOT_COMPLETED");
+   
+        Log.v(_TAG, Thread.currentThread().getName() + " Received broadcast");
+	    if( bootCompleted ){
+	    	Log.v(_TAG, "Starting smartcard service after boot completed");
+	    	Intent serviceIntent = new Intent(context, org.simalliance.openmobileapi.service.SmartcardService.class );
+	    	context.startService(serviceIntent);
+	    } else {
+	    	
+	    }
+	}
+};
diff --git a/src/org/simalliance/openmobileapi/service/Terminal.java b/src/org/simalliance/openmobileapi/service/Terminal.java
new file mode 100644
index 0000000..120f1f1
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/Terminal.java
@@ -0,0 +1,743 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+import android.content.Context;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.SmartcardService.SmartcardServiceSession;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Random;
+
+
+import android.content.pm.PackageManager;
+import org.simalliance.openmobileapi.service.security.AccessControlEnforcer;
+import org.simalliance.openmobileapi.service.security.ChannelAccess;
+
+
+/**
+ * Smartcard service base class for terminal resources.
+ */
+public abstract class Terminal implements ITerminal {
+
+    /** Random number generator used for handle creation. */
+    static Random mRandom = new Random();
+
+    protected Context mContext;
+
+    private final Map<Long, IChannel> mChannels = new HashMap<Long, IChannel>();
+    
+    private final Object mLock = new Object();
+
+    protected final String mName;
+
+    public volatile boolean mIsConnected;
+    
+	protected byte[] mSelectResponse;
+	
+    protected boolean mDefaultApplicationSelectedOnBasicChannel = true;
+
+ 
+    /**
+     * For each Terminal there will be one AccessController object.
+     */
+    private AccessControlEnforcer mAccessControlEnforcer;
+
+
+   /**
+     * Returns a concatenated response.
+     * 
+     * @param r1 the first part of the response.
+     * @param r2 the second part of the response.
+     * @param length the number of bytes of the second part to be appended.
+     * @return a concatenated response.
+     */
+    static byte[] appendResponse(byte[] r1, byte[] r2, int length) {
+        byte[] rsp = new byte[r1.length + length];
+        System.arraycopy(r1, 0, rsp, 0, r1.length);
+        System.arraycopy(r2, 0, rsp, r1.length, length);
+        return rsp;
+    }
+
+    /**
+     * Creates a formatted exception message.
+     * 
+     * @param commandName the name of the command. <code>null</code> if not
+     *            specified.
+     * @param sw the response status word.
+     * @return a formatted exception message.
+     */
+    static String createMessage(String commandName, int sw) {
+        StringBuffer message = new StringBuffer();
+        if (commandName != null) {
+            message.append(commandName).append(" ");
+        }
+        message.append("SW1/2 error: ");
+        message.append(Integer.toHexString(sw | 0x10000).substring(1));
+        return message.toString();
+    }
+
+    /**
+     * Creates a formatted exception message.
+     * 
+     * @param commandName the name of the command. <code>null</code> if not
+     *            specified.
+     * @param message the message to be formatted.
+     * @return a formatted exception message.
+     */
+    static String createMessage(String commandName, String message) {
+        if (commandName == null) {
+            return message;
+        }
+        return commandName + " " + message;
+    }
+
+    public Terminal(String name, Context context) {
+        this.mContext = context;
+        this.mName = name;
+    }
+
+    /**
+     * This method is called in SmartcardService:onDestroy 
+     * to clean up all open channels
+     */
+    public synchronized void closeChannels() {
+        Collection<IChannel> col = mChannels.values();
+        IChannel[] channelList = col.toArray(new IChannel[col.size()]);
+        for (IChannel channel : channelList) {
+            try {
+                closeChannel((Channel)channel);
+            } catch (Exception ignore) {
+            }
+        }
+    }
+
+    /**
+     * Closes the specified channel.
+     * 
+     * @param channel the channel to be closed.
+     * @throws CardException if closing the channel failed.
+     */
+    public void closeChannel(Channel channel) throws CardException {
+
+        synchronized(mLock) {
+	        try {
+	            internalCloseLogicalChannel(channel.getChannelNumber());
+	        } finally {
+	            mChannels.remove(channel.getHandle());
+	            if (mIsConnected && mChannels.isEmpty()) {
+	                try {
+	                    internalDisconnect();
+	                } catch (Exception ignore) {
+	                }
+	            }
+	        }
+        }
+    }
+
+    /**
+     * Creates a channel instance.
+     * 
+     * @param channelNumber the channel number according to ISO 7816-4.
+     * @param callback the callback used to detect the death of the client.
+     * @return a channel instance.
+     */
+    protected Channel createChannel(SmartcardServiceSession session, int channelNumber, ISmartcardServiceCallback callback) {
+        return new Channel(session, this, channelNumber, callback);
+    }
+
+    private IChannel getBasicChannel() {
+        for (IChannel channel : mChannels.values()) {
+            if (channel.getChannelNumber() == 0) {
+                return channel;
+            }
+        }
+        return null;
+    }
+
+    public synchronized IChannel getChannel(long hChannel) {
+        return mChannels.get(hChannel);
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Implements the terminal specific connect operation.
+     * 
+     * @throws CardException if connecting the card failed.
+     */
+    protected abstract void internalConnect() throws CardException;
+
+    /**
+     * Implements the terminal specific disconnect operation.
+     * 
+     * @throws CardException if disconnecting from the card failed.
+     */
+    protected abstract void internalDisconnect() throws CardException;
+
+    /**
+     * Implementation of the SELECT command.
+     * 
+     * @return the number of the logical channel according to ISO 7816-4.
+     * @throws CardException
+     */
+    abstract protected int internalOpenLogicalChannel() throws Exception;
+
+    /**
+     * Implementation of the MANAGE CHANNEL open and SELECT commands.
+     * 
+     * @return the number of the logical channel according to ISO 7816-4.
+     * @throws CardException
+     */
+    abstract protected int internalOpenLogicalChannel(byte[] aid) throws Exception;
+
+    /**
+     * Implementation of the MANAGE CHANNEL close command.
+     * 
+     * @param channelNumber
+     * @throws CardException
+     */
+    abstract protected void internalCloseLogicalChannel(int channelNumber) throws CardException;
+
+    /**
+     * Implements the terminal specific transmit operation.
+     * 
+     * @param command the command APDU to be transmitted.
+     * @return the response APDU received.
+     * @throws CardException if the transmit operation failed.
+     */
+    protected abstract byte[] internalTransmit(byte[] command) throws CardException;
+
+    /**
+     * Performs a select command on the basic channel without an AID parameter. <br>
+     * The card manager will be selected.
+     */
+    public void select() {
+    	mSelectResponse = null;
+        byte[] selectCommand = new byte[5];
+        selectCommand[0] = 0x00;
+        selectCommand[1] = (byte) 0xA4;
+        selectCommand[2] = 0x04;
+        selectCommand[3] = 0x00;
+        selectCommand[4] = 0x00;
+        try {
+        	mSelectResponse = transmit(selectCommand, 2, 0x9000, 0xFFFF, "SELECT");
+            
+        } catch (Exception exp) {
+            throw new NoSuchElementException(exp.getMessage());
+        }
+    }
+
+    /**
+     * Returns the ATR of the connected card or null if the ATR is not
+     * available.
+     * 
+     * @return the ATR of the connected card or null if the ATR is not
+     *         available.
+     */
+    public byte[] getAtr() {
+        return null;
+    }
+
+    /**
+     * Performs a select command on the basic channel
+     * 
+     * @param aid the aid which should be selected.
+     */
+    public void select(byte[] aid) {
+        if (aid == null) {
+            throw new NullPointerException("aid must not be null");
+        }
+        mSelectResponse = null;
+        byte[] selectCommand = new byte[aid.length + 6];
+        selectCommand[0] = 0x00;
+        selectCommand[1] = (byte) 0xA4;
+        selectCommand[2] = 0x04;
+        selectCommand[3] = 0x00;
+        selectCommand[4] = (byte) aid.length;
+        System.arraycopy(aid, 0, selectCommand, 5, aid.length);
+        try {
+        	mSelectResponse = transmit(selectCommand, 2, 0x9000, 0xFFFF, "SELECT");
+        } catch (Exception exp) {
+            throw new NoSuchElementException(exp.getMessage());
+        }
+    }
+
+    public Channel openBasicChannel(SmartcardServiceSession session, ISmartcardServiceCallback callback) throws CardException {
+        if (callback == null) {
+            throw new NullPointerException("callback must not be null");
+        }
+
+        synchronized (mLock) {
+        	if(!mDefaultApplicationSelectedOnBasicChannel) {
+        		throw new CardException("default application is not selected");
+        	}
+            if (getBasicChannel() != null) {
+                throw new CardException("basic channel in use");
+            }
+            if (mChannels.isEmpty()) {
+                internalConnect();
+            }
+
+
+            Channel basicChannel = createChannel(session, 0, callback);
+            basicChannel.hasSelectedAid(false, null);
+            registerChannel(basicChannel);
+            return basicChannel;
+        }
+    }
+
+    public Channel openBasicChannel(SmartcardServiceSession session, byte[] aid, ISmartcardServiceCallback callback) throws Exception {
+        if (callback == null) {
+            throw new NullPointerException("callback must not be null");
+        }
+        if (aid == null) {
+            throw new NullPointerException("aid must not be null");
+        }
+
+        synchronized (mLock) {
+            if (getBasicChannel() != null) {
+                throw new CardException("basic channel in use");
+            }
+            if (mChannels.isEmpty()) {
+                internalConnect();
+            }
+
+            try {
+                select(aid);
+            } catch (Exception e) {
+                if (mIsConnected && mChannels.isEmpty()) {
+                    internalDisconnect();
+                }
+                throw e;
+            }
+
+
+            Channel basicChannel = createChannel(session, 0, callback);
+            basicChannel.hasSelectedAid(true, aid);
+            mDefaultApplicationSelectedOnBasicChannel = false;
+            registerChannel(basicChannel);
+            return basicChannel;
+        }
+    }
+
+    public Channel openLogicalChannel(SmartcardServiceSession session, ISmartcardServiceCallback callback) throws Exception {
+        if (callback == null) {
+            throw new NullPointerException("callback must not be null");
+        }
+
+        synchronized (mLock) {
+            if (mChannels.isEmpty()) {
+                internalConnect();
+            }
+
+            int channelNumber = 0;
+            try {
+                channelNumber = internalOpenLogicalChannel();
+            } catch (Exception e) {
+                if (mIsConnected && mChannels.isEmpty()) {
+                    internalDisconnect();
+                }
+                throw e;
+            }
+
+
+            Channel logicalChannel = createChannel(session, channelNumber, callback);
+            logicalChannel.hasSelectedAid(false, null);
+            registerChannel(logicalChannel);
+            return logicalChannel;
+        }
+    }
+
+    public Channel openLogicalChannel(SmartcardServiceSession session, byte[] aid, ISmartcardServiceCallback callback) throws Exception {
+        if (callback == null) {
+            throw new NullPointerException("callback must not be null");
+        }
+        if (aid == null) {
+            throw new NullPointerException("aid must not be null");
+        }
+
+        synchronized (mLock) {
+            if (mChannels.isEmpty()) {
+                internalConnect();
+            }
+
+            int channelNumber = 0;
+            try {
+                channelNumber = internalOpenLogicalChannel(aid);
+            } catch (Exception e) {
+                if (mIsConnected && mChannels.isEmpty()) {
+                    internalDisconnect();
+                }
+                throw e;
+            }
+
+
+            Channel logicalChannel = createChannel(session, channelNumber, callback);
+            logicalChannel.hasSelectedAid(true, aid);
+            registerChannel(logicalChannel);
+            return logicalChannel;
+        }
+    }
+
+    public boolean isConnected() {
+        return mIsConnected;
+    }
+
+    /**
+     * Protocol specific implementation of the transmit operation. This method
+     * is synchronized in order to handle GET RESPONSE and command repetition
+     * without interruption by other commands.
+     * 
+     * @param cmd the command to be transmitted.
+     * @return the response received.
+     * @throws CardException if the transmit operation failed.
+     */
+    protected synchronized byte[] protocolTransmit(byte[] cmd) throws CardException {
+        byte[] command = cmd;
+        byte[] rsp = null;
+        synchronized (mLock) {
+        	rsp = internalTransmit(command);
+        }
+
+        if (rsp.length >= 2) {
+            int sw1 = rsp[rsp.length - 2] & 0xFF;
+            int sw2 = rsp[rsp.length - 1] & 0xFF;
+            if (sw1 == 0x6C) {
+                command[cmd.length - 1] = rsp[rsp.length - 1];
+                rsp = internalTransmit(command);
+            } else if (sw1 == 0x61) {
+                byte[] getResponseCmd = new byte[] {
+                        command[0], (byte) 0xC0, 0x00, 0x00, 0x00
+                };
+                byte[] response = new byte[rsp.length - 2];
+                System.arraycopy(rsp, 0, response, 0, rsp.length - 2);
+                while (true) {
+                    getResponseCmd[4] = rsp[rsp.length - 1];
+                    rsp = internalTransmit(getResponseCmd);
+                    if (rsp.length >= 2 && rsp[rsp.length - 2] == 0x61) {
+                        response = appendResponse(response, rsp, rsp.length - 2);
+                    } else {
+                        response = appendResponse(response, rsp, rsp.length);
+                        break;
+                    }
+                }
+                rsp = response;
+            } else if (rsp.length == 2 && sw1 == 0x63 && sw2 == 0x10) {
+                byte[] getResponseCmd = new byte[] {
+                        (byte)(command[0] & 0x03), (byte) 0xC0, 0x00, 0x00, 0x00
+                };
+                byte[] response = new byte[rsp.length - 2];
+                System.arraycopy(rsp, 0, response, 0, rsp.length - 2);
+                rsp[rsp.length - 1] = 0x00;
+                while (true) {
+                    getResponseCmd[4] = rsp[rsp.length - 1];
+                    rsp = internalTransmit(getResponseCmd);
+                    if (rsp.length >= 2 && rsp[rsp.length - 2] == 0x61) {
+                        response = appendResponse(response, rsp, rsp.length - 2);
+                    } else {
+                        response = appendResponse(response, rsp, rsp.length);
+                        break;
+                    }
+                }
+                rsp = response;
+            }
+        }
+        return rsp;
+    }
+
+    /**
+     * Creates a handle for the specified channel instances and adds the channel
+     * instance to the channel list.
+     * 
+     * @param channel
+     * @return the channel handle.
+     */
+    private long registerChannel(Channel channel) {
+        long hChannel = mRandom.nextInt();
+        hChannel <<= 32;
+        hChannel |= (((long) channel.hashCode()) & 0xFFFFFFFFL);
+
+        channel.setHandle(hChannel);
+
+        mChannels.put(hChannel, channel);
+
+        return hChannel;
+    }
+
+    /**
+     * Transmits the specified command and returns the response. Optionally
+     * checks the response length and the response status word. The status word
+     * check is implemented as follows (sw = status word of the response):
+     * <p>
+     * if ((sw & swMask) != (swExpected & swMask)) throw new CardException();
+     * </p>
+     * 
+     * @param cmd the command APDU to be transmitted.
+     * @param minRspLength the minimum length of received response to be
+     *            checked.
+     * @param swExpected the response status word to be checked.
+     * @param swMask the mask to be used for response status word comparison.
+     * @param commandName the name of the smart card command for logging
+     *            purposes. May be <code>null</code>.
+     * @return the response received.
+     * @throws CardException if the transmit operation or the minimum response
+     *             length check or the status word check failed.
+     */
+    public byte[] transmit(byte[] cmd, int minRspLength, int swExpected, int swMask,
+        String commandName) throws CardException {
+        byte[] rsp = null;
+        try {
+            rsp = protocolTransmit(cmd);
+        } catch (CardException e) {
+            if (commandName == null) {
+                throw e;
+            } else {
+                throw new CardException(createMessage(commandName, "transmit failed"), e);
+            }
+        }
+        if (minRspLength > 0) {
+            if (rsp == null || rsp.length < minRspLength) {
+                throw new CardException(createMessage(commandName, "response too small"));
+            }
+        }
+        if (swMask != 0) {
+            if (rsp == null || rsp.length < 2) {
+                throw new CardException(createMessage(commandName, "SW1/2 not available"));
+            }
+            int sw1 = rsp[rsp.length - 2] & 0xFF;
+            int sw2 = rsp[rsp.length - 1] & 0xFF;
+            int sw = (sw1 << 8) | sw2;
+            if ((sw & swMask) != (swExpected & swMask)) {
+                throw new CardException(createMessage(commandName, sw));
+            }
+        }
+        return rsp;
+    }
+    
+    /**
+     * Returns the data as received from the application select command inclusively the status word.
+     * The returned byte array contains the data bytes in the following order:
+     * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+     * @return The data as returned by the application select command inclusively the status word.
+     * @return Only the status word if the application select command has no returned data.
+     * @return null if an application select command has not been performed or the selection response can not
+     * be retrieved by the reader implementation.
+     */
+    public byte[] getSelectResponse()
+    {
+    	return mSelectResponse;
+    }
+    
+    /**
+     * Exchanges APDU (SELECT, READ/WRITE) to the 
+     * given EF by File ID and file path via iccIO.
+     * 
+     * The given command is checked and might be rejected.
+     * 
+     * @param fileID
+     * @param filePath
+     * @param cmd
+     * @return
+     */
+    public byte[] simIOExchange(int fileID, String filePath, byte[] cmd) 
+    	throws Exception {
+    	throw new Exception("SIM IO error!");
+    }
+    
+    
+
+    public ChannelAccess setUpChannelAccess( 
+    		PackageManager packageManager, 
+    		byte[] aid, 
+    		String packageName, 
+    		ISmartcardServiceCallback callback){
+    	if( mAccessControlEnforcer == null ){
+    		throw new AccessControlException("Access Control Enforcer not properly set up");
+    	}
+		mAccessControlEnforcer.setPackageManager(packageManager);
+    	return mAccessControlEnforcer.setUpChannelAccess(aid, packageName, callback);
+    }
+    
+    public synchronized boolean initializeAccessControl(boolean loadAtStartup, ISmartcardServiceCallback callback ){
+    	if( mAccessControlEnforcer == null ){
+    		mAccessControlEnforcer = new AccessControlEnforcer(this);
+    	}
+    	return mAccessControlEnforcer.initialize( loadAtStartup, callback );
+    }
+    
+    public AccessControlEnforcer getAccessControlEnforcer(){
+    	return mAccessControlEnforcer;
+    }
+    
+    public synchronized void resetAccessControl() {
+        if(mAccessControlEnforcer != null ) mAccessControlEnforcer.reset();
+    }
+
+    
+    /**
+     * Implementation of the SmartcardService Reader interface according to OMAPI.
+     */
+    final class SmartcardServiceReader extends ISmartcardServiceReader.Stub {
+
+        protected final SmartcardService mService;
+
+        private final ArrayList<SmartcardServiceSession> mSessions = new ArrayList<SmartcardServiceSession>();
+        
+        private final Object mLock = new Object();
+    	
+        public SmartcardServiceReader( SmartcardService service ){
+        	this.mService = service;
+        }
+        
+        public byte[] getAtr(){
+        	return Terminal.this.getAtr();
+        }
+        
+		@Override
+		public String getName(SmartcardError error) throws RemoteException {
+			SmartcardService.clearError(error);			
+			return Terminal.this.getName();
+		}
+
+		@Override
+		public boolean isSecureElementPresent(SmartcardError error)
+				throws RemoteException {
+			SmartcardService.clearError(error);			
+			try {
+				return Terminal.this.isCardPresent();
+			} catch (Exception e) {
+				SmartcardService.setError(error, e);
+			}
+			return false;
+		}
+
+		@Override
+		public ISmartcardServiceSession openSession(SmartcardError error)
+				throws RemoteException {
+			SmartcardService.clearError(error);			
+	        try {
+				if (!Terminal.this.isCardPresent()){
+					SmartcardService.setError(error,new IOException("Secure Element is not presented."));
+					return null;
+				}
+			} catch (CardException e) {
+				SmartcardService.setError(error,e);
+				return null;
+			}
+
+	        synchronized (mLock) {
+	        	try {
+		        	mService.initializeAccessControl(Terminal.this.getName(), null);
+	        	} catch (Exception e ){
+					SmartcardService.setError(error,e);
+					return null; // Reader.openSession() will throw an IOException when session is null
+	        	}
+	        	SmartcardServiceSession session = mService.new SmartcardServiceSession(this);
+	            mSessions.add(session);
+
+	            return session;
+	        }
+		}
+
+		@Override
+		public void closeSessions(SmartcardError error) throws RemoteException {
+
+			SmartcardService.clearError(error);			
+	        synchronized (mLock) {
+	            for (SmartcardServiceSession session : mSessions) {
+	                if (session != null && !session.isClosed()) {
+	                    session.closeChannels(error);
+	                    session.setClosed();
+	                }
+	            }
+	            mSessions.clear();
+	        }
+		}
+		
+	    /**
+	     * Closes the defined Session and all its allocated resources. <br>
+	     * After calling this method the Session can not be used for the
+	     * communication with the Secure Element any more.
+	     * 
+	     * @param session the Session that should be closed
+	     * @throws RemoteException 
+	     * @throws CardException 
+	     * @throws NullPointerException if Session is null
+	     */
+	    void closeSession(SmartcardServiceSession session) throws RemoteException, CardException {
+	        if (session == null) {
+	            throw new NullPointerException("session is null");
+	        }
+	        synchronized (mLock) {
+				if (!session.isClosed()) {
+					SmartcardError error = new SmartcardError();
+				    session.closeChannels(error);
+				    error.throwException();
+				    session.setClosed();
+				}
+	            mSessions.remove(session);
+	        }
+	    }
+		
+	    Terminal getTerminal() {
+	    	return Terminal.this;
+	    }
+    }
+    
+    public void dump(PrintWriter writer, String prefix) {
+	        writer.println(prefix + "SMARTCARD SERVICE TERMINAL: " + mName);
+	        writer.println();
+	 
+	        prefix += "  ";
+	
+	        writer.println(prefix + "mIsConnected:" + mIsConnected);
+	        writer.println();
+	
+	        /* Dump the list of currunlty openned channels */
+	        writer.println(prefix + "List of open channels:");
+	
+	        for(IChannel channel: mChannels.values()) {
+	            writer.println(prefix + "  channel " + channel.getChannelNumber() + ": ");
+	            writer.println(prefix + "    package      : " + channel.getChannelAccess().getPackageName());
+	            writer.println(prefix + "    pid          : " + channel.getChannelAccess().getCallingPid());
+	            writer.println(prefix + "    aid selected : " + channel.hasSelectedAid());
+	            writer.println(prefix + "    basic channel: " + channel.isBasicChannel());
+	        }
+	
+	        writer.println();
+	
+	        /* Dump ACE data */
+	        if(mAccessControlEnforcer != null) mAccessControlEnforcer.dump(writer, prefix);
+	    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/Util.java b/src/org/simalliance/openmobileapi/service/Util.java
new file mode 100644
index 0000000..39b2a16
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/Util.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service;
+
+public class Util {
+
+	public static final byte END = -1;
+	
+    public static byte[] mergeBytes(byte[] array1, byte[] array2) {
+        byte[] data = new byte[array1.length + array2.length];
+        int i = 0;
+        for (; i < array1.length; i++)
+            data[i] = array1[i];
+        for (int j = 0; j < array2.length; j++)
+            data[j + i] = array2[j];
+        return data;
+    }
+
+    public static byte[] getMid(byte[] array, int start, int length) {
+        byte[] data = new byte[length];
+        System.arraycopy(array, start, data, 0, length);
+        return data;
+    }
+
+    public static String bytesToString(byte[] bytes) {
+    	if(bytes == null)
+    		return "";
+        StringBuffer sb = new StringBuffer();
+        for (byte b : bytes) {
+            sb.append(String.format("%02x ", b & 0xFF));
+        }
+        String str = sb.toString();
+        if (str.length() > 0) {
+            str = str.substring(0, str.length() - 1);
+        }
+        return str;
+    }
+
+    public static String bytesToString(byte[] array,int offset,int length,
+					                String prefix) {
+	if (array==null) return null;
+	if (length==-1) length=array.length-offset;
+
+	StringBuffer buffer=new StringBuffer();
+	for (int ind=offset;ind<offset+length;ind++)
+	    buffer.append(prefix+Integer.toHexString(
+					   0x100+(array[ind] & 0xFF)).substring(1));
+	return buffer.toString();
+    }    
+    
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/AccessControlEnforcer.java b/src/org/simalliance/openmobileapi/service/security/AccessControlEnforcer.java
new file mode 100644
index 0000000..e45081c
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/AccessControlEnforcer.java
@@ -0,0 +1,512 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ * 
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.PrintWriter;
+import java.security.AccessControlException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.MissingResourceException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import org.simalliance.openmobileapi.service.CardException;
+import org.simalliance.openmobileapi.service.IChannel;
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.ITerminal;
+import org.simalliance.openmobileapi.service.SmartcardService;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.ChannelAccess.ACCESS;
+import org.simalliance.openmobileapi.service.security.ara.AraController;
+
+import org.simalliance.openmobileapi.service.security.arf.ArfController;
+ 
+
+public class AccessControlEnforcer {
+
+	private PackageManager mPackageManager = null;
+    
+    private AraController mAraController = null;
+    private boolean mUseAra = true;
+
+    private ArfController mArfController = null;
+    private boolean mUseArf = false;
+
+    private AccessRuleCache mAccessRuleCache = null;
+    private boolean mRulesRead = false;
+   
+    private ITerminal mTerminal = null;
+    
+    private ChannelAccess mInitialChannelAccess = new ChannelAccess();
+    private boolean mFullAccess = false;
+
+    protected boolean[] mNfcEventFlags = null;
+    
+    private final String ACCESS_CONTROL_ENFORCER = "Access Control Enforcer: ";
+
+    public AccessControlEnforcer( ITerminal terminal ) {
+
+    	mTerminal = terminal;
+        mAccessRuleCache = new AccessRuleCache();
+    }
+    
+    public PackageManager getPackageManager() {
+		return mPackageManager;
+	}
+
+	public void setPackageManager(PackageManager packageManager) {
+		this.mPackageManager = packageManager;
+	}
+
+	public ITerminal getTerminal(){
+		return mTerminal;
+	}
+
+	public AccessRuleCache getAccessRuleCache(){
+    	return mAccessRuleCache;
+    }
+    
+    public static byte[] getDefaultAccessControlAid(){
+    	return AraController.getAraMAid();
+    }
+
+    public synchronized void reset() {
+        // Destroy any previous Controler
+        // in order to reset the ACE
+        Log.i(SmartcardService._TAG, "Reset the ACE for terminal:" + mTerminal.getName());
+        mAraController = null;
+        mArfController = null;
+    }
+
+    public synchronized boolean initialize(boolean loadAtStartup, ISmartcardServiceCallback callback) {
+    	try {
+
+	    	boolean status = true;
+	    	String denyMsg = "";
+			// allow access to set up access control for a channel
+	    	mInitialChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
+	    	mInitialChannelAccess.setNFCEventAccess(ChannelAccess.ACCESS.ALLOWED);
+	    	mInitialChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
+
+	    	readSecurityProfile();
+	    	
+	        if(!mTerminal.getName().startsWith(SmartcardService._UICC_TERMINAL)) {
+	            // When SE is not the UICC then it's allowed to grant full access if no
+	            // rules can be retreived.
+	            mFullAccess = true;
+	        }
+	    	
+	    	// 1 - Let's try to use ARA
+	        if( mUseAra && mAraController == null)
+	        	mAraController = new AraController(this);
+	        
+	        if( mUseAra && mAraController != null ){
+	    		try {
+	    			mAraController.initialize(loadAtStartup, callback);
+	    			// disable other access methods
+	    			
+	    			Log.i(SmartcardService._TAG, "ARA applet is used for:" + mTerminal.getName());
+	    			mUseArf = false;
+	    			mFullAccess = false;
+	    			
+	    		} catch( Exception e ) {
+
+			     // ARA cannot be used since we got an exception during initialization
+	    			mUseAra = false; 
+	    			denyMsg = e.getLocalizedMessage();
+	    			
+	                 if( e instanceof MissingResourceException ) {
+	                     if(mTerminal.getName().startsWith(SmartcardService._UICC_TERMINAL)) {
+	                         // If the SE is a UICC then a possible explanation could simply
+	                         // be due to the fact that the UICC is old and doesn't
+	                         // support logical channel (and is not compliant with GP spec).
+	                         // in this case we should simply act as if no ARA was available
+	                         Log.w(SmartcardService._TAG, "Got MissingResourceException: Does the UICC support logical channel?");
+	                         Log.w(SmartcardService._TAG, "Full message: " +  e.getMessage());
+	                     } else {
+	                         // If the SE is not a UICC then this exception means that something
+	                         // wrong has occured!
+	                         throw new MissingResourceException( e.getMessage(), "", "");
+	                     }
+	                } else if( mAraController.isNoSuchElement() ) {
+	                	 Log.i(SmartcardService._TAG, "No ARA applet found in: " + mTerminal.getName());
+	    			} else {
+	    				// ARA is available but doesn't work properly.
+	    				// We are going to disable everything per security req.
+	    		        Log.i(SmartcardService._TAG, "AccessControlEnforcer - Problem accessing ARA, Access DENIED. " + e.getLocalizedMessage());
+
+	    		        // access is denied for any terminal if exception during accessing ARA has any other reason.
+	    				mUseArf = false;
+	    				mFullAccess = false;
+	    				status = false;
+	    			}
+	    		}
+	    	}
+
+	    	// 2 - Let's try to use ARF since ARA cannot be used
+	    	if(mUseArf && !mTerminal.getName().startsWith(SmartcardService._UICC_TERMINAL)) {
+	    		Log.i(SmartcardService._TAG, "Disable ARF for terminal: " + mTerminal.getName() + " (ARF is only available for UICC)");
+	            mUseArf = false; // Arf is only supproted on UICC
+	        }
+	    		
+	        if( mUseArf && mArfController == null)
+	    		mArfController = new ArfController(this);
+	    		
+	        if( mUseArf && mArfController != null) {
+	    		try {
+	    			mArfController.initialize(callback);
+	    			// disable other access methods
+	    			Log.i(SmartcardService._TAG, "ARF rules are used for:" + mTerminal.getName());
+	    			mFullAccess = false;
+	    		} catch( Exception e ) {
+	    			// ARF cannot be used since we got an exception
+	    			mUseArf = false;
+		    		status = false;
+	    			denyMsg = e.getLocalizedMessage();
+		    		Log.e(SmartcardService._TAG, e.getMessage() );
+				}
+	    	}
+
+	        /* 3 - Let's grant full access since neither ARA nor ARF can be used */
+	        if(mFullAccess) {
+	            mInitialChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
+	            mInitialChannelAccess.setNFCEventAccess(ChannelAccess.ACCESS.ALLOWED);
+	            mInitialChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
+	
+	            Log.i(SmartcardService._TAG, "Full access granted for:" + mTerminal.getName());
+	        }
+	
+	        /* 4 - Let's block everything since neither ARA, ARF or fullaccess can be used */
+	        if(!mUseArf && !mUseAra && !mFullAccess) {
+	            mInitialChannelAccess.setApduAccess(ChannelAccess.ACCESS.DENIED);
+	            mInitialChannelAccess.setNFCEventAccess(ChannelAccess.ACCESS.DENIED);
+	            mInitialChannelAccess.setAccess(ChannelAccess.ACCESS.DENIED, denyMsg);
+	
+	            Log.i(SmartcardService._TAG, "Deny any access to:" + mTerminal.getName());
+	        }
+	        mRulesRead = status;
+	    	return status;
+    	} finally {
+ 
+    	}
+    }
+    
+    public static Certificate decodeCertificate(byte[] certData) throws CertificateException {
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate) certFactory
+                .generateCertificate(new ByteArrayInputStream(certData));
+       
+        return cert;
+    }
+    
+    public synchronized void checkCommand(IChannel channel, byte[] command) {
+
+        ChannelAccess ca = channel.getChannelAccess();
+        if (ca == null) {
+
+            throw new AccessControlException(ACCESS_CONTROL_ENFORCER + "Channel access not set");
+        }
+        
+        String reason = ca.getReason();
+        if (reason.length() == 0) {
+            reason = "Command not allowed!";
+        }
+
+        if (ca.getAccess() != ACCESS.ALLOWED ) {
+
+            throw new AccessControlException(ACCESS_CONTROL_ENFORCER + reason);
+        }
+        if (ca.isUseApduFilter()) {
+            ApduFilter[] accessConditions = ca.getApduFilter();
+            if (accessConditions == null || accessConditions.length == 0) {
+
+                throw new AccessControlException(ACCESS_CONTROL_ENFORCER + "Access Rule not available: " + reason);
+            }
+            for (ApduFilter ac : accessConditions) {
+                if (CommandApdu.compareHeaders(command, ac.getMask(), ac.getApdu())) {
+
+                    return;
+                }
+            }
+
+            throw new AccessControlException(ACCESS_CONTROL_ENFORCER + "Access Rule does not match: " + reason);
+        }
+        if (ca.getApduAccess() == ChannelAccess.ACCESS.ALLOWED) {
+
+            return;
+        } else {
+
+            throw new AccessControlException(ACCESS_CONTROL_ENFORCER + "APDU access NOT allowed" );
+        }
+    }
+    
+    public ChannelAccess setUpChannelAccess( 
+    		byte[] aid,
+            String packageName, 
+            ISmartcardServiceCallback callback) {
+    	ChannelAccess channelAccess = null;
+
+    	// check result of channel access during initialization procedure    
+    	if( mInitialChannelAccess.getAccess() == ChannelAccess.ACCESS.DENIED ){
+    		throw new AccessControlException( ACCESS_CONTROL_ENFORCER + "access denied: " + mInitialChannelAccess.getReason() );
+    	}
+    	// this is the new GP Access Control Enforcer implementation
+    	if( mUseAra || mUseArf ){
+
+    		try {
+    			channelAccess = internal_setUpChannelAccess(aid, packageName, callback);
+    		} catch( Exception e ) {
+    			if( e instanceof MissingResourceException ) {
+    				throw new MissingResourceException( ACCESS_CONTROL_ENFORCER + e.getMessage(), "", "");
+    			} else {
+    				// access is denied for any terminal if exception during accessing ARA has any other reason.  
+					throw new AccessControlException( ACCESS_CONTROL_ENFORCER + "access denied: " + e.getMessage() );
+    			}
+    		}
+    	}
+
+    	if( channelAccess == null || // precautionary check
+			(channelAccess.getApduAccess() != ChannelAccess.ACCESS.ALLOWED &&
+			 channelAccess.isUseApduFilter() == false)) {
+
+    		if( this.mFullAccess == true ){ 
+    			// if full access is set then we reuse the initial channel access, 
+    			// since we got so far it allows everything with a descriptive reason.
+    			channelAccess = mInitialChannelAccess; 
+    		} else {
+    			throw new AccessControlException( ACCESS_CONTROL_ENFORCER + "no APDU access allowed!" );
+    		}
+    	}
+    	
+    	channelAccess.setPackageName(packageName);
+
+        return channelAccess.clone();
+    }
+    
+	private synchronized ChannelAccess internal_setUpChannelAccess(byte[] aid, String packageName,
+			ISmartcardServiceCallback callback) {
+		
+        ChannelAccess channelAccess = new ChannelAccess();
+        if (packageName == null || packageName.isEmpty()) {
+            throw new AccessControlException("package names must be specified");
+        }
+        if (aid == null || aid.length == 0) {
+            throw new AccessControlException("AID must be specified");
+        }
+        if (aid.length < 5 || aid.length > 16) {
+            throw new AccessControlException("AID has an invalid length");
+        }
+
+        try {
+        	// estimate SHA-1 hash value of the device application's certificate.
+	        Certificate[] appCerts = getAPPCerts(packageName);
+	        
+	        // APP certificates must be available => otherwise Exception
+	        if (appCerts == null || appCerts.length == 0) {
+	            throw new AccessControlException("Application certificates are invalid or do not exist.");
+	        }
+
+
+            channelAccess = getAccessRule(aid, appCerts, callback );
+
+        } catch (Throwable exp) {
+
+
+            throw new AccessControlException(exp.getMessage());
+        }
+
+        return channelAccess;
+	}    
+    
+    public ChannelAccess getAccessRule( byte[] aid, Certificate[] appCerts, ISmartcardServiceCallback callback  ) throws AccessControlException, CardException, CertificateEncodingException {
+
+    	ChannelAccess channelAccess = null;
+    	
+    	// if read all is true get rule from cache.
+		if( mRulesRead ){
+			// get rules from internal storage
+        	channelAccess = mAccessRuleCache.findAccessRule( aid, appCerts );
+		}
+    		
+    	// if no rule was found return an empty access rule
+    	// with all access denied.
+    	if( channelAccess == null ){
+    		channelAccess = new ChannelAccess();
+            channelAccess.setAccess(ChannelAccess.ACCESS.DENIED, "no access rule found!" );
+            channelAccess.setApduAccess(ChannelAccess.ACCESS.DENIED);
+    		channelAccess.setNFCEventAccess(ChannelAccess.ACCESS.DENIED);
+    	} 
+    	return channelAccess;
+    }
+	
+	
+    /**
+     * Returns Certificate chain for one package.
+     * 
+     * @param packageName
+     * @return
+     * @throws CertificateException
+     * @throws NoSuchAlgorithmException
+     * @throws AccessControlException
+     * @throws CardException
+     */
+    public Certificate[] getAPPCerts(String packageName)
+             throws CertificateException, NoSuchAlgorithmException, AccessControlException {
+    
+     	if(packageName == null || packageName.length() == 0)
+             throw new AccessControlException("Package Name not defined");
+     	
+     	PackageInfo foundPkgInfo;
+     	
+     	try {
+     		foundPkgInfo = mPackageManager.getPackageInfo(packageName,
+     												PackageManager.GET_SIGNATURES);
+     	} catch (NameNotFoundException ne) {
+     		throw new AccessControlException("Package does not exist");
+     	}
+     	
+         if (foundPkgInfo == null) {
+                 throw new AccessControlException("Package does not exist");
+             }
+     
+         ArrayList<Certificate> appCerts = new ArrayList<Certificate>();
+         
+         for (Signature signature : foundPkgInfo.signatures) {
+         	appCerts.add(decodeCertificate(signature.toByteArray()));
+         }
+         return appCerts.toArray(new Certificate[appCerts.size()]);
+    }
+    
+    public static byte[] getAppCertHash(Certificate appCert) throws CertificateEncodingException
+    {
+        /**
+         * Note: This loop is needed as workaround for a bug in Android 2.3.
+         * After a failed certificate verification in a previous step the
+         * MessageDigest.getInstance("SHA") call will fail with the
+         * AlgorithmNotSupported exception. But a second try will normally
+         * succeed.
+         */
+        MessageDigest md = null;
+        for (int i = 0; i < 10; i++) {
+            try {
+                md = MessageDigest.getInstance("SHA");
+                break;
+            } catch (Exception e) {
+            }
+        }
+        if (md == null) {
+            throw new AccessControlException("Hash can not be computed");
+        }
+        return md.digest(appCert.getEncoded());
+    }
+  
+    public synchronized boolean[] isNFCEventAllowed( 
+    		byte[] aid,
+    		String[] packageNames, 
+    		ISmartcardServiceCallback callback) 
+    				throws CardException
+    {
+    	if( mUseAra || mUseArf ){
+    		return internal_isNFCEventAllowed(aid, packageNames, callback);
+    	} else {
+			// 2012-09-27
+    		// if ARA and ARF is not available and terminal DOES NOT belong to a UICC -> mFullAccess is true
+    		// if ARA and ARF is not available and terminal belongs to a UICC -> mFullAccess is false
+    		boolean[] ret = new boolean[packageNames.length];
+    		for( int i = 0; i < ret.length; i++ ){
+    			ret[i] = this.mFullAccess; 
+    		}
+		    return ret;
+    	}
+    }
+    
+    private synchronized boolean[] internal_isNFCEventAllowed(byte[] aid,
+			   String[] packageNames, 
+			   ISmartcardServiceCallback callback) 
+					   throws CardException
+    {
+	 	// the NFC Event Flags boolean array is created and filled in internal_enableAccessConditions.
+	 	mNfcEventFlags = new boolean[packageNames.length];
+	 	int i=0;
+	 	ChannelAccess channelAccess = null;
+	 	for( String packageName : packageNames ) {
+	 		// estimate SHA-1 hash value of the device application's certificate.
+				Certificate[] appCerts;
+				try {
+					appCerts = getAPPCerts(packageName);
+		
+					// APP certificates must be available => otherwise Exception
+					if (appCerts == null || appCerts.length == 0) {
+						throw new AccessControlException("Application certificates are invalid or do not exist.");
+					}
+
+					channelAccess = getAccessRule(aid, appCerts, callback);
+					mNfcEventFlags[i] = (channelAccess.getNFCEventAccess() == ChannelAccess.ACCESS.ALLOWED);
+
+				} catch (Exception e) {
+					Log.w(SmartcardService._TAG, " Access Rules for NFC: " + e.getLocalizedMessage());
+					mNfcEventFlags[i] = false;
+				}
+				i++;
+	 	}
+		return mNfcEventFlags;
+	}
+    
+    
+    public void dump(PrintWriter writer, String prefix) {
+       writer.println(prefix + SmartcardService._TAG + ":");
+       prefix += "  ";
+
+       writer.println(prefix + "mUseArf: " + mUseArf);
+       writer.println(prefix + "mUseAra: " + mUseAra);
+       writer.println(prefix + "mInitialChannelAccess:");
+       writer.println(prefix + "  " + mInitialChannelAccess.toString());
+       writer.println();
+
+       /* Dump the access rule cache */
+       if(mAccessRuleCache != null) mAccessRuleCache.dump(writer, prefix);
+    }
+
+    private void readSecurityProfile() {
+        if(!Build.IS_DEBUGGABLE) {
+            mUseArf = true;
+            mUseAra = true;
+            mFullAccess = false; // Per default we don't grant full access.
+        } else {
+            String level = SystemProperties.get("service.seek", "useara usearf");
+            level = SystemProperties.get("persist.service.seek", level);
+
+            if(level.contains("usearf")) mUseArf = true; else mUseArf = false;
+            if(level.contains("useara")) mUseAra = true; else mUseAra = false;
+            if(level.contains("fullaccess")) mFullAccess = true; else mFullAccess = false;
+        }
+        Log.i(SmartcardService._TAG, "Allowed ACE mode: ara=" + mUseAra + " arf=" + mUseArf + " fullaccess=" + mFullAccess );
+    }
+    
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/AccessFilterUtil.java b/src/org/simalliance/openmobileapi/service/security/AccessFilterUtil.java
new file mode 100644
index 0000000..e190e8a
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/AccessFilterUtil.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ * 
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security;
+
+import org.simalliance.openmobileapi.service.Util;
+
+
+
+public class AccessFilterUtil {
+
+    public static ApduFilter[] parseAccessConditions(byte[] accessConditions) {
+        if (accessConditions.length == 0) {
+            return new ApduFilter[0];
+        }
+
+        if ((accessConditions.length % 8) != 0) {
+            throw new IllegalArgumentException("Access Conditions must have a length of 8 bytes");
+        }
+
+        int numOfACs = accessConditions.length / 8;
+        ApduFilter[] acs = new ApduFilter[numOfACs];
+        int offset = 0;
+        int length = 8;
+        int index = 0;
+        while ((offset + length) <= accessConditions.length && length != 0) {
+            acs[index] = new ApduFilter(Util.getMid(accessConditions, offset, length));
+            offset += length;
+            index++;
+        }
+        return acs;
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/AccessRuleCache.java b/src/org/simalliance/openmobileapi/service/security/AccessRuleCache.java
new file mode 100644
index 0000000..2a86b66
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/AccessRuleCache.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ * 
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security;
+
+import java.io.PrintWriter;
+import java.security.AccessControlException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.simalliance.openmobileapi.service.SmartcardService;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.AID_REF_DO;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.AR_DO;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Hash_REF_DO;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.REF_DO;
+
+import android.util.Log;
+
+
+public class AccessRuleCache {
+    // Previous "RefreshTag"
+	// 2012-09-25
+	// the refresh tag has to be valid as long as AxxController is valid
+	// a pure static element would cause that rules are not read any longer once the AxxController is recreated.
+    private byte[]  mRefreshTag=null;
+	
+    
+    private Map<REF_DO, ChannelAccess> mRuleCache = new HashMap<REF_DO, ChannelAccess>();
+
+    /**
+     * Clears access rule cache and refresh tag.
+     */
+	public void reset(){
+		mRefreshTag = null;
+    	mRuleCache.clear();
+    }
+
+    /**
+     * Clears access rule cache only.
+     */
+	public void clearCache(){
+    	mRuleCache.clear();
+    }
+	
+	public ChannelAccess put(REF_DO ref_do_key, AR_DO ar_do) {
+
+    	ChannelAccess channelAccess = mapArDo2ChannelAccess( ar_do );
+    	this.mRuleCache.put(ref_do_key, channelAccess);
+    	return channelAccess;
+	}
+	
+    public void putWithMerge( REF_DO ref_do, AR_DO ar_do ) {
+
+    	ChannelAccess channelAccess = mapArDo2ChannelAccess( ar_do );
+    	putWithMerge( ref_do, channelAccess );
+    }
+    
+    public void putWithMerge( REF_DO ref_do, ChannelAccess channelAccess ) {
+
+    	if( mRuleCache.containsKey(ref_do)){
+    		ChannelAccess ca = mRuleCache.get(ref_do);
+    		Log.v(SmartcardService._TAG, "Access Rule with " + ref_do.toString() + " already exists.");
+    		
+    		// if new ac condition is more restrictive then use their settings
+    		
+    		// if new rule says NFC is denied then use it
+    		// if current rule as undefined NFC rule then use setting of new rule.
+    		// current NFC	new NFC		resulting NFC
+    		// UNDEFINED	x			x
+    		// ALLOWED		!DENIED 	ALLOWED
+    		// ALLOWED		DENIED 		DENIED
+    		// DENIED	 	!DENIED 	DENIED
+    		// DENEID		DENIED  	DENIED
+    		if( channelAccess.getNFCEventAccess() == ChannelAccess.ACCESS.DENIED ||
+    				ca.getNFCEventAccess() == ChannelAccess.ACCESS.UNDEFINED ) {
+    			ca.setNFCEventAccess(channelAccess.getNFCEventAccess());
+    		} 
+    		
+    		// if new rule says APUD is denied then use it
+    		// if current rule as undefined APDU rule then use setting of new rule.
+    		// current APDU	new APDU	resulting APDU
+    		// UNDEFINED	x			x
+    		// ALLOWED		!DENIED 	ALLOWED
+    		// ALLOWED		DENIED 		DENIED
+    		// DENIED	 	!DENIED 	DENIED
+    		// DENEID		DENIED  	DENIED
+    		if( channelAccess.getApduAccess() == ChannelAccess.ACCESS.DENIED ||
+    				ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED ) {
+    			ca.setApduAccess(channelAccess.getApduAccess());
+    		} 
+    		
+    		
+    		// put APDU filter together if resulting APDU access is allowed.
+    		if( ca.getApduAccess() == ChannelAccess.ACCESS.ALLOWED ){
+    			if( channelAccess.isUseApduFilter() ){
+    				ca.setUseApduFilter(true);
+    				ApduFilter[] filter = ca.getApduFilter();
+    				ApduFilter[] filter2 = channelAccess.getApduFilter();
+    				if( filter == null || filter.length == 0 ){
+    					ca.setApduFilter(filter2);
+    				} else if( filter2 == null || filter2.length == 0){
+    					ca.setApduFilter(filter);
+    				} else {
+    					ApduFilter[] sum = new ApduFilter[filter.length + filter2.length];
+    					int i = 0;
+    					for( ApduFilter f : filter ){
+    						sum[i++] = f;
+    					}
+    					for( ApduFilter f : filter2 ){
+    						sum[i++] = f;
+    					}
+    					ca.setApduFilter(sum);
+    				}
+    			}
+    		} else {
+    			// if APDU access is not allowed the remove also all apdu filter 
+    			ca.setUseApduFilter(false);
+    			ca.setApduFilter(null);
+    		}
+    		Log.v(SmartcardService._TAG, "Merged Access Rule: " + ca.toString());
+    		return;
+    	}
+    	mRuleCache.put(ref_do, channelAccess);
+    }
+	
+    
+    public ChannelAccess findAccessRule( byte[] aid, Certificate[] appCerts) throws AccessControlException {
+
+    	
+    	
+    	// TODO: check difference between DeviceCertHash and Certificate Chain (EndEntityCertHash, IntermediateCertHash (1..n), RootCertHash)
+    	// The DeviceCertificate is equal to the EndEntityCertificate.
+    	// The android systems seems always to deliver only the EndEntityCertificate, but this seems not to be sure.
+    	// thats why we implement the whole chain.
+    	
+    	AID_REF_DO aid_ref_do = getAidRefDo(aid);    	        
+    	Hash_REF_DO hash_ref_do = null;
+        REF_DO ref_do = null;
+
+    	// Search Rule A ( Certificate(s); AID )
+    	// walk through certificate chain.
+    	for( Certificate appCert : appCerts ){
+    	
+			try {
+				hash_ref_do = new Hash_REF_DO(AccessControlEnforcer.getAppCertHash(appCert));
+		        ref_do = new REF_DO(aid_ref_do, hash_ref_do);
+
+		        if( mRuleCache.containsKey( ref_do ) ){
+		        	return mRuleCache.get( ref_do );
+		        } 
+			} catch (CertificateEncodingException e) {
+				throw new AccessControlException("Problem with Application Certificate.");
+			}
+    	}
+    	// no rule found, 
+    	// now we have to check if the given AID
+    	// is used together with another specific hash value (another device application)
+    	if( searchForRulesWithSpecificAidButOtherHash(aid_ref_do) != null ){
+    		Log.v(SmartcardService._TAG, "Conflict Resolution Case A returning access rule \'NEVER\'.");
+    		ChannelAccess ca = new ChannelAccess();
+    		ca.setApduAccess(ChannelAccess.ACCESS.DENIED);
+    		ca.setAccess(ChannelAccess.ACCESS.DENIED, "AID has a specific access rule with a different hash. (Case A)");
+    		ca.setNFCEventAccess(ChannelAccess.ACCESS.DENIED);
+    		return ca; 
+    	}
+    	
+
+    	// SearchRule B ( <AllDeviceApplications>; AID)
+    	aid_ref_do =  getAidRefDo(aid);    	        
+    	hash_ref_do = new Hash_REF_DO(); // empty hash ref
+        ref_do = new REF_DO(aid_ref_do, hash_ref_do);
+
+        if( mRuleCache.containsKey( ref_do ) ){
+        	return mRuleCache.get( ref_do );
+        }
+    	
+    	// Search Rule C ( Certificate(s); <AllSEApplications> )
+    	aid_ref_do = new AID_REF_DO(AID_REF_DO._TAG);        	
+    	for( Certificate appCert : appCerts ){
+	        try {
+				hash_ref_do = new Hash_REF_DO(AccessControlEnforcer.getAppCertHash(appCert));
+		        ref_do = new REF_DO(aid_ref_do, hash_ref_do);
+
+		        if( mRuleCache.containsKey( ref_do ) ){
+		        	return mRuleCache.get( ref_do );
+		        } 
+	        } catch (CertificateEncodingException e) {
+				throw new AccessControlException("Problem with Application Certificate.");
+			}
+    	}
+
+    	// no rule found, 
+    	// now we have to check if the all AID DO
+    	// is used together with another Hash
+    	if( this.searchForRulesWithAllAidButOtherHash() != null ){
+    		Log.v(SmartcardService._TAG, "Conflict Resolution Case C returning access rule \'NEVER\'.");
+    		ChannelAccess ca = new ChannelAccess();
+    		ca.setApduAccess(ChannelAccess.ACCESS.DENIED);
+    		ca.setAccess(ChannelAccess.ACCESS.DENIED, "An access rule with a different hash and all AIDs was found. (Case C)");
+    		ca.setNFCEventAccess(ChannelAccess.ACCESS.DENIED);
+    		return ca; 
+    	}
+
+    	
+    	// SearchRule D ( <AllDeviceApplications>; <AllSEApplications>)
+    	aid_ref_do =  new AID_REF_DO(AID_REF_DO._TAG); 
+    	hash_ref_do = new Hash_REF_DO();
+        ref_do = new REF_DO(aid_ref_do, hash_ref_do);
+
+        if( mRuleCache.containsKey( ref_do ) ){
+        	return mRuleCache.get( ref_do );
+        }
+        return null;
+    }
+
+    public static AID_REF_DO getAidRefDo( byte[] aid ){
+	    AID_REF_DO aid_ref_do = null;
+	    byte[] defaultAid = new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00 }; // this is the placeholder for the default aid.
+	    
+	    if( aid == null || Arrays.equals( aid, defaultAid )){
+	    	aid_ref_do = new AID_REF_DO(AID_REF_DO._TAG_DEFAULT_APPLICATION);        	
+	    } else {
+	    	aid_ref_do = new AID_REF_DO(AID_REF_DO._TAG, aid);        	
+	    }
+	    
+	    return aid_ref_do;
+    }
+
+    public static REF_DO buildHashMapKey( byte[] aid, byte[] appCertHash ){
+		// Build key
+	    Hash_REF_DO hash_ref_do = new Hash_REF_DO(appCertHash) ;
+	    REF_DO ref_do = new REF_DO(getAidRefDo(aid), hash_ref_do);
+	    
+	    return ref_do;
+    }
+    
+    
+    
+	/*
+     * The GP_SE_AC spec says:
+     * According to the rule conflict resolution process defined in section 3.2.1, if a specific rule exists
+	 * that associates another device application with the SE application identified by AID (e.g. there is
+	 * a rule associating AID with the hash of another device application), then the ARA-M (when
+	 * using GET DATA [Specific]) or the Access Control Enforcer (when using GET DATA [All]) shall
+	 * set the result of SearchRuleFor(DeviceApplicationCertificate, AID) to NEVER (i.e. precedence
+	 * of specific rules over generic rules)
+	 * 
+	 * In own words:
+	 * Search the rules cache for a rule that contains the wanted AID but with another specific Hash value.
+     */
+    private REF_DO searchForRulesWithSpecificAidButOtherHash(AID_REF_DO aid_ref_do) {
+    	
+    	// AID has to be specific
+    	if( aid_ref_do == null ){
+    		return null;
+    	}
+    	// C0 00 is specific -> default AID
+    	// 4F 00 is NOT specific -> all AIDs
+    	if( aid_ref_do.getTag() == AID_REF_DO._TAG && 
+    			(aid_ref_do.getAid() == null || aid_ref_do.getAid().length == 0)){
+    		return null;
+    	}
+    	
+    	Set<REF_DO> keySet = mRuleCache.keySet();
+    	Iterator<REF_DO> iter = keySet.iterator();
+    	while(iter.hasNext()){
+    		REF_DO ref_do = iter.next();
+   			if( aid_ref_do.equals(ref_do.getAidDo())) {
+   				if( ref_do.getHashDo() != null && 
+   						ref_do.getHashDo().getHash() != null &&
+   						ref_do.getHashDo().getHash().length > 0 ){
+   					// this ref_do contains the search AID and a specific hash value
+   					return ref_do;
+   				}
+    		}
+    	}
+    	return null;
+	}
+
+    /*
+     * The GP_SE_AC spec says:
+     * According to the rule conflict resolution process defined in section 3.2.1, if a specific rule exists
+	 * that associates another device application with the SE application identified by AID (e.g. there is
+	 * a rule associating AID with the hash of another device application), then the ARA-M (when
+	 * using GET DATA [Specific]) or the Access Control Enforcer (when using GET DATA [All]) shall
+	 * set the result of SearchRuleFor(DeviceApplicationCertificate, AID) to NEVER (i.e. precedence
+	 * of specific rules over generic rules)
+	 * 
+	 * In own words:
+	 * Search the rules cache for a rule that contains a Hash with an all SE AID (4F 00).
+    */
+    private Object searchForRulesWithAllAidButOtherHash() {
+
+    	AID_REF_DO aid_ref_do = new AID_REF_DO(AID_REF_DO._TAG);
+    	
+    	Set<REF_DO> keySet = mRuleCache.keySet();
+    	Iterator<REF_DO> iter = keySet.iterator();
+    	while(iter.hasNext()){
+    		REF_DO ref_do = iter.next();
+    		if( aid_ref_do.equals(ref_do.getAidDo())){
+    			// aid tlv is equal
+    			if( ref_do.getHashDo() != null && 
+    					(ref_do.getHashDo().getHash() != null && ref_do.getHashDo().getHash().length > 0)) {
+    				// return ref_do if
+    				// a HASH value is available and has a length > 0 (SHA1_LEN)
+    				return ref_do;
+    			}
+    		}
+    	}
+		return null;
+	}
+	
+    public static ChannelAccess mapArDo2ChannelAccess(AR_DO ar_do ){
+    	ChannelAccess channelAccess = new ChannelAccess();
+    	
+    	// check apdu access allowance
+    	if( ar_do.getApduArDo() != null ){
+        	// first if there is a rule for access, reset the general deny flag.
+    		channelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
+			channelAccess.setUseApduFilter(false);
+	    	
+	    	if( ar_do.getApduArDo().isApduAllowed() ){
+	    		// check the apdu filter
+	    		ArrayList<byte[]> apduHeaders = ar_do.getApduArDo().getApduHeaderList();
+	    		ArrayList<byte[]> filterMasks = ar_do.getApduArDo().getFilterMaskList();
+	    		if( apduHeaders != null &&
+	    			filterMasks != null && 
+	    			apduHeaders.size() > 0 &&
+	    			apduHeaders.size() == filterMasks.size()  ){
+	    			
+	    			ApduFilter[] accessConditions = new ApduFilter[apduHeaders.size()];
+	    			for( int i = 0; i < apduHeaders.size(); i++){
+	    				accessConditions[i] = new ApduFilter( apduHeaders.get(i), filterMasks.get(i));
+	    			}
+	    			channelAccess.setUseApduFilter(true);
+	    			channelAccess.setApduFilter(accessConditions);
+	    		} else {
+	    			// general APDU access
+	    			channelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
+	    		}
+	    	} else {
+	    		// apdu access is not allowed at all.
+	    		channelAccess.setApduAccess(ChannelAccess.ACCESS.DENIED);
+	    	}
+    	} else {
+    		channelAccess.setAccess(ChannelAccess.ACCESS.DENIED, "No APDU access rule available.!");
+    	}
+    	
+    	// check for NFC Event allowance
+    	if( ar_do.getNfcArDo() != null ){
+    		channelAccess.setNFCEventAccess(ar_do.getNfcArDo().isNfcAllowed() ? ChannelAccess.ACCESS.ALLOWED : ChannelAccess.ACCESS.DENIED);
+    	} else {
+			// GP says that by default NFC should have the same right as for APDU access
+    		channelAccess.setNFCEventAccess(channelAccess.getApduAccess());
+    	}
+    	return channelAccess;
+    }
+
+	public boolean isRefreshTagEqual(byte[]  refreshTag ) {
+		if( refreshTag == null || mRefreshTag == null )
+			return false;
+		
+        return Arrays.equals(refreshTag,mRefreshTag);
+	}
+
+	public byte[] getRefreshTag() {
+		return mRefreshTag;
+	}
+
+	public void setRefreshTag(byte[] refreshTag) {
+		this.mRefreshTag = refreshTag;
+	}
+	
+	
+    public void dump(PrintWriter writer, String prefix) {
+        writer.println(prefix + SmartcardService._TAG + ":");
+        prefix += "  ";
+
+        /* Dump the refresh tag */
+        writer.print(prefix + "Current refresh tag is: ");
+        if(mRefreshTag == null)  writer.print("<null>");
+        else for(byte oneByte: mRefreshTag) writer.printf("%02X:", oneByte);
+        writer.println();
+
+        /* Dump the rules cache */
+        writer.println(prefix + "rules dump:");
+        prefix += "  ";
+
+        int i = 0;
+        for (Map.Entry<REF_DO, ChannelAccess> entry : mRuleCache.entrySet()) {
+            i++;
+            writer.print(prefix + "rule " + i + ": ");
+            writer.println(entry.getKey().toString());
+
+            writer.print(prefix + "  ->");
+            writer.println(entry.getValue().toString());
+        }
+
+        writer.println();
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/ApduFilter.java b/src/org/simalliance/openmobileapi/service/security/ApduFilter.java
new file mode 100644
index 0000000..d913d43
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/ApduFilter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ * 
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security;
+
+import org.simalliance.openmobileapi.service.Util;
+
+
+public class ApduFilter {
+
+    protected byte[] mApdu;
+
+    protected byte[] mMask;
+
+    public static final int LENGTH = 8;
+
+    protected ApduFilter() {
+    	
+    }
+
+    public ApduFilter(byte[] apdu, byte[] mask) {
+        if (apdu.length != 4) {
+            throw new IllegalArgumentException("apdu length must be 4 bytes");
+        }
+        if (mask.length != 4) {
+            throw new IllegalArgumentException("mask length must be 4 bytes");
+        }
+
+        mApdu = apdu;
+        mMask = mask;
+    }
+    
+    public ApduFilter clone() {
+    	ApduFilter apduFilter = new ApduFilter();
+    	apduFilter.setApdu(mApdu.clone());
+    	apduFilter.setMask(mMask.clone());
+    	
+    	return apduFilter;
+    }
+
+    public ApduFilter(byte[] apduAndMask) {
+        if (apduAndMask.length != 8) {
+            throw new IllegalArgumentException("filter length must be 8 bytes");
+        }
+
+        mApdu = Util.getMid(apduAndMask, 0, 4);
+        mMask = Util.getMid(apduAndMask, 4, 4);
+    }
+
+    public byte[] getApdu() {
+        return mApdu;
+    }
+
+    public void setApdu(byte[] apdu) {
+        if (apdu.length != 4) {
+            throw new IllegalArgumentException("apdu length must be 4 bytes");
+        }
+        mApdu = apdu;
+    }
+
+    public byte[] getMask() {
+        return mMask;
+    }
+
+    public void setMask(byte[] mask) {
+        if (mask.length != 4) {
+            throw new IllegalArgumentException("mask length must be 4 bytes");
+        }
+        mMask = mask;
+    }
+
+    public byte[] toBytes() {
+        return Util.mergeBytes(mApdu, mMask);
+    }
+
+    @Override
+    public String toString() {
+        return "APDU Filter [apdu=" + Util.bytesToString(mApdu) + ", mask="
+                + Util.bytesToString(mMask) + "]";
+    }
+
+    public int getLength() {
+        return 8;
+    }
+
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/ChannelAccess.java b/src/org/simalliance/openmobileapi/service/security/ChannelAccess.java
new file mode 100644
index 0000000..e72ec22
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/ChannelAccess.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security;
+
+
+public class ChannelAccess {
+	
+	public enum ACCESS {
+		ALLOWED, DENIED, UNDEFINED;
+	}
+    
+    protected String CHANNEL_ACCESS_TAG = "ChannelAccess";
+    
+    protected String mPackageName = "";
+
+    protected ACCESS mAccess = ACCESS.UNDEFINED;
+    
+    protected ACCESS mApduAccess = ACCESS.UNDEFINED;
+
+    protected boolean mUseApduFilter = false;
+
+    protected int mCallingPid = 0;
+
+    protected String mReason = "no access by default";
+    
+    protected ACCESS mNFCEventAccess = ACCESS.UNDEFINED;
+
+    protected ApduFilter[] mApduFilter = null;
+
+    public ChannelAccess clone(){
+    	ChannelAccess ca = new ChannelAccess();
+    	ca.setAccess(this.mAccess, this.mReason);
+    	ca.setPackageName( this.mPackageName);
+    	ca.setApduAccess(this.mApduAccess);
+    	ca.setCallingPid(this.mCallingPid);
+    	ca.setNFCEventAccess(this.mNFCEventAccess);
+    	ca.setUseApduFilter(this.mUseApduFilter);
+    	if( this.mApduFilter != null ) {
+    		ApduFilter[] apduFilter = new ApduFilter[this.mApduFilter.length];
+    		int i = 0;
+    		for( ApduFilter filter : mApduFilter ){
+    			apduFilter[i++] = filter.clone(); 
+    		}
+    	   	ca.setApduFilter(apduFilter);
+    	} else {
+    		ca.setApduFilter(null);
+    	}
+    	return ca;
+    }
+    
+    public String getPackageName(){
+    	return mPackageName;
+    }
+    
+    public void setPackageName( String name ){
+    	this.mPackageName = name;
+    }
+    
+    public ACCESS getApduAccess() {
+        return mApduAccess;
+    }
+    
+    public void setApduAccess(ACCESS apduAccess) {
+        this.mApduAccess = apduAccess;
+    }
+
+
+    public ACCESS getAccess() {
+        return mAccess;
+    }
+
+    public void setAccess(ACCESS access, String reason) {
+        this.mAccess = access;
+        this.mReason = reason;
+    }
+
+    public boolean isUseApduFilter() {
+        return mUseApduFilter;
+    }
+
+    public void setUseApduFilter(boolean useApduFilter) {
+        this.mUseApduFilter = useApduFilter;
+    }
+
+    public void setCallingPid(int callingPid) {
+        this.mCallingPid = callingPid;
+    }
+
+    public int getCallingPid() {
+        return mCallingPid;
+    }
+
+    public String getReason() {
+        return mReason;
+    }
+    public ApduFilter[] getApduFilter() {
+        return mApduFilter;
+    }
+
+    public void setApduFilter(ApduFilter[] accessConditions) {
+        mApduFilter = accessConditions;
+    }
+    public ACCESS getNFCEventAccess() {
+        return mNFCEventAccess;
+    }
+
+    public void setNFCEventAccess(ACCESS access) {
+        this.mNFCEventAccess = access;
+    }
+    
+    @Override
+    public String toString(){
+    	StringBuilder sb = new StringBuilder();
+    	sb.append(this.getClass().getName());
+    	sb.append("\n [mPackageName=");
+    	sb.append(mPackageName);
+    	sb.append(", mAccess=");
+    	sb.append(mAccess);
+    	sb.append(", mApduAccess=");
+    	sb.append(mApduAccess);
+    	sb.append(", mUseApduFilter=");
+    	sb.append(mUseApduFilter);
+    	sb.append(", mApduFilter=");
+    	if( mApduFilter != null ){
+	    	for( ApduFilter f : mApduFilter ){
+	    		sb.append(f.toString());
+	    		sb.append(" ");
+	    	}
+    	} else {
+        	sb.append("null");
+    	}
+    	sb.append(", mCallingPid=");
+    	sb.append(mCallingPid);
+    	sb.append(", mReason=");
+    	sb.append(mReason);
+    	sb.append(", mNFCEventAllowed=");
+    	sb.append(mNFCEventAccess);
+    	sb.append("]\n");
+    	
+    	return sb.toString();
+    	
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/CommandApdu.java b/src/org/simalliance/openmobileapi/service/security/CommandApdu.java
new file mode 100644
index 0000000..2a9aecb
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/CommandApdu.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2010 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security;
+
+public class CommandApdu {
+
+    protected int mCla = 0x00;
+
+    protected int mIns = 0x00;
+
+    protected int mP1 = 0x00;
+
+    protected int mP2 = 0x00;
+
+    protected int mLc = 0x00;
+
+    protected byte[] mData = new byte[0];
+
+    protected int mLe = 0x00;
+
+    protected boolean mLeUsed = false;
+
+    public CommandApdu(int cla, int ins, int p1, int p2) {
+        mCla = cla;
+        mIns = ins;
+        mP1 = p1;
+        mP2 = p2;
+    }
+
+    public CommandApdu() {
+
+    }
+
+    public CommandApdu(int cla, int ins, int p1, int p2, byte[] data) {
+        mCla = cla;
+        mIns = ins;
+        mLc = data.length;
+        mP1 = p1;
+        mP2 = p2;
+        mData = data;
+    }
+
+    public CommandApdu(int cla, int ins, int p1, int p2, byte[] data, int le) {
+        mCla = cla;
+        mIns = ins;
+        mLc = data.length;
+        mP1 = p1;
+        mP2 = p2;
+        mData = data;
+        mLe = le;
+        mLeUsed = true;
+    }
+
+    public CommandApdu(int cla, int ins, int p1, int p2, int le) {
+        mCla = cla;
+        mIns = ins;
+        mP1 = p1;
+        mP2 = p2;
+        mLe = le;
+        mLeUsed = true;
+    }
+
+    public void setP1(int p1) {
+        mP1 = p1;
+    }
+
+    public void setP2(int p2) {
+        mP2 = p2;
+    }
+
+    public void setData(byte[] data) {
+        mLc = data.length;
+        mData = data;
+    }
+
+    public void setLe(int le) {
+        mLe = le;
+        mLeUsed = true;
+    }
+
+    public int getP1() {
+        return mP1;
+    }
+
+    public int getP2() {
+        return mP2;
+    }
+
+    public int getLc() {
+        return mLc;
+    }
+
+    public byte[] getData() {
+        return mData;
+    }
+
+    public int getLe() {
+        return mLe;
+    }
+
+    public byte[] toBytes() {
+        int length = 4; // CLA, INS, P1, P2
+        if (mData.length != 0) {
+            length += 1; // LC
+            length += mData.length; // DATA
+        }
+        if (mLeUsed) {
+            length += 1; // LE
+        }
+
+        byte[] apdu = new byte[length];
+
+        int index = 0;
+        apdu[index] = (byte) mCla;
+        index++;
+        apdu[index] = (byte) mIns;
+        index++;
+        apdu[index] = (byte) mP1;
+        index++;
+        apdu[index] = (byte) mP2;
+        index++;
+        if (mData.length != 0) {
+            apdu[index] = (byte) mLc;
+            index++;
+            System.arraycopy(mData, 0, apdu, index, mData.length);
+            index += mData.length;
+        }
+        if (mLeUsed) {
+            apdu[index] += (byte) mLe; // LE
+        }
+
+        return apdu;
+    }
+
+    public static boolean compareHeaders(byte[] header1, byte[] mask, byte[] header2) {
+        if (header1.length < 4 || header2.length < 4) {
+            return false;
+        }
+        byte[] compHeader = new byte[4];
+        compHeader[0] = (byte) (header1[0] & mask[0]);
+        compHeader[1] = (byte) (header1[1] & mask[1]);
+        compHeader[2] = (byte) (header1[2] & mask[2]);
+        compHeader[3] = (byte) (header1[3] & mask[3]);
+
+        if (((byte) compHeader[0] == (byte) header2[0])
+                && ((byte) compHeader[1] == (byte) header2[1])
+                && ((byte) compHeader[2] == (byte) header2[2])
+                && ((byte) compHeader[3] == (byte) header2[3])) {
+            return true;
+        }
+        return false;
+    }
+
+    public CommandApdu clone() {
+        CommandApdu apdu = new CommandApdu();
+        apdu.mCla = mCla;
+        apdu.mIns = mIns;
+        apdu.mP1 = mP1;
+        apdu.mP2 = mP2;
+        apdu.mLc = mLc;
+        apdu.mData = new byte[mData.length];
+        System.arraycopy(mData, 0, apdu.mData, 0, mData.length);
+        apdu.mLe = mLe;
+        apdu.mLeUsed = mLeUsed;
+        return apdu;
+    }
+
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/ResponseApdu.java b/src/org/simalliance/openmobileapi/service/security/ResponseApdu.java
new file mode 100644
index 0000000..0672d4b
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/ResponseApdu.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2010 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security;
+
+import java.security.AccessControlException;
+
+public class ResponseApdu {
+
+    protected int mSw1 = 0x00;
+
+    protected int mSw2 = 0x00;
+
+    protected byte[] mData = new byte[0];
+
+    public ResponseApdu(byte[] respApdu) {
+        if (respApdu.length < 2) {
+            return;
+        }
+        if (respApdu.length > 2) {
+            mData = new byte[respApdu.length - 2];
+            System.arraycopy(respApdu, 0, mData, 0, respApdu.length - 2);
+        }
+        mSw1 = 0x00FF & respApdu[respApdu.length - 2];
+        mSw2 = 0x00FF & respApdu[respApdu.length - 1];
+
+    }
+
+    public int getSW1() {
+        return mSw1;
+    }
+
+    public int getSW2() {
+        return mSw2;
+    }
+
+    public int getSW1SW2() {
+        return (mSw1 << 8) | mSw2;
+    }
+
+    public byte[] getData() {
+        return mData;
+    }
+
+    public void checkLengthAndStatus(int length, int sw1sw2, String message)
+            throws AccessControlException {
+        if (getSW1SW2() != sw1sw2 || mData.length != length) {
+            throw new AccessControlException("ResponseApdu is wrong at " + message);
+        }
+    }
+
+    public void checkLengthAndStatus(int length, int[] sw1sw2List, String message)
+            throws AccessControlException {
+        if (mData.length != length) {
+            throw new AccessControlException("ResponseApdu is wrong at " + message);
+        }
+        for (int sw1sw2 : sw1sw2List) {
+            if (getSW1SW2() == sw1sw2) {
+                return; // sw1sw2 matches => return
+            }
+        }
+        throw new AccessControlException("ResponseApdu is wrong at " + message);
+    }
+
+    public void checkStatus(int[] sw1sw2List, String message) throws AccessControlException {
+        for (int sw1sw2 : sw1sw2List) {
+            if (getSW1SW2() == sw1sw2) {
+                return; // sw1sw2 matches => return
+            }
+        }
+        throw new AccessControlException("ResponseApdu is wrong at " + message);
+    }
+
+    public void checkStatus(int sw1sw2, String message) throws AccessControlException {
+        if (getSW1SW2() != sw1sw2) {
+            throw new AccessControlException("ResponseApdu is wrong at " + message);
+        }
+    }
+
+    public boolean isStatus(int sw1sw2) {
+        if (getSW1SW2() == sw1sw2) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/ara/AccessRuleApplet.java b/src/org/simalliance/openmobileapi/service/security/ara/AccessRuleApplet.java
new file mode 100644
index 0000000..d08bed5
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/ara/AccessRuleApplet.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2010 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+
+
+package org.simalliance.openmobileapi.service.security.ara;
+
+
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.AccessControlException;
+
+import org.simalliance.openmobileapi.service.CardException;
+import org.simalliance.openmobileapi.service.IChannel;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.CommandApdu;
+import org.simalliance.openmobileapi.service.security.ResponseApdu;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.BerTlv;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.ParserException;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Response_DO_Factory;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Response_RefreshTag_DO;
+
+public class AccessRuleApplet {
+    
+    final private static String ACCESS_RULE_APPLET_TAG = "AccessRuleApplet";  
+
+    final private static int _MAX_LEN = 0xF0; // should be adapted by OEM, this is a defensive value since some devices/modems have problems with Le=0x00 or 0xFF.
+
+    final private static CommandApdu mGetAll = new CommandApdu(0x80, 0xCA, 0xFF, 0x40, _MAX_LEN);
+
+    final private static CommandApdu mGetSpecific = new CommandApdu(0x80, 0xCA, 0xFF, 0x50, _MAX_LEN);
+
+    final private static CommandApdu mGetNext = new CommandApdu(0x80, 0xCA, 0xFF, 0x60, _MAX_LEN);
+
+    final private static CommandApdu mGetRefreshTag = new CommandApdu(0x80, 0xCA, 0xDF, 0x20, _MAX_LEN );
+    
+
+    private IChannel mChannel = null;
+
+    public AccessRuleApplet(IChannel channel) {
+        mChannel = channel;
+    }
+
+    public byte[] readSpecificAccessRule( byte[] aid_ref_do ) throws AccessControlException, CardException {
+
+    	if( aid_ref_do == null ){
+			throw new AccessControlException("GET DATA (specific): Reference data object must not be null.");
+    	}
+    	
+    	ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    	int overallLen = 0;
+    	
+    	// send GET DATA (specific)
+    	CommandApdu apdu = (CommandApdu) mGetSpecific.clone();
+        apdu.setData(aid_ref_do);
+        ResponseApdu response = send(apdu);
+        
+        // OK
+        if( response.isStatus( 0x9000 ) ) {
+        	// check if more data has to be fetched
+        	BerTlv tempTlv = null;
+        	try {
+				tempTlv = BerTlv.decode(response.getData(), 0, false);
+			} catch (ParserException e) {
+				throw new AccessControlException("GET DATA (specific) not successfull. Tlv encoding wrong.");
+			}
+			
+			// the first data block contain the length of the TLV + Tag bytes + length bytes.
+			overallLen = tempTlv.getValueLength() + tempTlv.getValueIndex();
+			try {
+				stream.write(response.getData());
+			} catch (IOException e) {
+				throw new AccessControlException("GET DATA (specific) IO problem. " + e.getMessage() );
+			}
+
+			int le;
+			// send subsequent GET DATA (next) commands
+			while( stream.size() < overallLen ){
+				le = overallLen - stream.size();
+				if( le > _MAX_LEN ){
+					le = _MAX_LEN;
+				}
+		    	// send GET DATA (next)
+		    	apdu = (CommandApdu) mGetNext.clone();
+		        apdu.setLe(le);
+		        response = send(apdu);
+				
+		        // OK
+		        if( response.isStatus( 0x9000 ) ){
+					try {
+						stream.write(response.getData());
+					} catch (IOException e) {
+						throw new AccessControlException("GET DATA (next) IO problem. " + e.getMessage() );
+					}
+		        } else {
+		        	throw new AccessControlException( "GET DATA (next) not successfull, . SW1SW2=" + response.getSW1SW2()); 
+		        }
+			}
+
+        	return stream.toByteArray();
+        	// referenced data not found
+        } else if( response.isStatus( 0x6A88 )){
+        	return null;
+        } else {
+        	throw new AccessControlException("GET DATA (specific) not successfull. SW1SW2=" + response.getSW1SW2());
+        }
+    }
+
+    public byte[] readAllAccessRules() throws AccessControlException, CardException {
+    	
+    	ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    	int overallLen = 0;
+    	
+    	// send GET DATA (specific)
+    	CommandApdu apdu = (CommandApdu) mGetAll.clone();
+        ResponseApdu response = send(apdu);
+        
+        // OK
+        if( response.isStatus( 0x9000 ) ){
+        	
+        	// check if more data has to be fetched
+        	BerTlv tempTlv = null;
+        	try {
+				tempTlv = BerTlv.decode(response.getData(), 0, false);
+			} catch (ParserException e) {
+				throw new AccessControlException("GET DATA (all) not successfull. Tlv encoding wrong.");
+			}
+			
+			// the first data block contain the length of the TLV + Tag bytes + length bytes.
+			overallLen = tempTlv.getValueLength() + tempTlv.getValueIndex();
+
+			
+			try {
+				stream.write(response.getData());
+			} catch (IOException e) {
+				throw new AccessControlException("GET DATA (all) IO problem. " + e.getMessage() );
+			}
+
+			int le;
+			// send subsequent GET DATA (next) commands
+			while( stream.size() < overallLen ){
+				le = overallLen - stream.size();
+
+				if( le > _MAX_LEN ){
+					le = _MAX_LEN;
+				}
+		    	// send GET DATA (next)
+		    	apdu = (CommandApdu) mGetNext.clone();
+		        apdu.setLe(le);
+
+		        response = send(apdu);
+		        // OK
+		        if( response.isStatus( 0x9000 ) ) {
+					try {
+						stream.write(response.getData());
+					} catch (IOException e) {
+						throw new AccessControlException("GET DATA (next) IO problem. " + e.getMessage() );
+					}
+		        } else {
+		        	throw new AccessControlException( "GET DATA (next) not successfull, . SW1SW2=" + response.getSW1SW2()); 
+		        }
+			}
+
+        	return stream.toByteArray();
+        	// referenced data not found
+        } else if( response.isStatus( 0x6A88 )){
+        	return null;
+        } else {
+        	throw new AccessControlException("GET DATA (all) not successfull. SW1SW2=" + response.getSW1SW2());
+        }
+    }
+
+    public byte[] readRefreshTag() throws AccessControlException, CardException {
+
+    	// send GET DATA (specific)
+    	CommandApdu apdu = (CommandApdu) mGetRefreshTag.clone();
+        ResponseApdu response = send(apdu);
+        
+        // OK
+        if( response.isStatus( 0x9000 ) ){
+        	
+        	// check if more data has to be fetched
+        	BerTlv tempTlv = null;
+        	Response_RefreshTag_DO refreshDo;
+        	try {
+				tempTlv = Response_DO_Factory.createDO(response.getData());
+				if( tempTlv instanceof Response_RefreshTag_DO ) {
+					refreshDo = (Response_RefreshTag_DO)tempTlv;
+					return refreshDo.getRefreshTagArray();
+				} else {
+					throw new AccessControlException("GET REFRESH TAG returned invalid Tlv.");
+				}
+			} catch (ParserException e) {
+				throw new AccessControlException("GET REFRESH TAG not successfull. Tlv encoding wrong.");
+			}
+        } 
+        throw new AccessControlException("GET REFRESH TAG not successfull.");			
+    }
+
+    private ResponseApdu send(CommandApdu cmdApdu) throws CardException {
+
+        byte[] response = mChannel.transmit(cmdApdu.toBytes());
+
+        ResponseApdu resApdu = new ResponseApdu(response);
+        return resApdu;
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/ara/AraController.java b/src/org/simalliance/openmobileapi/service/security/ara/AraController.java
new file mode 100644
index 0000000..3b205e4
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/ara/AraController.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ * 
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security.ara;
+
+import android.util.Log;
+
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.MissingResourceException;
+
+import org.simalliance.openmobileapi.service.CardException;
+import org.simalliance.openmobileapi.service.IChannel;
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.ITerminal;
+import org.simalliance.openmobileapi.service.SmartcardService;
+import org.simalliance.openmobileapi.service.security.AccessControlEnforcer;
+import org.simalliance.openmobileapi.service.security.AccessRuleCache;
+import org.simalliance.openmobileapi.service.security.ChannelAccess;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.BerTlv;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.ParserException;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.REF_AR_DO;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Response_ALL_AR_DO;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Response_DO_Factory;
+
+public class AraController {
+
+	private AccessControlEnforcer mMaster = null;
+    private AccessRuleCache mAccessRuleCache = null;
+
+    private ITerminal mTerminal = null;
+    private AccessRuleApplet mApplet = null;
+    
+    
+    private boolean mNoSuchElement = false;
+
+    private String ACCESS_CONTROL_ENFORCER_TAG = "ACE ARA";
+
+    public static final byte[] ARA_M_AID = new byte[] {
+            (byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x51, (byte)0x41, (byte)0x43, (byte)0x4C,
+            (byte)0x00
+    };
+
+    public AraController(AccessControlEnforcer master ) {
+    	mMaster = master;
+    	mAccessRuleCache = mMaster.getAccessRuleCache();
+    	mTerminal = mMaster.getTerminal();
+
+    }
+
+    public boolean isNoSuchElement(){
+    	return mNoSuchElement;
+    }
+    
+    public static byte[] getAraMAid() {
+        return ARA_M_AID;
+    }
+    
+	public synchronized boolean initialize(
+			boolean loadAtStartup,
+			ISmartcardServiceCallback callback) 
+	{
+
+		IChannel channel = null;
+		try {
+			 channel = this.handleOpenChannel(callback);
+		} catch( MissingResourceException e ){
+			channel = null;
+		}
+		
+        if( channel == null ){
+        	throw new AccessControlException("could not open channel");
+        }
+
+        try {
+            // set new applet handler since a new channel is used.
+        	mApplet = new AccessRuleApplet(channel);
+        	byte[] tag = mApplet.readRefreshTag();
+        	// if refresh tag is equal to the previous one it is not
+        	// neccessary to read all rules again.
+        	if( mAccessRuleCache.isRefreshTagEqual(tag)) {
+                Log.d(ACCESS_CONTROL_ENFORCER_TAG, "Refresh tag has not changed. Using access rules from cache.");
+        		return false;
+        	}
+        	Log.d(ACCESS_CONTROL_ENFORCER_TAG, "Refresh tag has changed.");
+        	// set new refresh tag and empty cache.
+        	mAccessRuleCache.setRefreshTag(tag);
+        	mAccessRuleCache.clearCache();
+        	
+        	if( loadAtStartup ) {
+	            // Read content from ARA 
+	            Log.d(ACCESS_CONTROL_ENFORCER_TAG, "Read ARs from ARA");
+	        	this.readAllAccessRules();
+        	}
+        } catch (Exception e) {
+            Log.d(ACCESS_CONTROL_ENFORCER_TAG, "ARA error: " + e.getLocalizedMessage());
+            throw new AccessControlException(e.getLocalizedMessage()); // Throw Exception
+        } finally { 
+        	if( channel != null )
+        		closeChannel(channel);
+        }
+        return true;
+	}
+	
+	private IChannel handleOpenChannel( ISmartcardServiceCallback callback ){
+        IChannel channel = null;
+    	String reason = "";
+		
+        try {
+            channel = openChannel(mTerminal, getAraMAid(), callback);
+        } catch (Exception e) {
+            String msg = e.toString();
+            msg = " ARA-M couldn't be selected: " + msg;
+            Log.d(ACCESS_CONTROL_ENFORCER_TAG, msg);
+            if (e instanceof NoSuchElementException) { 
+            	mNoSuchElement = true;
+                // SELECT failed
+                // Access Rule Applet is not available => deny any access
+            	reason = " No Access because ARA-M is not available";
+                Log.d(ACCESS_CONTROL_ENFORCER_TAG, msg );
+                throw new AccessControlException(reason);
+            } else if( e instanceof MissingResourceException ){ 
+            	// re-throw exception
+            	// fixes issue 23
+            	// this indicates that no channel is left for accessing the SE element
+                Log.d(ACCESS_CONTROL_ENFORCER_TAG, "no channels left to access ARA-M: " + e.getMessage() );
+            	throw (MissingResourceException)e;
+        	} else { 
+                // MANAGE CHANNEL failed or general error
+        		// In order to be compliant with any UICC/SIM card on the market
+        		// we are going to ignore the error and says that the ARA-M is not available.
+        		// This not fully compliant with GP spec by required for mass compatibility.        		
+            	mNoSuchElement = true; 
+            	
+            	reason = msg;
+                Log.d(ACCESS_CONTROL_ENFORCER_TAG," ARA-M can not be accessed: " + msg);
+                throw new AccessControlException(reason);
+            }
+        }   // End of Exception handling
+        return channel;
+	}
+
+
+    /**
+     * 
+     * @return true if rules are read, false if not necessary or not available, but no error
+     * @throws AccessControlException
+     * @throws CardException
+     */
+    private boolean readAllAccessRules() throws AccessControlException, CardException {
+    	
+    	try {
+			byte[] data = mApplet.readAllAccessRules();
+			// no data returned, but no exception
+			// -> no rule.
+			if( data == null ) {
+				return false;
+			}
+			
+			BerTlv tlv = Response_DO_Factory.createDO( data );
+			if( tlv == null ) {
+				throw new AccessControlException("No valid data object found" );
+			} if( tlv instanceof Response_ALL_AR_DO ){
+				
+				ArrayList<REF_AR_DO> array = ((Response_ALL_AR_DO)tlv).getRefArDos();
+				if( array == null || array.size() == 0 ){
+					return false; // no rules
+				} else {
+					Iterator<REF_AR_DO> iter = array.iterator();
+					while( iter.hasNext() ){
+						REF_AR_DO ref_ar_do = iter.next();
+						this.mAccessRuleCache.putWithMerge(ref_ar_do.getRefDo(), ref_ar_do.getArDo());
+					}
+				}
+			} else {
+				throw new AccessControlException( "Applet returned invalid or wrong data object!");
+			}
+		} catch (ParserException e) {
+			throw new AccessControlException("Parsing Data Object Exception: " + e.getMessage());
+		}
+    	return true;
+    }
+    
+    private IChannel openChannel(ITerminal terminal, byte[] aid, ISmartcardServiceCallback callback) throws Exception
+    {
+
+
+        IChannel channel = terminal.openLogicalChannel(null, aid, callback);
+
+        // set access conditions to access ARA-M.
+        ChannelAccess araChannelAccess = new ChannelAccess();
+        araChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, ACCESS_CONTROL_ENFORCER_TAG);
+        araChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
+        channel.setChannelAccess(araChannelAccess);
+
+        return channel;
+}
+
+    private void closeChannel(IChannel channel) {
+        try {
+            if (channel != null && channel.getChannelNumber() != 0) {
+
+                channel.close();
+
+            }
+        } catch (CardException e) {
+        }
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/ASN1.java b/src/org/simalliance/openmobileapi/service/security/arf/ASN1.java
new file mode 100644
index 0000000..b3b37c7
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/ASN1.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf;
+
+/**
+ * Defines all tags for parsing PKCS#15 files
+ ***************************************************/
+public abstract class ASN1 {
+    
+    // ASN.1 tags
+    public static final byte TAG_Sequence   = 0x30;
+    public static final byte TAG_OctetString = 0x04;
+    public static final byte TAG_OID            = 0x06;
+
+    // EF_DIR tags
+    public static final byte TAG_ApplTemplate = 0x61;
+    public static final byte TAG_ApplIdentifier  = 0x4F;
+    public static final byte TAG_ApplLabel       = 0x50;
+    public static final byte TAG_ApplPath        = 0x51;
+    public static final byte TAG_FCP               = 0x62;
+
+    // Others tags
+    public static final byte TAG_Padding         = (byte)0xFF;
+}
\ No newline at end of file
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/ArfController.java b/src/org/simalliance/openmobileapi/service/security/arf/ArfController.java
new file mode 100644
index 0000000..bae18ae
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/ArfController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ * 
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.arf;
+
+import android.util.Log;
+
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.ITerminal;
+import org.simalliance.openmobileapi.service.SmartcardService;
+import org.simalliance.openmobileapi.service.security.AccessControlEnforcer;
+import org.simalliance.openmobileapi.service.security.AccessRuleCache;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Handler;
+
+public class ArfController {
+
+	private PKCS15Handler mPkcs15Handler = null;
+	private SecureElement mSecureElement = null;
+
+	private AccessControlEnforcer mMaster = null;
+    private AccessRuleCache mAccessRuleCache = null;
+    private ITerminal mTerminal = null;
+    
+    public ArfController(AccessControlEnforcer master) {
+    	mMaster = master;
+    	mAccessRuleCache = mMaster.getAccessRuleCache();
+    	mTerminal = mMaster.getTerminal();
+
+    }
+    
+	public synchronized boolean initialize(ISmartcardServiceCallback callback) {
+
+		
+    	if( mSecureElement == null ){
+    		mSecureElement = new SecureElement(this, mTerminal);
+    	}
+     	if( mPkcs15Handler == null ) {
+    		mPkcs15Handler = new PKCS15Handler(mSecureElement);
+    	}
+    	return mPkcs15Handler.loadAccessControlRules(mTerminal.getName());
+	}
+    
+    
+
+	public AccessRuleCache getAccessRuleCache(){
+		return mAccessRuleCache;
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/DERParser.java b/src/org/simalliance/openmobileapi/service/security/arf/DERParser.java
new file mode 100644
index 0000000..fb71932
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/DERParser.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf;
+
+import android.util.Log;
+import java.util.Arrays;
+
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception;
+
+/**
+ * Base class for parsing PKCS#15 files
+ ***************************************************/
+public class DERParser {
+
+    public static final String TAG = "AccessControl";
+    // DER parameters
+    private byte[] mDERBuffer;
+    private short mDERSize,mDERIndex,mTLVDataSize=0;
+
+    /**
+     * Returns "Base 128" encoded integer
+     * @return Converted integer 
+     */
+    private int readIntBase128() {
+        int value=0;
+        // If the MSb is set to 0, it is the last byte
+        do {
+            value=(value<<7) + (mDERBuffer[mDERIndex] & 0x7F);
+        } while ((mDERBuffer[mDERIndex++]&0x80) != 0);
+        return value;
+    }
+
+    /**
+     * Returns size of the TLV encoded value
+     * @return Size of the TLV
+     */
+    private short getTLVSize()
+    throws PKCS15Exception {
+        int size,TLVSize=0;
+
+        if (isEndofBuffer()) 
+            throw new PKCS15Exception("[Parser] Cannot retreive size");
+        // Determine data size
+        if ((TLVSize=(mDERBuffer[mDERIndex++] & 0xff))>=128) {
+            size=TLVSize-128;
+            for(TLVSize=0;size>0;size--) {
+                if (!isEndofBuffer()) 
+                    TLVSize=(TLVSize<<8)+(mDERBuffer[mDERIndex++] & 0xff);
+                else throw new PKCS15Exception("[Parser] Cannot retreive size");
+        }} 
+
+        // Check if the buffer contains enough data
+        if ((mDERIndex+TLVSize)>mDERSize)
+            throw new PKCS15Exception("[Parser] Not enough data");
+        return (short)TLVSize;
+    }
+
+    /**
+     * Returns type of the TLV encoded value
+     * @return Type of the TLV
+     */
+    private byte getTLVType()
+    throws PKCS15Exception {
+        if (isEndofBuffer()) 
+            throw new PKCS15Exception("[Parser] Cannot retreive type");
+        return mDERBuffer[mDERIndex++];
+    }
+
+
+    /**
+     * Constructor
+     * @param buffer file data
+     */
+    public DERParser(byte[] buffer)
+    throws PKCS15Exception {
+        mDERBuffer=buffer; 
+        mDERIndex=0; mDERSize=0;
+        if (mDERBuffer==null) return;
+        mDERSize=(short)mDERBuffer.length; 
+        mTLVDataSize=mDERSize;
+
+        // Remove padding
+        if (mDERSize==0) return;
+        if (mDERBuffer[mDERIndex]==ASN1.TAG_Padding) {
+            mTLVDataSize=0; 
+            while(++mDERIndex<mDERSize) {
+                if (mDERBuffer[mDERIndex]!=ASN1.TAG_Padding)
+                    throw new PKCS15Exception("[Parser] Incorrect file format");
+    }}}
+
+    /**
+     * Determines if we reached the end of the buffer
+     * @return True if end of buffer is reached; False otherwise
+     */
+    public boolean isEndofBuffer()
+    throws PKCS15Exception {
+        if (mDERIndex==mDERSize) return true;
+        if (mDERBuffer[mDERIndex]==ASN1.TAG_Padding) {
+            // Remove padding
+            while(++mDERIndex<mDERSize) {
+            if (mDERBuffer[mDERIndex]!=ASN1.TAG_Padding)
+                throw new PKCS15Exception("[Parser] Incorrect file format");
+            } return true;
+        } return false;
+    }
+
+    /**
+     * Parses TLV from current index
+     * @return Type of TLV structure
+     */
+    public byte parseTLV()
+    throws PKCS15Exception {
+        byte type=getTLVType();
+        mTLVDataSize=getTLVSize();
+        return type;
+    }
+
+    /**
+     * Parses TLV from current index and check if type is correct
+     * @param type Type required
+     * @return Length of TLV data structure
+     */
+    public short parseTLV(byte type)
+    throws PKCS15Exception {
+        if (getTLVType()==type) {
+            mTLVDataSize=getTLVSize();
+        } else throw new PKCS15Exception("[Parser] Unexpected type");
+        return mTLVDataSize;
+    }
+
+    /**
+     * Skips data of the current TLV structure
+     */
+    public void skipTLVData() {
+        mDERIndex+=mTLVDataSize;
+    }
+
+    /**
+     * Returns data of the current TLV structure
+     * @return Data of current TLV structure
+     */   
+    public byte[] getTLVData() {
+        byte[] data=Arrays.copyOfRange(mDERBuffer,mDERIndex,
+                                                        mDERIndex+mTLVDataSize);
+        mDERIndex+=mTLVDataSize;
+        return data;
+    }
+
+    /**
+     * Takes snaptshot of the current context
+     * @return Saved context
+     */
+    public short[] saveContext() {
+        short[] context=new short[2];
+        context[0]=mDERIndex; context[1]=mTLVDataSize;
+        return context;
+    }
+
+    /**
+     * Restores a context from a snapshot previously saved
+     * @param context Context snapshot
+     */
+    public void restoreContext(short[] context)
+    throws PKCS15Exception {
+        if ((context==null)||(context.length!=2))
+            throw new PKCS15Exception("[Parser] Invalid context");
+        if ((context[0]<0)||(context[0]>mDERSize))
+            throw new PKCS15Exception("[Parser] Index out of bound");
+        mDERIndex=context[0]; mTLVDataSize=context[1];
+    }
+
+    /**
+     * Parses standardized OID
+     * @return String containing OID
+     */
+    public String parseOID()
+    throws PKCS15Exception {
+        if (parseTLV(ASN1.TAG_OID)==0)
+            throw new PKCS15Exception("[Parser] OID Length is null");
+
+        int end=mDERIndex+mTLVDataSize;
+        StringBuffer oid=new StringBuffer();
+
+        // First subidentifier
+        int subid=readIntBase128();
+        // The first subidentifier contains the first two OID components
+        // X.Y is encoded as (X*40)+Y (0<=X<=2 and 0<=Y<=39 for X=0 or X=1)
+        if (subid<=79)
+            oid.append(subid/40).append('.').append(subid%40);
+        else oid.append("2.").append(subid-80);
+
+        while (mDERIndex< end)
+            oid.append('.').append(readIntBase128());
+        Log.d(TAG,"Found OID: "+oid.toString());
+        return oid.toString();
+    }
+
+    /**
+     * Parses PKCS#15 path attribute
+     * @return Path retreived from the attribute
+     */
+    public byte[] parsePathAttributes()
+    throws PKCS15Exception {
+        parseTLV(ASN1.TAG_Sequence);
+        parseTLV(ASN1.TAG_OctetString);
+        return getTLVData();
+    }
+}
\ No newline at end of file
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EF.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EF.java
new file mode 100644
index 0000000..90cd48a
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EF.java
@@ -0,0 +1,285 @@
+
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+import java.util.Arrays;
+
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.arf.ASN1;
+import org.simalliance.openmobileapi.service.security.arf.DERParser;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+
+import android.util.Log;
+
+public class EF {
+
+    public static final String TAG = "SmartcardService ACE ARF";
+
+    public static final int APDU_SUCCESS = 0x9000;
+    private static final int BUFFER_LEN = 253;
+    
+    // 2012-04-13
+    private static final short EF = 0x04;
+    private static final short TRANSPARENT = 0x00;
+    private static final short LINEAR_FIXED = 0x01;
+    private static final short UNKNOWN = 0xFF;
+
+    // Selected file parameters
+    private short mFileType = UNKNOWN,mFileStructure = UNKNOWN, mFileNbRecords;
+    private int mFileID,mFileSize,mFileRecordSize;
+    private String mFilePath;
+
+    
+	// Handle to "Secure Element" object
+    protected SecureElement mSEHandle=null;
+
+    public EF( SecureElement handle ){
+    	mSEHandle = handle;
+    }
+    
+    public int getFileId(){
+    	return mFileID;
+    }
+    public String getFilePath(){
+    	return mFilePath;
+    }
+    
+    private void decodeFileProperties(byte[] data ) 
+		throws SecureElementException
+	{
+		if( data != null ){
+			// check if first byte is the FCP tag
+			// then do USIM decoding
+			if( data[0] == 0x62 ){
+				decodeUSIMFileProps(data);
+			} else {
+				// otherwise sim decoding
+				decodeSIMFileProps(data);
+			}
+		}
+    }
+	
+	/**
+	 * Decodes file properties (SIM cards)
+	 * @param data TS 51.011 encoded characteristics
+	 */
+	private void decodeSIMFileProps(byte[] data)
+		throws SecureElementException 
+	{
+	    if ((data==null)||(data.length<15))
+	        throw new SecureElementException("Invalid Response data");
+	    
+	    // 2012-04-13
+	    // check type of file
+	    if( (short)(data[6] & 0xFF) == (short)0x04  ){
+	    	mFileType = EF;
+	    } else {
+	    	mFileType = UNKNOWN; // may also be DF or MF, but we are not interested in them.
+	    }
+	    if( (short)(data[13]&0xFF) == (short)0x00 ) {
+	    	mFileStructure = TRANSPARENT;
+	    } else  if( (short)(data[13]&0xFF) == (short)0x01 ) {
+	    	mFileStructure = LINEAR_FIXED;
+	    } else {
+	    	mFileStructure = UNKNOWN; // may also be cyclic
+	    }
+	    mFileSize=((data[2] & 0xFF)<<8)|(data[3] & 0xFF);
+	    
+	    // check if file is cyclic or linear fixed
+	    if (mFileType == EF && // is EF ?
+	    		mFileStructure != TRANSPARENT ) {
+	        mFileRecordSize=data[14] & 0xFF;
+	        mFileNbRecords=(short)(mFileSize/mFileRecordSize);
+	    }
+	}
+	
+	/**
+	 * Decodes file properties (USIM cards)
+	 * @param data TLV encoded characteristics 
+	 */
+	private void decodeUSIMFileProps(byte[] data)
+		throws SecureElementException 
+	{
+	    try {
+	        byte[] buffer=null;
+	        DERParser DER=new DERParser(data);
+	
+	        DER.parseTLV(ASN1.TAG_FCP);
+	        while(!DER.isEndofBuffer()) {
+	            switch(DER.parseTLV()) {
+	            case (byte)0x80: // File size
+	                buffer=DER.getTLVData();
+	                if ((buffer!=null)&&(buffer.length>=2))
+	                    mFileSize=((buffer[0] & 0xFF)<<8)|(buffer[1] & 0xFF);
+	                break;
+	            case (byte)0x82: // File descriptor
+	                buffer=DER.getTLVData();
+	                if ((buffer!=null)&&(buffer.length>=2)) {
+	                	if( (short)(buffer[0] & 0x07) == (short)0x01 ) {
+	                		mFileStructure = TRANSPARENT;
+	                	} else if( (short)(buffer[0] & 0x07) == (short)0x02 ) {
+	                		mFileStructure = LINEAR_FIXED;
+	                	} else {
+	                		mFileStructure = UNKNOWN; // may also be cyclic
+	                	}
+	                	
+	                	// check if bit 4,5,6 are set
+	                	// then this is a DF or ADF, but we mark it with UNKNOWN, 
+	                	// since we are only interested in EFs.
+	                	if( (short)(buffer[0] & 0x38) == (short)0x38 ) {
+	                		mFileType=UNKNOWN;
+	                	} else {
+	                		mFileType=EF;
+	                	}
+	                    if (buffer.length==5) {
+	                        mFileRecordSize=buffer[3] & 0xFF;
+	                        mFileNbRecords=(short)(buffer[4] & 0xFF);
+	                    }
+	                } break;
+	            default:
+	                DER.skipTLVData(); break;
+	            }
+	        }
+	    } catch(Exception e) {
+	        throw new SecureElementException("Invalid GetResponse");    
+	    }
+    }
+	
+	
+	/**
+	 * Selects a file (INS 0xA4)
+	 * @param path Path of the file
+	 * @return Command status code [sw1 sw2]
+	 */
+	public int selectFile(byte[] path)
+		throws SecureElementException 
+	{
+	    if ((path==null) || (path.length==0) || ((path.length%2)!=0))
+	        throw new SecureElementException("Incorrect path");
+	
+        int index;
+        int length=path.length;
+        if ((mSEHandle.getSeInterface() == SecureElement.SIM_IO)&&(length>2)) {
+            index=length-2; // Only FileID is usefull 
+            mFilePath=Util.bytesToString(path,0,index,"");
+    	} else { 
+    		index=0; 
+    		mFilePath=""; 
+    	}
+	    
+	    
+	    byte[] data=null;
+	    byte[] cmd= new byte[]{ 0x00,(byte)0xA4,0x00,0x04,0x02,0x00,0x00 };
+	
+	    mFileType=UNKNOWN;
+	    mFileStructure = UNKNOWN;
+	    mFileSize=0;
+	    mFileRecordSize=0;
+	    mFileNbRecords=0;
+	    
+	    // iterate through path
+	    for(int sw1;index<length;index+=2) {
+	        mFileID=((path[index]&0xFF)<<8) | (path[index+1] & 0xFF);
+	        cmd[5]=(byte)(mFileID>>8); 
+	        cmd[6]=(byte)mFileID;
+
+	        data=mSEHandle.exchangeAPDU( this, cmd);
+	
+	        // Check ADPU status
+	        sw1=data[data.length-2] & 0xFF;
+	        if ( (sw1!=0x62) &&
+	        	 (sw1!=0x63) &&
+	        	 (sw1!=0x90) &&
+	        	 (sw1!=0x91) ) 
+	        {
+	            return (sw1<<8) | (data[data.length-1] & 0xFF);
+	        }
+	    } 
+
+	    // Analyse file properties
+	    decodeFileProperties(data);
+	    
+	    if (mFileNbRecords==0) 
+	    	Log.d(TAG,"SelectFile ["+mFileSize+"b]");
+	    else
+	    	Log.d(TAG,"SelectFile ["+mFileNbRecords+"*"+mFileRecordSize+"b]");
+	    return APDU_SUCCESS;
+	}
+	
+	/**
+	 * Reads data from the current selected file (INS 0xB0)
+	 * @param offset Offset at which to start reading
+	 * @param nbBytes Number of bytes to read
+	 * @return Data retreived from the file
+	 */
+	public byte[] readBinary(int offset,int nbBytes)
+	throws SecureElementException {
+	    if (mFileSize==0) return null;
+	    if (nbBytes==-1) nbBytes=mFileSize;
+	    if (mFileType != EF) 
+	        throw new SecureElementException("Incorrect file type");
+	    if (mFileStructure != TRANSPARENT ) 
+	        throw new SecureElementException("Incorrect file structure");
+	
+	    int length,pos=0;
+	    byte[] result=new byte[nbBytes];
+	    byte[] cmd={ 0x00,(byte)0xB0,0x00,0x00,0x00 };
+	    
+	    while (nbBytes!=0) {
+	        if (nbBytes<BUFFER_LEN) 
+	        	length=nbBytes;
+	        else 
+	        	length=BUFFER_LEN; // Set to max buffer size
+	
+	        Log.d(TAG,"ReadBinary ["+offset+".."+length+"b]");
+	        
+	        cmd[2]=(byte)(offset>>8); 
+	        cmd[3]=(byte)offset; 
+	        cmd[4]=(byte)length;
+	        System.arraycopy(mSEHandle.exchangeAPDU(this, cmd),0,result,pos,length);
+	        nbBytes-=length; offset+=length; pos+=length;
+	    }  
+	    return result;
+	}
+	
+	/**
+	 * Reads a record from the current selected file (INS 0xB2)
+	 * @param record Record ID [0..n]
+	 * @return Data from requested record
+	 */
+	public byte[] readRecord(short record)
+	throws SecureElementException {
+	    // Check the type of current selected file
+	    if (mFileType != EF)
+	        throw new SecureElementException("Incorrect file type");
+	    if (mFileStructure != LINEAR_FIXED)
+	        throw new SecureElementException("Incorrect file structure");
+	
+	    // Check if requested record is valid
+	    if ((record<0) || (record>mFileNbRecords))
+	        throw new SecureElementException("Incorrect record number");
+	
+	    Log.d(TAG,"ReadRecord ["+record+"/"+mFileRecordSize+"b]");
+	    byte[] cmd= { 0x00,(byte)0xB2,(byte)record,0x04,(byte)mFileRecordSize };
+	    
+	    return Arrays.copyOf(mSEHandle.exchangeAPDU(this, cmd),mFileRecordSize);
+	}
+
+    /**
+     * Returns the number of records in the current selected file
+     * @return Number of records [0..n]
+     */
+    public short getFileNbRecords()
+    throws SecureElementException {
+        // Check the type of current selected file
+        if (mFileNbRecords<0)
+            throw new SecureElementException("Incorrect file type");
+        return mFileNbRecords;
+    }
+
+    
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACConditions.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACConditions.java
new file mode 100644
index 0000000..deba2fd
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACConditions.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+import android.util.Log;
+
+import java.util.Vector;
+
+import org.simalliance.openmobileapi.service.security.ApduFilter;
+import org.simalliance.openmobileapi.service.security.ChannelAccess;
+import org.simalliance.openmobileapi.service.security.arf.ASN1;
+import org.simalliance.openmobileapi.service.security.arf.DERParser;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.AID_REF_DO;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Hash_REF_DO;
+
+/**
+ * EF_ACConditions related features
+ ***************************************************/
+public class EFACConditions extends EF {
+
+    public static final String TAG = "ACE ARF EF_ACConditions";
+
+    // Identification of the cardlet
+    private AID_REF_DO mAid_Ref_Do=null;
+    
+    private byte[] mData = null;
+    
+    
+    /**
+     * Constructor
+     * @param secureElement SE on which ISO7816 commands are applied
+     * @param AID Identification of the applet
+     */
+    public EFACConditions(SecureElement handle,AID_REF_DO Aid_Ref_Do) {
+    	 super( handle );
+    	 
+    	 mAid_Ref_Do=Aid_Ref_Do;
+    }
+    
+    /**
+     * Decodes EF_ACConditions file
+     * @param buffer ASN.1 data
+     */
+    private void decodeDER(byte[] buffer)
+    	throws PKCS15Exception 
+	{
+        byte[] certificateHash=null;
+        DERParser DER=new DERParser(buffer);
+        
+        // the default channelAccess will deny every access.
+        ChannelAccess channelAccess = new ChannelAccess();
+        Hash_REF_DO hash_ref_do = new Hash_REF_DO();
+
+        // empty condition file
+        if (DER.isEndofBuffer()) { 
+        	mSEHandle.putAccessRule(mAid_Ref_Do, hash_ref_do, channelAccess);
+        	return;
+        }
+        
+        //----
+        // 2012-04-16
+        /*
+           Condition ::= SEQUENCE {
+  				cert   CertHash OPTIONAL,  
+  				accessRules	[0]AccessRules OPTIONAL	
+			}
+
+			AccessRules ::= SEQUENCE OF AccessRule
+
+			AccessRule ::=CHOICE {
+				apduAccessRule	[0]APDUAccessRule,
+				nfcAccessRule	[1]NFCAccessRule
+			}
+			
+			APDUAccessRule ::= CHOICE {
+			   	apduPermission [0] APDUPermission,
+			   	apduFilter [1] APDUFilter
+			}
+			 
+			APDUFilters ::= SEQUENCE OF APDUFilter
+			 
+			NFCAccessRule ::= CHOICE {
+			   	nfcPermission [0] NFCPermission
+			}
+         */
+    	while(!DER.isEndofBuffer()) {
+        	
+        	// if a hash value was found then access is allowed 
+        	// even if NO more access rule is given.
+        	// missing APDU Permission will always allow APDU access
+        	// missing NFC Permission will always allow NFC event.
+        	// See GPAC Chapter 7.1.7
+        	// See Examples in Annex C of GPAC
+            channelAccess = new ChannelAccess();
+        	channelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
+        	channelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
+        	channelAccess.setNFCEventAccess(ChannelAccess.ACCESS.ALLOWED);
+        	channelAccess.setUseApduFilter(false);
+
+        	if ( DER.parseTLV(ASN1.TAG_Sequence) > 0 ) {
+        		DERParser derRule = new DERParser( DER.getTLVData());
+        		derRule.parseTLV(ASN1.TAG_OctetString);
+                certificateHash=derRule.getTLVData();
+                
+                if (certificateHash.length!=Hash_REF_DO._SHA1_LEN &&
+                		certificateHash.length!=0) {
+                	// other hash than SHA-1 hash values are not supported.
+                	throw new PKCS15Exception("Invalid hash found!");
+                } else {
+                	hash_ref_do =new Hash_REF_DO(certificateHash);
+                } 
+                
+                // 2012-04-16
+                // parse optional Access Rule.
+                if( !derRule.isEndofBuffer() ) {
+                	
+	            	if( derRule.parseTLV() == (byte)0xA0 ) {
+
+	            		DERParser derAccessRules = new DERParser(derRule.getTLVData());
+	                	
+	                	while(!derAccessRules.isEndofBuffer()) {
+	                    	switch( derAccessRules.parseTLV() ){
+	                    		// APDU Access Rule
+		                    	case (byte)0xA0: 
+		                    		DERParser derApduRule = new DERParser( derAccessRules.getTLVData());
+		                    		byte tagApduAccessRule = derApduRule.parseTLV();
+		                    		
+		                    		if( tagApduAccessRule == (byte)0x80 ) { // APDU Permission  (primitive)
+		                    			
+		                					channelAccess.setApduAccess(
+		                							derApduRule.getTLVData()[0] == 0x01 ? ChannelAccess.ACCESS.ALLOWED : ChannelAccess.ACCESS.DENIED);
+		                				
+		                    		} else if( tagApduAccessRule == (byte)0xA1 ) { // APDU Filter (constructed)
+		                    			
+		                    			DERParser derApduFilter = new DERParser( derApduRule.getTLVData() );
+		                				byte tag = derApduFilter.parseTLV();
+		                				
+		                				if( tag == ASN1.TAG_OctetString ) { 
+
+		                					Vector<ApduFilter> apduFilter = new Vector<ApduFilter>();
+		                					
+		                					// collect all apdu filter tlvs.
+		                					apduFilter.add(new ApduFilter( derApduFilter.getTLVData()));
+		                					
+		                					while( !derApduFilter.isEndofBuffer()) {
+		                						if( derApduFilter.parseTLV() == ASN1.TAG_OctetString ) {
+		                        					apduFilter.add(new ApduFilter( derApduFilter.getTLVData()));
+		                						}
+		                					}
+		                					channelAccess.setUseApduFilter(true);
+		                					channelAccess.setApduFilter(apduFilter.toArray(new ApduFilter[apduFilter.size()]));
+		                    			} else {
+			                           		throw new PKCS15Exception("Invalid element found!");
+		                    			}
+		                				
+		                    		} else {
+		                           		throw new PKCS15Exception("Invalid element found!");
+		                    		}
+	                    		break;
+	                    		// NFC Access Rule
+		                    	case (byte)0xA1: 
+		                    		DERParser derNfc = new DERParser(derAccessRules.getTLVData());
+		                    	
+		                    		if( derNfc.parseTLV() == (byte)0x80 ) { // NFC Permission (primitive)
+		                    			channelAccess.setNFCEventAccess(
+		                    					derNfc.getTLVData()[0] == (byte)0x01 ? ChannelAccess.ACCESS.ALLOWED : ChannelAccess.ACCESS.DENIED);
+		                    		} else {
+		                        		throw new PKCS15Exception("Invalid element found!");
+		                    		}
+		                    		break;
+		                    	default:
+		                    		throw new PKCS15Exception("Invalid element found!");
+	                    	}
+	                    }
+	                } else {
+	                	// no explicit access rule given.
+	                }
+                }
+            } else {
+            	// coding 30 00 -> empty hash value given (all applications)
+            }
+            //----
+        	mSEHandle.putAccessRule(mAid_Ref_Do, hash_ref_do, channelAccess);
+        } ;
+    }
+
+
+    /**
+     * Stores a restricted list of certificate hashes
+     * @param path Path of the "EF_ACConditions" file
+     */
+    public void addRestrictedHashes(byte[] path) {
+        try {
+            Log.v(TAG,"Reading and analysing EF_ACConditions...");
+            if (selectFile(path) == APDU_SUCCESS) {
+            	mData = readBinary(0,Util.END);
+                decodeDER(mData);
+            } else {
+            	Log.e(TAG,"EF_ACConditions not found!");
+            }
+        } catch (Exception e) {
+        	/*Nothing to do*/
+        	Log.e( TAG, "Exception: " + e.getMessage());
+        } 
+    }
+
+    /**
+     * Stores a restricted list of certificate hashes
+     * @param path Path of the "EF_ACConditions" file
+     */
+    public void addRestrictedHashesFromData(byte[] data) {
+        try {
+            Log.v(TAG,"Analysing cached EF_ACConditions data...");
+            if( data != null ) {
+            	mData = data;
+                decodeDER(mData);
+            } else {
+            	Log.e(TAG,"EF_ACConditions data not available!");
+            }
+        } catch (Exception e) {
+        	/*Nothing to do*/
+        	Log.e( TAG, "Exception: " + e.getMessage());
+        } 
+    }
+
+	public byte[] getData() {
+		return mData;
+	}
+
+}
+
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACMain.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACMain.java
new file mode 100644
index 0000000..2c6932f
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACMain.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+import android.util.Log;
+import java.util.Arrays;
+
+import org.simalliance.openmobileapi.service.security.arf.ASN1;
+import org.simalliance.openmobileapi.service.security.arf.DERParser;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception;
+
+/**
+ * EF_ACMain related features
+ ***************************************************/
+public class EFACMain extends EF {
+
+    public static final String TAG = "ACE ARF EF_ACMain";
+    // Length of the "RefreshTag" 
+    public static final short REFRESH_TAG_LEN=8;
+
+    // "EF Access Control Main" path
+    private byte[] mACMainPath=null;
+
+    /**
+     * Decodes EF_ACMain file
+     * @param buffer ASN.1 data
+     * @return Path to "Access Control Rules"
+     */
+    private byte[] decodeDER(byte[] buffer)
+    	throws PKCS15Exception 
+	{
+        DERParser DER=new DERParser(buffer);
+        DER.parseTLV(ASN1.TAG_Sequence);
+        if (DER.parseTLV(ASN1.TAG_OctetString)!=REFRESH_TAG_LEN)
+            throw new PKCS15Exception("[Parser] RefreshTag length not valid");
+
+        byte[] refreshTag=DER.getTLVData();
+        if (!Arrays.equals(refreshTag,this.mSEHandle.getRefreshTag())) {
+        	mSEHandle.setRefreshTag(refreshTag);
+            return DER.parsePathAttributes();
+        }
+        return null; // RefreshTag not updated
+    }
+
+
+	/**
+     * Constructor
+     * @param secureElement SE on which ISO7816 commands are applied
+     */
+    public EFACMain(SecureElement handle,byte[] path) {
+    	super(handle);
+        mACMainPath=path;
+    }
+
+    /**
+     * Selects and Analyses EF_ACMain file
+     * @return Path to "EF_ACRules" if "RefreshTag" has been updated;
+     *             <code>null</code> otherwise
+     */
+    public byte[] analyseFile()
+    	throws PKCS15Exception,SecureElementException 
+    {
+        Log.v(TAG,"Analysing EF_ACMain...");
+        byte[] path = mACMainPath;
+
+        /*
+        // 2012-04-12
+        // extend path if ODF path was determined from EF DIR.
+        if( mSEHandle.getPKCS15Path() != null ) {
+        	path = new byte[mSEHandle.getPKCS15Path().length + mACMainPath.length];
+        	System.arraycopy(mSEHandle.getPKCS15Path(), 0, path, 0, mSEHandle.getPKCS15Path().length);
+        	System.arraycopy(mACMainPath, 0, path, mSEHandle.getPKCS15Path().length, mACMainPath.length );
+        } 
+        //---
+         * 
+         */
+        
+        if ( selectFile(path) != APDU_SUCCESS) {
+            throw new PKCS15Exception("EF_ACMain not found!");
+        }
+        return decodeDER(readBinary(0,Util.END));
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACRules.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACRules.java
new file mode 100644
index 0000000..6431bde
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFACRules.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.arf.ASN1;
+import org.simalliance.openmobileapi.service.security.arf.DERParser;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFACConditions;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.AID_REF_DO;
+
+/**
+ * EF_ACRules related features
+ ***************************************************/
+public class EFACRules extends EF {
+
+    public static final String TAG = "ACE ARF EF_ACRules";
+    // AID used to store rules for default application
+    public static final byte[] DEFAULT_APP = new byte[0];
+    
+    protected Map<String, byte[]> mAcConditionDataCache = new HashMap<String, byte[]>();
+    
+
+    /**
+     * Decodes EF_ACRules file
+     * @param buffer ASN.1 data
+     */
+    private void decodeDER(byte[] buffer)
+    	throws PKCS15Exception 
+    {
+        byte[] AID=null;
+        DERParser DER=new DERParser(buffer);
+
+        // mapping to GPAC data objects
+        int tag = 0;
+        
+        while(!DER.isEndofBuffer()) {
+            DER.parseTLV(ASN1.TAG_Sequence);
+            switch(DER.parseTLV()) {
+            case (byte)0xA0: // Restricted AID
+                DER.parseTLV(ASN1.TAG_OctetString);
+                AID=DER.getTLVData();
+                tag = AID_REF_DO._TAG;
+                break;
+            case (byte)0x81: // Rules for default Application
+                AID=null; 
+            	tag = AID_REF_DO._TAG_DEFAULT_APPLICATION;	
+            	break;
+            case (byte)0x82: // Rules for default case
+                AID=DEFAULT_APP; 
+            	tag = AID_REF_DO._TAG;
+            	break;
+            default:
+                throw new PKCS15Exception("[Parser] Unexpected ACRules entry");
+            } 
+            byte[] path = DER.parsePathAttributes();
+            
+            // 2012-09-04
+            // optimization of reading EF ACCondition 
+            if( path != null  ){
+                String pathString = Util.bytesToString(path);
+                EFACConditions temp = new EFACConditions(mSEHandle,new AID_REF_DO(tag, AID ));
+                // check if EF was already read before
+                if( this.mAcConditionDataCache.containsKey(pathString )){
+                	// yes, then reuse data
+                	temp.addRestrictedHashesFromData(this.mAcConditionDataCache.get(pathString));
+                } else {
+                	// no, read EF and add to rules cache
+                    temp.addRestrictedHashes(path);
+                    if( temp.getData() != null ){
+                    	// if data are read the put it into cache.
+                    	this.mAcConditionDataCache.put(pathString, temp.getData());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Constructor
+     * @param secureElement SE on which ISO7816 commands are applied
+     */
+    public EFACRules(SecureElement handle) {
+    	super( handle );
+    }
+
+    /**
+     * Selects and Analyses EF_ACRules file
+     * @param path Path of the "EF_ACRules" file
+     */
+    public void analyseFile(byte[] path)
+    	throws PKCS15Exception,SecureElementException {
+    	
+        Log.v(TAG,"Analysing EF_ACRules...");
+        
+        // clear EF AC Condition data cache.
+        mAcConditionDataCache.clear();
+        
+        if ( selectFile(path)!= APDU_SUCCESS)
+            throw new PKCS15Exception("EF_ACRules not found!!");
+
+        try { 
+        	decodeDER( readBinary(0,Util.END));
+        } catch(PKCS15Exception e) {
+            throw e;
+    }}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFDIR.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFDIR.java
new file mode 100644
index 0000000..992ae30
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFDIR.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+import android.util.Log;
+import java.util.Arrays;
+
+import org.simalliance.openmobileapi.service.security.arf.ASN1;
+import org.simalliance.openmobileapi.service.security.arf.DERParser;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception;
+
+/**
+ * EF_DIR related features
+ ***************************************************/
+public class EFDIR extends EF{
+
+    public static final String TAG = "ACE ARF EF_Dir";
+    // Standardized ID for EF_DIR file
+    public static final byte[] EFDIR_PATH = { 0x3F,0x00,0x2F,0x00 };
+
+
+    /**
+     * Decodes EF_DIR file
+     * @param buffer ASN.1 data
+     * @param AID Record key to search for
+     * @return Path to "EF_ODF" when an expected record is found;
+     *             <code>null</code> otherwise
+     */
+    private byte[] decodeDER(byte[] buffer,byte[] AID)
+    throws PKCS15Exception {
+        DERParser DER=new DERParser(buffer);
+        DER.parseTLV(ASN1.TAG_ApplTemplate);
+        // Application Identifier
+        DER.parseTLV(ASN1.TAG_ApplIdentifier);
+        if (!Arrays.equals(DER.getTLVData(),AID)) 
+            return null; // Record for another AID
+
+        // Application Label or Application Path
+        byte objectType=DER.parseTLV();
+        if (objectType==ASN1.TAG_ApplLabel) { 
+            // Application Label [Optional]
+            DER.getTLVData();
+            DER.parseTLV(ASN1.TAG_ApplPath);
+        } else if (objectType!=ASN1.TAG_ApplPath) 
+                     throw new PKCS15Exception("[Parser] Application Tag expected");
+        // Application Path
+        return DER.getTLVData();
+    }
+
+
+    /**
+     * Constructor
+     * @param secureElement SE on which ISO7816 commands are applied
+     */
+    public EFDIR(SecureElement handle) {
+    	super(handle);
+    }
+
+    /**
+     * Analyses DIR file and lookups for AID record
+     * @param AID Record key to search for
+     * @return Path to "EF_ODF" when an expected record is found;
+     *             <code>null</code> otherwise
+     */
+    public byte[] lookupAID(byte[] AID) throws PKCS15Exception,SecureElementException {
+        Log.v(TAG,"Analysing EF_DIR...");
+        
+        if (selectFile(EFDIR_PATH)!= APDU_SUCCESS)
+            throw new PKCS15Exception("EF_DIR not found!!");
+
+        byte[] data,ODFPath=null;
+        short index=1;
+        while(index<=getFileNbRecords()) {
+            data=readRecord(index++);
+            if ((ODFPath=decodeDER(data,AID))!=null)
+            	break;
+        } 
+        return ODFPath;
+    }
+
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFDODF.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFDODF.java
new file mode 100644
index 0000000..865271b
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFDODF.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+import org.simalliance.openmobileapi.service.security.arf.ASN1;
+import org.simalliance.openmobileapi.service.security.arf.DERParser;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception;
+
+import android.util.Log;
+
+/**
+ * EF_DODF related features
+ ***************************************************/
+public class EFDODF extends EF{
+
+    public static final String TAG = "ACE ARF EF_DODF";
+    // OID defined by Global Platform for the "Access Control"
+    public static final String AC_OID = "1.2.840.114283.200.1.1";
+
+    /**
+     * Decodes EF_DODF file
+     * @param buffer ASN.1 data
+     * @return Path to "Access Control Main" from "Access Control" OID;
+     *             <code>null</code> otherwise
+     */
+    private byte[] decodeDER(byte[] buffer)
+    throws PKCS15Exception {
+        byte objectType;
+        short[] context=null;
+        DERParser DER=new DERParser(buffer);
+
+        while(!DER.isEndofBuffer()) {
+            if (DER.parseTLV()==(byte)0xA1) { // OidDO Data Object
+                // Common Object Attributes
+                DER.parseTLV(ASN1.TAG_Sequence); 
+                DER.skipTLVData();
+                // Common Data Object Attributes
+                DER.parseTLV(ASN1.TAG_Sequence); 
+                DER.skipTLVData();
+
+                objectType=DER.parseTLV();
+                if (objectType==(byte)0xA0) { // SubClassAttributes [Optional]
+                    DER.skipTLVData();
+                    objectType=DER.parseTLV();
+                }
+                if (objectType==(byte)0xA1) { // OidDO
+                    DER.parseTLV(ASN1.TAG_Sequence);
+                    context=DER.saveContext();
+                    if (DER.parseOID().compareTo(AC_OID)!=0) {
+                        DER.restoreContext(context); 
+                        DER.skipTLVData();
+                    } else return DER.parsePathAttributes();
+                } else throw new PKCS15Exception("[Parser] OID Tag expected");
+            } else DER.skipTLVData();
+        } 
+        return null; // No "Access Control" OID found
+    }
+
+
+    /**
+     * Constructor
+     * @param secureElement SE on which ISO7816 commands are applied
+     */
+    public EFDODF(SecureElement handle) {
+    	super(handle);
+    }
+
+    /**
+     * Selects and Analyses EF_DODF file
+     * @param path Path of the "EF_DODF" file
+     * @return Path to "EF_ACMain" from "Access Control" OID;
+     *             <code>null</code> otherwise
+      */
+    public byte[] analyseFile(byte[] path)
+    	throws PKCS15Exception,SecureElementException 
+	{
+        Log.v(TAG,"Analysing EF_DODF...");
+        
+        if (selectFile(path)!=APDU_SUCCESS)
+            throw new PKCS15Exception("EF_DODF not found!");
+        
+        return decodeDER(readBinary(0,Util.END));
+    }
+
+}
+
+
+
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFODF.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFODF.java
new file mode 100644
index 0000000..fdb344b
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/EFODF.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+import org.simalliance.openmobileapi.service.security.arf.DERParser;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception;
+
+import android.util.Log;
+
+/**
+ * EF_ODF related features
+ ***************************************************/
+public class EFODF extends EF {
+
+    public static final String TAG = "SmartcardService ACE ARF";
+    // Standardized ID for EF_ODF file
+    public static final byte[] EFODF_PATH = { 0x50,0x31 };
+
+    /**
+     * Decodes EF_ODF file
+     * @param buffer ASN.1 data
+     * @return Path to "EF_DODF" from "DODF Tag" entry;
+     *             <code>null</code> otherwise
+     */
+    private byte[] decodeDER(byte[] buffer)
+    throws PKCS15Exception {
+        DERParser DER=new DERParser(buffer);
+        while(!DER.isEndofBuffer()) {
+            if (DER.parseTLV()==(byte)0xA7)  { // DODF
+                return DER.parsePathAttributes();
+            } else DER.skipTLVData();
+        } return null; // No "DODF Tag" entry found
+    }
+
+
+    /**
+     * Constructor
+     * @param secureElement SE on which ISO7816 commands are applied
+     */
+    public EFODF(SecureElement handle) {
+    	super(handle);
+    }
+
+    /**
+     * Selects and Analyses EF_ODF file
+     * @return Path to "EF_DODF" from "DODF Tag" entry;
+     *             <code>null</code> otherwise
+     */
+    public byte[] analyseFile( byte[] pkcs15Path )  throws PKCS15Exception,SecureElementException {
+        Log.v(TAG,"Analysing EF_ODF...");
+
+        
+        // 2012-04-12
+        // extend path if ODF path was determined from EF DIR.
+        byte[] path = null;
+        if( pkcs15Path != null ){
+        	path = new byte[pkcs15Path.length + EFODF_PATH.length];
+        	System.arraycopy(pkcs15Path, 0, path, 0, pkcs15Path.length);
+        	System.arraycopy(EFODF_PATH, 0, path, pkcs15Path.length, EFODF_PATH.length );
+        } else {
+        	path = EFODF_PATH;
+        }
+        //---
+        
+        if ( selectFile(path)!= APDU_SUCCESS)
+            throw new PKCS15Exception("EF_ODF not found!!");
+        
+        return decodeDER(readBinary(0,Util.END));
+    }
+
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/PKCS15Exception.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/PKCS15Exception.java
new file mode 100644
index 0000000..b61b103
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/PKCS15Exception.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+/**
+ * Handles PKCS#15 errors
+ ***************************************************/
+public class PKCS15Exception extends Exception {
+
+	private static final long serialVersionUID = 1556408586814064005L;
+
+	public PKCS15Exception(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/PKCS15Handler.java b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/PKCS15Handler.java
new file mode 100644
index 0000000..08c22e1
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/PKCS15/PKCS15Handler.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf.PKCS15;
+
+import java.security.AccessControlException;
+import java.util.MissingResourceException;
+
+import org.simalliance.openmobileapi.service.IChannel;
+import org.simalliance.openmobileapi.service.security.arf.SecureElement;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFACMain;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFACRules;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFDIR;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFDODF;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFODF;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception;
+
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
+
+/**
+ * Handles PKCS#15 topology
+ ***************************************************/
+public class PKCS15Handler {
+
+    public static final String TAG = "SmartcardService ACE ARF";
+    
+    // AID of the GPAC Applet/ADF
+    public static final byte[] GPAC_ARF_AID =
+    	{(byte)0xA0,0x00,0x00,0x00,0x18,0x47,0x50,0x41,0x43,0x2D,0x31,0x35};
+    // AID of the PKCS#15 ADF
+    public static final byte[] PKCS15_AID = 
+       { (byte)0xA0,0x00,0x00,0x00,0x63,0x50,0x4B,0x43,0x53,0x2D,0x31,0x35 };
+    
+    // AIDs of "Access Control Rules" containers
+    public static final byte[][] CONTAINER_AIDS= {
+    	PKCS15_AID,
+    	GPAC_ARF_AID, 
+        null 
+    };
+
+    // Handle to "Secure Element"
+    private SecureElement mSEHandle;
+    // "Secure Element" label
+    private String mSELabel=null;
+
+    // Handle to "Logical Channel" allocated by the SE
+    private IChannel mArfChannel=null;
+
+    // "EF Access Control Main" object
+    private EFACMain mACMainObject=null;
+    // EF AC Rules object
+    private EFACRules mACRulesObject=null;
+    
+    private byte[] mPkcs15Path = null;
+    private byte[] mACMainPath = null;
+    
+    // SIM Allowed modes:
+    private boolean mSimIoAllowed;
+    private boolean mSimAllianceAllowed;
+        
+    /**
+     * Updates "Access Control Rules"
+     */
+    private boolean updateACRules() 
+    	throws Exception, PKCS15Exception, SecureElementException 
+	{
+        byte[] ACRulesPath=null;
+        try { 
+        	ACRulesPath=mACMainObject.analyseFile(); 
+    	} catch (Exception e) {
+            mACMainObject=null;       
+            mSEHandle.resetAccessRules();
+            throw e;
+    	}
+        // Check if rules must be updated
+        if (ACRulesPath != null) {
+        	Log.d(TAG, "Access Rules needs to be updated...");
+            if (mACRulesObject==null) {
+            	mACRulesObject=new EFACRules(mSEHandle);
+            }
+            mSEHandle.clearAccessRuleCache();
+        	mACRulesObject.analyseFile(ACRulesPath);
+        	return true;
+        } else {
+        	Log.d(TAG, "Refresh Tag has not been changed...");
+        	return false;
+        }
+    }
+
+    /**
+     * Initializes "Access Control" entry point [ACMain]
+     */
+    private void initACEntryPoint()
+    	throws PKCS15Exception, SecureElementException 
+	{
+
+        byte[] DODFPath=null;
+        
+        readAllowedSimMode();
+        
+        for(int ind=0;ind<CONTAINER_AIDS.length;ind++) {
+            if (selectACRulesContainer(CONTAINER_AIDS[ind])) {
+
+                byte[] acMainPath = null;
+                if( mACMainPath==null){
+                    EFODF ODFObject=new EFODF(mSEHandle);
+                    DODFPath=ODFObject.analyseFile(mPkcs15Path);
+                    EFDODF DODFObject=new EFDODF(mSEHandle);
+                	acMainPath=DODFObject.analyseFile(DODFPath);
+                	mACMainPath = acMainPath;
+                } else {
+                	if( mPkcs15Path != null ) {
+                		acMainPath = new byte[mPkcs15Path.length + mACMainPath.length];
+                    	System.arraycopy(mPkcs15Path, 0, acMainPath, 0, mPkcs15Path.length);
+                    	System.arraycopy(mACMainPath, 0, acMainPath, mPkcs15Path.length, mACMainPath.length );
+                	} else {
+                		acMainPath = mACMainPath;
+                	}
+                }
+                mACMainObject=new EFACMain(mSEHandle,acMainPath);
+                break;
+            }
+    	}
+    }
+
+    /**
+     * Selects "Access Control Rules" container
+     * @param AID Identification of the GPAC Applet/PKCS#15 ADF;
+     *                    <code>null</code> for EF_DIR file
+     * @return <code>true</code> when container is active;
+     *             <code>false</code> otherwise
+     */
+    private boolean selectACRulesContainer(byte[] aid)
+    	throws PKCS15Exception,SecureElementException 
+	{
+        boolean isActiveContainer=true;
+        
+        if (aid==null) {
+        	mArfChannel = null;
+        	
+        	// some devices use logical channels to access filesystem directly. This is done with an empty byte array.
+        	// if open logical channel does not work, last fallback is using SIM_IO (AT-CRSM).
+        	// 2012-11-08
+        	if(mSimAllianceAllowed)
+        		mArfChannel = mSEHandle.openLogicalArfChannel(new byte[]{});
+
+            if (mArfChannel != null) {
+                Log.i(TAG, "Logical channels are used to access to PKC15");
+                mSEHandle.setSeInterface(SecureElement.SIM_ALLIANCE);
+            }
+            else {
+                if(mSimIoAllowed) {
+                    // Since ARF gets only active if the terminal belongs to a SIM/UICC
+                    // we have to switch to SIM_IO
+                    Log.i(TAG, "Fall back into ARF with SIM_IO");
+                    mSEHandle.setSeInterface(SecureElement.SIM_IO);
+                }
+                else {
+                    Log.i(TAG, "SIM IO is not allowed: cannot access to ARF");
+                    isActiveContainer = false;
+                }
+        	}
+
+            if(isActiveContainer && mPkcs15Path == null ) { // estimate PKCS15 path only if it is not known already.
+    			mACMainPath = null;
+	        	// EF_DIR parsing
+	            EFDIR DIRObject=new EFDIR(mSEHandle);
+	            mPkcs15Path=DIRObject.lookupAID(PKCS15_AID);
+	            if( mPkcs15Path == null ) { 
+	            	Log.i(TAG, "Cannot use ARF: cannot select PKCS#15 directory via EF Dir");
+	            	// TODO: Here it might be possible to set a default path 
+	            	// so that SIMs without EF-Dir could be supported.
+	            	throw new PKCS15Exception("Cannot select PKCS#15 directory via EF Dir");
+	            }
+        	}
+        }
+        // if an AID is given use logical channel.
+        else {
+	        if(!mSimAllianceAllowed) {
+	            isActiveContainer = false;
+	        }
+	        else {
+	            // Selection of Applet/ADF via AID is done via SCAPI and logical Channels
+	            mSEHandle.setSeInterface(SecureElement.SIM_ALLIANCE);
+	            if ((mArfChannel=mSEHandle.openLogicalArfChannel(aid))==null) {
+	                isActiveContainer=false;
+	                Log.w(TAG,"GPAC/PKCS#15 ADF not found!!");
+	            }
+	            else {
+	                // ARF is selected via AID.
+	                if( mPkcs15Path != null ){ // if there is a change from path selection to AID selection, then reset AC Main path.
+	                    mACMainPath = null;
+	                }
+	                mPkcs15Path = null; // selection is done via AID
+	            }
+        	}
+        } 
+        return isActiveContainer;
+    }
+
+    /**
+     * Constructor
+     * @param handle Handle to "Secure Element"
+     */
+    public PKCS15Handler(SecureElement handle) {
+        mSEHandle=handle;
+    }
+
+    /**
+     * Loads "Access Control Rules" from container
+     * @return false if access rules where not read due to constant refresh tag.
+     */
+    public synchronized boolean loadAccessControlRules(String secureElement) {
+        mSELabel=secureElement;
+        Log.v(TAG,"- Loading "+mSELabel+" rules...");
+        try { 
+       		initACEntryPoint();
+        	return updateACRules();
+        } catch (Exception e) {
+        	if( e instanceof MissingResourceException ){ 
+            	// this indicates that no channel is left for accessing the SE element
+                throw (MissingResourceException)e;
+        	}
+            Log.e(TAG,mSELabel+" rules not correctly initialized! " + e.getLocalizedMessage());
+            throw new AccessControlException(e.getLocalizedMessage());
+        } finally {
+	        // Close previously opened channel
+	        if (mArfChannel!=null)
+	        	mSEHandle.closeArfChannel();
+        }
+    }
+    
+    /**
+     * Read security allowed sim mode
+     */
+    private void readAllowedSimMode() {
+        if(!Build.IS_DEBUGGABLE) {
+            mSimIoAllowed = true;
+            mSimAllianceAllowed = true;
+        } else {
+            String level = SystemProperties.get("service.seek.arf", "simio simalliance");
+            level = SystemProperties.get("persist.service.seek.arf", level);
+
+            if(level.contains("simio")) mSimIoAllowed = true; else mSimIoAllowed = false;
+            if(level.contains("simalliance")) mSimAllianceAllowed = true; else mSimAllianceAllowed = false;
+        }
+
+        Log.i(TAG, "Allowed SIM mode: SimIo=" + mSimIoAllowed + " SimAlliance=" + mSimAllianceAllowed );
+    }    
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/SecureElement.java b/src/org/simalliance/openmobileapi/service/security/arf/SecureElement.java
new file mode 100644
index 0000000..cbe4952
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/SecureElement.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.security.arf;
+
+import android.util.Log;
+import java.util.MissingResourceException;
+import org.simalliance.openmobileapi.service.IChannel;
+import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
+import org.simalliance.openmobileapi.service.ITerminal;
+import org.simalliance.openmobileapi.service.SmartcardService;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.ChannelAccess;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+import org.simalliance.openmobileapi.service.security.arf.PKCS15.EF;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.AID_REF_DO;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Hash_REF_DO;
+import org.simalliance.openmobileapi.service.security.gpac.dataobjects.REF_DO;
+
+/**
+ * Provides high-level functions for SE communication
+ ***************************************************/
+public class SecureElement {
+
+    public static final String TAG = "SmartcardService ACE ARF";
+
+    // Logical channel used for SE communication (optional)
+    private IChannel mArfChannel=null;
+    // Handle to a built-in "Secure Element"
+    private ITerminal mTerminalHandle=null;
+    // Arf Controller within the SCAPI handler 
+    private ArfController mArfHandler=null; 
+    // Callback used during "Secure Element" communication
+    private final ISmartcardServiceCallback mCallback = 
+    		new ISmartcardServiceCallback.Stub(){};
+
+    public static final short SIM_IO = 1;
+    public static final short SIM_ALLIANCE = 0;
+
+    // Interface for exchanging APDU commands
+    private short mSEInterface=SIM_ALLIANCE;
+
+    /**
+     * Constructor
+     * 
+     * @param arfHandler - handle to the owning arf controller object
+     * @param handle - handle to the SE terminal to be accessed.
+     */
+    public SecureElement(ArfController arfHandler,ITerminal handle) {
+        mTerminalHandle=handle;
+        mArfHandler=arfHandler;
+    }
+    
+    public short getSeInterface(){
+    	return mSEInterface;
+    }
+    
+    public void setSeInterface(short seInterface){
+    	mSEInterface = seInterface;
+    }
+ 
+    /**
+     * Transmits ADPU commands
+     * @param cmd APDU command
+     * @return Data returned by the APDU command
+     */
+    public byte[] exchangeAPDU(EF ef, byte[] cmd)
+    	throws SecureElementException {
+        try {
+            if (mSEInterface==SIM_IO) { 
+
+                return mTerminalHandle.simIOExchange(ef.getFileId(),ef.getFilePath(),cmd);
+            } else { 
+
+            	return mArfChannel.transmit(cmd);
+            }
+		} catch (Exception e) {
+	            throw new SecureElementException("Secure Element access error " + e.getLocalizedMessage());
+	    }
+    }
+
+    /**
+     * Opens a logical channel to ARF Applet or ADF
+     * @param AID Applet identifier
+     * @return Handle to "Logical Channel" allocated by the SE;
+     *             <code>0</code> if error occurred
+     */
+    public IChannel openLogicalArfChannel(byte[] AID) {
+        try {
+
+            mArfChannel=mTerminalHandle.openLogicalChannel(null,AID,mCallback);
+            setUpChannelAccess(mArfChannel);
+            return mArfChannel;
+        } catch(Exception e) { 
+        	if( e instanceof MissingResourceException ){ 
+            	// this indicates that no channel is left for accessing the SE element
+                Log.d(TAG, "no channels left to access ARF: " + e.getMessage() );
+                throw (MissingResourceException)e;
+        	} else {
+        		Log.e(TAG,"Error opening logical channel " + e.getLocalizedMessage());
+        	}
+        	mArfChannel = null;
+        	return null; 
+        }
+    }
+
+    /**
+     * Closes a logical channel previously allocated by the SE
+     * @param handle Handle to open channel
+     */
+    public void closeArfChannel() {
+        try {
+            if( mArfChannel != null){
+
+            	mArfChannel.close();
+            	mArfChannel = null;
+            } else {
+
+            }
+            
+        } catch(Exception e) { 
+        	Log.e(TAG,"Error closing channel " + e.getLocalizedMessage()); 
+    	}
+    }
+
+    /**
+     * Set up channel access to allow, 
+     * so that PKCS15 files can be read.
+     * 
+     * @param channel
+     */
+    private void setUpChannelAccess( IChannel channel ){
+        // set access conditions to access ARF.
+        ChannelAccess arfChannelAccess = new ChannelAccess();
+        arfChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
+        arfChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED); 
+        channel.setChannelAccess(arfChannelAccess);
+
+    }
+
+    public byte[] getRefreshTag() {
+		if( mArfHandler != null ){
+			return mArfHandler.getAccessRuleCache().getRefreshTag();
+		}
+		return null;
+	}
+
+	public void setRefreshTag(byte[] refreshTag) {
+		if( mArfHandler != null ) {
+			mArfHandler.getAccessRuleCache().setRefreshTag(refreshTag);
+		}
+	}
+
+    public void putAccessRule( AID_REF_DO aid_ref_do, Hash_REF_DO hash_ref_do, ChannelAccess channelAccess ) {
+    	
+    	REF_DO ref_do = new REF_DO(aid_ref_do, hash_ref_do);
+    	mArfHandler.getAccessRuleCache().putWithMerge(ref_do, channelAccess);
+    }
+
+	public void resetAccessRules() {
+		this.mArfHandler.getAccessRuleCache().reset();
+	}
+	public void clearAccessRuleCache() {
+		this.mArfHandler.getAccessRuleCache().clearCache();
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/arf/SecureElementException.java b/src/org/simalliance/openmobileapi/service/security/arf/SecureElementException.java
new file mode 100644
index 0000000..4580f85
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/arf/SecureElementException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 Deutsche Telekom, A.G.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.arf;
+
+/**
+ * Handles "Secure Element" access errors
+ ***************************************************/
+public class SecureElementException extends Exception {
+
+	private static final long serialVersionUID = 530360632436123998L;
+
+	public SecureElementException(String message) {
+        super(message);
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/AID_REF_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/AID_REF_DO.java
new file mode 100644
index 0000000..a092720
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/AID_REF_DO.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+
+/**
+ * The AID-REF-DO is used for retrieving and storing 
+ * the corresponding access rules for an SE application 
+ * (which is identified by its AID) from and to the ARA. 
+ * Two different AID reference data objects exist and one 
+ * of these can be chosen and applied for 
+ * a GET DATA and STORE DATA command
+ * 
+ * 
+ *
+ */
+public class AID_REF_DO extends BerTlv {
+
+	public final static int _TAG = 0x4F;
+	public final static int _TAG_DEFAULT_APPLICATION = 0xC0;
+	
+	private byte[] mAid = null;
+	
+	public AID_REF_DO(byte[] rawData, int tag, int valueIndex, int valueLength) {
+		super(rawData, tag, valueIndex, valueLength);
+	}
+
+	
+	public AID_REF_DO( int tag, byte[] aid){
+		super( aid, tag, 0, (aid==null ?  0 : aid.length));
+		mAid = aid;
+	}
+
+	public AID_REF_DO( int tag){
+		super( null, tag, 0, 0);
+		mAid = tag == _TAG_DEFAULT_APPLICATION ? null : new byte[0];
+	}
+	
+	@Override
+	public String toString(){
+		StringBuilder b = new StringBuilder();
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		b.append("AID_REF_DO: ");
+		try {
+			this.build(out);
+			b.append(BerTlv.toHex(out.toByteArray()));
+		} catch (Exception e ){
+			b.append(e.getLocalizedMessage());
+		}
+		return b.toString();
+	}
+	
+	public byte[] getAid(){
+		return mAid;
+	}
+	
+	/**
+	 * Tags: C0 -> Length: 0 -> Default selected application (all channels)
+	 * 		 4F -> Length: 0 or 5 - 16 bytes
+	 *
+	 * Value:
+	 * AID: identifies a specific application
+	 * Empty: refers to all SE applications
+	 * 
+	 * Length:
+	 * 5-16 for an AID according to ISO/IEC7816-5
+	 * 0 for empty value field
+ 	 */
+	@Override
+	public void interpret() 
+		throws ParserException {
+	
+		mAid = null;
+		
+		byte[] data = getRawData();
+		int index = getValueIndex();
+
+		if( getTag() == _TAG_DEFAULT_APPLICATION ) {
+			if( getValueLength() != 0 ){
+				throw new ParserException("Invalid value length for AID-REF-DO!");
+			}
+		} else if( getTag() == _TAG ){
+			
+			// sanity checks
+			if( (getValueLength() < 5 || getValueLength() > 16) && getValueLength() != 0) {
+				throw new ParserException("Invalid value length for AID-REF-DO!");
+			}
+
+			if( index + getValueLength() > data.length){
+				throw new ParserException( "Not enough data for AID-REF-DO!");
+			}
+			
+			mAid = new byte[getValueLength()];
+			System.arraycopy(data, index, mAid, 0, getValueLength());
+			
+		} else {
+			throw new ParserException( "Invalid Tag for AID-REF-DO!");
+		}
+	}
+	
+	/**
+	 * Tags: C0 -> Length: 0 -> Default selected application (all channels)
+	 * 		 4F -> Length: 0 or 5 - 16 bytes
+	 *
+	 * Value:
+	 * AID: identifies a specific application
+	 * Empty: refers to all SE applications
+	 * 
+	 * Length:
+	 * 5-16 for an AID according to ISO/IEC7816-5
+	 * 0 for empty value field
+ 	 */
+	@Override
+	public void build( ByteArrayOutputStream stream) 
+		throws DO_Exception {
+		
+		if( getTag() == _TAG_DEFAULT_APPLICATION ) {
+			if( mAid != null ){
+				throw new DO_Exception("No value allowed for default selected application!");
+			}
+			stream.write(getTag());
+			stream.write(0x00);
+		} else if( getTag() == _TAG ){
+			
+			// sanity check
+			if( getValueLength() != 0 ) {
+				if (getValueLength() < 5 || getValueLength() > 16) {
+					throw new DO_Exception("Invalid length of AID!");
+				}
+			}
+
+			stream.write(getTag());
+			if( mAid != null && mAid.length > 0 ) {
+				stream.write(mAid.length);
+				try {
+					stream.write(mAid);
+				} catch( IOException ioe ){
+					throw new DO_Exception("AID could not be written!");
+				}
+			} else {
+				stream.write(0x00);
+			}
+			
+		} else {
+			throw new DO_Exception( "AID-REF-DO must either be C0 or 4F!");
+		}
+	}
+	
+	@Override
+	public boolean equals( Object obj ){
+		boolean equals = false;
+		
+		if( obj instanceof AID_REF_DO ){
+			equals = super.equals(obj);
+			
+			if( equals ){
+				AID_REF_DO aid_ref_do = (AID_REF_DO)obj;
+				if( this.mAid == null && aid_ref_do.mAid == null )
+					equals &= true;
+				else {
+					equals &= Arrays.equals(mAid, aid_ref_do.mAid);
+				}
+			}
+		}
+		return equals;
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/APDU_AR_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/APDU_AR_DO.java
new file mode 100644
index 0000000..41f477e
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/APDU_AR_DO.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+
+/**
+ * APDU-AR-DO:
+ * An APDU access rule data object defines an access rule for APDU access. 
+ * The APDU access can either be restricted by a general rule 
+ * based on an access is NEVER/ ALWAYS allowed policy or 
+ * by a specific rule based on APDU filters which defines the range 
+ * of allowed APDUs more precisely. 
+ *
+ * 
+ *
+ */
+public class APDU_AR_DO extends BerTlv {
+	
+	public final static int _TAG = 0xD0;
+
+	private boolean mApduAllowed = false;
+	private ArrayList<byte[]> mApduHeader = new ArrayList<byte[]>();
+	private ArrayList<byte[]> mFilterMask = new ArrayList<byte[]>();
+	
+	public APDU_AR_DO(byte[] rawData, int valueIndex, int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+	
+	public APDU_AR_DO( boolean allowed ){
+		super( null, _TAG, 0, 0);
+		mApduAllowed = allowed;
+	}
+	
+	public APDU_AR_DO( ArrayList<byte[]> apduHeader, ArrayList<byte[]> filterMask ){
+		super( null, _TAG, 0, 0);
+		mApduHeader = apduHeader;
+		mFilterMask = filterMask;
+	}
+
+	public boolean isApduAllowed(){
+		return mApduAllowed;
+	}
+	
+	public ArrayList<byte[]> getApduHeaderList(){
+		return mApduHeader;
+	}
+
+	public ArrayList<byte[]> getFilterMaskList(){
+		return mFilterMask;
+	}
+
+	@Override
+	/**
+	 * Tag: D0
+	 * Length: 1 or n*8
+	 * 	1 if value contains a general APDU access rule.
+	 * 	n*8 if value contains a specific APDU access rule.
+
+	 * Value:
+	 * Contains a general APDU access rule:
+	 * 	NEVER (00): APDU access is not allowed
+	 *  ALWAYS(01): APDU access is allowed
+	 *  or
+	 *  contains a specific APDU access rule based on one or more APDU filter(s):
+	 *  APDU filter: 8 bytes APDU filter mask consists of:
+	 *  4 bytes APDU header (defines the header of allowed APDUs)
+	 *  4 bytes APDU mask (bit set defines the bits which shall be considered 
+	 *  for the APDU header comparison)
+	 *  An APDU filter has to be applied as follows:
+	 *  	if((APDUHeader & FilterMask) == FilterAPDUHeader)
+	 *                 then allow APDU
+	 */
+	public void interpret() 
+		throws ParserException {
+		
+		mApduAllowed = false;
+		mApduHeader.clear();
+		mFilterMask.clear();
+	
+		byte[] data = getRawData();
+		int index = getValueIndex();
+		
+		if( index + getValueLength() > data.length){
+			throw new ParserException( "Not enough data for APDU_AR_DO!");
+		}
+		
+		// APDU-AR-DO contains either a flag which allows/disallows APDU communication
+		// or
+		// it contains APDU filter (APDUHeader | FilterMask) which should have length n*8.
+		if( getValueLength() == 1 ){
+			mApduAllowed = (data[index] == 0x01);
+		} else if(getValueLength() % 8 == 0 ) {
+			mApduAllowed = true;
+			
+			for( int i = index; i < index + getValueLength(); i +=8 ){
+				byte[] apduHeader = new byte[4];
+				byte[] filterMask = new byte[4];
+				
+				apduHeader[0] = data[i+0];
+				apduHeader[1] = data[i+1];
+				apduHeader[2] = data[i+2];
+				apduHeader[3] = data[i+3];
+				filterMask[0] = data[i+4];
+				filterMask[1] = data[i+5];
+				filterMask[2] = data[i+6];
+				filterMask[3] = data[i+7];
+				
+				mApduHeader.add(apduHeader);
+				mFilterMask.add(filterMask);
+			}
+		} else {
+			throw new ParserException( "Invalid length of APDU-AR-DO!" );
+		}
+	}
+	
+	@Override
+	/**
+	 * Tag: D0
+	 * Length: 1 or n*8
+	 * 	1 if value contains a general APDU access rule.
+	 * 	n*8 if value contains a specific APDU access rule.
+
+	 * Value:
+	 * Contains a general APDU access rule:
+	 * 	NEVER (00): APDU access is not allowed
+	 *  ALWAYS(01): APDU access is allowed
+	 *  or
+	 *  contains a specific APDU access rule based on one or more APDU filter(s):
+	 *  APDU filter: 8 bytes APDU filter mask consists of:
+	 *  4 bytes APDU header (defines the header of allowed APDUs)
+	 *  4 bytes APDU mask (bit set defines the bits which shall be considered 
+	 *  for the APDU header comparison)
+	 *  An APDU filter has to be applied as follows:
+	 *  	if((APDUHeader & FilterMask) == FilterAPDUHeader)
+	 *                 then allow APDU
+	 */
+	public void build( ByteArrayOutputStream stream )
+		throws DO_Exception {
+
+		// APDU header and filter mask has to have the same size
+		// even if they are not used (then size() == 0 ).
+		if(mApduHeader.size() !=  this.mFilterMask.size()){
+			throw new DO_Exception( "APDU filter is invalid");
+		}
+		
+		// write tag
+		stream.write(getTag());
+		
+		// check if APDU Flag shall be written
+		if( mApduHeader.size() == 0){
+			stream.write(0x01);
+			stream.write(this.mApduAllowed ? 0x01 : 0x00 );
+		} else {
+			ByteArrayOutputStream temp = new ByteArrayOutputStream();
+			for( int i = 0; i < mApduHeader.size(); i++ ){
+				byte[] apduHeader = mApduHeader.get(i);
+				byte[] filterMask = mFilterMask.get(i);
+				
+				if( apduHeader.length != 4 || filterMask.length != 4 ){
+					throw new DO_Exception("APDU filter is invalid!");
+				}
+				
+				try {
+					temp.write(apduHeader);
+					temp.write(filterMask);
+				} catch (IOException e) {
+					throw new DO_Exception("APDU Filter Memory IO problem! " +  e.getMessage());
+				}
+			}
+			
+			BerTlv.encodeLength(temp.size(), stream);
+			try {
+				stream.write(temp.toByteArray());
+			} catch (IOException e) {
+				throw new DO_Exception("APDU Filter Memory IO problem! " +  e.getMessage());
+			}
+		}
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/AR_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/AR_DO.java
new file mode 100644
index 0000000..b87e7c6
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/AR_DO.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+
+/**
+ * This class represents the Access rule data object (AR-DO), according
+ * to GP Secure Element Control Access.
+ * 
+ * The AR-DO contains one or two access rules of type APDU or NFC. 
+ * 
+ * 
+ *
+ */
+public class AR_DO extends BerTlv{
+	
+	public final static int _TAG = 0xE3;
+	
+	private APDU_AR_DO mApduAr = null;
+	private NFC_AR_DO  mNfcAr = null;
+
+	public AR_DO(byte[] rawData, int valueIndex, int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+	
+	public AR_DO( APDU_AR_DO apdu_ar_do, NFC_AR_DO nfc_ar_do ){
+		super( null, _TAG, 0, 0);
+		mApduAr = apdu_ar_do;
+		mNfcAr = nfc_ar_do;
+	}
+	
+	public APDU_AR_DO getApduArDo(){
+		return mApduAr;
+	}
+
+	public NFC_AR_DO getNfcArDo(){
+		return mNfcAr;
+	}
+	
+	@Override
+	/**
+	 * Interpret value.
+	 * 
+	 * Tag: E3
+	 * 
+	 * Value:
+	 * Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO
+	 * A concatenation of one or two AR-DO(s). If two AR-DO(s) are present 
+	 * these must have different types. 
+	 */
+	public void interpret() 
+		throws ParserException {
+		
+		this.mApduAr = null;
+		this.mNfcAr = null;
+	
+		byte[] data = getRawData();
+		int index = getValueIndex();
+		
+		if( index + getValueLength() > data.length){
+			throw new ParserException( "Not enough data for AR_DO!");
+		}
+		
+		do {
+			BerTlv temp = BerTlv.decode(data, index);
+			
+			if( temp.getTag() == APDU_AR_DO._TAG ) { // APDU-AR-DO
+				mApduAr = new APDU_AR_DO( data, temp.getValueIndex(), temp.getValueLength());
+				mApduAr.interpret();
+			} else if( temp.getTag() == NFC_AR_DO._TAG ) { // NFC-AR-DO
+				mNfcAr = new NFC_AR_DO( data, temp.getValueIndex(), temp.getValueLength());
+				mNfcAr.interpret();
+			} else {
+				// un-comment following line if a more restrictive 
+				// behavior is necessary.
+				//throw new ParserException("Invalid DO in AR-DO!");
+			}
+			index = temp.getValueIndex() + temp.getValueLength();
+		} while ( getValueIndex() + getValueLength() > index  );
+		
+		if( mApduAr == null && mNfcAr == null ){
+			throw new ParserException("No valid DO in AR-DO!");
+		}
+	}
+	
+	@Override
+	/**
+	 * Interpret value.
+	 * 
+	 * Tag: E3
+	 * 
+	 * Value:
+	 * Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO
+	 * A concatenation of one or two AR-DO(s). If two AR-DO(s) are present 
+	 * these must have different types. 
+	 */
+	public void build( ByteArrayOutputStream stream )
+		throws DO_Exception {
+		
+		// write tag
+		stream.write(getTag());
+
+		ByteArrayOutputStream temp = new ByteArrayOutputStream();
+		if( mApduAr != null ){
+			mApduAr.build(temp);
+		}
+		
+		if( mNfcAr != null ){
+			mNfcAr.build(temp);
+		}
+		
+		BerTlv.encodeLength(temp.size(), stream);
+		try {
+			stream.write(temp.toByteArray());
+		} catch (IOException e) {
+			throw new DO_Exception("AR-DO Memory IO problem! " +  e.getMessage());
+		}
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/BerTlv.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/BerTlv.java
new file mode 100644
index 0000000..7acbc6d
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/BerTlv.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+
+
+public class BerTlv {
+	
+	private byte[] mRawData = null;
+	
+	private int mTag = 0;
+	
+	private int mValueIndex = 0;
+	private int mValueLength = 0;
+	
+	public BerTlv( byte[] rawData, int tag, int valueIndex, int valueLength ) {
+		mRawData = rawData;
+		mTag = tag;
+		mValueIndex = valueIndex;
+		mValueLength = valueLength;
+	}
+
+	public static String toHex(byte[] digest) {
+	    String digits = "0123456789abcdef";
+	    StringBuilder sb = new StringBuilder(digest.length * 2);
+	    for (byte b : digest) {
+	        int bi = b & 0xff;
+	        sb.append(digits.charAt(bi >> 4));
+	        sb.append(digits.charAt(bi & 0xf));
+	    }
+	    return sb.toString();
+	}	
+	
+    public static BerTlv decode( byte[] data, int startIndex )
+    	throws ParserException {
+    		return BerTlv.decode(data, startIndex, true);
+    }
+
+    public static BerTlv decode( byte[] data, int startIndex, boolean containsAllData )
+		throws ParserException {
+		
+    	if( data == null || data.length == 0 ){
+    		throw new ParserException("No data given!");
+    	}
+    	
+		int curIndex = startIndex;
+		int tag = 0;
+		
+	    /* tag */
+		if( curIndex < data.length ) {
+		    int temp = data[curIndex++] & 0xff;
+		    switch (temp) {
+		    case 0xff: // tag is in two byte format
+		    case 0xdf:
+				if( curIndex < data.length ) {
+			    	tag = ((temp & 0xff) << 8) | (data[curIndex++] & 0xff);
+				} else {
+					throw new ParserException("Index " + curIndex + " out of range! [0..[" + data.length);
+				}
+		    	break;
+		
+		    default: // tag is in single-byte format
+		    	tag = temp;
+		        break;
+		    }
+		} else {
+			throw new ParserException("Index " + curIndex + " out of range! [0..[" + data.length);
+		}
+	    
+		/* length */
+	    int length;
+		if( curIndex < data.length ) {
+		    int temp = data[curIndex++] & 0xff;
+		    if (temp < 0x80) {
+		        length = temp;
+		    } else if (temp == 0x81) {
+				if( curIndex < data.length ) {
+			        length = data[curIndex++] & 0xff;
+			        if (length < 0x80) {
+			            throw new ParserException("Invalid TLV length encoding!");
+			        }
+			        if(containsAllData &&
+			           data.length < length + curIndex) {
+			            throw new ParserException("Not enough data provided!");
+			        }
+				} else {
+					throw new ParserException("Index " + curIndex + " out of range! [0..[" + data.length);
+				}
+		    } else if (temp == 0x82) {
+				if( (curIndex + 1)< data.length ) {
+			        length = ((data[curIndex] & 0xff) << 8) | (data[curIndex + 1] & 0xff);
+				} else {
+					throw new ParserException("Index out of range! [0..[" + data.length);
+				}
+		        curIndex += 2;
+		        if (length < 0x100) {
+		            throw new ParserException("Invalid TLV length encoding!");
+		        }
+		        if (containsAllData && 
+		        	data.length < length + curIndex) {
+		            throw new ParserException("Not enough data provided!");
+		        }
+		    } else if (temp == 0x83) {
+				if( (curIndex + 2)< data.length ) {
+			        length = ((data[curIndex] & 0xff) << 16)
+			                | ((data[curIndex + 1] & 0xff) << 8)
+			                | (data[curIndex + 2] & 0xff);
+				} else {
+					throw new ParserException("Index out of range! [0..[" + data.length);
+				}
+		        curIndex += 3;
+		        if (length < 0x10000) {
+		            throw new ParserException("Invalid TLV length encoding!");
+		        }
+		        if (containsAllData &&
+		        	data.length < length + curIndex) {
+		            throw new ParserException("Not enough data provided!");
+		        }
+		    } else {
+		        throw new ParserException("Unsupported TLV length encoding!");
+		    }
+		} else {
+			throw new ParserException("Index " + curIndex + " out of range! [0..[" + data.length);
+		}
+	    // create object
+	    return new BerTlv( data, tag, curIndex, length);
+    }
+
+	public void interpret() 
+		throws ParserException {
+		// has to be overwritten in derived classes.
+	}
+	
+	/**
+	 * Builds up the TLV into a byte stream.
+	 * 
+	 * Tags can be encoded as one or two bytes
+	 * 
+	 * @param stream
+	 * @throws DO_Exception
+	 */
+	public void build( ByteArrayOutputStream stream )
+		throws DO_Exception {
+		
+		// put tag into stream
+		if( mTag > 0xFF ){
+			stream.write(((mTag & 0x0000FF00)>>8));
+			stream.write((mTag & 0x000000FF));
+		} else {
+			stream.write((mTag & 0x000000FF));
+		}
+		
+		// write length
+		encodeLength( mValueLength, stream );
+		
+		// write value
+		if( mValueLength > 0 ){
+			stream.write(mRawData, mValueIndex, mValueLength);
+		}
+	}
+	
+    public int getTag(){
+    	return mTag;
+    }
+    
+
+    public int getValueIndex(){
+    	return mValueIndex;
+    }
+    
+
+	public byte[] getValue(){
+		// sanity checks
+		if( mRawData == null || 
+			mValueLength == 0 || 
+			mValueIndex < 0 || mValueIndex > mRawData.length ||
+			mValueIndex + mValueLength > mRawData.length )
+			return null;
+		
+    	byte[] data = new byte[mValueLength];
+    	
+    	System.arraycopy(mRawData, mValueIndex, data, 0, mValueLength);
+    	
+    	return data;
+    }
+	
+	protected byte[] getRawData(){
+		return mRawData;
+	}
+	
+	public int getValueLength() {
+        return mValueLength;
+	}
+	
+	/**
+	 * Encodes length according to ASN1.
+	 * Supported are length values up to 3 bytes -> 83 xx yy zz.
+	 * 
+	 * @param length
+	 * @param stream
+	 */
+	public static void encodeLength( int length, ByteArrayOutputStream stream){
+		
+	    if (length > 0x0000FFFF ) {
+	    	stream.write(0x83);
+			stream.write(((length & 0x00FF0000)>>16));
+			stream.write(((length & 0x0000FF00)>>8));
+			stream.write((length & 0x000000FF));
+	    } else if( length > 0x000000FF){
+	    	stream.write(0x82);
+			stream.write(((length & 0x0000FF00)>>8));
+			stream.write((length & 0x000000FF));
+	    } else if( length > 0x0000007F){
+	    	stream.write(0x81);
+			stream.write((length & 0x000000FF));
+	    } else {
+			stream.write((length & 0x000000FF));
+	    }
+	}
+	
+	@Override
+	public boolean equals(Object obj){
+		boolean equals = false;
+		
+		if( obj instanceof BerTlv ){
+			BerTlv berTlv = (BerTlv)obj;
+			
+			equals = this.mTag == berTlv.mTag;
+			
+			if(equals ){
+				byte[] test1 = this.getValue();
+				byte[] test2 = berTlv.getValue();
+				
+				if( test1 != null ){
+					//equals &= test1.equals(test2); 
+					equals &= Arrays.equals(test1, test2);
+				} else if( test1 == null && test2 == null ){
+					equals &= true;
+				}
+			}
+		}
+		return equals;
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/DO_Exception.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/DO_Exception.java
new file mode 100644
index 0000000..e374371
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/DO_Exception.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+public class DO_Exception extends Exception {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3917637590082486538L;
+
+	public DO_Exception() {
+		super();
+		// TODO Auto-generated constructor stub
+	}
+
+
+	public DO_Exception(String arg0, Throwable arg1) {
+		super(arg0, arg1);
+		// TODO Auto-generated constructor stub
+	}
+
+	public DO_Exception(String arg0) {
+		super(arg0);
+		// TODO Auto-generated constructor stub
+	}
+
+	public DO_Exception(Throwable arg0) {
+		super(arg0);
+		// TODO Auto-generated constructor stub
+	}
+
+	
+	
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Hash_REF_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Hash_REF_DO.java
new file mode 100644
index 0000000..2763765
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Hash_REF_DO.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * Hash-REF-DO:
+ * The Hash-REF-DO is used for retrieving and storing 
+ * the corresponding access rules for a device application 
+ * (which is identified by the hash value of its certificate) 
+ * from and to the ARA
+ * 
+ * 
+ *
+ */
+public class Hash_REF_DO extends BerTlv {
+	
+	public final static int _TAG = 0xC1;
+	public final static int _SHA1_LEN = 20;
+
+	private byte[] mHash = null;
+	
+	public Hash_REF_DO(byte[] rawData, int valueIndex, int valueLength){
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+
+	public Hash_REF_DO(byte[] hash){
+		super(hash, _TAG, 0, (hash == null ? 0 : hash.length));
+		mHash = hash;
+	}
+
+	public Hash_REF_DO(){
+		super(null, _TAG, 0, 0);
+		mHash = null;
+	}
+	
+	public byte[] getHash(){
+		return mHash;
+	}
+	
+	@Override
+	public String toString(){
+		StringBuilder b = new StringBuilder();
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		b.append("Hash_REF_DO: ");
+		try {
+			this.build(out);
+			b.append(BerTlv.toHex(out.toByteArray()));
+		} catch (Exception e ){
+			b.append(e.getLocalizedMessage());
+		}
+		return b.toString();
+	}
+	
+	/**
+	 * Tags: C1 
+	 * Length: 0 or _SHA1_LEN bytes
+	 *
+	 * Value:
+	 * Hash: identifies a specific device application
+	 * Empty: refers to all device applications
+	 * 
+	 * Length:
+	 * _SHA1_LEN for 20 bytes SHA-1 hash value
+	 * 0 for empty value field
+ 	 */
+	@Override
+	public void interpret() 
+		throws ParserException {
+		
+		mHash = null;
+	
+		byte[] data = getRawData();
+		int index = getValueIndex();
+	
+		// sanity checks
+		if( getValueLength() != 0 && getValueLength() != _SHA1_LEN ) {
+			throw new ParserException("Invalid value length for Hash-REF-DO!");
+		}
+
+		if( getValueLength() == _SHA1_LEN ) {
+			if( index + getValueLength() > data.length){
+				throw new ParserException( "Not enough data for Hash-REF-DO!");
+			}
+			
+			mHash = new byte[getValueLength()];
+			System.arraycopy(data, index, mHash, 0, getValueLength());
+		}
+	}
+	
+	/**
+	 * Tags: C1 
+	 * Length: 0 or 20 bytes
+	 *
+	 * Value:
+	 * Hash: identifies a specific device application
+	 * Empty: refers to all device applications
+	 * 
+	 * Length:
+	 * _SHA1_LEN for 20 bytes SHA-1 hash value
+	 * 0 for empty value field
+ 	 */
+	@Override
+	public void build( ByteArrayOutputStream stream) 
+		throws DO_Exception {
+
+		// sanity checks
+		if( mHash != null && 
+			!(mHash.length != _SHA1_LEN || mHash.length != 0) ) {
+			throw new DO_Exception("Hash value must be " + _SHA1_LEN + " bytes in length!");
+		}
+
+		stream.write(getTag());
+		
+		if( mHash == null ) {
+			stream.write(0x00);
+		} else {
+			try {
+				stream.write(mHash.length);
+				stream.write(mHash);
+			} catch( IOException ioe ){
+				throw new DO_Exception("Hash could not be written!");
+			}
+		}
+	}
+	
+	@Override
+	public boolean equals( Object obj ){
+		boolean equals = false;
+		
+		if( obj instanceof Hash_REF_DO ){
+			equals = super.equals(obj);
+			
+			if( equals ){
+				Hash_REF_DO hash_ref_do = (Hash_REF_DO)obj;
+				if( this.mHash == null && hash_ref_do.mHash == null )
+					equals &= true;
+				else {
+					if( this.mHash == null && hash_ref_do.mHash != null ){
+						equals &= (hash_ref_do.mHash.length == 0);
+					} else if( this.mHash != null && hash_ref_do.mHash == null ){
+						equals &= (this.mHash.length == 0);
+					} else {
+						//equals &= this.mHash.equals(hash_ref_do.mHash);
+						equals &= Arrays.equals(mHash, hash_ref_do.mHash);
+					}
+				}
+			}
+		}
+		return equals;
+	} 
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/NFC_AR_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/NFC_AR_DO.java
new file mode 100644
index 0000000..5476aba
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/NFC_AR_DO.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.io.ByteArrayOutputStream;
+
+
+/**
+ * NFC-AR-DO:
+ * In the NFC use case, mobile device application gather information 
+ * from their associated card application using the SE access API. 
+ * However, when the card application needs to trigger its associated mobile application, 
+ * it sends an HCI EVT_TRANSACTION according to ETSI TS 102 622 [102 622] over SWP to the device. 
+ * This event is handled by the NFC chipset stack which has to start 
+ * the corresponding device application. Disclosure of this event to malicious applications 
+ * can lead to phishing and denial of service attacks.
+ * To prevent this, it shall be possible to use the applications signature to authorize 
+ * device applications to receive HCI events issued by the secure element application.
+ * An NFC event data object defines an access rule for generating NFC events for 
+ * a specific terminal application. The NFC event access can be restricted by a rule based 
+ * on an event access is NEVER/ ALWAYS allowed policy. 
+ *
+ * 
+ *
+ */
+public class NFC_AR_DO extends BerTlv {
+	
+	public final static int _TAG = 0xD1;
+
+	private boolean mNfcAllowed = false;
+	
+	public NFC_AR_DO(byte[] rawData, int valueIndex, int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+	
+	public NFC_AR_DO( boolean allowed ){
+		super( null, _TAG, 0, 0);
+		mNfcAllowed = allowed;
+	}
+	
+	public boolean isNfcAllowed(){
+		return mNfcAllowed;
+	}
+
+	@Override
+	/**
+	 * Tag: D1
+	 * Length: 1
+	 * Value: 
+	 * Contains a NFC event access rule:
+	 * NEVER (00): NFC event access is not allowed
+	 * ALWAYS(01): NFC event access is allowed
+     *
+	 */
+	public void interpret() 
+		throws ParserException {
+
+		mNfcAllowed = false;
+		
+		byte[] data = getRawData();
+		int index = getValueIndex();
+		
+		if( index + getValueLength() > data.length){
+			throw new ParserException( "Not enough data for NFC_AR_DO!");
+		}
+		
+		if( getValueLength() != 1 ){
+			throw new ParserException( "Invalid length of NFC-AR-DO!" );
+		}
+		mNfcAllowed = (data[index] == 0x01);
+	}
+	
+	@Override
+	/**
+	 * Tag: D1
+	 * Length: 1
+	 * Value: 
+	 * Contains a NFC event access rule:
+	 * NEVER (00): NFC event access is not allowed
+	 * ALWAYS(01): NFC event access is allowed
+     *
+	 */
+	public void build( ByteArrayOutputStream stream )
+		throws DO_Exception {
+
+		// write tag
+		stream.write(getTag());
+		stream.write(0x01);
+		stream.write(mNfcAllowed ? 0x01 : 0x00 );
+	}
+	
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/ParserException.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/ParserException.java
new file mode 100644
index 0000000..a12313c
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/ParserException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+public class ParserException extends Exception {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3917637590082486538L;
+
+	public ParserException() {
+		super();
+		// TODO Auto-generated constructor stub
+	}
+
+
+	public ParserException(String arg0, Throwable arg1) {
+		super(arg0, arg1);
+		// TODO Auto-generated constructor stub
+	}
+
+	public ParserException(String arg0) {
+		super(arg0);
+		// TODO Auto-generated constructor stub
+	}
+
+	public ParserException(Throwable arg0) {
+		super(arg0);
+		// TODO Auto-generated constructor stub
+	}
+
+	
+	
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/REF_AR_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/REF_AR_DO.java
new file mode 100644
index 0000000..03555c6
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/REF_AR_DO.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+
+/**
+ * REF-AR_DO:
+ * The REF-AR-DO contains access rules inclusively its corresponding references 
+ * for the SE application (AID reference) and device application (hash reference). 
+ *  
+ * 
+ *
+ */
+public class REF_AR_DO extends BerTlv {
+	
+	public final static int _TAG = 0xE2;
+	
+	private REF_DO mRefDo = null;
+	private AR_DO mArDo = null;
+
+	public REF_AR_DO(byte[] rawData, int valueIndex, int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+
+	public REF_AR_DO() {
+		super(null, _TAG, 0, 0);
+	}
+	
+	public REF_AR_DO(REF_DO ref_do, AR_DO ar_do ) {
+		super(null, _TAG, 0, 0);
+		mRefDo = ref_do;
+		mArDo = ar_do;
+	}	
+	
+	public REF_DO getRefDo() {
+		return mRefDo;
+	}
+
+	public AR_DO getArDo() {
+		return mArDo;
+	}
+
+	/**
+	 * Interpret data.
+	 * 
+	 * Tags: E2 
+	 * Length: n
+	 *
+	 * Value:
+	 * REF-DO | AR-DO: A concatenation of an REF-DO and an AR-DO. 
+	 * The REF-DO must correspond to the succeeding AR-DO. 
+ 	 * 
+	 * Length:
+	 * n bytes.
+ 	 */
+	@Override
+	public void interpret() 
+		throws ParserException {
+
+		mRefDo = null;
+		mArDo = null;
+		
+		byte[] data = getRawData();
+		int index = getValueIndex();
+		
+		if( index + getValueLength() > data.length){
+			throw new ParserException( "Not enough data for AR_DO!");
+		}
+		
+		do {
+			BerTlv temp = BerTlv.decode(data, index);
+			if( temp.getTag() == REF_DO._TAG  ) { // REF-DO
+				mRefDo = new REF_DO( data, temp.getValueIndex(), temp.getValueLength());
+				mRefDo.interpret();
+			} else if( temp.getTag() == AR_DO._TAG ) { // AR-DO
+				mArDo = new AR_DO( data, temp.getValueIndex(), temp.getValueLength());
+				mArDo.interpret();
+			} else {
+				// uncomment following line if a more restrictive 
+				// behavior is necessary.
+				//throw new ParserException("Invalid DO in REF-AR-DO!");
+			}
+			index = temp.getValueIndex() + temp.getValueLength();
+		} while( getValueIndex() + getValueLength() > index );
+
+		// check for mandatory TLVs.
+		if( mRefDo == null ) {
+			throw new ParserException("Missing Ref-DO in REF-AR-DO!");
+		}
+		if( mArDo == null ) {
+			throw new ParserException("Missing AR-DO in REF-AR-DO!");
+		}
+	}
+	
+	
+	/**
+	 * Tag: E2 
+	 * Length: n
+	 * Value:
+	 * REF-DO | AR-DO: A concatenation of an REF-DO and an AR-DO. 
+	 */
+	@Override
+	public void build(ByteArrayOutputStream stream )
+		throws DO_Exception {
+		ByteArrayOutputStream temp = new ByteArrayOutputStream();
+		
+		if( mRefDo == null || mArDo == null ){
+			throw new DO_Exception( "REF-AR-DO: Required DO missing!");
+		}
+		stream.write(getTag());
+		
+		mRefDo.build(temp);
+		mArDo.build(temp);
+		
+		byte[] data = temp.toByteArray();
+		BerTlv.encodeLength(data.length, stream);
+		try {
+			stream.write(data);
+		} catch (IOException e) {
+			throw new DO_Exception("REF-AR-DO Memory IO problem! " +  e.getMessage());
+		}
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/REF_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/REF_DO.java
new file mode 100644
index 0000000..a31e539
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/REF_DO.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+
+/**
+ * REF-DO:
+ * The REF-DO contains a reference to uniquely assign 
+ * or identify an access rule for an SE application (with an AID reference) 
+ * and for a device application (with a hash reference).
+ *  
+ * 
+ *
+ */
+public class REF_DO extends BerTlv {
+	
+	public final static int _TAG = 0xE1;
+	
+	private AID_REF_DO mAidDo = null;
+	private Hash_REF_DO mHashDo = null;
+
+	public REF_DO(byte[] rawData, int valueIndex, int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+
+	public REF_DO(AID_REF_DO aid_ref_do, Hash_REF_DO hash_ref_do ) {
+		super(null, _TAG, 0, 0);
+		mAidDo = aid_ref_do;
+		mHashDo = hash_ref_do;
+	}
+	
+	@Override
+	public String toString(){
+		StringBuilder b = new StringBuilder();
+		b.append("REF_DO: ");
+		if( mAidDo != null ){
+			b.append(mAidDo.toString());
+			b.append(' ' );
+		}
+		if( mHashDo != null ){
+			b.append(mHashDo.toString());
+		}
+		return b.toString();
+	}
+	
+	
+	public AID_REF_DO getAidDo() {
+		return mAidDo;
+	}
+
+	public Hash_REF_DO getHashDo() {
+		return mHashDo;
+	}
+
+	/**
+	 * Interpret data.
+	 * 
+	 * Tags: E1 -> Length: n
+	 *
+	 * Value:
+	 * AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a Hash-REF-DO. 
+	 * 
+	 * Length:
+	 * n bytes.
+ 	 */
+	@Override
+	public void interpret() 
+		throws ParserException {
+		
+		mAidDo = null;
+		mHashDo = null;
+
+		byte[] data = getRawData();
+		int index = getValueIndex();
+		
+		if( index + getValueLength() > data.length){
+			throw new ParserException( "Not enough data for AR_DO!");
+		}
+		
+		do {
+			BerTlv temp = BerTlv.decode(data, index);
+		
+			if( temp.getTag() == AID_REF_DO._TAG || temp.getTag() == AID_REF_DO._TAG_DEFAULT_APPLICATION ) { // AID-REF-DO
+				mAidDo = new AID_REF_DO( data, temp.getTag(), temp.getValueIndex(), temp.getValueLength());
+				mAidDo.interpret();
+			} else if( temp.getTag() == Hash_REF_DO._TAG ) { // Hash-REF-DO
+				mHashDo = new Hash_REF_DO( data, temp.getValueIndex(), temp.getValueLength());
+				mHashDo.interpret();
+			} else {
+				// uncomment following line if a more restrictive 
+				// behaviour is necessary.
+				// throw new ParserException("Invalid DO in REF-DO!");
+			}
+		    index = temp.getValueIndex() + temp.getValueLength();
+		} while( getValueIndex() + getValueLength() > index );
+		   
+		// check if there is a AID-REF-DO
+		if( mAidDo == null ){
+			throw new ParserException("Missing AID-REF-DO in REF-DO!");
+		}
+		// check if there is a Hash-REF-DO
+		if( mHashDo == null ){
+			throw new ParserException("Missing Hash-REF-DO in REF-DO!");
+		}
+	}
+	
+	/**
+	 * Tag: E1 
+	 * Length: n
+	 * Value:
+	 * AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a Hash-REF-DO. 
+	 */
+	@Override
+	public void build(ByteArrayOutputStream stream )
+		throws DO_Exception {
+		ByteArrayOutputStream temp = new ByteArrayOutputStream();
+		
+		if( mAidDo == null || mHashDo == null ){
+			throw new DO_Exception( "REF-DO: Required DO missing!");
+		}
+		
+		mAidDo.build(temp);
+		mHashDo.build(temp);
+		
+		byte[] data = temp.toByteArray();
+		BerTlv tlv = new BerTlv( data, getTag(), 0, data.length );
+		tlv.build(stream);
+	}
+	
+	@Override 
+	public boolean equals(Object obj ){
+		boolean equals = false;
+		if( obj instanceof REF_DO ){
+			equals = super.equals(obj);
+			REF_DO ref_do = (REF_DO)obj;
+			if( mAidDo == null && ref_do.mAidDo == null ){
+				equals &= true;
+			} else if( mAidDo != null && ref_do.mAidDo != null ){
+				equals &= mAidDo.equals(ref_do.mAidDo);
+			} else {
+				equals = false;
+			}
+			if( mHashDo == null && ref_do.mHashDo == null ){
+				equals &= true;
+			} else if( mHashDo != null && ref_do.mHashDo != null ){
+				equals &= mHashDo.equals(ref_do.mHashDo);
+			} else {
+				equals = false;
+			}
+		}
+		return equals;
+	}
+	
+	@Override
+	public int hashCode () {
+		ByteArrayOutputStream stream = new ByteArrayOutputStream();
+		try {
+			this.build(stream);
+		} catch (DO_Exception e) {
+			return 1;
+		}
+		byte[] data = stream.toByteArray();
+		int hash = Arrays.hashCode(data);
+		//int hash = data.hashCode(); 
+		return hash;
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_ALL_AR_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_ALL_AR_DO.java
new file mode 100644
index 0000000..bcf10b8
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_ALL_AR_DO.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.util.ArrayList;
+
+/**
+ * Response-ALL-AR-DO
+ * All access rules stored in the Secure Element have to be returned by the ARA-M 
+ * after a GET DATA (All) command in the response data field within a Response-ALL-AR-DO. 
+ * The GET DATA command can also be applied iteratively with subsequent GET DATA (Next) commands 
+ * if the Response-ALL-AR-DO is too large for the GET DATA (All) command. 
+ * The length field of the Response-ALL-AR-DO shall always contain the full length 
+ * of the DOs value to determine on device side if a subsequent GET DATA (Next) command 
+ * is needed.
+ *
+ * 
+ *
+ */
+public class Response_ALL_AR_DO extends BerTlv {
+	
+	public final static int _TAG = 0xFF40;
+	
+	private ArrayList<REF_AR_DO> mRefArDos = new ArrayList<REF_AR_DO>();
+
+	public Response_ALL_AR_DO(byte[] rawData, int valueIndex,
+			int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+	
+	public ArrayList<REF_AR_DO> getRefArDos(){
+		return mRefArDos;
+	}
+
+	@Override
+	/**
+	 * Tag: FF 40
+	 * 
+	 * Length: n or 0
+	 * If n is equal to zero, then there are no rules to fetch.
+	 * 
+	 * Value: 
+	 * REF-AR-DO 1..n or empty
+	 * An REF-AR-DO if access rules exist. 
+	 * REF-AR-DOs can occur several times in a concatenated DO chain if several REF-AR-DO exist 
+	 * on the SE. 
+	 * The value is empty if access rules do not exist.
+	 */
+	public void interpret() 
+		throws ParserException {
+
+		mRefArDos.clear();
+	
+		byte[] data = getRawData();
+		int index = getValueIndex();
+		
+		if( getValueLength() == 0 ){
+			// No Access rule available for the requested reference.
+			return;
+		}
+		
+		if( index + getValueLength() > data.length){
+			throw new ParserException( "Not enough data for Response_AR_DO!");
+		}
+		
+		BerTlv temp;
+		int currentPos = index;
+		int endPos = index + getValueLength();
+		do {
+			temp = BerTlv.decode(data, currentPos);
+			
+			REF_AR_DO tempRefArDo;
+			
+			if( temp.getTag() == REF_AR_DO._TAG) { // REF-AR-DO tag
+				tempRefArDo = new REF_AR_DO( data, temp.getValueIndex(), temp.getValueLength());
+				tempRefArDo.interpret();
+				mRefArDos.add(tempRefArDo);
+			} else {
+				// uncomment following line if a more restrictive 
+				// behavior is necessary.
+				//throw new ParserException("Invalid DO in Response-ALL-AR-DO!");
+			}
+			// get REF-AR-DOs as long as data is available.
+			currentPos = temp.getValueIndex() + temp.getValueLength();
+		} while( currentPos < endPos );  
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_ARAC_AID_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_ARAC_AID_DO.java
new file mode 100644
index 0000000..ed6b248
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_ARAC_AID_DO.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+import java.util.ArrayList;
+
+
+
+/**
+ * Response_ARAC_AID_DO
+ * 
+ * A list of AIDs containing an AID for each ARA-C.
+ * 
+ * In response to STORE DATA (Command-Get-ClientAIDs-DO), 
+ * the ARA-M shall return the AID of each of the ARA-Cs 
+ * currently registered within a Response-ARAC-AID-DO.
+ *
+ * 
+ *
+ */
+public class Response_ARAC_AID_DO extends BerTlv {
+	
+	public final static int _TAG = 0xFF70;
+	
+	private ArrayList<AID_REF_DO> mAidDos = new ArrayList<AID_REF_DO>();
+
+	public Response_ARAC_AID_DO(byte[] rawData, int valueIndex,
+			int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+	
+	public ArrayList<AID_REF_DO> getAidRefDos(){
+		return mAidDos;
+	}
+
+	@Override
+	/**
+	 * Tag: FF 70
+	 * 
+	 * Length: n or 0
+	 * If n is equal to zero, then there are no rules to fetch.
+	 * 
+	 * Value: 
+	 * AID-REF-DO 1..n or empty
+	 * AID-REF-DOs can occur several times in a concatenated DO chain if several ARA-C instances exist 
+	 * on the SE. 
+	 * The value is empty if no ARA-C instance exist.
+	 */
+	public void interpret() 
+		throws ParserException {
+
+		mAidDos.clear();
+	
+		byte[] data = getRawData();
+		int index = getValueIndex();
+		
+		if( getValueLength() == 0 ){
+			// No Access rule available for the requested reference.
+			return;
+		}
+		
+		if( index + getValueLength() > data.length){
+			throw new ParserException( "Not enough data for Response_ARAC_AID_DO!");
+		}
+		
+		BerTlv temp;
+		int currentPos = index;
+		int endPos = index + getValueLength();
+		do {
+			temp = BerTlv.decode(data, currentPos);
+			
+			AID_REF_DO tempAidDo;
+			
+			if( temp.getTag() == AID_REF_DO._TAG || 
+					temp.getTag() == AID_REF_DO._TAG_DEFAULT_APPLICATION ) { 
+				tempAidDo = new AID_REF_DO( data, temp.getTag(), temp.getValueIndex(), temp.getValueLength());
+				tempAidDo.interpret();
+				mAidDos.add(tempAidDo);
+			} else {
+				// uncomment following line if a more restrictive 
+				// behavior is necessary.
+				//throw new ParserException("Invalid DO in Response_ARAC_AID_DO!");
+			}
+			// get AID-REF-DOs as long as data is available.
+			currentPos = temp.getValueIndex() + temp.getValueLength();
+		} while( currentPos < endPos );  
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_AR_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_AR_DO.java
new file mode 100644
index 0000000..51483e9
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_AR_DO.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+
+
+/**
+ * Response-AR-DO
+ * If access rules can be found in the Secure Element which corresponds to the specified AR-DO 
+ * in the GET DATA (Specific) command these must be returned by the ARA-M 
+ * in the response data field within a Response-AR-DO. 
+ * The GET DATA command can also be applied iteratively with subsequent GET DATA (next) commands 
+ * if the Response-AR-DO is too large for the GET DATA (Specific) command. 
+ * The length field of the Response-AR-DO shall always contain the full length of the DOs value 
+ * to determine on device side if a subsequent GET DATA (Next) command is needed.
+ *
+ * 
+ *
+ */
+public class Response_AR_DO extends BerTlv {
+	
+	public final static int _TAG = 0xFF50;
+	
+	private AR_DO mArDo = null;
+
+	public Response_AR_DO(byte[] rawData, int valueIndex,
+			int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+	}
+	
+	public AR_DO getArDo(){
+		return mArDo;
+	}
+
+	@Override
+	/**
+	 * Tag: FF 50
+	 * 
+	 * Length: n or 0
+	 * If n is equal to zero, then there are no rules to fetch.
+	 * 
+	 * Value: 
+	 * An AR-DO if the referenced access rules exist. 
+	 * The value is empty if access rules do not exist to the defined reference
+	 */
+	public void interpret() 
+		throws ParserException {
+	
+		byte[] data = getRawData();
+		int index = getValueIndex();
+		
+		if( getValueLength() == 0 ){
+			// No Access rule available for the requested reference.
+			return;
+		}
+		
+		if( index + getValueLength() > data.length){
+			throw new ParserException( "Not enough data for Response_AR_DO!");
+		}
+		
+		int currentPos = index;
+		int endPos = index + getValueLength();
+		do {
+			BerTlv temp = BerTlv.decode(data, currentPos);
+			
+			if( temp.getTag() == AR_DO._TAG) { // AR-DO tag
+				mArDo = new AR_DO( data, temp.getValueIndex(), temp.getValueLength());
+				mArDo.interpret();
+			} else {
+				// un-comment following line if a more restrictive 
+				// behavior is necessary.
+				//throw new ParserException("Invalid DO in Response-AR-DO!");
+			}
+			// get REF-AR-DOs as long as data is available.
+			currentPos = temp.getValueIndex() + temp.getValueLength();
+		} while( currentPos < endPos );  
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_DO_Factory.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_DO_Factory.java
new file mode 100644
index 0000000..2816790
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_DO_Factory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+
+
+public class Response_DO_Factory {
+
+	public static BerTlv createDO( byte[] data ) throws ParserException{
+		
+		BerTlv tempTlv = BerTlv.decode(data, 0 );
+		
+		BerTlv retTlv = null;
+		
+		switch( tempTlv.getTag() ){
+		
+		case Response_RefreshTag_DO._TAG:
+			retTlv = new Response_RefreshTag_DO( data, tempTlv.getValueIndex(), tempTlv.getValueLength());
+			break;
+		case Response_ARAC_AID_DO._TAG:
+			retTlv = new Response_ARAC_AID_DO( data, tempTlv.getValueIndex(), tempTlv.getValueLength());
+			break;
+		
+		case Response_ALL_AR_DO._TAG:
+			retTlv = new Response_ALL_AR_DO( data, tempTlv.getValueIndex(), tempTlv.getValueLength());
+			break;
+		case Response_AR_DO._TAG:
+			retTlv = new Response_AR_DO( data, tempTlv.getValueIndex(), tempTlv.getValueLength());
+			break;
+		default:
+			retTlv = tempTlv;
+		}
+		
+		retTlv.interpret();
+		
+		return retTlv;
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_RefreshTag_DO.java b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_RefreshTag_DO.java
new file mode 100644
index 0000000..5ee7b85
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/security/gpac/dataobjects/Response_RefreshTag_DO.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012 Giesecke & Devrient GmbH.
+ *
+ * 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.
+ */
+package org.simalliance.openmobileapi.service.security.gpac.dataobjects;
+
+
+/**
+ * Response-RefreshTag DO
+ * The GET DATA (RefreshTag) command has to return a refresh tag indicating changes 
+ * in the access control data in a RefreshTag DO. 
+ * This refresh tag is an attribute (8-byte random number) of the ARA-M which is 
+ * newly generated if the ARA-M detects an update of access control data 
+ * in the Secure Element.
+ *
+ * 
+ *
+ */
+public class Response_RefreshTag_DO extends BerTlv {
+
+	public final static int _TAG = 0xDF20;
+	
+	private long mRefreshTag;
+	private byte[] mRefreshTagArray = null;
+	
+	public Response_RefreshTag_DO(byte[] rawData, int valueIndex,
+			int valueLength) {
+		super(rawData, _TAG, valueIndex, valueLength);
+
+	}
+	
+	public long getRefreshTag(){
+		return mRefreshTag;
+	}
+	
+	public byte[] getRefreshTagArray(){
+		return mRefreshTagArray;
+	}
+
+	@Override
+	/**
+	 * Tag: DF 20
+	 * Length: 8 bytes
+	 * Value:
+	 * The RefreshTag is an 8 bytes random number. 
+	 * A new RefreshTag value indicates changes in the access control data 
+	 * stored in the SE.
+	 */
+	public void interpret() 
+		throws ParserException {
+
+		mRefreshTag = 0;
+		
+		if( super.getValueLength() != 8 ){
+			throw new ParserException( "Invalid length of RefreshTag DO!" );
+		}
+		
+		byte[] data = super.getRawData();
+		int index = super.getValueIndex();
+		
+		if( index + super.getValueLength() > data.length ){
+			throw new ParserException( "Not enough data for RefreshTag DO!" );
+		}
+		mRefreshTagArray = new byte[super.getValueLength()];
+		System.arraycopy(data, index, mRefreshTagArray, 0, mRefreshTagArray.length);
+		
+		long temp;
+		temp = data[index++];
+		mRefreshTag =(temp << 56L);
+		temp = data[index++];
+		mRefreshTag +=(temp << 48L);
+		temp = data[index++];
+		mRefreshTag +=(temp << 40L);
+		temp = data[index++];
+		mRefreshTag +=(temp << 32L);
+		temp = data[index++];
+		mRefreshTag +=(temp << 24L);
+		temp = data[index++];
+		mRefreshTag +=(temp << 16L);
+		temp = data[index++];
+		mRefreshTag +=(temp <<  8L);
+		temp = data[index++];
+		mRefreshTag +=(temp);
+	}
+}
diff --git a/src/org/simalliance/openmobileapi/service/terminals/ASSDTerminal.java b/src/org/simalliance/openmobileapi/service/terminals/ASSDTerminal.java
new file mode 100644
index 0000000..fc0b638
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/terminals/ASSDTerminal.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.terminals;
+
+import android.content.Context;
+
+
+import java.util.MissingResourceException;
+import java.util.NoSuchElementException;
+
+import org.simalliance.openmobileapi.service.CardException;
+import org.simalliance.openmobileapi.service.SmartcardService;
+import org.simalliance.openmobileapi.service.Terminal;
+
+final class ASSDTerminal extends Terminal {
+
+    private static boolean JNILoaded = false;
+
+    public ASSDTerminal(Context context) {
+        super(SmartcardService._SD_TERMINAL + " - Secure SD Card", context);
+    }
+
+    @Override
+    protected void internalConnect() throws CardException {
+        if (JNILoaded == false) {
+            throw new CardException("JNI failed");
+        }
+
+        try {
+            if (Open() == false) {
+                throw new CardException("open SE failed");
+            }
+        } catch (Exception e) {
+            throw new CardException("open SE failed");
+        }
+        mDefaultApplicationSelectedOnBasicChannel = true;
+        mIsConnected = true;
+    }
+
+    @Override
+    protected void internalDisconnect() throws CardException {
+        if (JNILoaded == false) {
+            throw new CardException("JNI failed");
+        }
+
+        try {
+            Close();
+        } catch (Exception e) {
+        } finally {
+            mIsConnected = false;
+        }
+    }
+
+    public boolean isCardPresent() throws CardException {
+        if (JNILoaded == false) {
+            return false;
+        }
+
+        try {
+            return IsPresent();
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    @Override
+    protected byte[] internalTransmit(byte[] command) throws CardException {
+        if (JNILoaded == false) {
+            throw new CardException("JNI failed");
+        }
+
+        try {
+            byte[] response = Transmit(command);
+            if (response == null) {
+                throw new CardException("transmit failed");
+            }
+            return response;
+        } catch (Exception e) {
+            throw new CardException("transmit failed");
+        }
+    }
+
+    @Override
+    protected void internalCloseLogicalChannel(int channelNumber) throws CardException {
+        if (channelNumber > 0) {
+            byte cla = (byte) channelNumber;
+            if (channelNumber > 3) {
+                cla |= 0x40;
+            }
+            byte[] manageChannelClose = new byte[] {
+                    cla, 0x70, (byte) 0x80, (byte) channelNumber
+            };
+            transmit(manageChannelClose, 2, 0x9000, 0xFFFF, "MANAGE CHANNEL");
+        }
+    }
+
+    @Override
+    protected int internalOpenLogicalChannel() throws Exception {
+    	
+    	mSelectResponse = null;
+        byte[] manageChannelCommand = new byte[] {
+                0x00, 0x70, 0x00, 0x00, 0x01
+        };
+        byte[] rsp = transmit(manageChannelCommand, 3, 0x9000, 0xFFFF, "MANAGE CHANNEL");
+        if (rsp.length != 3) {
+            throw new MissingResourceException("unsupported MANAGE CHANNEL response data", "", "");
+        }
+        int channelNumber = rsp[0] & 0xFF;
+        if (channelNumber == 0 || channelNumber > 19) {
+            throw new MissingResourceException("invalid logical channel number returned", "", "");
+        }
+
+        return channelNumber;
+    }
+
+    @Override
+    protected int internalOpenLogicalChannel(byte[] aid) throws Exception {
+        int channelNumber = internalOpenLogicalChannel();
+
+        if (aid == null) {
+            throw new NullPointerException("aid must not be null");
+        }
+        mSelectResponse = null;
+        byte[] selectCommand = new byte[aid.length + 6];
+        selectCommand[0] = (byte) channelNumber;
+        if (channelNumber > 3) {
+            selectCommand[0] |= 0x40;
+        }
+        selectCommand[1] = (byte) 0xA4;
+        selectCommand[2] = 0x04;
+        selectCommand[4] = (byte) aid.length;
+        System.arraycopy(aid, 0, selectCommand, 5, aid.length);
+        try {
+        	mSelectResponse = transmit(selectCommand, 2, 0x9000, 0xFFFF, "SELECT");
+        } catch (CardException e) {
+            internalCloseLogicalChannel(channelNumber);
+            throw new NoSuchElementException(e.getMessage());
+        }
+
+        return channelNumber;
+    }
+
+    static {
+        try {
+            Runtime.getRuntime().loadLibrary("assd");
+            JNILoaded = true;
+        } catch (Throwable t) {
+        }
+    }
+
+    private native void Close() throws Exception;
+
+    private native boolean Open() throws Exception;
+
+    private native boolean IsPresent() throws Exception;
+
+    private native byte[] Transmit(byte[] command) throws Exception;
+}
diff --git a/src/org/simalliance/openmobileapi/service/terminals/SmartMxTerminal.java b/src/org/simalliance/openmobileapi/service/terminals/SmartMxTerminal.java
new file mode 100644
index 0000000..782581d
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/terminals/SmartMxTerminal.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.terminals;
+
+import android.content.Context;
+import android.nfc.INfcAdapterExtras;
+import android.nfc.NfcAdapter;
+import android.os.Binder;
+import android.os.Bundle;
+import android.util.Log;
+
+
+import java.util.MissingResourceException;
+import java.util.NoSuchElementException;
+
+import org.simalliance.openmobileapi.service.CardException;
+import org.simalliance.openmobileapi.service.SmartcardService;
+import org.simalliance.openmobileapi.service.Terminal;
+
+
+public class SmartMxTerminal extends Terminal {
+
+    private INfcAdapterExtras ex;
+	private Binder binder = new Binder();
+	
+    public SmartMxTerminal(Context context) {
+        super(SmartcardService._eSE_TERMINAL + " - SmartMX", context);
+    }
+
+    public boolean isCardPresent() throws CardException {
+    	try {
+            NfcAdapter adapter =  NfcAdapter.getDefaultAdapter(mContext);
+            if(adapter == null) {
+                throw new CardException("Cannot get NFC Default Adapter");
+            }
+            return adapter.isEnabled();
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    @Override
+    protected void internalConnect() throws CardException {
+        NfcAdapter adapter =  NfcAdapter.getDefaultAdapter(mContext);
+        if(adapter == null) {
+            throw new CardException("Cannot get NFC Default Adapter");
+        }
+        
+        ex = adapter.getNfcAdapterExtrasInterface();
+        if(ex == null)  {
+            throw new CardException("Cannot get NFC Extra interface");
+        }
+
+		try {
+            Bundle b = ex.open("org.simalliance.openmobileapi.service", binder);
+            if (b == null) {
+                throw new CardException("open SE failed");
+            }
+        } catch (Exception e) {
+            throw new CardException("open SE failed");
+        }
+        mDefaultApplicationSelectedOnBasicChannel = true;
+        mIsConnected = true;
+    }
+
+    @Override
+    protected void internalDisconnect() throws CardException {
+        try {
+            Bundle b = ex.close("org.simalliance.openmobileapi.service", binder);
+            if (b == null) {
+                throw new CardException("close SE failed");
+            }
+        } catch (Exception e) {
+            throw new CardException("close SE failed");
+        }
+    }
+
+    @Override
+    protected byte[] internalTransmit(byte[] command) throws CardException {
+        try {
+            Bundle b = ex.transceive("org.simalliance.openmobileapi.service", command);
+            if (b == null) {
+                throw new CardException("exchange APDU failed");
+            }
+            return b.getByteArray("out");
+        } catch (Exception e) {
+            throw new CardException("exchange APDU failed");
+        }
+    }
+
+    @Override
+    protected int internalOpenLogicalChannel() throws Exception {
+
+    	mSelectResponse = null;
+        byte[] manageChannelCommand = new byte[] {
+                0x00, 0x70, 0x00, 0x00, 0x01
+        };
+        byte[] rsp = transmit(manageChannelCommand, 2, 0x9000, 0, "MANAGE CHANNEL");
+        if ((rsp.length == 2) && ((rsp[0] == (byte) 0x68) && (rsp[1] == (byte) 0x81))) {
+            throw new NoSuchElementException("logical channels not supported");
+        }
+        if (rsp.length == 2 && (rsp[0] == (byte) 0x6A && rsp[1] == (byte) 0x81)) {
+            throw new MissingResourceException("no free channel available", "", "");
+        }
+        if (rsp.length != 3) {
+            throw new MissingResourceException("unsupported MANAGE CHANNEL response data", "", "");
+        }
+        int channelNumber = rsp[0] & 0xFF;
+        if (channelNumber == 0 || channelNumber > 19) {
+            throw new MissingResourceException("invalid logical channel number returned", "", "");
+        }
+
+        return channelNumber;
+    }
+
+    @Override
+    protected int internalOpenLogicalChannel(byte[] aid) throws Exception {
+
+        if (aid == null) {
+            throw new NullPointerException("aid must not be null");
+        }
+    	mSelectResponse = null;
+
+        byte[] manageChannelCommand = new byte[] {
+                0x00, 0x70, 0x00, 0x00, 0x01
+        };
+        byte[] rsp = transmit(manageChannelCommand, 2, 0x9000, 0, "MANAGE CHANNEL");
+        if ((rsp.length == 2) && ((rsp[0] == (byte) 0x68) && (rsp[1] == (byte) 0x81))) {
+            throw new NoSuchElementException("logical channels not supported");
+        }
+        if (rsp.length == 2 && (rsp[0] == (byte) 0x6A && rsp[1] == (byte) 0x81)) {
+            throw new MissingResourceException("no free channel available", "", "");
+        }
+        if (rsp.length != 3) {
+            throw new MissingResourceException("unsupported MANAGE CHANNEL response data", "", "");
+        }
+        int channelNumber = rsp[0] & 0xFF;
+        if (channelNumber == 0 || channelNumber > 19) {
+            throw new MissingResourceException("invalid logical channel number returned", "", "");
+        }
+
+        byte[] selectCommand = new byte[aid.length + 6];
+        selectCommand[0] = (byte) channelNumber;
+        if (channelNumber > 3) {
+            selectCommand[0] |= 0x40;
+        }
+        selectCommand[1] = (byte) 0xA4;
+        selectCommand[2] = 0x04;
+        selectCommand[4] = (byte) aid.length;
+        System.arraycopy(aid, 0, selectCommand, 5, aid.length);
+        try {
+        	mSelectResponse = transmit(selectCommand, 2, 0x9000, 0xFFFF, "SELECT");
+        } catch (CardException exp) {
+            internalCloseLogicalChannel(channelNumber);
+            throw new NoSuchElementException(exp.getMessage());
+        }
+
+        return channelNumber;
+    }
+
+    @Override
+    protected void internalCloseLogicalChannel(int channelNumber) throws CardException {
+        if (channelNumber > 0) {
+            byte cla = (byte) channelNumber;
+            if (channelNumber > 3) {
+                cla |= 0x40;
+            }
+            byte[] manageChannelClose = new byte[] {
+                    cla, 0x70, (byte) 0x80, (byte) channelNumber
+            };
+            transmit(manageChannelClose, 2, 0x9000, 0xFFFF, "MANAGE CHANNEL");
+        }
+    }
+}
diff --git a/src/org/simalliance/openmobileapi/service/terminals/UiccTerminal.java b/src/org/simalliance/openmobileapi/service/terminals/UiccTerminal.java
new file mode 100644
index 0000000..d28bc87
--- /dev/null
+++ b/src/org/simalliance/openmobileapi/service/terminals/UiccTerminal.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package org.simalliance.openmobileapi.service.terminals;
+
+import android.content.Context;
+import org.simalliance.openmobileapi.service.CardException;
+import org.simalliance.openmobileapi.service.SmartcardService;
+import org.simalliance.openmobileapi.service.Terminal;
+import org.simalliance.openmobileapi.service.Util;
+import org.simalliance.openmobileapi.service.security.arf.SecureElementException;
+
+
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.TelephonyProperties;
+
+import java.util.MissingResourceException;
+import java.util.NoSuchElementException;
+
+public class UiccTerminal extends Terminal {
+
+	private ITelephony manager = null;
+
+	private int[] channelId = new int[20];
+	
+	private String currentSelectedFilePath = "";
+	
+	public UiccTerminal(Context context) {
+		super(SmartcardService._UICC_TERMINAL + " - UICC", context);
+
+		try {
+			manager = ITelephony.Stub.asInterface(ServiceManager
+					.getService(Context.TELEPHONY_SERVICE));
+		} catch (Exception ex) {
+		}
+
+		for (int i = 0; i < channelId.length; i++)
+			channelId[i] = 0;
+	}
+
+	public boolean isCardPresent() throws CardException {
+		String prop = SystemProperties
+				.get(TelephonyProperties.PROPERTY_SIM_STATE);
+		if ("READY".equals(prop)) {
+			return true;
+		}
+		return false;
+	}
+
+	@Override
+	protected void internalConnect() throws CardException {
+		if (manager == null) {
+			throw new CardException("Cannot connect to Telephony Service");
+		}
+		mIsConnected = true;
+	}
+
+	@Override
+	protected void internalDisconnect() throws CardException {
+	}
+
+	private byte[] StringToByteArray(String s) {
+		byte[] b = new byte[s.length() / 2];
+		for (int i = 0; i < b.length; i++) {
+			b[i] = (byte) Integer.parseInt(s.substring(2 * i, 2 * i + 2), 16);
+		}
+		return b;
+	}
+
+	private String ByteArrayToString(byte[] b, int start) {
+		StringBuffer s = new StringBuffer();
+		for (int i = start; i < b.length; i++) {
+			s.append(Integer.toHexString(0x100 + (b[i] & 0xff)).substring(1));
+		}
+		return s.toString();
+	}
+
+    /**
+     * Clear the channel number
+     *
+     * @param cla
+     *
+     * @return the cla without channel number
+     */
+    private byte clearChannelNumber(byte cla) {
+        // bit 7 determines which standard is used
+        boolean isFirstInterindustryClassByteCoding = ((cla & 0x40) == 0x00);
+
+        if(isFirstInterindustryClassByteCoding){
+            // First Interindustry Class Byte Coding
+            // see 11.1.4.1: channel number is encoded in the 2 rightmost bits
+            return (byte)(cla & 0xFC);
+        }else{
+            // Further Interindustry Class Byte Coding
+            // see 11.1.4.2: channel number is encoded in the 4 rightmost bits
+            return (byte)(cla & 0xF0);
+        }
+    }
+	
+	@Override
+	protected byte[] internalTransmit(byte[] command) throws CardException {
+		int cla = clearChannelNumber(command[0]) & 0xff;
+		int ins = command[1] & 0xff;
+		int p1 = command[2] & 0xff;
+		int p2 = command[3] & 0xff;
+		int p3 = -1;
+		if (command.length > 4) {
+			p3 = command[4] & 0xff;
+		}
+		String data = null;
+		if (command.length > 5) {
+			data = ByteArrayToString(command, 5);
+		}
+
+		int channelNumber = parseChannelNumber(command[0]);
+
+		if (channelNumber == 0) {
+			
+			try {
+				String response = manager.transmitIccBasicChannel(cla, ins, p1,
+						p2, p3, data);
+				return StringToByteArray(response);
+			} catch (Exception ex) {
+				throw new CardException("transmit command failed");
+			}
+			
+		} else {
+			if ((channelNumber > 0) && (channelId[channelNumber] == 0)) {
+				throw new CardException("channel not open");
+			}
+
+			try {
+				String response = manager.transmitIccLogicalChannel(cla, ins, channelId[channelNumber], p1, p2, p3, data);
+				return StringToByteArray(response);
+			} catch (Exception ex) {
+				throw new CardException("transmit command failed");
+			}
+		}
+	}
+
+    /**
+     * Exchanges APDU (SELECT, READ/WRITE) to the 
+     * given EF by File ID and file path via iccIO.
+     * 
+     * The given command is checked and might be rejected.
+     * 
+     * @param fileID
+     * @param filePath
+     * @param cmd
+     * @return
+     */
+	@Override
+    public byte[] simIOExchange(int fileID,String filePath,byte[] cmd)
+    		throws Exception {
+        try {
+            int ins = 0;
+            int p1=cmd[2] & 0xff;
+            int p2=cmd[3] & 0xff;
+            int p3=cmd[4] & 0xff;
+            switch(cmd[1]) {
+                case (byte)0xB0: ins=176; break;
+                case (byte)0xB2: ins=178; break;
+        	    case (byte)0xA4: ins=192;  p1=0; p2=0; p3=15; break;
+                default:  
+                    throw new SecureElementException("Unknown SIM_IO command");
+            } 
+
+	        if(filePath != null && filePath.length() > 0) {
+	            currentSelectedFilePath = filePath;
+	        }
+	
+	        byte[] ret = manager.transmitIccSimIO(fileID, ins, p1, p2, p3, currentSelectedFilePath);
+ 
+            return ret;
+        } catch (Exception e) {
+            throw new Exception("SIM IO access error");
+    }}
+	
+	
+	/**
+	 * Extracts the channel number from a CLA byte. Specified in GlobalPlatform
+	 * Card Specification 2.2.0.7: 11.1.4 Class Byte Coding
+	 * 
+	 * @param cla
+	 *            the command's CLA byte
+	 * @return the channel number within [0x00..0x0F]
+	 */
+	private int parseChannelNumber(byte cla) {
+		// bit 7 determines which standard is used
+		boolean isFirstInterindustryClassByteCoding = ((cla & 0x40) == 0x00);
+		
+		if(isFirstInterindustryClassByteCoding){
+			// First Interindustry Class Byte Coding
+			// see 11.1.4.1: channel number is encoded in the 2 rightmost bits
+			return cla & 0x03;
+		}else{
+			// Further Interindustry Class Byte Coding
+			// see 11.1.4.2: channel number is encoded in the 4 rightmost bits
+			return (cla & 0x0F) + 4;
+		}
+	}
+
+	@Override
+	protected int internalOpenLogicalChannel() throws Exception {
+
+		mSelectResponse = null;
+		throw new UnsupportedOperationException(
+				"open channel without select AID is not supported by UICC");
+	}
+
+	@Override
+	protected int internalOpenLogicalChannel(byte[] aid) throws Exception {
+
+		if (aid == null) {
+			throw new NullPointerException("aid must not be null");
+		}
+		mSelectResponse = null;
+		for (int i = 1; i < channelId.length; i++)
+			if (channelId[i] == 0) {
+				channelId[i] = manager.openIccLogicalChannel(ByteArrayToString(
+						aid, 0));
+
+				if (!(channelId[i] > 0)) { // channelId[i] == 0
+					channelId[i] = 0;
+					int lastError = manager.getLastError();
+
+					if (lastError == 2) {
+						throw new MissingResourceException(
+								"all channels are used", "", "");
+					}
+					if (lastError == 3) {
+						throw new NoSuchElementException("applet not found");
+					}
+					throw new CardException("open channel failed");
+				}
+				return i;
+			}
+		throw new MissingResourceException("out of channels", "","");
+	}
+
+	@Override
+	protected void internalCloseLogicalChannel(int channelNumber)
+			throws CardException {
+		if (channelNumber == 0) {
+			return;
+		}
+		if (channelId[channelNumber] == 0) {
+			throw new CardException("channel not open");
+		}
+		try {
+			if (manager.closeIccLogicalChannel(channelId[channelNumber]) == false) {
+				throw new CardException("close channel failed");
+			}
+		} catch (Exception ex) {
+			throw new CardException("close channel failed");
+		}
+		channelId[channelNumber] = 0;
+	}
+}