362 lines
16 KiB
Java
362 lines
16 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|