OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / nfc / tech / MifareClassic.java
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package android.nfc.tech;
18
19 import android.nfc.Tag;
20 import android.nfc.TagLostException;
21 import android.os.RemoteException;
22
23 import java.io.IOException;
24 import java.nio.ByteBuffer;
25 import java.nio.ByteOrder;
26
27 /**
28  * Technology class representing MIFARE Classic tags (also known as MIFARE Standard).
29  *
30  * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology
31  * MIFARE Classic tags will still be scanned, but will only show the NfcA technology.
32  *
33  * <p>MIFARE Classic tags have sectors that each contain blocks. The block size is constant at
34  * 16 bytes, but the number of sectors and the sector size varies by product. MIFARE has encryption
35  * built in and each sector has two keys associated with it, as well as ACLs to determine what
36  * level acess each key grants. Before operating on a sector you must call either
37  * {@link #authenticateSectorWithKeyA(int, byte[])} or
38  * {@link #authenticateSectorWithKeyB(int, byte[])} to gain authorization for your request.
39  */
40 public final class MifareClassic extends BasicTagTechnology {
41     /**
42      * The well-known default MIFARE read key. All keys are set to this at the factory.
43      * Using this key will effectively make the payload in the sector public.
44      */
45     public static final byte[] KEY_DEFAULT =
46             {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
47     /**
48      * The well-known, default MIFARE Application Directory read key.
49      */
50     public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
51             {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
52     /**
53      * The well-known, default read key for NDEF data on a MIFARE Classic
54      */
55     public static final byte[] KEY_NFC_FORUM =
56             {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
57
58     /** A Mifare Classic compatible card of unknown type */
59     public static final int TYPE_UNKNOWN = -1;
60     /** A MIFARE Classic tag */
61     public static final int TYPE_CLASSIC = 0;
62     /** A MIFARE Plus tag */
63     public static final int TYPE_PLUS = 1;
64     /** A MIFARE Pro tag */
65     public static final int TYPE_PRO = 2;
66
67     /** The tag contains 16 sectors, each holding 4 blocks. */
68     public static final int SIZE_1K = 1024;
69     /** The tag contains 32 sectors, each holding 4 blocks. */
70     public static final int SIZE_2K = 2048;
71     /**
72      * The tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
73      * contain 16 blocks.
74      */
75     public static final int SIZE_4K = 4096;
76     /** The tag contains 5 sectors, each holding 4 blocks. */
77     public static final int SIZE_MINI = 320;
78
79     /** Size of a Mifare Classic block (in bytes) */
80     public static final int BLOCK_SIZE = 16;
81
82     private static final int MAX_BLOCK_COUNT = 256;
83     private static final int MAX_SECTOR_COUNT = 40;
84
85     private boolean mIsEmulated;
86     private int mType;
87     private int mSize;
88
89     /**
90      * Returns an instance of this tech for the given tag. If the tag doesn't support
91      * this tech type null is returned.
92      *
93      * @param tag The tag to get the tech from
94      */
95     public static MifareClassic get(Tag tag) {
96         if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
97         try {
98             return new MifareClassic(tag);
99         } catch (RemoteException e) {
100             return null;
101         }
102     }
103
104     /** @hide */
105     public MifareClassic(Tag tag) throws RemoteException {
106         super(tag, TagTechnology.MIFARE_CLASSIC);
107
108         NfcA a = NfcA.get(tag);  // Mifare Classic is always based on NFC a
109
110         mIsEmulated = false;
111
112         switch (a.getSak()) {
113         case 0x08:
114             mType = TYPE_CLASSIC;
115             mSize = SIZE_1K;
116             break;
117         case 0x09:
118             mType = TYPE_CLASSIC;
119             mSize = SIZE_MINI;
120             break;
121         case 0x10:
122             mType = TYPE_PLUS;
123             mSize = SIZE_2K;
124             // SecLevel = SL2
125             break;
126         case 0x11:
127             mType = TYPE_PLUS;
128             mSize = SIZE_4K;
129             // Seclevel = SL2
130             break;
131         case 0x18:
132             mType = TYPE_CLASSIC;
133             mSize = SIZE_4K;
134             break;
135         case 0x28:
136             mType = TYPE_CLASSIC;
137             mSize = SIZE_1K;
138             mIsEmulated = true;
139             break;
140         case 0x38:
141             mType = TYPE_CLASSIC;
142             mSize = SIZE_4K;
143             mIsEmulated = true;
144             break;
145         case 0x88:
146             mType = TYPE_CLASSIC;
147             mSize = SIZE_1K;
148             // NXP-tag: false
149             break;
150         case 0x98:
151         case 0xB8:
152             mType = TYPE_PRO;
153             mSize = SIZE_4K;
154             break;
155         default:
156             // Stack incorrectly reported a MifareClassic. We cannot handle this
157             // gracefully - we have no idea of the memory layout. Bail.
158             throw new RuntimeException(
159                     "Tag incorrectly enumerated as Mifare Classic, SAK = " + a.getSak());
160         }
161     }
162
163     /** Returns the type of the tag, determined at discovery time */
164     public int getType() {
165         return mType;
166     }
167
168     /** Returns the size of the tag in bytes, determined at discovery time */
169     public int getSize() {
170         return mSize;
171     }
172
173     /** Returns true if the tag is emulated, determined at discovery time.
174      * These are actually smart-cards that emulate a Mifare Classic interface.
175      * They can be treated identically to a Mifare Classic tag.
176      * @hide
177      */
178     public boolean isEmulated() {
179         return mIsEmulated;
180     }
181
182     /** Returns the number of sectors on this tag, determined at discovery time */
183     public int getSectorCount() {
184         switch (mSize) {
185         case SIZE_1K:
186             return 16;
187         case SIZE_2K:
188             return 32;
189         case SIZE_4K:
190             return 40;
191         case SIZE_MINI:
192             return 5;
193         default:
194             return 0;
195         }
196     }
197
198     /** Returns the total block count, determined at discovery time */
199     public int getBlockCount() {
200         return mSize / BLOCK_SIZE;
201     }
202
203     /** Returns the block count for the given sector, determined at discovery time */
204     public int getBlockCountInSector(int sectorIndex) {
205         validateSector(sectorIndex);
206
207         if (sectorIndex < 32) {
208             return 4;
209         } else {
210             return 16;
211         }
212     }
213
214     /** Return the sector index of a given block */
215     public int blockToSector(int blockIndex) {
216         validateBlock(blockIndex);
217
218         if (blockIndex < 32 * 4) {
219             return blockIndex / 4;
220         } else {
221             return 32 + (blockIndex - 32 * 4) / 16;
222         }
223     }
224
225     /** Return the first block of a given sector */
226     public int sectorToBlock(int sectorIndex) {
227         if (sectorIndex < 32) {
228             return sectorIndex * 4;
229         } else {
230             return 32 * 4 + (sectorIndex - 32) * 16;
231         }
232     }
233
234     // Methods that require connect()
235     /**
236      * Authenticate a sector.
237      * <p>Every sector has an A and B key with different access privileges,
238      * this method attempts to authenticate against the A key.
239      * <p>This requires a that the tag be connected.
240      */
241     public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
242         return authenticate(sectorIndex, key, true);
243     }
244
245     /**
246      * Authenticate a sector.
247      * <p>Every sector has an A and B key with different access privileges,
248      * this method attempts to authenticate against the B key.
249      * <p>This requires a that the tag be connected.
250      */
251     public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
252         return authenticate(sectorIndex, key, false);
253     }
254
255     private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
256         validateSector(sector);
257         checkConnected();
258
259         byte[] cmd = new byte[12];
260
261         // First byte is the command
262         if (keyA) {
263             cmd[0] = 0x60; // phHal_eMifareAuthentA
264         } else {
265             cmd[0] = 0x61; // phHal_eMifareAuthentB
266         }
267
268         // Second byte is block address
269         // Authenticate command takes a block address. Authenticating a block
270         // of a sector will authenticate the entire sector.
271         cmd[1] = (byte) sectorToBlock(sector);
272
273         // Next 4 bytes are last 4 bytes of UID
274         byte[] uid = getTag().getId();
275         System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
276
277         // Next 6 bytes are key
278         System.arraycopy(key, 0, cmd, 6, 6);
279
280         try {
281             if (transceive(cmd, false) != null) {
282                 return true;
283             }
284         } catch (TagLostException e) {
285             throw e;
286         } catch (IOException e) {
287             // No need to deal with, will return false anyway
288         }
289         return false;
290     }
291
292     /**
293      * Read 16-byte block.
294      * <p>This requires a that the tag be connected.
295      * @throws IOException
296      */
297     public byte[] readBlock(int blockIndex) throws IOException {
298         validateBlock(blockIndex);
299         checkConnected();
300
301         byte[] cmd = { 0x30, (byte) blockIndex };
302         return transceive(cmd, false);
303     }
304
305     /**
306      * Write 16-byte block.
307      * <p>This requires a that the tag be connected.
308      * @throws IOException
309      */
310     public void writeBlock(int blockIndex, byte[] data) throws IOException {
311         validateBlock(blockIndex);
312         checkConnected();
313         if (data.length != 16) {
314             throw new IllegalArgumentException("must write 16-bytes");
315         }
316
317         byte[] cmd = new byte[data.length + 2];
318         cmd[0] = (byte) 0xA0; // MF write command
319         cmd[1] = (byte) blockIndex;
320         System.arraycopy(data, 0, cmd, 2, data.length);
321
322         transceive(cmd, false);
323     }
324
325     /**
326      * Increment a value block, and store the result in temporary memory.
327      * @param blockIndex
328      * @throws IOException
329      */
330     public void increment(int blockIndex, int value) throws IOException {
331         validateBlock(blockIndex);
332         validateValueOperand(value);
333         checkConnected();
334
335         ByteBuffer cmd = ByteBuffer.allocate(6);
336         cmd.order(ByteOrder.LITTLE_ENDIAN);
337         cmd.put( (byte) 0xC1 );
338         cmd.put( (byte) blockIndex );
339         cmd.putInt(value);
340
341         transceive(cmd.array(), false);
342     }
343
344     /**
345      * Decrement a value block, and store the result in temporary memory.
346      * @param blockIndex
347      * @throws IOException
348      */
349     public void decrement(int blockIndex, int value) throws IOException {
350         validateBlock(blockIndex);
351         validateValueOperand(value);
352         checkConnected();
353
354         ByteBuffer cmd = ByteBuffer.allocate(6);
355         cmd.order(ByteOrder.LITTLE_ENDIAN);
356         cmd.put( (byte) 0xC0 );
357         cmd.put( (byte) blockIndex );
358         cmd.putInt(value);
359
360         transceive(cmd.array(), false);
361     }
362
363     /**
364      * Copy from temporary memory to value block.
365      * @param blockIndex
366      * @throws IOException
367      */
368     public void transfer(int blockIndex) throws IOException {
369         validateBlock(blockIndex);
370         checkConnected();
371
372         byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
373
374         transceive(cmd, false);
375     }
376
377     /**
378      * Copy from value block to temporary memory.
379      * @param blockIndex
380      * @throws IOException
381      */
382     public void restore(int blockIndex) throws IOException {
383         validateBlock(blockIndex);
384         checkConnected();
385
386         byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
387
388         transceive(cmd, false);
389     }
390
391     /**
392      * Send raw NfcA data to a tag and receive the response.
393      * <p>
394      * This method will block until the response is received. It can be canceled
395      * with {@link #close}.
396      * <p>Requires {@link android.Manifest.permission#NFC} permission.
397      * <p>This requires a that the tag be connected.
398      *
399      * @param data bytes to send
400      * @return bytes received in response
401      * @throws IOException if the target is lost or connection closed
402      */
403     public byte[] transceive(byte[] data) throws IOException {
404         return transceive(data, true);
405     }
406
407     private static void validateSector(int sector) {
408         // Do not be too strict on upper bounds checking, since some cards
409         // have more addressable memory than they report. For example,
410         // Mifare Plus 2k cards will appear as Mifare Classic 1k cards when in
411         // Mifare Classic compatibility mode.
412         // Note that issuing a command to an out-of-bounds block is safe - the
413         // tag should report error causing IOException. This validation is a
414         // helper to guard against obvious programming mistakes.
415         if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
416             throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
417         }
418     }
419
420     private static void validateBlock(int block) {
421         // Just looking for obvious out of bounds...
422         if (block < 0 || block >= MAX_BLOCK_COUNT) {
423             throw new IndexOutOfBoundsException("block out of bounds: " + block);
424         }
425     }
426
427     private static void validateValueOperand(int value) {
428         if (value < 0) {
429             throw new IllegalArgumentException("value operand negative");
430         }
431     }
432 }