Merge "Open Mobile API for frameworks/base"
am: 183865f3e1
Change-Id: I96804d1c8e75a6d9d1475438a1753f54e07d0b8a
This commit is contained in:
@@ -200,6 +200,11 @@ java_library {
|
||||
"core/java/android/nfc/INfcUnlockHandler.aidl",
|
||||
"core/java/android/nfc/INfcDta.aidl",
|
||||
"core/java/android/nfc/ITagRemovedCallback.aidl",
|
||||
"core/java/android/se/omapi/ISecureElementService.aidl",
|
||||
"core/java/android/se/omapi/ISecureElementListener.aidl",
|
||||
"core/java/android/se/omapi/ISecureElementChannel.aidl",
|
||||
"core/java/android/se/omapi/ISecureElementReader.aidl",
|
||||
"core/java/android/se/omapi/ISecureElementSession.aidl",
|
||||
"core/java/android/os/IBatteryPropertiesListener.aidl",
|
||||
"core/java/android/os/IBatteryPropertiesRegistrar.aidl",
|
||||
"core/java/android/os/ICancellationSignal.aidl",
|
||||
|
||||
@@ -37013,6 +37013,59 @@ package android.sax {
|
||||
|
||||
}
|
||||
|
||||
package android.se.omapi {
|
||||
|
||||
public class Channel {
|
||||
method public void close();
|
||||
method public byte[] getSelectResponse();
|
||||
method public android.se.omapi.Session getSession();
|
||||
method public boolean isBasicChannel();
|
||||
method public boolean isClosed();
|
||||
method public boolean selectNext() throws java.io.IOException;
|
||||
method public byte[] transmit(byte[]) throws java.io.IOException;
|
||||
}
|
||||
|
||||
public abstract interface ISecureElementListener implements android.os.IInterface {
|
||||
method public abstract void serviceConnected() throws android.os.RemoteException;
|
||||
}
|
||||
|
||||
public static abstract class ISecureElementListener.Stub extends android.os.Binder implements android.se.omapi.ISecureElementListener {
|
||||
ctor public ISecureElementListener.Stub();
|
||||
method public android.os.IBinder asBinder();
|
||||
method public static android.se.omapi.ISecureElementListener asInterface(android.os.IBinder);
|
||||
method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
|
||||
}
|
||||
|
||||
public class Reader {
|
||||
method public void closeSessions();
|
||||
method public java.lang.String getName();
|
||||
method public android.se.omapi.SEService getSEService();
|
||||
method public boolean isSecureElementPresent();
|
||||
method public android.se.omapi.Session openSession() throws java.io.IOException;
|
||||
}
|
||||
|
||||
public class SEService {
|
||||
ctor public SEService(android.content.Context, android.se.omapi.ISecureElementListener);
|
||||
method public android.se.omapi.Reader[] getReaders();
|
||||
method public java.lang.String getVersion();
|
||||
method public boolean isConnected();
|
||||
method public void shutdown();
|
||||
}
|
||||
|
||||
public class Session {
|
||||
method public void close();
|
||||
method public void closeChannels();
|
||||
method public byte[] getATR();
|
||||
method public android.se.omapi.Reader getReader();
|
||||
method public boolean isClosed();
|
||||
method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException;
|
||||
method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException;
|
||||
method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException;
|
||||
method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.security {
|
||||
|
||||
public final class KeyChain {
|
||||
|
||||
251
core/java/android/se/omapi/Channel.java
Normal file
251
core/java/android/se/omapi/Channel.java
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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) 2015-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.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Instances of this class represent an ISO/IEC 7816-4 channel opened to a
|
||||
* Secure Element. It can be either a logical channel or the basic channel. They
|
||||
* can be used to send APDUs to the secure element. Channels are opened by
|
||||
* calling the Session.openBasicChannel(byte[]) or
|
||||
* Session.openLogicalChannel(byte[]) methods.
|
||||
*
|
||||
* @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
|
||||
*/
|
||||
public class Channel {
|
||||
|
||||
private static final String TAG = "OMAPI.Channel";
|
||||
private Session mSession;
|
||||
private final ISecureElementChannel mChannel;
|
||||
private final SEService mService;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
Channel(SEService service, Session session, ISecureElementChannel channel) {
|
||||
if (service == null || session == null || channel == null) {
|
||||
throw new IllegalArgumentException("Parameters cannot be null");
|
||||
}
|
||||
mService = service;
|
||||
mSession = session;
|
||||
mChannel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this channel to the Secure Element. If the method is called when
|
||||
* the channel is already closed, this method will be ignored. The close()
|
||||
* method shall wait for completion of any pending transmit(byte[] command)
|
||||
* before closing the channel.
|
||||
*/
|
||||
public void close() {
|
||||
if (!isClosed()) {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mChannel.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error closing channel", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if this channel is closed.
|
||||
*
|
||||
* @return <code>true</code> if the channel is closed or in case of an error.
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
if (!mService.isConnected()) {
|
||||
Log.e(TAG, "service not connected to system");
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return mChannel.isClosed();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Exception in isClosed()");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean telling if this channel is the basic channel.
|
||||
*
|
||||
* @return <code>true</code> if this channel is a basic channel. <code>false</code> if
|
||||
* this channel is a logical channel.
|
||||
*/
|
||||
public boolean isBasicChannel() {
|
||||
if (!mService.isConnected()) {
|
||||
throw new IllegalStateException("service not connected to system");
|
||||
}
|
||||
try {
|
||||
return mChannel.isBasicChannel();
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit an APDU command (as per ISO/IEC 7816-4) to the Secure Element. The
|
||||
* underlying layers generate as many TPDUs as necessary to transport this APDU. The
|
||||
* API shall ensure that all available data returned from Secure Element, including
|
||||
* concatenated responses, are retrieved and made available to the calling application. If a
|
||||
* warning status code is received the API wont check for further response data but will
|
||||
* return all data received so far and the warning status code.<br>
|
||||
* The transport part is invisible from the application. The generated response is the
|
||||
* response of the APDU which means that all protocols related responses are handled
|
||||
* inside the API or the underlying implementation.<br>
|
||||
* The transmit method shall support extended length APDU commands independently of
|
||||
* the coding within the ATR.<br>
|
||||
* For status word '61 XX' the API or underlying implementation shall issue a GET
|
||||
* RESPONSE command as specified by ISO 7816-4 standard with LE=XX; for the status
|
||||
* word '6C XX', the API or underlying implementation shall reissue the input command
|
||||
* with LE=XX. For other status words, the API (or underlying implementation) shall return
|
||||
* the complete response including data and status word to the device application. The API
|
||||
* (or underlying implementation) shall not handle internally the received status words. The
|
||||
* channel shall not be closed even if the Secure Element answered with an error code.
|
||||
* The system ensures the synchronization between all the concurrent calls to this method,
|
||||
* and that only one APDU will be sent at a time, irrespective of the number of TPDUs that
|
||||
* might be required to transport it to the SE. The entire APDU communication to this SE is
|
||||
* locked to the APDU.<br>
|
||||
* The channel information in the class byte in the APDU will be ignored. The system will
|
||||
* add any required information to ensure the APDU is transported on this channel.
|
||||
* The only restrictions on the set of commands that can be sent is defined below, the API
|
||||
* implementation shall be able to send all other commands: <br>
|
||||
* <ul>
|
||||
* <li>MANAGE_CHANNEL commands are not allowed.</li>
|
||||
* <li>SELECT by DF Name (p1=04) are not allowed.</li>
|
||||
* <li>CLA bytes with channel numbers are de-masked.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param command the APDU command to be transmitted, as a byte array.
|
||||
*
|
||||
* @return the response received, as a byte array. The returned byte array contains the data
|
||||
* bytes in the following order:
|
||||
* [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
|
||||
*
|
||||
* @throws IOException if there is a communication problem to the reader or the Secure Element.
|
||||
* @throws IllegalStateException if the channel is used after being closed.
|
||||
* @throws IllegalArgumentException if the command byte array is less than 4 bytes long.
|
||||
* @throws IllegalArgumentException if Lc byte is inconsistent with length of the byte array.
|
||||
* @throws IllegalArgumentException if CLA byte is invalid according to [2] (0xff).
|
||||
* @throws IllegalArgumentException if INS byte is invalid according to [2] (0x6x or 0x9x).
|
||||
* @throws SecurityException if the command is filtered by the security policy.
|
||||
* @throws NullPointerException if command is NULL.
|
||||
*/
|
||||
public @NonNull byte[] transmit(byte[] command) throws IOException {
|
||||
if (!mService.isConnected()) {
|
||||
throw new IllegalStateException("service not connected to system");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
byte[] response = mChannel.transmit(command);
|
||||
if (response == null) {
|
||||
throw new IOException("Error in communicating with Secure Element");
|
||||
}
|
||||
return response;
|
||||
} catch (RemoteException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the session that has opened this channel.
|
||||
*
|
||||
* @return the session object this channel is bound to.
|
||||
*/
|
||||
public @NonNull Session getSession() {
|
||||
return mSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data as received from the application select command inclusively the status word
|
||||
* received at applet selection.
|
||||
* The returned byte array contains the data bytes in the following order:
|
||||
* [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
|
||||
* @return The data as returned by the application select command inclusively the status word.
|
||||
* Only the status word if the application select command has no returned data.
|
||||
* Returns null if an application select command has not been performed or the selection
|
||||
* response can not be retrieved by the reader implementation.
|
||||
*/
|
||||
public @Nullable byte[] getSelectResponse() {
|
||||
if (!mService.isConnected()) {
|
||||
throw new IllegalStateException("service not connected to system");
|
||||
}
|
||||
|
||||
byte[] response;
|
||||
try {
|
||||
response = mChannel.getSelectResponse();
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException(e.getMessage());
|
||||
}
|
||||
|
||||
if (response != null && response.length == 0) {
|
||||
response = null;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a selection of the next Applet on this channel that matches to the partial AID
|
||||
* specified in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method.
|
||||
* This mechanism can be used by a device application to iterate through all Applets
|
||||
* matching to the same partial AID.
|
||||
* If selectNext() returns true a new Applet was successfully selected on this channel.
|
||||
* If no further Applet exists with matches to the partial AID this method returns false
|
||||
* and the already selected Applet stays selected. <br>
|
||||
*
|
||||
* Since the API cannot distinguish between a partial and full AID the API shall rely on the
|
||||
* response of the Secure Element for the return value of this method. <br>
|
||||
* The implementation of the underlying SELECT command within this method shall use
|
||||
* the same values as the corresponding openBasicChannel(byte[] aid) or
|
||||
* openLogicalChannel(byte[] aid) command with the option: <br>
|
||||
* P2='02' (Next occurrence) <br>
|
||||
* The select response stored in the Channel object shall be updated with the APDU
|
||||
* response of the SELECT command.
|
||||
|
||||
* @return <code>true</code> if new Applet was selected on this channel.
|
||||
<code>false</code> he already selected Applet stays selected on this channel.
|
||||
*
|
||||
* @throws IOException if there is a communication problem to the reader or the Secure Element.
|
||||
* @throws IllegalStateException if the channel is used after being closed.
|
||||
* @throws UnsupportedOperationException if this operation is not supported by the card.
|
||||
*/
|
||||
public boolean selectNext() throws IOException {
|
||||
if (!mService.isConnected()) {
|
||||
throw new IllegalStateException("service not connected to system");
|
||||
}
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
return mChannel.selectNext();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
80
core/java/android/se/omapi/ISecureElementChannel.aidl
Normal file
80
core/java/android/se/omapi/ISecureElementChannel.aidl
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Contributed by: Giesecke & Devrient GmbH.
|
||||
*/
|
||||
|
||||
package android.se.omapi;
|
||||
|
||||
import android.se.omapi.ISecureElementSession;
|
||||
|
||||
/** @hide */
|
||||
interface ISecureElementChannel {
|
||||
|
||||
/**
|
||||
* Closes the specified connection and frees internal resources.
|
||||
* A logical channel will be closed.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Tells if this channel is closed.
|
||||
*
|
||||
* @return <code>true</code> if the channel is closed,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Returns a boolean telling if this channel is the basic channel.
|
||||
*
|
||||
* @return <code>true</code> if this channel is a basic channel.
|
||||
* <code>false</code> if this channel is a logical channel.
|
||||
*/
|
||||
boolean isBasicChannel();
|
||||
|
||||
/**
|
||||
* Returns the data as received from the application select command
|
||||
* inclusively the status word. The returned byte array contains the data
|
||||
* bytes in the following order:
|
||||
* [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
|
||||
*/
|
||||
byte[] getSelectResponse();
|
||||
|
||||
/**
|
||||
* Transmits the specified command APDU and returns the response APDU.
|
||||
* MANAGE channel commands are not supported.
|
||||
* Selection of applets is not supported in logical channels.
|
||||
*/
|
||||
byte[] transmit(in byte[] command);
|
||||
|
||||
/**
|
||||
* Performs a selection of the next Applet on this channel that matches to
|
||||
* the partial AID specified in the openBasicChannel(byte[] aid) or
|
||||
* openLogicalChannel(byte[] aid) method. This mechanism can be used by a
|
||||
* device application to iterate through all Applets matching to the same
|
||||
* partial AID.
|
||||
* If selectNext() returns true a new Applet was successfully selected on
|
||||
* this channel.
|
||||
* If no further Applet exists with matches to the partial AID this method
|
||||
* returns false and the already selected Applet stays selected.
|
||||
*
|
||||
* @return <code>true</code> if new Applet was successfully selected.
|
||||
* <code>false</code> if no further Applet exists which matches the
|
||||
* partial AID.
|
||||
*/
|
||||
boolean selectNext();
|
||||
}
|
||||
30
core/java/android/se/omapi/ISecureElementListener.aidl
Normal file
30
core/java/android/se/omapi/ISecureElementListener.aidl
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Contributed by: Giesecke & Devrient GmbH.
|
||||
*/
|
||||
|
||||
package android.se.omapi;
|
||||
|
||||
/**
|
||||
* Interface to receive call-backs when the service is connected.
|
||||
*/
|
||||
interface ISecureElementListener {
|
||||
/**
|
||||
* Called by the framework when the service is connected.
|
||||
*/
|
||||
void serviceConnected();
|
||||
}
|
||||
51
core/java/android/se/omapi/ISecureElementReader.aidl
Normal file
51
core/java/android/se/omapi/ISecureElementReader.aidl
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Contributed by: Giesecke & Devrient GmbH.
|
||||
*/
|
||||
|
||||
package android.se.omapi;
|
||||
|
||||
import android.se.omapi.ISecureElementSession;
|
||||
|
||||
/** @hide */
|
||||
interface ISecureElementReader {
|
||||
|
||||
/**
|
||||
* Returns true if a card is present in the specified reader.
|
||||
* Returns false if a card is not present in the specified reader.
|
||||
*/
|
||||
boolean isSecureElementPresent();
|
||||
|
||||
/**
|
||||
* Connects to a secure element in this reader. <br>
|
||||
* This method prepares (initialises) the Secure Element for communication
|
||||
* before the Session object is returned (e.g. powers the Secure Element by
|
||||
* ICC ON if its not already on). There might be multiple sessions opened at
|
||||
* the same time on the same reader. The system ensures the interleaving of
|
||||
* APDUs between the respective sessions.
|
||||
*
|
||||
* @return a Session object to be used to create Channels.
|
||||
*/
|
||||
ISecureElementSession openSession();
|
||||
|
||||
/**
|
||||
* Close all the sessions opened on this reader. All the channels opened by
|
||||
* all these sessions will be closed.
|
||||
*/
|
||||
void closeSessions();
|
||||
|
||||
}
|
||||
50
core/java/android/se/omapi/ISecureElementService.aidl
Normal file
50
core/java/android/se/omapi/ISecureElementService.aidl
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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) 2015-2017, The Linux Foundation.
|
||||
*/
|
||||
/*
|
||||
* Contributed by: Giesecke & Devrient GmbH.
|
||||
*/
|
||||
|
||||
package android.se.omapi;
|
||||
|
||||
import android.se.omapi.ISecureElementReader;
|
||||
|
||||
/**
|
||||
* SecureElement service interface.
|
||||
* @hide
|
||||
*/
|
||||
interface ISecureElementService {
|
||||
|
||||
/**
|
||||
* Returns the friendly names of available Secure Element readers.
|
||||
*/
|
||||
String[] getReaders();
|
||||
|
||||
/**
|
||||
* Returns SecureElement Service reader object to the given name.
|
||||
*/
|
||||
ISecureElementReader getReader(String reader);
|
||||
|
||||
/**
|
||||
* Checks if the application defined by the package name is allowed to
|
||||
* receive NFC transaction events for the defined AID.
|
||||
*/
|
||||
boolean[] isNFCEventAllowed(String reader, in byte[] aid,
|
||||
in String[] packageNames);
|
||||
|
||||
}
|
||||
73
core/java/android/se/omapi/ISecureElementSession.aidl
Normal file
73
core/java/android/se/omapi/ISecureElementSession.aidl
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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) 2015-2017, The Linux Foundation.
|
||||
*/
|
||||
/*
|
||||
* Contributed by: Giesecke & Devrient GmbH.
|
||||
*/
|
||||
|
||||
package android.se.omapi;
|
||||
|
||||
import android.se.omapi.ISecureElementChannel;
|
||||
import android.se.omapi.ISecureElementReader;
|
||||
import android.se.omapi.ISecureElementListener;
|
||||
|
||||
/** @hide */
|
||||
interface ISecureElementSession {
|
||||
|
||||
/**
|
||||
* Returns the ATR of the connected card or null if the ATR is not available
|
||||
*/
|
||||
byte[] getAtr();
|
||||
|
||||
/**
|
||||
* Close the connection with the Secure Element. This will close any
|
||||
* channels opened by this application with this Secure Element.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Close any channel opened on this session.
|
||||
*/
|
||||
void closeChannels();
|
||||
|
||||
|
||||
/**
|
||||
* Tells if this session is closed.
|
||||
*
|
||||
* @return <code>true</code> if the session is closed, false otherwise.
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Opens a connection using the basic channel of the card in the
|
||||
* specified reader and returns a channel handle. Selects the specified
|
||||
* applet if aid != null.
|
||||
* Logical channels cannot be opened with this connection.
|
||||
* Use interface method openLogicalChannel() to open a logical channel.
|
||||
*/
|
||||
ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2,
|
||||
ISecureElementListener listener);
|
||||
|
||||
/**
|
||||
* Opens a connection using the next free logical channel of the card in the
|
||||
* specified reader. Selects the specified applet.
|
||||
* Selection of other applets with this connection is not supported.
|
||||
*/
|
||||
ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2,
|
||||
ISecureElementListener listener);
|
||||
}
|
||||
151
core/java/android/se/omapi/Reader.java
Normal file
151
core/java/android/se/omapi/Reader.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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) 2015-2017, The Linux Foundation.
|
||||
*/
|
||||
/*
|
||||
* Contributed by: Giesecke & Devrient GmbH.
|
||||
*/
|
||||
|
||||
package android.se.omapi;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Instances of this class represent Secure Element Readers supported to this
|
||||
* device. These Readers can be physical devices or virtual devices. They can be
|
||||
* removable or not. They can contain Secure Element that can or cannot be
|
||||
* removed.
|
||||
*
|
||||
* @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
|
||||
*/
|
||||
public class Reader {
|
||||
|
||||
private static final String TAG = "OMAPI.Reader";
|
||||
private final String mName;
|
||||
private final SEService mService;
|
||||
private ISecureElementReader mReader;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
|
||||
Reader(SEService service, String name, ISecureElementReader reader) throws
|
||||
IOException {
|
||||
if (reader == null || service == null || name == null) {
|
||||
throw new IllegalArgumentException("Parameters cannot be null");
|
||||
}
|
||||
mName = name;
|
||||
mService = service;
|
||||
mReader = reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of this reader.
|
||||
* <ul>
|
||||
* <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li>
|
||||
* <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
|
||||
* <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
|
||||
* </ul>
|
||||
* Slot is a decimal number without leading zeros. The Numbering must start with 1
|
||||
* (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
|
||||
* The slot number “1” for a reader is optional
|
||||
* (SIM and SIM1 are both valid for the first SIM-reader,
|
||||
* but if there are two readers then the second reader must be named SIM2).
|
||||
* This applies also for other SD or SE readers.
|
||||
*
|
||||
* @return the reader name, as a String.
|
||||
*/
|
||||
public @NonNull String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to a Secure Element in this reader. <br>
|
||||
* This method prepares (initialises) the Secure Element for communication
|
||||
* before the Session object is returned (e.g. powers the Secure Element by
|
||||
* ICC ON if its not already on). There might be multiple sessions opened at
|
||||
* the same time on the same reader. The system ensures the interleaving of
|
||||
* APDUs between the respective sessions.
|
||||
*
|
||||
* @throws IOException if something went wrong with the communicating to the
|
||||
* Secure Element or the reader.
|
||||
* @return a Session object to be used to create Channels.
|
||||
*/
|
||||
public @NonNull Session openSession() throws IOException {
|
||||
if (!mService.isConnected()) {
|
||||
throw new IllegalStateException("service is not connected");
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
ISecureElementSession session;
|
||||
try {
|
||||
session = mReader.openSession();
|
||||
} catch (RemoteException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
if (session == null) {
|
||||
throw new IOException("service session is null.");
|
||||
}
|
||||
return new Session(mService, session, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a Secure Element is present in this reader.
|
||||
*
|
||||
* @throws IllegalStateException if the service is not connected
|
||||
* @return <code>true</code> if the SE is present, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isSecureElementPresent() {
|
||||
if (!mService.isConnected()) {
|
||||
throw new IllegalStateException("service is not connected");
|
||||
}
|
||||
|
||||
try {
|
||||
return mReader.isSecureElementPresent();
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("Error in isSecureElementPresent()");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Secure Element service this reader is bound to.
|
||||
*
|
||||
* @return the SEService object.
|
||||
*/
|
||||
public @NonNull SEService getSEService() {
|
||||
return mService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all the sessions opened on this reader.
|
||||
* All the channels opened by all these sessions will be closed.
|
||||
*/
|
||||
public void closeSessions() {
|
||||
if (!mService.isConnected()) {
|
||||
Log.e(TAG, "service is not connected");
|
||||
return;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mReader.closeSessions();
|
||||
} catch (RemoteException ignore) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
222
core/java/android/se/omapi/SEService.java
Normal file
222
core/java/android/se/omapi/SEService.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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) 2015-2017, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Contributed by: Giesecke & Devrient GmbH.
|
||||
*/
|
||||
|
||||
package android.se.omapi;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* The SEService realises the communication to available Secure Elements on the
|
||||
* device. This is the entry point of this API. It is used to connect to the
|
||||
* infrastructure and get access to a list of Secure Element Readers.
|
||||
*
|
||||
* @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a>
|
||||
*/
|
||||
public class SEService {
|
||||
|
||||
private static final String TAG = "OMAPI.SEService";
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
/** The client context (e.g. activity). */
|
||||
private final Context mContext;
|
||||
|
||||
/** The backend system. */
|
||||
private volatile ISecureElementService mSecureElementService;
|
||||
|
||||
/**
|
||||
* Class for interacting with the main interface of the backend.
|
||||
*/
|
||||
private ServiceConnection mConnection;
|
||||
|
||||
/**
|
||||
* Collection of available readers
|
||||
*/
|
||||
private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
|
||||
|
||||
/**
|
||||
* Listener object that allows the notification of the caller if this
|
||||
* SEService could be bound to the backend.
|
||||
*/
|
||||
private ISecureElementListener mSEListener;
|
||||
|
||||
/**
|
||||
* Establishes a new connection that can be used to connect to all the
|
||||
* Secure Elements available in the system. The connection process can be
|
||||
* quite long, so it happens in an asynchronous way. It is usable only if
|
||||
* the specified listener is called or if isConnected() returns
|
||||
* <code>true</code>. <br>
|
||||
* The call-back object passed as a parameter will have its
|
||||
* serviceConnected() method called when the connection actually happen.
|
||||
*
|
||||
* @param context
|
||||
* the context of the calling application. Cannot be
|
||||
* <code>null</code>.
|
||||
* @param listener
|
||||
* a ISecureElementListener object. Can be <code>null</code>.
|
||||
*/
|
||||
public SEService(Context context, ISecureElementListener listener) {
|
||||
|
||||
if (context == null) {
|
||||
throw new NullPointerException("context must not be null");
|
||||
}
|
||||
|
||||
mContext = context;
|
||||
mSEListener = listener;
|
||||
|
||||
mConnection = new ServiceConnection() {
|
||||
|
||||
public synchronized void onServiceConnected(
|
||||
ComponentName className, IBinder service) {
|
||||
|
||||
mSecureElementService = ISecureElementService.Stub.asInterface(service);
|
||||
if (mSEListener != null) {
|
||||
try {
|
||||
mSEListener.serviceConnected();
|
||||
} catch (RemoteException ignore) { }
|
||||
}
|
||||
Log.i(TAG, "Service onServiceConnected");
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
mSecureElementService = null;
|
||||
Log.i(TAG, "Service onServiceDisconnected");
|
||||
}
|
||||
};
|
||||
|
||||
Intent intent = new Intent(ISecureElementService.class.getName());
|
||||
intent.setClassName("com.android.se",
|
||||
"com.android.se.SecureElementService");
|
||||
boolean bindingSuccessful =
|
||||
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
||||
if (bindingSuccessful) {
|
||||
Log.i(TAG, "bindService successful");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not the service is connected.
|
||||
*
|
||||
* @return <code>true</code> if the service is connected.
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return mSecureElementService != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of available Secure Element readers.
|
||||
* There must be no duplicated objects in the returned list.
|
||||
* All available readers shall be listed even if no card is inserted.
|
||||
*
|
||||
* @return The readers list, as an array of Readers. If there are no
|
||||
* readers the returned array is of length 0.
|
||||
*/
|
||||
public @NonNull Reader[] getReaders() {
|
||||
if (mSecureElementService == null) {
|
||||
throw new IllegalStateException("service not connected to system");
|
||||
}
|
||||
String[] readerNames;
|
||||
try {
|
||||
readerNames = mSecureElementService.getReaders();
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Reader[] readers = new Reader[readerNames.length];
|
||||
int i = 0;
|
||||
for (String readerName : readerNames) {
|
||||
if (mReaders.get(readerName) == null) {
|
||||
try {
|
||||
mReaders.put(readerName, new Reader(this, readerName,
|
||||
getReader(readerName)));
|
||||
readers[i++] = mReaders.get(readerName);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error adding Reader: " + readerName, e);
|
||||
}
|
||||
} else {
|
||||
readers[i++] = mReaders.get(readerName);
|
||||
}
|
||||
}
|
||||
return readers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all Secure Elements resources allocated by this SEService
|
||||
* (including any binding to an underlying service).
|
||||
* As a result isConnected() will return false after shutdown() was called.
|
||||
* After this method call, the SEService object is not connected.
|
||||
* It is recommended to call this method in the termination method of the calling application
|
||||
* (or part of this application) which is bound to this SEService.
|
||||
*/
|
||||
public void shutdown() {
|
||||
synchronized (mLock) {
|
||||
if (mSecureElementService != null) {
|
||||
for (Reader reader : mReaders.values()) {
|
||||
try {
|
||||
reader.closeSessions();
|
||||
} catch (Exception ignore) { }
|
||||
}
|
||||
}
|
||||
try {
|
||||
mContext.unbindService(mConnection);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Do nothing and fail silently since an error here indicates
|
||||
// that binding never succeeded in the first place.
|
||||
}
|
||||
mSecureElementService = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the OpenMobile API specification this
|
||||
* implementation is based on.
|
||||
*
|
||||
* @return String containing the OpenMobile API version (e.g. "3.0").
|
||||
*/
|
||||
public String getVersion() {
|
||||
return "3.2";
|
||||
}
|
||||
|
||||
@NonNull ISecureElementListener getListener() {
|
||||
return mSEListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a Reader instance from the SecureElementService
|
||||
*/
|
||||
private @NonNull ISecureElementReader getReader(String name) {
|
||||
try {
|
||||
return mSecureElementService.getReader(name);
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
347
core/java/android/se/omapi/Session.java
Normal file
347
core/java/android/se/omapi/Session.java
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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.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 (RemoteException e) {
|
||||
throw new IOException(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 (RemoteException e) {
|
||||
throw new IOException(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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user