001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.cardreader;
028:
029: import java.io.IOException;
030: import java.io.InterruptedIOException;
031: import com.sun.midp.security.*;
032:
033: /**
034: * This class represents card slot abstraction.
035: */
036: public class CardSlot {
037: /**
038: * Device object.
039: */
040: private CardDevice device;
041:
042: /**
043: * Local slot number (inside the device).
044: */
045: private int slotNumber;
046:
047: /**
048: * Security token for this slot.
049: */
050: SecurityToken securityToken;
051:
052: /**
053: * Indicates that the slot has been opened.
054: */
055: private boolean opened;
056:
057: /** Unique card session identifier. */
058: private int cardSessionId;
059:
060: /** This field is used to assign unique card session identifiers. */
061: private static int sessionId = 1;
062:
063: /**
064: * ATR bytes kept since last reset.
065: */
066: private byte[] ATR;
067:
068: /**
069: * A flag which shows if the card was changed.
070: */
071: private boolean cardChanged;
072:
073: /**
074: * isAlive command APDU.
075: */
076: private byte[] isAliveAPDU;
077:
078: /**
079: * isAlive command response buffer.
080: */
081: private byte[] isAliveResponse;
082:
083: /**
084: * Creates card slot on the specified device.
085: *
086: * @param device Card device for which the slot is created
087: * @param slot Local device slot number
088: * @param token Security token for this slot
089: * @throws IOException If slot creation failed.
090: */
091: public CardSlot(CardDevice device, int slot, SecurityToken token)
092: throws IOException {
093: this .device = device;
094: this .slotNumber = slot;
095: this .securityToken = token;
096: opened = false;
097: cardChanged = true;
098: isAliveAPDU = new byte[] { 0, 0x70, (byte) 0x80, 0 };
099: isAliveResponse = new byte[2];
100: }
101:
102: /**
103: * Performs data transfer to the device slot.
104: * It must be called where slot is locked.
105: *
106: * @param request Request APDU bytes
107: * @param response Response bytes
108: * @return Length of response
109: * @throws IOException If a data transfer failed or
110: * the card was changed.
111: */
112: public int xferData(byte[] request, byte[] response)
113: throws IOException {
114: return xferData(false, request, response);
115: }
116:
117: /**
118: * Performs data transfer to the device slot.
119: * It must be called where slot is locked.
120: *
121: * @param aliveChecking true if isAlive command is performed,
122: * false otherwise
123: * @param request Request APDU bytes
124: * @param response Response bytes
125: * @return Length of response
126: * @throws IOException If a data transfer failed or
127: * the card was changed.
128: */
129: private int xferData(boolean aliveChecking, byte[] request,
130: byte[] response) throws IOException {
131: int bytes_read;
132:
133: if (isCardChanged() && !aliveChecking) {
134: unlockSlot();
135: throw new InterruptedIOException("Card changed");
136: }
137:
138: try {
139: bytes_read = device.xfer(request, response);
140: } catch (IOException e) {
141: if (isCardChanged() && !aliveChecking) {
142: unlockSlot();
143: throw new InterruptedIOException("Card changed: " + e);
144: }
145: unlockSlot();
146: throw e;
147: } catch (RuntimeException e) { // We must unlock slot
148: unlockSlot();
149: throw e;
150: }
151: if (isCardChanged() && !aliveChecking) {
152: unlockSlot();
153: throw new InterruptedIOException("Card changed");
154: }
155: return bytes_read;
156: }
157:
158: /**
159: * Checks if the the connection is still live or not. It
160: * is a public wrapper for <code>doIsAlive</code>. Also
161: * checks if a card was changed. Slot must not be locked.
162: * @return <code>true</code> if the connection is alive
163: */
164: public boolean isAlive() {
165: boolean alive = doIsAlive();
166: if (!alive) {
167: try {
168: if (!alive) {
169: lockSlot();
170: boolean changed = isCardChanged();
171: unlockSlot();
172: if (changed) {
173: alive = doIsAlive();
174: }
175: }
176: } catch (IOException e) {
177: alive = false;
178: }
179: }
180: return alive;
181: }
182:
183: /**
184: * Checks if the the connection is still live or not by trying to
185: * send an APDU to close channel 0. If we get an IOException back
186: * that means a card or cref is gone.
187: * @return <code>true</code> if the connection is alive
188: */
189: private boolean doIsAlive() {
190: try {
191: lockSlot();
192: xferData(true, isAliveAPDU, isAliveResponse);
193: unlockSlot();
194: return true;
195: } catch (IOException e) {
196: return false;
197: }
198: }
199:
200: /**
201: * Gets ATR bytes from the device.
202: *
203: * @return ATR bytes.
204: */
205: public byte[] getATR() {
206: byte[] result;
207:
208: if (opened && ATR.length > 0) {
209: result = new byte[ATR.length];
210: System.arraycopy(ATR, 0, result, 0, ATR.length);
211: } else {
212: result = null;
213: }
214: return result;
215: }
216:
217: /**
218: * Gets local device slot number.
219: *
220: * @return Local slot number.
221: */
222: public int getSlotNumber() {
223: return slotNumber;
224: }
225:
226: /**
227: * Returns the card session identifier.
228: * This number is different for different card sessions.
229: * @return the card session identifier or -1 when slot is closed
230: */
231: public int getCardSessionId() {
232: return (opened && !cardChanged) ? cardSessionId : -1;
233: }
234:
235: /**
236: * Starts operations with the slot.
237: * All the transfer operations (<code>xferData</code>)
238: * should be performed when the device is locked.
239: * If any problem occurs an unlock must be done.
240: *
241: * @throws IOException If something fails.
242: */
243: public void lockSlot() throws IOException {
244: try {
245: device.lock();
246: open();
247: device.selectSlot(slotNumber);
248: } catch (IOException e) {
249: unlockSlot();
250: throw e;
251: }
252: }
253:
254: /**
255: * Ends operations with the slot.
256: *
257: * IOException is ignored.
258: */
259: public void unlockSlot() {
260: try {
261: device.unlock();
262: } catch (IOException e) {
263: }
264: }
265:
266: /**
267: * Makes sure that slot is ready for connection.
268: *
269: * @return true if this is first connection to inserted card
270: * @throws IOException If something fails.
271: */
272: public boolean initConnection() throws IOException {
273: boolean prevCardChanged;
274: lockSlot();
275: prevCardChanged = isCardChanged();
276: unlockSlot();
277: cardChanged = false;
278: return prevCardChanged;
279: }
280:
281: /**
282: * Initializes ACL for the slot.
283: */
284: public void initACL() {
285: boolean changed;
286: try {
287: lockSlot();
288: changed = isCardChanged();
289: unlockSlot();
290: if (changed) {
291: com.sun.satsa.acl.AccessControlManager.init(slotNumber);
292: }
293: } catch (IOException e) {
294: } // ignored
295: }
296:
297: /**
298: * Closes the slot in case of error.
299: * <code>device.closeSlot</code> usually
300: * does nothing.
301: * Used for socket-like 'devices'.
302: *
303: * @throws IOException If something fails.
304: */
305: public void closeSlot() throws IOException {
306: if (opened) {
307: device.closeSlot(slotNumber);
308: }
309: opened = false;
310: }
311:
312: /**
313: * Gets device of this slot. For using in derived classes.
314: *
315: * @return Slot device
316: */
317: protected CardDevice getDevice() {
318: return this .device;
319: }
320:
321: /**
322: * Checks if this slot is SAT slot.
323: * @return SAT check result
324: * @throws IOException If an error occured.
325: */
326: public boolean isSAT() throws IOException {
327: return device.isSatSlot(slotNumber);
328: }
329:
330: /**
331: * Checks if the card was withdrawn or inserted.
332: * The device must be locked.
333: *
334: * @return true if a card was changed, false otherwise
335: * @throws IOException If something wrong.
336: */
337: private boolean isCardChanged() throws IOException {
338: try {
339: if (device.isCardChanged()) {
340: closeSlot();
341: }
342: open();
343: } catch (IOException e) {
344: unlockSlot();
345: throw e;
346: }
347: if (cardChanged) {
348: return true;
349: }
350: return false;
351: }
352:
353: /**
354: * Tries to open slot if it closed. Performes the 'RESET' command.
355: * The device must be locked.
356: *
357: * @throws IOException If open fails.
358: */
359: private void open() throws IOException {
360: boolean openedSuccessfuly = false;
361: if (!opened) {
362: try {
363: device.openSlot(slotNumber, securityToken);
364: openedSuccessfuly = true;
365: device.selectSlot(slotNumber);
366: device.reset();
367: cardSessionId = sessionId++;
368: } catch (IOException ie) {
369: if (openedSuccessfuly) {
370: try {
371: device.closeSlot(slotNumber);
372: } catch (IOException ign) {
373: } // ignored
374: }
375: throw new IOException("Cannot open slot #" + slotNumber
376: + ": " + ie);
377: }
378: ATR = device.getATR();
379: cardChanged = true;
380: opened = true;
381: }
382: }
383: }
|