Session.java revision 1d0b15510aa0452340ea9fd00938adda32c09635
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
23package android.se.omapi;
24
25import android.annotation.NonNull;
26import android.annotation.Nullable;
27import android.os.RemoteException;
28import android.os.ServiceSpecificException;
29import android.util.Log;
30
31import java.io.IOException;
32import 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 */
42public 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
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     */
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     */
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     */
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     */
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     */
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     */
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     * Open a logical channel with the Secure Element, selecting the Applet represented by
229     * the given AID. If the AID is null, which means no Applet is to be selected on this
230     * channel, the default Applet is used. It's up to the Secure Element to choose which
231     * logical channel will be used.
232     * With previous release (V2.05) it was not possible to set P2 value, this value was always
233     * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
234     * recommended that the device allows all values for P2, however only the following values
235     * are mandatory: '00', '04', '08', '0C'(as defined in [2])
236     * The implementation of the underlying SELECT command within this method shall be
237     * based on ISO 7816-4 with following options:
238     *
239     * <ul>
240     * <li>CLA = '01' to '03', '40 to 4F'</li>
241     * <li>INS = 'A4'</li>
242     * <li>P1 = '04' (Select by DF name/application identifier)</li>
243     * </ul>
244     *
245     * The select response data can be retrieved with byte[] getSelectResponse().
246     * The API shall handle received status word as follow. If the status word indicates that the
247     * Secure Element was able to open a channel (e.g. status word '90 00' or status words
248     * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
249     * channel opened and the next getSelectResponse() shall return the received status
250     * word.
251     * Other received status codes indicating that the Secure Element was able not to open a
252     * channel shall be considered as an error and the corresponding channel shall not be
253     * opened.
254     * In case of UICC it is recommended for the API to reject the opening of the logical
255     * channel without a specific AID, by always answering null to such a request.
256     * The function without P2 as parameter is provided for backwards compatibility and will
257     * fall back to a select command with P2=00.
258     *
259     * @param aid the AID of the Applet to be selected on this channel, as a
260     *            byte array.
261     * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
262     * @throws IOException if there is a communication problem to the reader or
263     *             the Secure Element.
264     * @throws IllegalStateException if the Secure Element is used after being
265     *             closed.
266     * @throws IllegalArgumentException if the aid's length is not within 5 to
267     *             16 (inclusive).
268     * @throws SecurityException if the calling application cannot be granted
269     *             access to this AID or the default Applet on this
270     *             session.
271     * @throws NoSuchElementException if the AID on the Secure Element is not
272     *             available or cannot be selected or a logical channel is already
273     *             open to a non-multiselectable Applet.
274     * @throws UnsupportedOperationException if the given P2 parameter is not
275     *             supported by the device.
276     * @return an instance of Channel. Null if the Secure Element is unable to
277     *         provide a new logical channel.
278     */
279    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
280            throws IOException {
281        if (!mService.isConnected()) {
282            throw new IllegalStateException("service not connected to system");
283        }
284        synchronized (mLock) {
285            try {
286                ISecureElementChannel channel = mSession.openLogicalChannel(
287                        aid,
288                        p2,
289                        mReader.getSEService().getListener());
290                if (channel == null) {
291                    return null;
292                }
293                return new Channel(mService, this, channel);
294            } catch (ServiceSpecificException e) {
295                if (e.errorCode == SEService.IO_ERROR) {
296                    throw new IOException(e.getMessage());
297                } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
298                    throw new NoSuchElementException(e.getMessage());
299                } else {
300                    throw new IllegalStateException(e.getMessage());
301                }
302            } catch (RemoteException e) {
303                throw new IllegalStateException(e.getMessage());
304            }
305        }
306    }
307}
308