Path: blob/master/src/java.smartcardio/share/classes/sun/security/smartcardio/PCSCTerminals.java
41159 views
/*1* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.smartcardio;2627import java.util.*;28import java.lang.ref.*;2930import javax.smartcardio.*;31import static javax.smartcardio.CardTerminals.State.*;3233import static sun.security.smartcardio.PCSC.*;3435/**36* TerminalFactorySpi implementation class.37*38* @since 1.639* @author Andreas Sterbenz40*/41final class PCSCTerminals extends CardTerminals {4243// SCARDCONTEXT, currently shared between all threads/terminals44private static long contextId;4546// terminal state used by waitForCard()47private Map<String,ReaderState> stateMap;4849PCSCTerminals() {50// empty51}5253static synchronized void initContext() throws PCSCException {54if (contextId == 0) {55contextId = SCardEstablishContext(SCARD_SCOPE_USER);56}57}5859private static final Map<String,Reference<TerminalImpl>> terminals60= new HashMap<String,Reference<TerminalImpl>>();6162private static synchronized TerminalImpl implGetTerminal(String name) {63Reference<TerminalImpl> ref = terminals.get(name);64TerminalImpl terminal = (ref != null) ? ref.get() : null;65if (terminal != null) {66return terminal;67}68terminal = new TerminalImpl(contextId, name);69terminals.put(name, new WeakReference<TerminalImpl>(terminal));70return terminal;7172}7374public synchronized List<CardTerminal> list(State state) throws CardException {75if (state == null) {76throw new NullPointerException();77}78try {79String[] readerNames = SCardListReaders(contextId);80List<CardTerminal> list = new ArrayList<CardTerminal>(readerNames.length);81if (stateMap == null) {82// If waitForChange() has never been called, treat event83// queries as status queries.84if (state == CARD_INSERTION) {85state = CARD_PRESENT;86} else if (state == CARD_REMOVAL) {87state = CARD_ABSENT;88}89}90for (String readerName : readerNames) {91CardTerminal terminal = implGetTerminal(readerName);92ReaderState readerState;93switch (state) {94case ALL:95list.add(terminal);96break;97case CARD_PRESENT:98if (terminal.isCardPresent()) {99list.add(terminal);100}101break;102case CARD_ABSENT:103if (terminal.isCardPresent() == false) {104list.add(terminal);105}106break;107case CARD_INSERTION:108readerState = stateMap.get(readerName);109if ((readerState != null) && readerState.isInsertion()) {110list.add(terminal);111}112break;113case CARD_REMOVAL:114readerState = stateMap.get(readerName);115if ((readerState != null) && readerState.isRemoval()) {116list.add(terminal);117}118break;119default:120throw new CardException("Unknown state: " + state);121}122}123return Collections.unmodifiableList(list);124} catch (PCSCException e) {125throw new CardException("list() failed", e);126}127}128129private static class ReaderState {130private int current, previous;131ReaderState() {132current = SCARD_STATE_UNAWARE;133previous = SCARD_STATE_UNAWARE;134}135int get() {136return current;137}138void update(int newState) {139previous = current;140current = newState;141}142boolean isInsertion() {143return !present(previous) && present(current);144}145boolean isRemoval() {146return present(previous) && !present(current);147}148static boolean present(int state) {149return (state & SCARD_STATE_PRESENT) != 0;150}151}152153public synchronized boolean waitForChange(long timeout) throws CardException {154if (timeout < 0) {155throw new IllegalArgumentException156("Timeout must not be negative: " + timeout);157}158if (stateMap == null) {159// We need to initialize the state database.160// Do that with a recursive call, which will return immediately161// because we pass SCARD_STATE_UNAWARE.162// After that, proceed with the real call.163stateMap = new HashMap<String,ReaderState>();164waitForChange(0);165}166if (timeout == 0) {167timeout = TIMEOUT_INFINITE;168}169try {170String[] readerNames = SCardListReaders(contextId);171int n = readerNames.length;172if (n == 0) {173throw new IllegalStateException("No terminals available");174}175int[] status = new int[n];176ReaderState[] readerStates = new ReaderState[n];177for (int i = 0; i < readerNames.length; i++) {178String name = readerNames[i];179ReaderState state = stateMap.get(name);180if (state == null) {181state = new ReaderState();182}183readerStates[i] = state;184status[i] = state.get();185}186status = SCardGetStatusChange(contextId, timeout, status, readerNames);187stateMap.clear(); // remove any readers that are no longer available188for (int i = 0; i < n; i++) {189ReaderState state = readerStates[i];190state.update(status[i]);191stateMap.put(readerNames[i], state);192}193return true;194} catch (PCSCException e) {195if (e.code == SCARD_E_TIMEOUT) {196return false;197} else {198throw new CardException("waitForChange() failed", e);199}200}201}202203static List<CardTerminal> waitForCards(List<? extends CardTerminal> terminals,204long timeout, boolean wantPresent) throws CardException {205// the argument sanity checks are performed in206// javax.smartcardio.TerminalFactory or TerminalImpl207208long thisTimeout;209if (timeout == 0) {210timeout = TIMEOUT_INFINITE;211thisTimeout = TIMEOUT_INFINITE;212} else {213// if timeout is not infinite, do the initial call that retrieves214// the status with a 0 timeout. Otherwise, we might get incorrect215// timeout exceptions (seen on Solaris with PC/SC shim)216thisTimeout = 0;217}218219String[] names = new String[terminals.size()];220int i = 0;221for (CardTerminal terminal : terminals) {222if (terminal instanceof TerminalImpl == false) {223throw new IllegalArgumentException224("Invalid terminal type: " + terminal.getClass().getName());225}226TerminalImpl impl = (TerminalImpl)terminal;227names[i++] = impl.name;228}229230int[] status = new int[names.length];231Arrays.fill(status, SCARD_STATE_UNAWARE);232233try {234while (true) {235// note that we pass "timeout" on each native PC/SC call236// that means that if we end up making multiple (more than 2)237// calls, we might wait too long.238// for now assume that is unlikely and not a problem.239status = SCardGetStatusChange(contextId, thisTimeout, status, names);240thisTimeout = timeout;241242List<CardTerminal> results = null;243for (i = 0; i < names.length; i++) {244boolean nowPresent = (status[i] & SCARD_STATE_PRESENT) != 0;245if (nowPresent == wantPresent) {246if (results == null) {247results = new ArrayList<CardTerminal>();248}249results.add(implGetTerminal(names[i]));250}251}252253if (results != null) {254return Collections.unmodifiableList(results);255}256}257} catch (PCSCException e) {258if (e.code == SCARD_E_TIMEOUT) {259return Collections.emptyList();260} else {261throw new CardException("waitForCard() failed", e);262}263}264}265266}267268269