summaryrefslogtreecommitdiff
path: root/omapi
diff options
context:
space:
mode:
Diffstat (limited to 'omapi')
-rw-r--r--omapi/OWNERS5
-rw-r--r--omapi/aidl/Android.bp32
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl46
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl40
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl44
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl45
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl48
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementChannel.aidl84
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementListener.aidl28
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementReader.aidl58
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementService.aidl58
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementSession.aidl77
-rw-r--r--omapi/aidl/vts/functional/AccessControlApp/Android.bp54
-rw-r--r--omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp428
-rw-r--r--omapi/aidl/vts/functional/config/Android.bp26
-rw-r--r--omapi/aidl/vts/functional/config/omapi_uuid_map_config.xsd40
-rw-r--r--omapi/aidl/vts/functional/config/schema/current.txt30
-rw-r--r--omapi/aidl/vts/functional/config/schema/last_current.txt0
-rw-r--r--omapi/aidl/vts/functional/config/schema/last_removed.txt0
-rw-r--r--omapi/aidl/vts/functional/config/schema/removed.txt1
-rw-r--r--omapi/aidl/vts/functional/omapi/Android.bp60
-rw-r--r--omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp641
-rw-r--r--omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.xml35
-rw-r--r--omapi/java/Android.bp17
-rw-r--r--omapi/java/android/se/OWNERS5
-rw-r--r--omapi/java/android/se/omapi/Channel.java257
-rw-r--r--omapi/java/android/se/omapi/OWNERS5
-rw-r--r--omapi/java/android/se/omapi/Reader.java178
-rw-r--r--omapi/java/android/se/omapi/SEService.java327
-rw-r--r--omapi/java/android/se/omapi/Session.java361
30 files changed, 3030 insertions, 0 deletions
diff --git a/omapi/OWNERS b/omapi/OWNERS
new file mode 100644
index 000000000000..5682fd3281f4
--- /dev/null
+++ b/omapi/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 456592
+
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
new file mode 100644
index 000000000000..d80317bb8c60
--- /dev/null
+++ b/omapi/aidl/Android.bp
@@ -0,0 +1,32 @@
+// Copyright 2020, 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+ name: "android.se.omapi",
+ vendor_available: true,
+ srcs: ["android/se/omapi/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl
new file mode 100644
index 000000000000..725013a35cde
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementChannel {
+ void close();
+ boolean isClosed();
+ boolean isBasicChannel();
+ byte[] getSelectResponse();
+ byte[] transmit(in byte[] command);
+ boolean selectNext();
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl
new file mode 100644
index 000000000000..77e1c53f47ac
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementListener {
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl
new file mode 100644
index 000000000000..2b10c473c902
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementReader {
+ boolean isSecureElementPresent();
+ android.se.omapi.ISecureElementSession openSession();
+ void closeSessions();
+ boolean reset();
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl
new file mode 100644
index 000000000000..0c8e431d18a1
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017, 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.
+ *//*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementService {
+ String[] getReaders();
+ android.se.omapi.ISecureElementReader getReader(in String reader);
+ boolean[] isNfcEventAllowed(in String reader, in byte[] aid, in String[] packageNames, in int userId);
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl
new file mode 100644
index 000000000000..06287c551f5c
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017, 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.
+ *//*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementSession {
+ byte[] getAtr();
+ void close();
+ void closeChannels();
+ boolean isClosed();
+ android.se.omapi.ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener);
+ android.se.omapi.ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener);
+}
diff --git a/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl
new file mode 100644
index 000000000000..bbd3c148caaf
--- /dev/null
+++ b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017, 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 android.se.omapi;
+
+import android.se.omapi.ISecureElementSession;
+
+/** @hide */
+@VintfStability
+interface ISecureElementChannel {
+
+ /**
+ * Closes the specified connection and frees internal resources.
+ * A logical channel will be closed.
+ */
+ void close();
+
+ /**
+ * 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();
+
+ /**
+ * 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.
+ *
+ * @param command Command APDU, its structure is defined in ISO/IEC 7816-4
+ * in Standard byte format
+ */
+ byte[] transmit(in byte[] command);
+
+ /**
+ * 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();
+}
diff --git a/omapi/aidl/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl
new file mode 100644
index 000000000000..479dcd7d5acf
--- /dev/null
+++ b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017, 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 android.se.omapi;
+
+/**
+ * Interface to receive call-backs when the service is connected.
+ * @hide
+ */
+@VintfStability
+interface ISecureElementListener {
+}
diff --git a/omapi/aidl/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl
new file mode 100644
index 000000000000..a6979face61f
--- /dev/null
+++ b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017, 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 android.se.omapi;
+
+import android.se.omapi.ISecureElementSession;
+
+/** @hide */
+@VintfStability
+interface ISecureElementReader {
+
+ /**
+ * 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();
+
+ /**
+ * 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 it is 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.
+ */
+ ISecureElementSession openSession();
+
+ /**
+ * Close all the sessions opened on this reader. All the channels opened by
+ * all these sessions will be closed.
+ */
+ void closeSessions();
+
+ /**
+ * Closes all the sessions opened on this reader and resets the reader.
+ * All the channels opened by all these sessions will be closed.
+ * @return true if the reset is successful, false otherwise.
+ */
+ boolean reset();
+}
diff --git a/omapi/aidl/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/android/se/omapi/ISecureElementService.aidl
new file mode 100644
index 000000000000..13707ec22b5c
--- /dev/null
+++ b/omapi/aidl/android/se/omapi/ISecureElementService.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementReader;
+
+/**
+ * SecureElement service interface.
+ * @hide
+ */
+@VintfStability
+interface ISecureElementService {
+
+ /**
+ * Returns the friendly names of available Secure Element readers.
+ * <ul>
+ * <li>If the reader is a SIM reader, then its name must be "SIM[Slot]".</li>
+ * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
+ * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
+ * </ul>
+ * Slot is a decimal number without leading zeros. The Numbering must start with 1
+ * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
+ */
+ String[] getReaders();
+
+ /**
+ * Returns SecureElement Service reader object to the given name.
+ */
+ ISecureElementReader getReader(in String reader);
+
+ /**
+ * Checks if the application defined by the package name is allowed to
+ * receive NFC transaction events for the defined AID.
+ */
+ boolean[] isNfcEventAllowed(in String reader, in byte[] aid,
+ in String[] packageNames, in int userId);
+
+}
diff --git a/omapi/aidl/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl
new file mode 100644
index 000000000000..129ecc4ddaa3
--- /dev/null
+++ b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementChannel;
+import android.se.omapi.ISecureElementReader;
+import android.se.omapi.ISecureElementListener;
+
+/** @hide */
+@VintfStability
+interface ISecureElementSession {
+
+ /**
+ * 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();
+
+ /**
+ * Close any channel opened on this session.
+ */
+ void closeChannels();
+
+ /**
+ * 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. Selects the specified
+ * applet if aid != null.
+ * Logical channels cannot be opened with this connection.
+ * Use interface method openLogicalChannel() to open a logical channel.
+ * Listener is passed to secure element service and used to monitor whether
+ * the client application that uses OMAPI is still alive or not.
+ */
+ ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2,
+ in ISecureElementListener listener);
+
+ /**
+ * 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.
+ * Listener is passed to secure element service and used to monitor whether
+ * the client application that uses OMAPI is still alive or not.
+ */
+ ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2,
+ in ISecureElementListener listener);
+}
diff --git a/omapi/aidl/vts/functional/AccessControlApp/Android.bp b/omapi/aidl/vts/functional/AccessControlApp/Android.bp
new file mode 100644
index 000000000000..f03c3f6eb647
--- /dev/null
+++ b/omapi/aidl/vts/functional/AccessControlApp/Android.bp
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2021 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "VtsHalOmapiSeAccessControlTestCases",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalOmapiSeAccessControlTestCases.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libnativehelper",
+ "libutils",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "VtsHalHidlTargetTestBase",
+ "android.se.omapi-V1-ndk",
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Werror",
+ ],
+ require_root: true,
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp
new file mode 100644
index 000000000000..9ea65431417a
--- /dev/null
+++ b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2021 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 <aidl/android/se/omapi/BnSecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementChannel.h>
+#include <aidl/android/se/omapi/ISecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementReader.h>
+#include <aidl/android/se/omapi/ISecureElementService.h>
+#include <aidl/android/se/omapi/ISecureElementSession.h>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/String16.h>
+
+using namespace std;
+using namespace ::testing;
+using namespace android;
+
+int main(int argc, char** argv) {
+ InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ return status;
+}
+
+namespace {
+
+class OMAPISEAccessControlTest : public TestWithParam<std::string> {
+ protected:
+
+ class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {};
+
+ /**
+ * Verifies TLV data
+ *
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ bool verifyBerTlvData(std::vector<uint8_t> tlv) {
+ if (tlv.size() == 0) {
+ LOG(ERROR) << "Invalid tlv, null";
+ return false;
+ }
+ int i = 0;
+ if ((tlv[i++] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ i++;
+ }
+
+ int len = tlv[i++] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength; j > 0; j--) {
+ len += (len << 8) + (tlv[i++] & 0xFF);
+ }
+ }
+ // Additional 2 bytes for the SW
+ return (tlv.size() == (i + len + 2));
+ }
+
+ void testSelectableAid(
+ std::vector<std::vector<uint8_t>> authorizedAids) {
+ for (auto aid : authorizedAids) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+ }
+ }
+ }
+ }
+
+ void testUnauthorisedAid(
+ std::vector<std::vector<uint8_t>> unAuthorizedAids) {
+ for (auto aid : unAuthorizedAids) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (!res.isOk()) {
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ }
+ }
+ }
+ }
+
+ void testTransmitAPDU(
+ std::vector<uint8_t> aid,
+ std::vector<std::vector<uint8_t>> apdus) {
+ for (auto apdu : apdus) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+ bool status = false;
+ std::vector<uint8_t> selectResponse = {};
+ std::vector<uint8_t> transmitResponse = {};
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+
+ res = channel->transmit(apdu, &transmitResponse);
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ ASSERT_TRUE(res.isOk()) << "failed to transmit";
+ }
+ }
+ }
+ }
+
+ void testUnauthorisedAPDU(
+ std::vector<uint8_t> aid,
+ std::vector<std::vector<uint8_t>> apdus) {
+ for (auto apdu : apdus) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+ bool status = false;
+ std::vector<uint8_t> selectResponse = {};
+ std::vector<uint8_t> transmitResponse = {};
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+
+ res = channel->transmit(apdu, &transmitResponse);
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ if (!res.isOk()) {
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ }
+ }
+ }
+ }
+
+ bool supportOMAPIReaders() {
+ return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str()));
+ }
+
+ void getFirstApiLevel(int32_t* outApiLevel) {
+ int32_t firstApiLevel = property_get_int32(FEATURE_SE_API_LEVEL.c_str(), -1);
+ if (firstApiLevel < 0) {
+ firstApiLevel = property_get_int32(FEATURE_SE_SDK_VERSION.c_str(), -1);
+ }
+ ASSERT_GT(firstApiLevel, 0); // first_api_level must exist
+ *outApiLevel = firstApiLevel;
+ return;
+ }
+
+ bool supportsHardware() {
+ bool lowRamDevice = property_get_bool(FEATURE_SE_LOW_RAM.c_str(), true);
+ return !lowRamDevice || deviceSupportsFeature(FEATURE_SE_HARDWARE_WATCH.c_str()) ||
+ deviceSupportsFeature(FEATURE_SE_OMAPI_SERVICE.c_str()); // android.se.omapi
+ }
+
+ void SetUp() override {
+ ASSERT_TRUE(supportsHardware());
+ int32_t apiLevel;
+ getFirstApiLevel(&apiLevel);
+ ASSERT_TRUE(apiLevel > 27);
+ ASSERT_TRUE(supportOMAPIReaders());
+ LOG(INFO) << "get OMAPI service with name:" << GetParam();
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str()));
+ mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder);
+ ASSERT_TRUE(mOmapiSeService);
+
+ std::vector<std::string> readers = {};
+
+ if (mOmapiSeService != NULL) {
+ auto status = mOmapiSeService->getReaders(&readers);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ for (auto readerName : readers) {
+ // Filter eSE readers only
+ if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader;
+ status = mOmapiSeService->getReader(readerName, &reader);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ mVSReaders[readerName] = reader;
+ }
+ }
+ }
+ }
+
+ void TearDown() override {
+ if (mOmapiSeService != nullptr) {
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ reader->closeSessions();
+ }
+ }
+ }
+ }
+
+ static inline std::string const ESE_READER_PREFIX = "eSE";
+ static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+ static inline std::string const FEATURE_SE_LOW_RAM = "ro.config.low_ram";
+ static inline std::string const FEATURE_SE_HARDWARE_WATCH = "android.hardware.type.watch";
+ static inline std::string const FEATURE_SE_OMAPI_SERVICE = "com.android.se";
+ static inline std::string const FEATURE_SE_SDK_VERSION = "ro.build.version.sdk";
+ static inline std::string const FEATURE_SE_API_LEVEL = "ro.product.first_api_level";
+
+ std::vector<uint8_t> AID_40 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x40};
+ std::vector<uint8_t> AID_41 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x41};
+ std::vector<uint8_t> AID_42 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x42};
+ std::vector<uint8_t> AID_43 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x43};
+ std::vector<uint8_t> AID_44 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x44};
+ std::vector<uint8_t> AID_45 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x45};
+ std::vector<uint8_t> AID_46 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x46};
+ std::vector<uint8_t> AID_47 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x47};
+ std::vector<uint8_t> AID_48 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x48};
+ std::vector<uint8_t> AID_49 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x49};
+ std::vector<uint8_t> AID_4A = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4A};
+ std::vector<uint8_t> AID_4B = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4B};
+ std::vector<uint8_t> AID_4C = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4C};
+ std::vector<uint8_t> AID_4D = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4D};
+ std::vector<uint8_t> AID_4E = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4E};
+ std::vector<uint8_t> AID_4F = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4F};
+
+ std::vector<std::vector<uint8_t>> AUTHORIZED_AID = {AID_40, AID_41, AID_42, AID_44, AID_45,
+ AID_47, AID_48, AID_49, AID_4A, AID_4B,
+ AID_4C, AID_4D, AID_4E, AID_4F};
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_AID = {AID_43, AID_46};
+
+ /* Authorized APDU for AID_40 */
+ std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_40 = {
+ {0x00, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ };
+ /* Unauthorized APDU for AID_40 */
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_40 = {
+ {0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x06, 0x00, 0x00, 0x00},
+ };
+
+ /* Authorized APDU for AID_41 */
+ std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_41 = {
+ {0x94, 0x06, 0x00, 0x00},
+ {0x94, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}};
+ /* Unauthorized APDU for AID_41 */
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_41 = {
+ {0x00, 0x06, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ {0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x08, 0x00, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ };
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService;
+
+ std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>>
+ mVSReaders = {};
+};
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAID) {
+ testSelectableAid(AUTHORIZED_AID);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorizedAID) {
+ testUnauthorisedAid(UNAUTHORIZED_AID);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID40) {
+ testTransmitAPDU(AID_40, AUTHORIZED_APDU_AID_40);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID40) {
+ testUnauthorisedAPDU(AID_40, UNAUTHORIZED_APDU_AID_40);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID41) {
+ testTransmitAPDU(AID_41, AUTHORIZED_APDU_AID_41);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID41) {
+ testUnauthorisedAPDU(AID_41, UNAUTHORIZED_APDU_AID_41);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEAccessControlTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(
+ aidl::android::se::omapi::ISecureElementService::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEAccessControlTest);
+
+} // namespace
diff --git a/omapi/aidl/vts/functional/config/Android.bp b/omapi/aidl/vts/functional/config/Android.bp
new file mode 100644
index 000000000000..7c08257bf828
--- /dev/null
+++ b/omapi/aidl/vts/functional/config/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2022 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+xsd_config {
+ name: "omapi_uuid_map_config",
+ srcs: ["omapi_uuid_map_config.xsd"],
+ api_dir: "schema",
+ package_name: "omapi.uuid.map.config",
+}
diff --git a/omapi/aidl/vts/functional/config/omapi_uuid_map_config.xsd b/omapi/aidl/vts/functional/config/omapi_uuid_map_config.xsd
new file mode 100644
index 000000000000..ffeb7a032c38
--- /dev/null
+++ b/omapi/aidl/vts/functional/config/omapi_uuid_map_config.xsd
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<xs:schema version="2.0"
+ attributeFormDefault="unqualified"
+ elementFormDefault="qualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="ref_do">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="uuid_ref_do" maxOccurs="unbounded" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="uids">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element type="xs:short" name="uid" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element type="xs:string" name="uuid"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/omapi/aidl/vts/functional/config/schema/current.txt b/omapi/aidl/vts/functional/config/schema/current.txt
new file mode 100644
index 000000000000..c2e930b949d9
--- /dev/null
+++ b/omapi/aidl/vts/functional/config/schema/current.txt
@@ -0,0 +1,30 @@
+// Signature format: 2.0
+package omapi.uuid.map.config {
+
+ public class RefDo {
+ ctor public RefDo();
+ method public java.util.List<omapi.uuid.map.config.RefDo.UuidRefDo> getUuid_ref_do();
+ }
+
+ public static class RefDo.UuidRefDo {
+ ctor public RefDo.UuidRefDo();
+ method public omapi.uuid.map.config.RefDo.UuidRefDo.Uids getUids();
+ method public String getUuid();
+ method public void setUids(omapi.uuid.map.config.RefDo.UuidRefDo.Uids);
+ method public void setUuid(String);
+ }
+
+ public static class RefDo.UuidRefDo.Uids {
+ ctor public RefDo.UuidRefDo.Uids();
+ method public java.util.List<java.lang.Short> getUid();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static omapi.uuid.map.config.RefDo read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
diff --git a/omapi/aidl/vts/functional/config/schema/last_current.txt b/omapi/aidl/vts/functional/config/schema/last_current.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/omapi/aidl/vts/functional/config/schema/last_current.txt
diff --git a/omapi/aidl/vts/functional/config/schema/last_removed.txt b/omapi/aidl/vts/functional/config/schema/last_removed.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/omapi/aidl/vts/functional/config/schema/last_removed.txt
diff --git a/omapi/aidl/vts/functional/config/schema/removed.txt b/omapi/aidl/vts/functional/config/schema/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/omapi/aidl/vts/functional/config/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/omapi/aidl/vts/functional/omapi/Android.bp b/omapi/aidl/vts/functional/omapi/Android.bp
new file mode 100644
index 000000000000..c41479f9e9cf
--- /dev/null
+++ b/omapi/aidl/vts/functional/omapi/Android.bp
@@ -0,0 +1,60 @@
+//
+// Copyright (C) 2021 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "VtsHalOmapiSeServiceV1_TargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalOmapiSeServiceV1_TargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libnativehelper",
+ "libutils",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "VtsHalHidlTargetTestBase",
+ "android.se.omapi-V1-ndk",
+ "android.hardware.audio.common.test.utility",
+ "libxml2",
+ ],
+ data: [
+ ":omapi_uuid_map_config",
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Werror",
+ ],
+ require_root: true,
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ test_config: "VtsHalOmapiSeServiceV1_TargetTest.xml",
+}
diff --git a/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp
new file mode 100644
index 000000000000..5303651b4b22
--- /dev/null
+++ b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2021 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 <aidl/android/se/omapi/BnSecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementChannel.h>
+#include <aidl/android/se/omapi/ISecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementReader.h>
+#include <aidl/android/se/omapi/ISecureElementService.h>
+#include <aidl/android/se/omapi/ISecureElementSession.h>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/String16.h>
+#include "utility/ValidateXml.h"
+
+using namespace std;
+using namespace ::testing;
+using namespace android;
+
+int main(int argc, char** argv) {
+ InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ return status;
+}
+
+namespace {
+
+class OMAPISEServiceHalTest : public TestWithParam<std::string> {
+ protected:
+ class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {};
+
+ void testSelectableAid(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> aid, std::vector<uint8_t>& selectResponse) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ }
+
+ void testNonSelectableAid(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> aid) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ LOG(ERROR) << res.getMessage();
+ ASSERT_FALSE(res.isOk()) << "expected to fail to open channel for this test";
+ }
+
+ /**
+ * Verifies TLV data
+ *
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ bool verifyBerTlvData(std::vector<uint8_t> tlv) {
+ if (tlv.size() == 0) {
+ LOG(ERROR) << "Invalid tlv, null";
+ return false;
+ }
+ int i = 0;
+ if ((tlv[i++] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ i++;
+ }
+
+ int len = tlv[i++] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength; j > 0; j--) {
+ len += (len << 8) + (tlv[i++] & 0xFF);
+ }
+ }
+ // Additional 2 bytes for the SW
+ return (tlv.size() == (i + len + 2));
+ }
+
+ void internalTransmitApdu(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+ std::vector<uint8_t> selectResponse = {};
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ res = channel->transmit(apdu, &transmitResponse);
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+ ASSERT_TRUE(res.isOk()) << "failed to transmit";
+ }
+
+ bool supportOMAPIReaders() {
+ return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str()));
+ }
+
+ std::optional<std::string> getUuidMappingFile() {
+ char value[PROPERTY_VALUE_MAX] = {0};
+ int len = property_get("ro.boot.product.hardware.sku", value, "config");
+ std::string uuidMappingConfigFile = UUID_MAPPING_CONFIG_PREFIX
+ + std::string(value, len)
+ + UUID_MAPPING_CONFIG_EXT;
+ std::string uuidMapConfigPath;
+ // Search in predefined folders
+ for (auto path : UUID_MAPPING_CONFIG_PATHS) {
+ uuidMapConfigPath = path + uuidMappingConfigFile;
+ auto confFile = fopen(uuidMapConfigPath.c_str(), "r");
+ if (confFile) {
+ fclose(confFile);
+ return uuidMapConfigPath;
+ }
+ }
+ return std::optional<std::string>();
+ }
+
+ void SetUp() override {
+ LOG(INFO) << "get OMAPI service with name:" << GetParam();
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str()));
+ mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder);
+ ASSERT_TRUE(mOmapiSeService);
+
+ std::vector<std::string> readers = {};
+
+ if (omapiSecureService() != NULL) {
+ auto status = omapiSecureService()->getReaders(&readers);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ for (auto readerName : readers) {
+ // Filter eSE readers only
+ if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader;
+ status = omapiSecureService()->getReader(readerName, &reader);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ mVSReaders[readerName] = reader;
+ }
+ }
+ }
+ }
+
+ void TearDown() override {
+ if (mOmapiSeService != nullptr) {
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ reader->closeSessions();
+ }
+ }
+ }
+ }
+
+ bool isDebuggableBuild() {
+ char value[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.system.build.type", value, "");
+ if (strcmp(value, "userdebug") == 0) {
+ return true;
+ }
+ if (strcmp(value, "eng") == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> omapiSecureService() {
+ return mOmapiSeService;
+ }
+
+ static inline std::string const ESE_READER_PREFIX = "eSE";
+ static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+
+ std::vector<uint8_t> SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31};
+ std::vector<uint8_t> LONG_SELECT_RESPONSE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41,
+ 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64,
+ 0x43, 0x54, 0x53, 0x32};
+ std::vector<uint8_t> NON_SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0xFF};
+
+ std::vector<std::vector<uint8_t>> ILLEGAL_COMMANDS_TRANSMIT = {
+ {0x00, 0x70, 0x00, 0x00},
+ {0x00, 0x70, 0x80, 0x00},
+ {0x00, 0xA4, 0x04, 0x04, 0x10, 0x4A, 0x53, 0x52, 0x31, 0x37, 0x37,
+ 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x30}};
+
+ /* OMAPI APDU Test case 1 and 3 */
+ std::vector<std::vector<uint8_t>> NO_DATA_APDU = {{0x00, 0x06, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ {0x94, 0x06, 0x00, 0x00},
+ {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}};
+
+ /* OMAPI APDU Test case 2 and 4 */
+ std::vector<std::vector<uint8_t>> DATA_APDU = {{0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x80, 0x08, 0x00, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}};
+
+ /* Case 2 APDU command expects the P2 received in the SELECT command as 1-byte outgoing data */
+ std::vector<uint8_t> CHECK_SELECT_P2_APDU = {0x00, 0xF4, 0x00, 0x00, 0x00};
+
+ /* OMAPI APDU Test case 1 and 3 */
+ std::vector<std::vector<uint8_t>> SW_62xx_NO_DATA_APDU = {{0x00, 0xF3, 0x00, 0x06},
+ {0x00, 0xF3, 0x00, 0x0A, 0x01, 0xAA}};
+
+ /* OMAPI APDU Test case 2 and 4 */
+ std::vector<uint8_t> SW_62xx_DATA_APDU = {0x00, 0xF3, 0x00, 0x08, 0x00};
+ std::vector<uint8_t> SW_62xx_VALIDATE_DATA_APDU = {0x00, 0xF3, 0x00, 0x0C, 0x01, 0xAA, 0x00};
+ std::vector<std::vector<uint8_t>> SW_62xx = {
+ {0x62, 0x00}, {0x62, 0x81}, {0x62, 0x82}, {0x62, 0x83}, {0x62, 0x85}, {0x62, 0xF1},
+ {0x62, 0xF2}, {0x63, 0xF1}, {0x63, 0xF2}, {0x63, 0xC2}, {0x62, 0x02}, {0x62, 0x80},
+ {0x62, 0x84}, {0x62, 0x86}, {0x63, 0x00}, {0x63, 0x81}};
+
+ std::vector<std::vector<uint8_t>> SEGMENTED_RESP_APDU = {
+ // Get response Case2 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC2, 0x08, 0x00, 0x00},
+ // Get response Case4 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC4, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00},
+ // Get response Case2 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC6, 0x08, 0x00, 0x00},
+ // Get response Case4 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC8, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00},
+ // Test device buffer capacity 7FFF data
+ {0x00, 0xC2, 0x7F, 0xFF, 0x00},
+ // Get response 6CFF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xCF, 0x08, 0x00, 0x00},
+ // Get response with another CLA with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x94, 0xC2, 0x08, 0x00, 0x00}};
+ long SERVICE_CONNECTION_TIME_OUT = 3000;
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService;
+
+ std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>>
+ mVSReaders = {};
+
+ std::string UUID_MAPPING_CONFIG_PREFIX = "hal_uuid_map_";
+ std::string UUID_MAPPING_CONFIG_EXT = ".xml";
+ std::string UUID_MAPPING_CONFIG_PATHS[3] = {"/odm/etc/", "/vendor/etc/", "/etc/"};
+};
+
+/** Tests getReaders API */
+TEST_P(OMAPISEServiceHalTest, TestGetReaders) {
+ std::vector<std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> eseReaders =
+ {};
+
+ for (const auto& [name, reader] : mVSReaders) {
+ bool status = false;
+ LOG(INFO) << "Name of the reader: " << name;
+
+ if (reader) {
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ }
+ ASSERT_TRUE(status);
+
+ if (name.find(ESE_READER_PREFIX) == std::string::npos) {
+ LOG(ERROR) << "Incorrect Reader name";
+ FAIL();
+ }
+
+ if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ eseReaders.push_back(reader);
+ } else {
+ LOG(INFO) << "Reader not supported: " << name;
+ FAIL();
+ }
+ }
+
+ if (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())) {
+ ASSERT_GE(eseReaders.size(), 1);
+ } else {
+ ASSERT_TRUE(eseReaders.size() == 0);
+ }
+}
+
+/** Tests OpenBasicChannel API when aid is null */
+TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNullAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ std::vector<uint8_t> aid = {};
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ bool result = false;
+
+ auto status = reader->openSession(&session);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+ if (!session) {
+ LOG(ERROR) << "Could not open session";
+ FAIL();
+ }
+
+ status = session->openBasicChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (channel != nullptr) {
+ status = channel->isBasicChannel(&result);
+ ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened";
+ }
+ }
+ }
+}
+
+/** Tests OpenBasicChannel API when aid is provided */
+TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNonNullAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ bool result = false;
+
+ auto status = reader->openSession(&session);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+ if (!session) {
+ LOG(ERROR) << "Could not open session";
+ FAIL();
+ }
+
+ status = session->openBasicChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (channel != nullptr) {
+ status = channel->isBasicChannel(&result);
+ ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened";
+ }
+ }
+ }
+}
+
+/** Tests Select API */
+TEST_P(OMAPISEServiceHalTest, TestSelectableAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ testSelectableAid(reader, SELECTABLE_AID, selectResponse);
+ }
+ }
+}
+
+/** Tests Select API */
+TEST_P(OMAPISEServiceHalTest, TestLongSelectResponse) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ testSelectableAid(reader, LONG_SELECT_RESPONSE_AID, selectResponse);
+ ASSERT_TRUE(verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+ }
+ }
+}
+
+/** Test to fail open channel with wrong aid */
+TEST_P(OMAPISEServiceHalTest, TestWrongAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ testNonSelectableAid(reader, NON_SELECTABLE_AID);
+ }
+ }
+}
+
+/** Tests with invalid cmds in Transmit */
+TEST_P(OMAPISEServiceHalTest, TestSecurityExceptionInTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+ std::vector<uint8_t> selectResponse = {};
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+
+ for (auto cmd : ILLEGAL_COMMANDS_TRANSMIT) {
+ std::vector<uint8_t> response = {};
+ res = channel->transmit(cmd, &response);
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ }
+ }
+}
+
+/**
+ * Tests Transmit API for all readers.
+ *
+ * Checks the return status and verifies the size of the
+ * response.
+ */
+TEST_P(OMAPISEServiceHalTest, TestTransmitApdu) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : NO_DATA_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ ASSERT_GE(response.size(), 2);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ }
+
+ for (auto apdu : DATA_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ /* 256 byte data and 2 bytes of status word */
+ ASSERT_GE(response.size(), 258);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ }
+ }
+ }
+}
+
+/**
+ * Tests if underlying implementations returns the correct Status Word
+ *
+ * TO verify that :
+ * - the device does not modify the APDU sent to the Secure Element
+ * - the warning code is properly received by the application layer as SW answer
+ * - the verify that the application layer can fetch the additionnal data (when present)
+ */
+TEST_P(OMAPISEServiceHalTest, testStatusWordTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : SW_62xx_NO_DATA_APDU) {
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_GE(response.size(), 2);
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+ }
+
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ std::vector<uint8_t> apdu = SW_62xx_DATA_APDU;
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_GE(response.size(), 3);
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ std::vector<uint8_t> apdu = SW_62xx_VALIDATE_DATA_APDU;
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ ASSERT_GE(response.size(), apdu.size() + 2);
+ std::vector<uint8_t> responseSubstring((response.begin() + 0),
+ (response.begin() + apdu.size()));
+ // We should not care about which channel number is actually assigned.
+ responseSubstring[0] = apdu[0];
+ ASSERT_TRUE((responseSubstring == apdu));
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+ }
+ }
+}
+
+/** Test if the responses are segmented by the underlying implementation */
+TEST_P(OMAPISEServiceHalTest, TestSegmentedResponseTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : SEGMENTED_RESP_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ int expectedLength = (0x00 << 24) | (0x00 << 16) | (apdu[2] << 8) | apdu[3];
+ ASSERT_EQ(response.size(), (expectedLength + 2));
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ ASSERT_EQ((response[response.size() - 3] & 0xFF), (0xFF));
+ }
+ }
+ }
+}
+
+/**
+ * Tests the P2 value of the select command.
+ *
+ * Verifies that the default P2 value (0x00) is not modified by the underlying implementation.
+ */
+TEST_P(OMAPISEServiceHalTest, TestP2Value) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, CHECK_SELECT_P2_APDU, response);
+ ASSERT_GE(response.size(), 3);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ ASSERT_EQ((response[response.size() - 3] & 0xFF), (0x00));
+ }
+ }
+}
+
+TEST_P(OMAPISEServiceHalTest, TestUuidMappingConfig) {
+ constexpr const char* xsd = "/data/local/tmp/omapi_uuid_map_config.xsd";
+ auto uuidMappingFile = getUuidMappingFile();
+ ASSERT_TRUE(uuidMappingFile.has_value()) << "Unable to determine UUID mapping config file path";
+ LOG(INFO) << "UUID Mapping config file: " << uuidMappingFile.value();
+ EXPECT_VALID_XML(uuidMappingFile->c_str(), xsd);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEServiceHalTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(
+ aidl::android::se::omapi::ISecureElementService::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEServiceHalTest);
+
+} // namespace
diff --git a/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.xml b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.xml
new file mode 100644
index 000000000000..3ee0414711f3
--- /dev/null
+++ b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Runs VtsHalOmapiSeServiceV1_TargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push"
+ value="omapi_uuid_map_config.xsd->/data/local/tmp/omapi_uuid_map_config.xsd" />
+ <option name="push"
+ value="VtsHalOmapiSeServiceV1_TargetTest->/data/local/tmp/VtsHalOmapiSeServiceV1_TargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalOmapiSeServiceV1_TargetTest" />
+ </test>
+</configuration>
diff --git a/omapi/java/Android.bp b/omapi/java/Android.bp
new file mode 100644
index 000000000000..8d38da048d9b
--- /dev/null
+++ b/omapi/java/Android.bp
@@ -0,0 +1,17 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "framework-omapi-sources",
+ srcs: [
+ "**/*.java",
+ "**/*.aidl",
+ ],
+ visibility: ["//frameworks/base"],
+}
diff --git a/omapi/java/android/se/OWNERS b/omapi/java/android/se/OWNERS
new file mode 100644
index 000000000000..5682fd3281f4
--- /dev/null
+++ b/omapi/java/android/se/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 456592
+
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/omapi/java/android/se/omapi/Channel.java b/omapi/java/android/se/omapi/Channel.java
new file mode 100644
index 000000000000..90ce11ae0313
--- /dev/null
+++ b/omapi/java/android/se/omapi/Channel.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent an ISO/IEC 7816-4 channel opened to a
+ * Secure Element. It can be either a logical channel or the basic 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://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public final class Channel implements java.nio.channels.Channel {
+
+ private static final String TAG = "OMAPI.Channel";
+ private Session mSession;
+ private final ISecureElementChannel mChannel;
+ private final SEService mService;
+ private final Object mLock = new Object();
+
+ Channel(@NonNull SEService service, @NonNull Session session,
+ @NonNull ISecureElementChannel channel) {
+ if (service == null || session == null || channel == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ 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 (isOpen()) {
+ synchronized (mLock) {
+ try {
+ mChannel.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Error closing channel", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Tells if this channel is open.
+ *
+ * @return <code>false</code> if the channel is closed or in case of an error.
+ * <code>true</code> otherwise.
+ */
+ public boolean isOpen() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return false;
+ }
+ try {
+ return !mChannel.isClosed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in isClosed()");
+ return false;
+ }
+ }
+
+ /**
+ * 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.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ return mChannel.isBasicChannel();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Transmit an APDU command (as per ISO/IEC 7816-4) to the Secure Element. The
+ * underlying layers generate as many TPDUs as necessary to transport this APDU. The
+ * API shall ensure that all available data returned from Secure Element, including
+ * concatenated responses, are retrieved and made available to the calling application. If a
+ * warning status code is received the API wont check for further response data but will
+ * return all data received so far and the warning status code.<br>
+ * The transport part is invisible from the application. The generated response is the
+ * response of the APDU which means that all protocols related responses are handled
+ * inside the API or the underlying implementation.<br>
+ * The transmit method shall support extended length APDU commands independently of
+ * the coding within the ATR.<br>
+ * For status word '61 XX' the API or underlying implementation shall issue a GET
+ * RESPONSE command as specified by ISO 7816-4 standard with LE=XX; for the status
+ * word '6C XX', the API or underlying implementation shall reissue the input command
+ * with LE=XX. For other status words, the API (or underlying implementation) shall return
+ * the complete response including data and status word to the device application. The API
+ * (or underlying implementation) shall not handle internally the received status words. The
+ * channel shall not be closed even if the Secure Element answered with an error code.
+ * 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. The entire APDU communication to this SE is
+ * locked to the APDU.<br>
+ * The channel information in the class byte in the APDU will be ignored. The system will
+ * add any required information to ensure the APDU is transported on this channel.
+ * The only restrictions on the set of commands that can be sent is defined below, the API
+ * implementation shall be able to send all other commands: <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. 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;]
+ *
+ * @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 Lc byte is inconsistent with length of the byte array.
+ * @throws IllegalArgumentException if CLA byte is invalid according to [2] (0xff).
+ * @throws IllegalArgumentException if INS byte is invalid according to [2] (0x6x or 0x9x).
+ * @throws SecurityException if the command is filtered by the security policy.
+ * @throws NullPointerException if command is NULL.
+ */
+ public @NonNull byte[] transmit(@NonNull byte[] command) throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ synchronized (mLock) {
+ try {
+ byte[] response = mChannel.transmit(command);
+ if (response == null) {
+ throw new IOException("Error in communicating with Secure Element");
+ }
+ return response;
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Get the session that has opened this channel.
+ *
+ * @return the session object this channel is bound to.
+ */
+ public @NonNull Session getSession() {
+ return mSession;
+ }
+
+ /**
+ * Returns the data as received from the application select command inclusively the status word
+ * received at applet selection.
+ * 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 @Nullable byte[] getSelectResponse() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+
+ byte[] response;
+ try {
+ response = mChannel.getSelectResponse();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(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. <br>
+ *
+ * Since the API cannot distinguish between a partial and full AID the API shall rely on the
+ * response of the Secure Element for the return value of this method. <br>
+ * The implementation of the underlying SELECT command within this method shall use
+ * the same values as the corresponding openBasicChannel(byte[] aid) or
+ * openLogicalChannel(byte[] aid) command with the option: <br>
+ * P2='02' (Next occurrence) <br>
+ * The select response stored in the Channel object shall be updated with the APDU
+ * response of the SELECT command.
+
+ * @return <code>true</code> if new Applet was selected on this channel.
+ <code>false</code> the already selected Applet stays selected on this channel.
+ *
+ * @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 UnsupportedOperationException if this operation is not supported by the card.
+ */
+ public boolean selectNext() throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ synchronized (mLock) {
+ return mChannel.selectNext();
+ }
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+}
diff --git a/omapi/java/android/se/omapi/OWNERS b/omapi/java/android/se/omapi/OWNERS
new file mode 100644
index 000000000000..5682fd3281f4
--- /dev/null
+++ b/omapi/java/android/se/omapi/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 456592
+
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/omapi/java/android/se/omapi/Reader.java b/omapi/java/android/se/omapi/Reader.java
new file mode 100644
index 000000000000..3c2135d9bc9d
--- /dev/null
+++ b/omapi/java/android/se/omapi/Reader.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent Secure Element Readers supported 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://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public final class Reader {
+
+ private static final String TAG = "OMAPI.Reader";
+ private final String mName;
+ private final SEService mService;
+ private ISecureElementReader mReader;
+ private final Object mLock = new Object();
+
+
+ Reader(@NonNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader) {
+ if (reader == null || service == null || name == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ mName = name;
+ mService = service;
+ mReader = reader;
+ }
+
+ /**
+ * Return the name of this reader.
+ * <ul>
+ * <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li>
+ * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
+ * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
+ * </ul>
+ * Slot is a decimal number without leading zeros. The Numbering must start with 1
+ * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
+ * The slot number “1” for a reader is optional
+ * (SIM and SIM1 are both valid for the first SIM-reader,
+ * but if there are two readers then the second reader must be named SIM2).
+ * This applies also for other SD or SE readers.
+ *
+ * @return the reader name, as a String.
+ */
+ public @NonNull 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 @NonNull Session openSession() throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service is not connected");
+ }
+
+ synchronized (mLock) {
+ ISecureElementSession session;
+ try {
+ session = mReader.openSession();
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ 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.
+ *
+ * @throws IllegalStateException if the service is not connected
+ * @return <code>true</code> if the SE is present, <code>false</code> otherwise.
+ */
+ public boolean isSecureElementPresent() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service is not connected");
+ }
+
+ try {
+ return mReader.isSecureElementPresent();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Error in isSecureElementPresent()");
+ }
+ }
+
+ /**
+ * Return the Secure Element service this reader is bound to.
+ *
+ * @return the SEService object.
+ */
+ public @NonNull 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.isConnected()) {
+ Log.e(TAG, "service is not connected");
+ return;
+ }
+ synchronized (mLock) {
+ try {
+ mReader.closeSessions();
+ } catch (RemoteException ignore) { }
+ }
+ }
+
+ /**
+ * Close all the sessions opened on this reader and reset the reader.
+ * All the channels opened by all these sessions will be closed.
+ * @return <code>true</code> if reset success, <code>false</code> otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION)
+ public boolean reset() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service is not connected");
+ return false;
+ }
+ synchronized (mLock) {
+ try {
+ closeSessions();
+ return mReader.reset();
+ } catch (RemoteException ignore) {
+ return false;
+ }
+ }
+ }
+}
diff --git a/omapi/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java
new file mode 100644
index 000000000000..f42ca364b6d9
--- /dev/null
+++ b/omapi/java/android/se/omapi/SEService.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.BroadcastBehavior;
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+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;
+
+import java.util.HashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * 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 v3.0</a>
+ */
+public final class SEService {
+
+ /**
+ * Error code used with ServiceSpecificException.
+ * Thrown if there was an error communicating with the Secure Element.
+ *
+ * @hide
+ */
+ public static final int IO_ERROR = 1;
+
+ /**
+ * Error code used with ServiceSpecificException.
+ * Thrown if AID cannot be selected or is not available when opening
+ * a logical channel.
+ *
+ * @hide
+ */
+ public static final int NO_SUCH_ELEMENT_ERROR = 2;
+
+ /**
+ * Interface to send call-backs to the application when the service is connected.
+ */
+ public interface OnConnectedListener {
+ /**
+ * Called by the framework when the service is connected.
+ */
+ void onConnected();
+ }
+
+ /**
+ * Broadcast Action: Intent to notify if the secure element state is changed.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @BroadcastBehavior(registeredOnly = true, protectedBroadcast = true)
+ public static final String ACTION_SECURE_ELEMENT_STATE_CHANGED =
+ "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED";
+
+ /**
+ * Mandatory extra containing the reader name of the state changed secure element.
+ *
+ * @see Reader#getName()
+ */
+ public static final String EXTRA_READER_NAME = "android.se.omapi.extra.READER_NAME";
+
+ /**
+ * Mandatory extra containing the connected state of the state changed secure element.
+ *
+ * True if the secure element is connected correctly, false otherwise.
+ */
+ public static final String EXTRA_READER_STATE = "android.se.omapi.extra.READER_STATE";
+
+ /**
+ * Listener object that allows the notification of the caller if this
+ * SEService could be bound to the backend.
+ */
+ private class SEListener extends ISecureElementListener.Stub {
+ public OnConnectedListener mListener = null;
+ public Executor mExecutor = null;
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ public void onConnected() {
+ if (mListener != null && mExecutor != null) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onConnected();
+ }
+ });
+ }
+ }
+ }
+ private SEListener mSEListener = new SEListener();
+
+ private static final String TAG = "OMAPI.SEService";
+
+ private static final String UICC_TERMINAL = "SIM";
+
+ private final Object mLock = new Object();
+
+ /** The client context (e.g. activity). */
+ private final Context mContext;
+
+ /** The backend system. */
+ private volatile ISecureElementService mSecureElementService;
+
+ /**
+ * Class for interacting with the main interface of the backend.
+ */
+ private ServiceConnection mConnection;
+
+ /**
+ * Collection of available readers
+ */
+ private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
+
+ /**
+ * 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
+ * onConnected() method called when the connection actually happen.
+ *
+ * @param context
+ * the context of the calling application. Cannot be
+ * <code>null</code>.
+ * @param listener
+ * a OnConnectedListener object.
+ * @param executor
+ * an Executor which will be used when invoking the callback.
+ */
+ public SEService(@NonNull Context context, @NonNull Executor executor,
+ @NonNull OnConnectedListener listener) {
+
+ if (context == null || listener == null || executor == null) {
+ throw new NullPointerException("Arguments must not be null");
+ }
+
+ mContext = context;
+ mSEListener.mListener = listener;
+ mSEListener.mExecutor = executor;
+
+ mConnection = new ServiceConnection() {
+
+ public synchronized void onServiceConnected(
+ ComponentName className, IBinder service) {
+
+ mSecureElementService = ISecureElementService.Stub.asInterface(service);
+ if (mSEListener != null) {
+ mSEListener.onConnected();
+ }
+ Log.i(TAG, "Service onServiceConnected");
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mSecureElementService = null;
+ Log.i(TAG, "Service onServiceDisconnected");
+ }
+ };
+
+ Intent intent = new Intent(ISecureElementService.class.getName());
+ intent.setClassName("com.android.se",
+ "com.android.se.SecureElementService");
+ boolean bindingSuccessful =
+ mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ if (bindingSuccessful) {
+ Log.i(TAG, "bindService successful");
+ }
+ }
+
+ /**
+ * Tells whether or not the service is connected.
+ *
+ * @return <code>true</code> if the service is connected.
+ */
+ public boolean isConnected() {
+ return mSecureElementService != null;
+ }
+
+ /**
+ * Returns an array of available Secure Element readers.
+ * There must be no duplicated objects in the returned list.
+ * All available readers shall be listed even if no card is inserted.
+ *
+ * @return An array of Readers. If there are no readers the returned array
+ * is of length 0.
+ */
+ public @NonNull Reader[] getReaders() {
+ loadReaders();
+
+ return mReaders.values().toArray(new Reader[0]);
+ }
+
+ /**
+ * Obtain a UICC Reader instance with specific slot number from the SecureElementService
+ *
+ * @param slotNumber The index of the uicc slot. The index starts from 1.
+ * @throws IllegalArgumentException if the reader object corresponding to the uiccSlotNumber
+ * is not exist.
+ * @return A Reader object for this uicc slot.
+ */
+ public @NonNull Reader getUiccReader(int slotNumber) {
+ if (slotNumber < 1) {
+ throw new IllegalArgumentException("slotNumber should be larger than 0");
+ }
+ loadReaders();
+
+ String readerName = UICC_TERMINAL + slotNumber;
+ Reader reader = mReaders.get(readerName);
+
+ if (reader == null) {
+ throw new IllegalArgumentException("Reader:" + readerName + " doesn't exist");
+ }
+
+ return reader;
+ }
+
+ /**
+ * Releases all Secure Elements resources allocated by this SEService
+ * (including any binding to an underlying service).
+ * As a result isConnected() will return false after shutdown() was called.
+ * After this method call, the SEService object is not connected.
+ * This method should be called when connection to the Secure Element is not needed
+ * or in the termination method of the calling application
+ * (or part of this application) which is bound to this SEService.
+ */
+ public void shutdown() {
+ synchronized (mLock) {
+ if (mSecureElementService != null) {
+ for (Reader reader : mReaders.values()) {
+ try {
+ 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.
+ }
+ mSecureElementService = null;
+ }
+ }
+
+ /**
+ * Returns the version of the OpenMobile API specification this
+ * implementation is based on.
+ *
+ * @return String containing the OpenMobile API version (e.g. "3.0").
+ */
+ public @NonNull String getVersion() {
+ return "3.3";
+ }
+
+ @NonNull ISecureElementListener getListener() {
+ return mSEListener;
+ }
+
+ /**
+ * Obtain a Reader instance from the SecureElementService
+ */
+ private @NonNull ISecureElementReader getReader(String name) {
+ try {
+ return mSecureElementService.getReader(name);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Load available Secure Element Readers
+ */
+ private void loadReaders() {
+ if (mSecureElementService == null) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ String[] readerNames;
+ try {
+ readerNames = mSecureElementService.getReaders();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
+ for (String readerName : readerNames) {
+ if (mReaders.get(readerName) == null) {
+ try {
+ mReaders.put(readerName, new Reader(this, readerName,
+ getReader(readerName)));
+ } catch (Exception e) {
+ Log.e(TAG, "Error adding Reader: " + readerName, e);
+ }
+ }
+ }
+ }
+}
diff --git a/omapi/java/android/se/omapi/Session.java b/omapi/java/android/se/omapi/Session.java
new file mode 100644
index 000000000000..d5f8c82bf47e
--- /dev/null
+++ b/omapi/java/android/se/omapi/Session.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+/*
+ * Copyright (c) 2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.NoSuchElementException;
+
+/**
+ * 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 Applet 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 v3.0</a>
+ */
+public final class Session {
+
+ private final Object mLock = new Object();
+ private final SEService mService;
+ private final Reader mReader;
+ private final ISecureElementSession mSession;
+ private static final String TAG = "OMAPI.Session";
+
+ Session(@NonNull SEService service, @NonNull ISecureElementSession session,
+ @NonNull Reader reader) {
+ if (service == null || reader == null || session == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ mService = service;
+ mReader = reader;
+ mSession = session;
+ }
+
+ /**
+ * Get the reader that provides this session.
+ *
+ * @return The Reader object.
+ */
+ public @NonNull 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.
+ *
+ * @throws IllegalStateException if there was an error connecting to SE or
+ * if the service was not connected.
+ * @return the ATR as a byte array or null.
+ */
+ public @Nullable byte[] getATR() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ return mSession.getAtr();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * 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.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return;
+ }
+ synchronized (mLock) {
+ try {
+ mSession.close();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error closing session", e);
+ }
+ }
+ }
+
+ /**
+ * Tells if this session is closed.
+ *
+ * @return <code>true</code> if the session is closed, false otherwise.
+ */
+ public boolean isClosed() {
+ try {
+ return mSession.isClosed();
+ } catch (RemoteException e) {
+ // If there was an error here, then the session is considered close
+ return true;
+ }
+ }
+
+ /**
+ * Close any channel opened on this session.
+ */
+ public void closeChannels() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return;
+ }
+
+ synchronized (mLock) {
+ try {
+ mSession.closeChannels();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error closing channels", e);
+ }
+ }
+ }
+
+ /**
+ * 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, it 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.
+ * With previous release (V2.05) it was not possible to set P2 value, this value was always
+ * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+ * recommended that the device allows all values for P2, however only the following values
+ * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+ * The implementation of the underlying SELECT command within this method shall be
+ * based on ISO 7816-4 with following options:
+ * <ul>
+ * <li>CLA = '00'</li>
+ * <li>INS = 'A4'</li>
+ * <li>P1 = '04' (Select by DF name/application identifier)</li>
+ * </ul>
+ *
+ * The select response data can be retrieved with byte[] getSelectResponse().
+ * The API shall handle received status word as follow. If the status word indicates that the
+ * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+ * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+ * channel opened and the next getSelectResponse() shall return the received status
+ * word.
+ * Other received status codes indicating that the Secure Element was able not to open a
+ * channel shall be considered as an error and the corresponding channel shall not be
+ * opened.
+ * The function without P2 as parameter is provided for backwards compatibility and will
+ * fall back to a select command with P2='00'.
+ *
+ * @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.
+ * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+ * @throws IOException if there is a communication problem to the reader or
+ * the Secure Element.
+ * @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 Applet on this
+ * session.
+ * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+ * selected.
+ * @throws UnsupportedOperationException if the given P2 parameter is not
+ * supported by the device
+ * @return an instance of Channel if available or null.
+ */
+ public @Nullable Channel openBasicChannel(@Nullable byte[] aid, @Nullable byte p2)
+ throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+
+ synchronized (mLock) {
+ try {
+ ISecureElementChannel channel = mSession.openBasicChannel(aid, p2,
+ mReader.getSEService().getListener());
+ if (channel == null) {
+ return null;
+ }
+ return new Channel(mService, this, channel);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == SEService.IO_ERROR) {
+ throw new IOException(e.getMessage());
+ } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+ throw new NoSuchElementException(e.getMessage());
+ } else {
+ throw new IllegalStateException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * This method is provided to ease the development of mobile application and for compliancy
+ * with existing applications.
+ * This method is equivalent to openBasicChannel(aid, P2=0x00)
+ *
+ * @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.
+ * @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 Applet on this
+ * session.
+ * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+ * selected.
+ * @throws UnsupportedOperationException if the given P2 parameter is not
+ * supported by the device
+ * @return an instance of Channel if available or null.
+ */
+ public @Nullable Channel openBasicChannel(@Nullable byte[] aid) throws IOException {
+ return openBasicChannel(aid, (byte) 0x00);
+ }
+
+ /**
+ * Open a logical channel with the Secure Element, selecting the Applet represented by
+ * the given AID. If the AID is 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.
+ * With previous release (V2.05) it was not possible to set P2 value, this value was always
+ * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+ * recommended that the device allows all values for P2, however only the following values
+ * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+ * The implementation of the underlying SELECT command within this method shall be
+ * based on ISO 7816-4 with following options:
+ *
+ * <ul>
+ * <li>CLA = '01' to '03', '40 to 4F'</li>
+ * <li>INS = 'A4'</li>
+ * <li>P1 = '04' (Select by DF name/application identifier)</li>
+ * </ul>
+ *
+ * The select response data can be retrieved with byte[] getSelectResponse().
+ * The API shall handle received status word as follow. If the status word indicates that the
+ * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+ * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+ * channel opened and the next getSelectResponse() shall return the received status
+ * word.
+ * Other received status codes indicating that the Secure Element was able not to open a
+ * channel shall be considered as an error and the corresponding channel shall not be
+ * opened.
+ * In case of UICC it is recommended for the API to reject the opening of the logical
+ * channel without a specific AID, by always answering null to such a request.
+ * The function without P2 as parameter is provided for backwards compatibility and will
+ * fall back to a select command with P2=00.
+ *
+ * @param aid the AID of the Applet to be selected on this channel, as a
+ * byte array.
+ * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+ * @throws IOException if there is a communication problem to the reader or
+ * the Secure Element.
+ * @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 Applet on this
+ * session.
+ * @throws NoSuchElementException if the AID on the Secure Element is not
+ * available or cannot be selected or a logical channel is already
+ * open to a non-multiselectable Applet.
+ * @throws UnsupportedOperationException if the given P2 parameter is not
+ * supported by the device.
+ * @return an instance of Channel. Null if the Secure Element is unable to
+ * provide a new logical channel.
+ */
+ public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
+ throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ synchronized (mLock) {
+ try {
+ ISecureElementChannel channel = mSession.openLogicalChannel(
+ aid,
+ p2,
+ mReader.getSEService().getListener());
+ if (channel == null) {
+ return null;
+ }
+ return new Channel(mService, this, channel);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == SEService.IO_ERROR) {
+ throw new IOException(e.getMessage());
+ } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+ throw new NoSuchElementException(e.getMessage());
+ } else {
+ throw new IllegalStateException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * This method is provided to ease the development of mobile application and for compliancy
+ * with existing applications.
+ * This method is equivalent to openLogicalChannel(aid, P2=0x00)
+ *
+ * @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.
+ * @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 Applet on this
+ * session.
+ * @throws NoSuchElementException if the AID on the Secure Element is not
+ * available or cannot be selected or a logical channel is already
+ * open to a non-multiselectable Applet.
+ * @throws UnsupportedOperationException if the given P2 parameter is not
+ * supported by the device.
+ * @return an instance of Channel. Null if the Secure Element is unable to
+ * provide a new logical channel.
+ */
+ public @Nullable Channel openLogicalChannel(@Nullable byte[] aid) throws IOException {
+ return openLogicalChannel(aid, (byte) 0x00);
+ }
+}