blob: 5db3c1a9a707ea4a6688d8bb75eb32b011035298 [file] [log] [blame]
Ruchi Kandoia1f94012017-12-08 15:07:03 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16/*
17 * Copyright (c) 2015-2017, The Linux Foundation.
18 */
19/*
20 * Contributed by: Giesecke & Devrient GmbH.
21 */
22
23package android.se.omapi;
24
25import android.annotation.NonNull;
26import android.annotation.Nullable;
27import android.os.RemoteException;
Ruchi Kandoi816a0532018-02-01 16:15:25 -080028import android.os.ServiceSpecificException;
Ruchi Kandoia1f94012017-12-08 15:07:03 -080029import android.util.Log;
30
31import java.io.IOException;
32
33/**
34 * Instances of this class represent an ISO/IEC 7816-4 channel opened to a
35 * Secure Element. It can be either a logical channel or the basic channel. They
36 * can be used to send APDUs to the secure element. Channels are opened by
37 * calling the Session.openBasicChannel(byte[]) or
38 * Session.openLogicalChannel(byte[]) methods.
39 *
40 * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
41 */
Ruchi Kandoif0082402018-03-27 10:03:34 -070042public final class Channel implements java.nio.channels.Channel {
Ruchi Kandoia1f94012017-12-08 15:07:03 -080043
44 private static final String TAG = "OMAPI.Channel";
45 private Session mSession;
46 private final ISecureElementChannel mChannel;
47 private final SEService mService;
48 private final Object mLock = new Object();
49
Ruchi Kandoid785fc42018-03-22 11:06:36 -070050 Channel(@NonNull SEService service, @NonNull Session session,
51 @NonNull ISecureElementChannel channel) {
Ruchi Kandoia1f94012017-12-08 15:07:03 -080052 if (service == null || session == null || channel == null) {
53 throw new IllegalArgumentException("Parameters cannot be null");
54 }
55 mService = service;
56 mSession = session;
57 mChannel = channel;
58 }
59
60 /**
61 * Closes this channel to the Secure Element. If the method is called when
62 * the channel is already closed, this method will be ignored. The close()
63 * method shall wait for completion of any pending transmit(byte[] command)
64 * before closing the channel.
65 */
66 public void close() {
Ruchi Kandoif0082402018-03-27 10:03:34 -070067 if (isOpen()) {
Ruchi Kandoia1f94012017-12-08 15:07:03 -080068 synchronized (mLock) {
69 try {
70 mChannel.close();
71 } catch (Exception e) {
72 Log.e(TAG, "Error closing channel", e);
73 }
74 }
75 }
76 }
77
78 /**
Ruchi Kandoif0082402018-03-27 10:03:34 -070079 * Tells if this channel is open.
Ruchi Kandoia1f94012017-12-08 15:07:03 -080080 *
Ruchi Kandoif0082402018-03-27 10:03:34 -070081 * @return <code>false</code> if the channel is closed or in case of an error.
82 * <code>true</code> otherwise.
Ruchi Kandoia1f94012017-12-08 15:07:03 -080083 */
Ruchi Kandoif0082402018-03-27 10:03:34 -070084 public boolean isOpen() {
Ruchi Kandoia1f94012017-12-08 15:07:03 -080085 if (!mService.isConnected()) {
86 Log.e(TAG, "service not connected to system");
Ruchi Kandoif0082402018-03-27 10:03:34 -070087 return false;
Ruchi Kandoia1f94012017-12-08 15:07:03 -080088 }
89 try {
Ruchi Kandoif0082402018-03-27 10:03:34 -070090 return !mChannel.isClosed();
Ruchi Kandoia1f94012017-12-08 15:07:03 -080091 } catch (RemoteException e) {
92 Log.e(TAG, "Exception in isClosed()");
Ruchi Kandoif0082402018-03-27 10:03:34 -070093 return false;
Ruchi Kandoia1f94012017-12-08 15:07:03 -080094 }
95 }
96
97 /**
98 * Returns a boolean telling if this channel is the basic channel.
99 *
100 * @return <code>true</code> if this channel is a basic channel. <code>false</code> if
101 * this channel is a logical channel.
102 */
103 public boolean isBasicChannel() {
104 if (!mService.isConnected()) {
105 throw new IllegalStateException("service not connected to system");
106 }
107 try {
108 return mChannel.isBasicChannel();
109 } catch (RemoteException e) {
110 throw new IllegalStateException(e.getMessage());
111 }
112 }
113
114 /**
115 * Transmit an APDU command (as per ISO/IEC 7816-4) to the Secure Element. The
116 * underlying layers generate as many TPDUs as necessary to transport this APDU. The
117 * API shall ensure that all available data returned from Secure Element, including
118 * concatenated responses, are retrieved and made available to the calling application. If a
119 * warning status code is received the API wont check for further response data but will
120 * return all data received so far and the warning status code.<br>
121 * The transport part is invisible from the application. The generated response is the
122 * response of the APDU which means that all protocols related responses are handled
123 * inside the API or the underlying implementation.<br>
124 * The transmit method shall support extended length APDU commands independently of
125 * the coding within the ATR.<br>
126 * For status word '61 XX' the API or underlying implementation shall issue a GET
127 * RESPONSE command as specified by ISO 7816-4 standard with LE=XX; for the status
128 * word '6C XX', the API or underlying implementation shall reissue the input command
129 * with LE=XX. For other status words, the API (or underlying implementation) shall return
130 * the complete response including data and status word to the device application. The API
131 * (or underlying implementation) shall not handle internally the received status words. The
132 * channel shall not be closed even if the Secure Element answered with an error code.
133 * The system ensures the synchronization between all the concurrent calls to this method,
134 * and that only one APDU will be sent at a time, irrespective of the number of TPDUs that
135 * might be required to transport it to the SE. The entire APDU communication to this SE is
136 * locked to the APDU.<br>
137 * The channel information in the class byte in the APDU will be ignored. The system will
138 * add any required information to ensure the APDU is transported on this channel.
139 * The only restrictions on the set of commands that can be sent is defined below, the API
140 * implementation shall be able to send all other commands: <br>
141 * <ul>
142 * <li>MANAGE_CHANNEL commands are not allowed.</li>
143 * <li>SELECT by DF Name (p1=04) are not allowed.</li>
144 * <li>CLA bytes with channel numbers are de-masked.</li>
145 * </ul>
146 *
147 * @param command the APDU command to be transmitted, as a byte array.
148 *
149 * @return the response received, as a byte array. The returned byte array contains the data
150 * bytes in the following order:
151 * [&lt;first data byte&gt;, ..., &lt;last data byte&gt;, &lt;sw1&gt;, &lt;sw2&gt;]
152 *
153 * @throws IOException if there is a communication problem to the reader or the Secure Element.
154 * @throws IllegalStateException if the channel is used after being closed.
155 * @throws IllegalArgumentException if the command byte array is less than 4 bytes long.
156 * @throws IllegalArgumentException if Lc byte is inconsistent with length of the byte array.
157 * @throws IllegalArgumentException if CLA byte is invalid according to [2] (0xff).
158 * @throws IllegalArgumentException if INS byte is invalid according to [2] (0x6x or 0x9x).
159 * @throws SecurityException if the command is filtered by the security policy.
160 * @throws NullPointerException if command is NULL.
161 */
Ruchi Kandoid785fc42018-03-22 11:06:36 -0700162 public @NonNull byte[] transmit(@NonNull byte[] command) throws IOException {
Ruchi Kandoia1f94012017-12-08 15:07:03 -0800163 if (!mService.isConnected()) {
164 throw new IllegalStateException("service not connected to system");
165 }
166 synchronized (mLock) {
167 try {
168 byte[] response = mChannel.transmit(command);
169 if (response == null) {
170 throw new IOException("Error in communicating with Secure Element");
171 }
172 return response;
Ruchi Kandoi816a0532018-02-01 16:15:25 -0800173 } catch (ServiceSpecificException e) {
Ruchi Kandoia1f94012017-12-08 15:07:03 -0800174 throw new IOException(e.getMessage());
Ruchi Kandoi816a0532018-02-01 16:15:25 -0800175 } catch (RemoteException e) {
176 throw new IllegalStateException(e.getMessage());
Ruchi Kandoia1f94012017-12-08 15:07:03 -0800177 }
178 }
179 }
180
181 /**
182 * Get the session that has opened this channel.
183 *
184 * @return the session object this channel is bound to.
185 */
186 public @NonNull Session getSession() {
187 return mSession;
188 }
189
190 /**
191 * Returns the data as received from the application select command inclusively the status word
192 * received at applet selection.
193 * The returned byte array contains the data bytes in the following order:
194 * [&lt;first data byte&gt;, ..., &lt;last data byte&gt;, &lt;sw1&gt;, &lt;sw2&gt;]
195 * @return The data as returned by the application select command inclusively the status word.
196 * Only the status word if the application select command has no returned data.
197 * Returns null if an application select command has not been performed or the selection
198 * response can not be retrieved by the reader implementation.
199 */
200 public @Nullable byte[] getSelectResponse() {
201 if (!mService.isConnected()) {
202 throw new IllegalStateException("service not connected to system");
203 }
204
205 byte[] response;
206 try {
207 response = mChannel.getSelectResponse();
208 } catch (RemoteException e) {
209 throw new IllegalStateException(e.getMessage());
210 }
211
212 if (response != null && response.length == 0) {
213 response = null;
214 }
215 return response;
216 }
217
218 /**
219 * Performs a selection of the next Applet on this channel that matches to the partial AID
220 * specified in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method.
221 * This mechanism can be used by a device application to iterate through all Applets
222 * matching to the same partial AID.
223 * If selectNext() returns true a new Applet was successfully selected on this channel.
224 * If no further Applet exists with matches to the partial AID this method returns false
225 * and the already selected Applet stays selected. <br>
226 *
227 * Since the API cannot distinguish between a partial and full AID the API shall rely on the
228 * response of the Secure Element for the return value of this method. <br>
229 * The implementation of the underlying SELECT command within this method shall use
230 * the same values as the corresponding openBasicChannel(byte[] aid) or
231 * openLogicalChannel(byte[] aid) command with the option: <br>
232 * P2='02' (Next occurrence) <br>
233 * The select response stored in the Channel object shall be updated with the APDU
234 * response of the SELECT command.
235
236 * @return <code>true</code> if new Applet was selected on this channel.
237 <code>false</code> he already selected Applet stays selected on this channel.
238 *
239 * @throws IOException if there is a communication problem to the reader or the Secure Element.
240 * @throws IllegalStateException if the channel is used after being closed.
241 * @throws UnsupportedOperationException if this operation is not supported by the card.
242 */
243 public boolean selectNext() throws IOException {
244 if (!mService.isConnected()) {
245 throw new IllegalStateException("service not connected to system");
246 }
247 try {
248 synchronized (mLock) {
249 return mChannel.selectNext();
250 }
Ruchi Kandoi816a0532018-02-01 16:15:25 -0800251 } catch (ServiceSpecificException e) {
Ruchi Kandoia1f94012017-12-08 15:07:03 -0800252 throw new IOException(e.getMessage());
Ruchi Kandoi816a0532018-02-01 16:15:25 -0800253 } catch (RemoteException e) {
254 throw new IllegalStateException(e.getMessage());
Ruchi Kandoia1f94012017-12-08 15:07:03 -0800255 }
256 }
257}