blob: a174215bcc3a5222c6063d528ff935dc7938b12e [file] [log] [blame]
/*
* Copyright (C) 2011, 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.
*/
/*
* Contributed by: Giesecke & Devrient GmbH.
*/
package org.simalliance.openmobileapi;
import java.io.IOException;
import java.util.MissingResourceException;
import java.util.NoSuchElementException;
import org.simalliance.openmobileapi.service.ISmartcardServiceChannel;
import org.simalliance.openmobileapi.service.ISmartcardServiceSession;
import org.simalliance.openmobileapi.service.SmartcardError;
import android.os.RemoteException;
/**
* 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 application 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 v2.02</a>
*/
public class Session {
private final Object mLock = new Object();
private final SEService mService;
private final Reader mReader;
private final ISmartcardServiceSession mSession;
Session(SEService service, ISmartcardServiceSession session, Reader reader) {
mService = service;
mReader = reader;
mSession = session;
}
/**
* Get the reader that provides this session.
*
* @return The Reader object.
*/
public 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.
*
* @return the ATR as a byte array or null.
*/
public byte[] getATR() {
if (mService == null || mService.isConnected() == false) {
throw new IllegalStateException("service not connected to system");
}
if( mSession == null ){
throw new NullPointerException("service session is null");
}
try {
return mSession.getAtr();
} catch (Exception e) {
return null;
}
}
/**
* 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 == null || mService.isConnected() == false) {
throw new IllegalStateException("service not connected to system");
}
if( mSession != null ){
synchronized (mLock) {
SmartcardError error = new SmartcardError();
try {
mSession.close(error);
} catch (RemoteException e) {
throw new RuntimeException(e.getMessage());
}
SEService.checkForException(error);
}
}
}
/**
* Tells if this session is closed.
*
* @return <code>true</code> if the session is closed, false otherwise.
*/
public boolean isClosed() {
try {
if( mSession == null ){
return true;
}
return mSession.isClosed();
} catch (RemoteException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* Close any channel opened on this session.
* @throws IOException
*/
public void closeChannels() {
if (mService == null || mService.isConnected() == false) {
throw new IllegalStateException("service not connected to system");
}
if( mSession != null ){
synchronized (mLock) {
SmartcardError error = new SmartcardError();
try {
mSession.closeChannels(error);
} catch (RemoteException e) {
throw new RuntimeException(e.getMessage());
}
SEService.checkForException(error);
}
}
}
/**
* 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, which 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. If such a restriction is not
* possible, then openBasicChannel(null) should always return null and therefore prevent access to the
* default Applet on the basic channel.
* <p>
*
* The optional select response data of an applet can be retrieved with byte[] getSelectResponse().
*
* @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 (e.g. if the SE is not responding).
* @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 application on this
* session.
*
* @throws NoSuchElementException if an Applet with the defined AID does not exist in the SE
*
* @return an instance of Channel if available or null.
*/
public Channel openBasicChannel(byte[] aid) throws IOException {
if (mService == null || mService.isConnected() == false) {
throw new IllegalStateException("service not connected to system");
}
if( mSession == null ){
throw new NullPointerException("service session is null");
}
if (getReader() == null) {
throw new NullPointerException("reader must not be null");
}
synchronized (mLock) {
ISmartcardServiceChannel channel;
SmartcardError error = new SmartcardError();
try {
channel = mSession.openBasicChannelAid(
aid,
mService.getCallback(),
error);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
SEService.checkForException(error);
error.clear();
boolean b = basicChannelInUse(error);
SEService.checkForException(error);
if ( b ) {
return null;
}
error.clear();
b = channelCannotBeEstablished(error);
SEService.checkForException(error);
if (b) {
return null;
}
if(aid == null || aid.length == 0)
{
error.clear();
b = isDefaultApplicationSelected(error);
SEService.checkForException(error);
if (!b) {
return null;
}
}
error.clear();
checkIfAppletAvailable(error);
SEService.checkForException(error);
if (channel == null)
return null;
return new Channel(mService, this, channel );
}
}
/**
* Open a logical channel with the Secure Element, selecting the Applet
* represented by the given AID. The AID can be 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.
* <p>
* The optional select response data of an applet can be retrieved with byte[] getSelectResponse().
* <p>
* A logical channel to an applet can be opened multiple times if the applet implements MultiSelectable.
*
* @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. (e.g. if the SE is
* not responding)
* @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 application on this
* session.
* @throws NoSuchElementException if an Applet with the defined AID does not exist in the SE or a logical channel is already open to a non-multiselectable applet
*
* @return an instance of Channel. Null if the Secure Element is unable to
* provide a new logical channel.
*/
public Channel openLogicalChannel(byte[] aid) throws IOException {
if (mService == null || mService.isConnected() == false) {
throw new IllegalStateException("service not connected to system");
}
if( mSession == null ){
throw new NullPointerException("service session is null");
}
if (getReader() == null) {
throw new NullPointerException("reader must not be null");
}
synchronized (mLock) {
SmartcardError error = new SmartcardError();
ISmartcardServiceChannel channel;
try {
channel = mSession.openLogicalChannel(
aid,
mService.getCallback(),
error);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
SEService.checkForException(error);
error.clear();
boolean b = channelCannotBeEstablished(error);
SEService.checkForException(error);
if (b) {
return null;
}
error.clear();
checkIfAppletAvailable(error);
SEService.checkForException(error);
if (channel == null)
return null;
return new Channel(mService, this, channel);
}
}
// ******************************************************************
// package private methods
// ******************************************************************
private boolean isDefaultApplicationSelected(SmartcardError error) {
Exception exp = error.createException();
if (exp != null) {
String msg = exp.getMessage();
if (msg != null) {
if (msg.contains("default application is not selected")) {
return false;
}
}
}
return true;
}
private boolean basicChannelInUse(SmartcardError error) {
Exception exp = error.createException();
if (exp != null) {
String msg = exp.getMessage();
if (msg != null) {
if (msg.contains("basic channel in use")) {
return true;
}
}
}
return false;
}
private boolean channelCannotBeEstablished(SmartcardError error) {
Exception exp = error.createException();
if (exp != null) {
if (exp instanceof MissingResourceException) {
return true;
}
String msg = exp.getMessage();
if (msg != null) {
if (msg.contains("channel in use")) {
return true;
}
if (msg.contains("open channel failed")) {
return true;
}
if (msg.contains("out of channels")) {
return true;
}
if (msg.contains("MANAGE CHANNEL")) {
return true;
}
}
}
return false;
}
private void checkIfAppletAvailable(SmartcardError error) throws NoSuchElementException {
Exception exp = error.createException();
if (exp != null) {
if(exp instanceof NoSuchElementException) {
throw new NoSuchElementException("Applet with the defined aid does not exist in the SE");
}
}
}
}