| /* |
| * 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 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(SEService service, ISecureElementSession session, 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(byte[] aid, 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(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(byte[] aid, byte p2) throws IOException { |
| |
| if ((mReader.getName().startsWith("SIM")) && (aid == null)) { |
| Log.e(TAG, "NULL AID not supported on " + mReader.getName()); |
| return null; |
| } |
| |
| 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(byte[] aid) throws IOException { |
| return openLogicalChannel(aid, (byte) 0x00); |
| } |
| } |