1 /*
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) 2017, The Linux Foundation.
18  */
19 /*
20  * Contributed by: Giesecke & Devrient GmbH.
21  */
22 
23 package android.se.omapi;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.os.RemoteException;
28 import android.os.ServiceSpecificException;
29 import android.util.Log;
30 
31 import java.io.IOException;
32 import java.util.NoSuchElementException;
33 
34 /**
35  * Instances of this class represent a connection session to one of the Secure
36  * Elements available on the device. These objects can be used to get a
37  * communication channel with an Applet in the Secure Element.
38  * This channel can be the basic channel or a logical channel.
39  *
40  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
41  */
42 public final class Session {
43 
44     private final Object mLock = new Object();
45     private final SEService mService;
46     private final Reader mReader;
47     private final ISecureElementSession mSession;
48     private static final String TAG = "OMAPI.Session";
49 
Session(@onNull SEService service, @NonNull ISecureElementSession session, @NonNull Reader reader)50     Session(@NonNull SEService service, @NonNull ISecureElementSession session,
51             @NonNull Reader reader) {
52         if (service == null || reader == null || session == null) {
53             throw new IllegalArgumentException("Parameters cannot be null");
54         }
55         mService = service;
56         mReader = reader;
57         mSession = session;
58     }
59 
60     /**
61      * Get the reader that provides this session.
62      *
63      * @return The Reader object.
64      */
getReader()65     public @NonNull Reader getReader() {
66         return mReader;
67     }
68 
69     /**
70      * Get the Answer to Reset of this Secure Element. <br>
71      * The returned byte array can be null if the ATR for this Secure Element is
72      * not available.
73      *
74      * @throws IllegalStateException if there was an error connecting to SE or
75      *                               if the service was not connected.
76      * @return the ATR as a byte array or null.
77      */
getATR()78     public @Nullable byte[] getATR() {
79         if (!mService.isConnected()) {
80             throw new IllegalStateException("service not connected to system");
81         }
82         try {
83             return mSession.getAtr();
84         } catch (RemoteException e) {
85             throw new IllegalStateException(e.getMessage());
86         }
87     }
88 
89     /**
90      * Close the connection with the Secure Element. This will close any
91      * channels opened by this application with this Secure Element.
92      */
close()93     public void close() {
94         if (!mService.isConnected()) {
95             Log.e(TAG, "service not connected to system");
96             return;
97         }
98         synchronized (mLock) {
99             try {
100                 mSession.close();
101             } catch (RemoteException e) {
102                 Log.e(TAG, "Error closing session", e);
103             }
104         }
105     }
106 
107     /**
108      * Tells if this session is closed.
109      *
110      * @return <code>true</code> if the session is closed, false otherwise.
111      */
isClosed()112     public boolean isClosed() {
113         try {
114             return mSession.isClosed();
115         } catch (RemoteException e) {
116             // If there was an error here, then the session is considered close
117             return true;
118         }
119     }
120 
121     /**
122      * Close any channel opened on this session.
123      */
closeChannels()124     public void closeChannels() {
125         if (!mService.isConnected()) {
126             Log.e(TAG, "service not connected to system");
127             return;
128         }
129 
130         synchronized (mLock) {
131             try {
132                 mSession.closeChannels();
133             } catch (RemoteException e) {
134                 Log.e(TAG, "Error closing channels", e);
135             }
136         }
137     }
138 
139     /**
140      * Get an access to the basic channel, as defined in the ISO/IEC 7816-4 specification (the
141      * one that has number 0). The obtained object is an instance of the Channel class.
142      * If the AID is null, it means no Applet is to be selected on this channel and the default
143      * Applet is used. If the AID is defined then the corresponding Applet is selected.
144      * Once this channel has been opened by a device application, it is considered as "locked"
145      * by this device application, and other calls to this method will return null, until the
146      * channel is closed. Some Secure Elements (like the UICC) might always keep the basic channel
147      * locked (i.e. return null to applications), to prevent access to the basic channel, while
148      * some other might return a channel object implementing some kind of filtering on the
149      * commands, restricting the set of accepted command to a smaller set.
150      * It is recommended for the UICC to reject the opening of the basic channel to a specific
151      * applet, by always answering null to such a request.
152      * For other Secure Elements, the recommendation is to accept opening the basic channel
153      * on the default applet until another applet is selected on the basic channel. As there is no
154      * other way than a reset to select again the default applet, the implementation of the
155      * transport API should guarantee that the openBasicChannel(null) command will return
156      * null until a reset occurs.
157      * With previous release (V2.05) it was not possible to set P2 value, this value was always
158      * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
159      * recommended that the device allows all values for P2, however only the following values
160      * are mandatory: '00', '04', '08', '0C'(as defined in [2])
161      * The implementation of the underlying SELECT command within this method shall be
162      * based on ISO 7816-4 with following options:
163      * <ul>
164      * <li>CLA = '00'</li>
165      * <li>INS = 'A4'</li>
166      * <li>P1 = '04' (Select by DF name/application identifier)</li>
167      * </ul>
168      *
169      * The select response data can be retrieved with byte[] getSelectResponse().
170      * The API shall handle received status word as follow. If the status word indicates that the
171      * Secure Element was able to open a channel (e.g. status word '90 00' or status words
172      * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
173      * channel opened and the next getSelectResponse() shall return the received status
174      * word.
175      * Other received status codes indicating that the Secure Element was able not to open a
176      * channel shall be considered as an error and the corresponding channel shall not be
177      * opened.
178      * The function without P2 as parameter is provided for backwards compatibility and will
179      * fall back to a select command with P2='00'.
180      *
181      * @param aid the AID of the Applet to be selected on this channel, as a
182      *            byte array, or null if no Applet is to be selected.
183      * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
184      * @throws IOException if there is a communication problem to the reader or
185      *             the Secure Element.
186      * @throws IllegalStateException if the Secure Element session is used after
187      *             being closed.
188      * @throws IllegalArgumentException if the aid's length is not within 5 to
189      *             16 (inclusive).
190      * @throws SecurityException if the calling application cannot be granted
191      *             access to this AID or the default Applet on this
192      *             session.
193      * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
194      *             selected.
195      * @throws UnsupportedOperationException if the given P2 parameter is not
196      *             supported by the device
197      * @return an instance of Channel if available or null.
198      */
openBasicChannel(@ullable byte[] aid, @Nullable byte p2)199     public @Nullable Channel openBasicChannel(@Nullable byte[] aid, @Nullable byte p2)
200             throws IOException {
201         if (!mService.isConnected()) {
202             throw new IllegalStateException("service not connected to system");
203         }
204 
205         synchronized (mLock) {
206             try {
207                 ISecureElementChannel channel = mSession.openBasicChannel(aid, p2,
208                         mReader.getSEService().getListener());
209                 if (channel == null) {
210                     return null;
211                 }
212                 return new Channel(mService, this, channel);
213             } catch (ServiceSpecificException e) {
214                 if (e.errorCode == SEService.IO_ERROR) {
215                     throw new IOException(e.getMessage());
216                 } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
217                     throw new NoSuchElementException(e.getMessage());
218                 } else {
219                     throw new IllegalStateException(e.getMessage());
220                 }
221             } catch (RemoteException e) {
222                 throw new IllegalStateException(e.getMessage());
223             }
224         }
225     }
226 
227     /**
228      * This method is provided to ease the development of mobile application and for compliancy
229      * with existing applications.
230      * This method is equivalent to openBasicChannel(aid, P2=0x00)
231      *
232      * @param aid the AID of the Applet to be selected on this channel, as a
233      *            byte array, or null if no Applet is to be selected.
234      * @throws IOException if there is a communication problem to the reader or
235      *             the Secure Element.
236      * @throws IllegalStateException if the Secure Element session is used after
237      *             being closed.
238      * @throws IllegalArgumentException if the aid's length is not within 5 to
239      *             16 (inclusive).
240      * @throws SecurityException if the calling application cannot be granted
241      *             access to this AID or the default Applet on this
242      *             session.
243      * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
244      *             selected.
245      * @throws UnsupportedOperationException if the given P2 parameter is not
246      *             supported by the device
247      * @return an instance of Channel if available or null.
248      */
openBasicChannel(@ullable byte[] aid)249     public @Nullable Channel openBasicChannel(@Nullable byte[] aid) throws IOException {
250         return openBasicChannel(aid, (byte) 0x00);
251     }
252 
253     /**
254      * Open a logical channel with the Secure Element, selecting the Applet represented by
255      * the given AID. If the AID is null, which means no Applet is to be selected on this
256      * channel, the default Applet is used. It's up to the Secure Element to choose which
257      * logical channel will be used.
258      * With previous release (V2.05) it was not possible to set P2 value, this value was always
259      * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
260      * recommended that the device allows all values for P2, however only the following values
261      * are mandatory: '00', '04', '08', '0C'(as defined in [2])
262      * The implementation of the underlying SELECT command within this method shall be
263      * based on ISO 7816-4 with following options:
264      *
265      * <ul>
266      * <li>CLA = '01' to '03', '40 to 4F'</li>
267      * <li>INS = 'A4'</li>
268      * <li>P1 = '04' (Select by DF name/application identifier)</li>
269      * </ul>
270      *
271      * The select response data can be retrieved with byte[] getSelectResponse().
272      * The API shall handle received status word as follow. If the status word indicates that the
273      * Secure Element was able to open a channel (e.g. status word '90 00' or status words
274      * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
275      * channel opened and the next getSelectResponse() shall return the received status
276      * word.
277      * Other received status codes indicating that the Secure Element was able not to open a
278      * channel shall be considered as an error and the corresponding channel shall not be
279      * opened.
280      * In case of UICC it is recommended for the API to reject the opening of the logical
281      * channel without a specific AID, by always answering null to such a request.
282      * The function without P2 as parameter is provided for backwards compatibility and will
283      * fall back to a select command with P2=00.
284      *
285      * @param aid the AID of the Applet to be selected on this channel, as a
286      *            byte array.
287      * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
288      * @throws IOException if there is a communication problem to the reader or
289      *             the Secure Element.
290      * @throws IllegalStateException if the Secure Element is used after being
291      *             closed.
292      * @throws IllegalArgumentException if the aid's length is not within 5 to
293      *             16 (inclusive).
294      * @throws SecurityException if the calling application cannot be granted
295      *             access to this AID or the default Applet on this
296      *             session.
297      * @throws NoSuchElementException if the AID on the Secure Element is not
298      *             available or cannot be selected or a logical channel is already
299      *             open to a non-multiselectable Applet.
300      * @throws UnsupportedOperationException if the given P2 parameter is not
301      *             supported by the device.
302      * @return an instance of Channel. Null if the Secure Element is unable to
303      *         provide a new logical channel.
304      */
openLogicalChannel(@ullable byte[] aid, @Nullable byte p2)305     public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
306             throws IOException {
307         if (!mService.isConnected()) {
308             throw new IllegalStateException("service not connected to system");
309         }
310         synchronized (mLock) {
311             try {
312                 ISecureElementChannel channel = mSession.openLogicalChannel(
313                         aid,
314                         p2,
315                         mReader.getSEService().getListener());
316                 if (channel == null) {
317                     return null;
318                 }
319                 return new Channel(mService, this, channel);
320             } catch (ServiceSpecificException e) {
321                 if (e.errorCode == SEService.IO_ERROR) {
322                     throw new IOException(e.getMessage());
323                 } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
324                     throw new NoSuchElementException(e.getMessage());
325                 } else {
326                     throw new IllegalStateException(e.getMessage());
327                 }
328             } catch (RemoteException e) {
329                 throw new IllegalStateException(e.getMessage());
330             }
331         }
332     }
333 
334     /**
335      * This method is provided to ease the development of mobile application and for compliancy
336      * with existing applications.
337      * This method is equivalent to openLogicalChannel(aid, P2=0x00)
338      *
339      * @param aid the AID of the Applet to be selected on this channel, as a
340      *            byte array.
341      * @throws IOException if there is a communication problem to the reader or
342      *             the Secure Element.
343      * @throws IllegalStateException if the Secure Element is used after being
344      *             closed.
345      * @throws IllegalArgumentException if the aid's length is not within 5 to
346      *             16 (inclusive).
347      * @throws SecurityException if the calling application cannot be granted
348      *             access to this AID or the default Applet on this
349      *             session.
350      * @throws NoSuchElementException if the AID on the Secure Element is not
351      *             available or cannot be selected or a logical channel is already
352      *             open to a non-multiselectable Applet.
353      * @throws UnsupportedOperationException if the given P2 parameter is not
354      *             supported by the device.
355      * @return an instance of Channel. Null if the Secure Element is unable to
356      *         provide a new logical channel.
357      */
openLogicalChannel(@ullable byte[] aid)358     public @Nullable Channel openLogicalChannel(@Nullable byte[] aid) throws IOException {
359         return openLogicalChannel(aid, (byte) 0x00);
360     }
361 }
362