summaryrefslogtreecommitdiff
path: root/smcc/src/main/java/org/openecard/scio/osx/CardImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/main/java/org/openecard/scio/osx/CardImpl.java')
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/CardImpl.java289
1 files changed, 289 insertions, 0 deletions
diff --git a/smcc/src/main/java/org/openecard/scio/osx/CardImpl.java b/smcc/src/main/java/org/openecard/scio/osx/CardImpl.java
new file mode 100644
index 00000000..de798817
--- /dev/null
+++ b/smcc/src/main/java/org/openecard/scio/osx/CardImpl.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openecard.scio.osx;
+
+import static org.openecard.scio.osx.PCSC.SCARD_LEAVE_CARD;
+import static org.openecard.scio.osx.PCSC.SCARD_RESET_CARD;
+import static org.openecard.scio.osx.PCSC.SCARD_SHARE_DIRECT;
+import static org.openecard.scio.osx.PCSC.SCARD_SHARE_SHARED;
+import static org.openecard.scio.osx.PCSC.SCARD_W_REMOVED_CARD;
+import static org.openecard.scio.osx.PCSC.SCardBeginTransaction;
+import static org.openecard.scio.osx.PCSC.SCardConnect;
+import static org.openecard.scio.osx.PCSC.SCardControl;
+import static org.openecard.scio.osx.PCSC.SCardDisconnect;
+import static org.openecard.scio.osx.PCSC.SCardEndTransaction;
+import static org.openecard.scio.osx.PCSC.SCardStatus;
+import static org.openecard.scio.osx.PCSC.SCardTransmit;
+import static org.openecard.scio.osx.PlatformPCSC.SCARD_PROTOCOL_T0;
+import static org.openecard.scio.osx.PlatformPCSC.SCARD_PROTOCOL_T1;
+
+import javax.smartcardio.ATR;
+import javax.smartcardio.Card;
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardPermission;
+
+/**
+ * Card implementation.
+ *
+ * @since 1.6
+ * @author Andreas Sterbenz
+ */
+final class CardImpl extends Card {
+
+ private static enum State {
+ OK, REMOVED, DISCONNECTED
+ };
+
+ // the terminal that created this card
+ private final TerminalImpl terminal;
+
+ // the native SCARDHANDLE
+ final long cardId;
+
+ // atr of this card
+ private final ATR atr;
+
+ // protocol in use, one of SCARD_PROTOCOL_T0 and SCARD_PROTOCOL_T1
+ final int protocol;
+
+ // the basic logical channel (channel 0)
+ private final ChannelImpl basicChannel;
+
+ // state of this card connection
+ private volatile State state;
+
+ // thread holding exclusive access to the card, or null
+ private volatile Thread exclusiveThread;
+
+ CardImpl(TerminalImpl terminal, String protocol) throws PCSCException {
+ this.terminal = terminal;
+ int sharingMode = SCARD_SHARE_SHARED;
+ int connectProtocol;
+ if (protocol.equals("*")) {
+ connectProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
+ } else if (protocol.equalsIgnoreCase("T=0")) {
+ connectProtocol = SCARD_PROTOCOL_T0;
+ } else if (protocol.equalsIgnoreCase("T=1")) {
+ connectProtocol = SCARD_PROTOCOL_T1;
+ } else if (protocol.equalsIgnoreCase("direct")) {
+ // testing
+ connectProtocol = 0;
+ sharingMode = SCARD_SHARE_DIRECT;
+ } else {
+ throw new IllegalArgumentException("Unsupported protocol " + protocol);
+ }
+ cardId = SCardConnect(terminal.contextId, terminal.name, sharingMode,
+ connectProtocol);
+ byte[] status = new byte[2];
+ byte[] atrBytes = SCardStatus(cardId, status);
+ atr = new ATR(atrBytes);
+ this.protocol = status[1] & 0xff;
+ basicChannel = new ChannelImpl(this, 0);
+ state = State.OK;
+ }
+
+ void checkState() {
+ State s = state;
+ if (s == State.DISCONNECTED) {
+ throw new IllegalStateException("Card has been disconnected");
+ } else if (s == State.REMOVED) {
+ throw new IllegalStateException("Card has been removed");
+ }
+ }
+
+ boolean isValid() {
+ if (state != State.OK) {
+ return false;
+ }
+ // ping card via SCardStatus
+ try {
+ SCardStatus(cardId, new byte[2]);
+ return true;
+ } catch (PCSCException e) {
+ state = State.REMOVED;
+ return false;
+ }
+ }
+
+ private void checkSecurity(String action) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new CardPermission(terminal.name, action));
+ }
+ }
+
+ void handleError(PCSCException e) {
+ if (e.code == SCARD_W_REMOVED_CARD) {
+ state = State.REMOVED;
+ }
+ }
+
+ public ATR getATR() {
+ return atr;
+ }
+
+ public String getProtocol() {
+ switch (protocol) {
+ case SCARD_PROTOCOL_T0:
+ return "T=0";
+ case SCARD_PROTOCOL_T1:
+ return "T=1";
+ default:
+ // should never occur
+ return "Unknown protocol " + protocol;
+ }
+ }
+
+ public CardChannel getBasicChannel() {
+ checkSecurity("getBasicChannel");
+ checkState();
+ return basicChannel;
+ }
+
+ private static int getSW(byte[] b) {
+ if (b.length < 2) {
+ return -1;
+ }
+ int sw1 = b[b.length - 2] & 0xff;
+ int sw2 = b[b.length - 1] & 0xff;
+ return (sw1 << 8) | sw2;
+ }
+
+ private static byte[] commandOpenChannel = new byte[] { 0, 0x70, 0, 0, 1 };
+
+ public CardChannel openLogicalChannel() throws CardException {
+ checkSecurity("openLogicalChannel");
+ checkState();
+ checkExclusive();
+ try {
+ byte[] response = SCardTransmit(cardId, protocol, commandOpenChannel, 0,
+ commandOpenChannel.length);
+ if ((response.length != 3) || (getSW(response) != 0x9000)) {
+ throw new CardException("openLogicalChannel() failed, card response: "
+ + PCSC.toString(response));
+ }
+ return new ChannelImpl(this, response[0]);
+ } catch (PCSCException e) {
+ handleError(e);
+ throw new CardException("openLogicalChannel() failed", e);
+ }
+ }
+
+ void checkExclusive() throws CardException {
+ Thread t = exclusiveThread;
+ if (t == null) {
+ return;
+ }
+ if (t != Thread.currentThread()) {
+ throw new CardException("Exclusive access established by another Thread");
+ }
+ }
+
+ public synchronized void beginExclusive() throws CardException {
+ checkSecurity("exclusive");
+ checkState();
+ if (exclusiveThread != null) {
+ throw new CardException(
+ "Exclusive access has already been assigned to Thread "
+ + exclusiveThread.getName());
+ }
+ try {
+ SCardBeginTransaction(cardId);
+ } catch (PCSCException e) {
+ handleError(e);
+ throw new CardException("beginExclusive() failed", e);
+ }
+ exclusiveThread = Thread.currentThread();
+ }
+
+ public synchronized void endExclusive() throws CardException {
+ checkState();
+ if (exclusiveThread != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Exclusive access not assigned to current Thread");
+ }
+ try {
+ SCardEndTransaction(cardId, SCARD_LEAVE_CARD);
+ } catch (PCSCException e) {
+ handleError(e);
+ throw new CardException("endExclusive() failed", e);
+ } finally {
+ exclusiveThread = null;
+ }
+ }
+
+ public byte[] transmitControlCommand(int controlCode, byte[] command)
+ throws CardException {
+ checkSecurity("transmitControl");
+ checkState();
+ checkExclusive();
+ if (command == null) {
+ throw new NullPointerException();
+ }
+ try {
+ byte[] r = SCardControl(cardId, controlCode, command);
+ return r;
+ } catch (PCSCException e) {
+ handleError(e);
+ throw new CardException("transmitControlCommand() failed", e);
+ }
+ }
+
+ public void disconnect(boolean reset) throws CardException {
+ if (reset) {
+ checkSecurity("reset");
+ }
+ if (state != State.OK) {
+ return;
+ }
+ checkExclusive();
+ try {
+ SCardDisconnect(cardId, (reset ? SCARD_LEAVE_CARD : SCARD_RESET_CARD));
+ } catch (PCSCException e) {
+ throw new CardException("disconnect() failed", e);
+ } finally {
+ state = State.DISCONNECTED;
+ exclusiveThread = null;
+ }
+ }
+
+ public String toString() {
+ return "PC/SC card in " + terminal.getName() + ", protocol "
+ + getProtocol() + ", state " + state;
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ if (state == State.OK) {
+ SCardDisconnect(cardId, SCARD_LEAVE_CARD);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+}