Skip to content

Most visited

Recently visited

navigation
CardReader / src / com.example.android.cardreader /

LoyaltyCardReader.java

1
/*
2
 * Copyright (C) 2013 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
package com.example.android.cardreader;
17
 
18
import android.nfc.NfcAdapter;
19
import android.nfc.Tag;
20
import android.nfc.tech.IsoDep;
21
 
22
import com.example.android.common.logger.Log;
23
 
24
import java.io.IOException;
25
import java.lang.ref.WeakReference;
26
import java.util.Arrays;
27
 
28
/**
29
 * Callback class, invoked when an NFC card is scanned while the device is running in reader mode.
30
 *
31
 * Reader mode can be invoked by calling NfcAdapter
32
 */
33
public class LoyaltyCardReader implements NfcAdapter.ReaderCallback {
34
    private static final String TAG = "LoyaltyCardReader";
35
    // AID for our loyalty card service.
36
    private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222";
37
    // ISO-DEP command HEADER for selecting an AID.
38
    // Format: [Class | Instruction | Parameter 1 | Parameter 2]
39
    private static final String SELECT_APDU_HEADER = "00A40400";
40
    // "OK" status word sent in response to SELECT AID command (0x9000)
41
    private static final byte[] SELECT_OK_SW = {(byte) 0x90, (byte) 0x00};
42
 
43
    // Weak reference to prevent retain loop. mAccountCallback is responsible for exiting
44
    // foreground mode before it becomes invalid (e.g. during onPause() or onStop()).
45
    private WeakReference<AccountCallback> mAccountCallback;
46
 
47
    public interface AccountCallback {
48
        public void onAccountReceived(String account);
49
    }
50
 
51
    public LoyaltyCardReader(AccountCallback accountCallback) {
52
        mAccountCallback = new WeakReference<AccountCallback>(accountCallback);
53
    }
54
 
55
    /**
56
     * Callback when a new tag is discovered by the system.
57
     *
58
     * <p>Communication with the card should take place here.
59
     *
60
     * @param tag Discovered tag
61
     */
62
    @Override
63
    public void onTagDiscovered(Tag tag) {
64
        Log.i(TAG, "New tag discovered");
65
        // Android's Host-based Card Emulation (HCE) feature implements the ISO-DEP (ISO 14443-4)
66
        // protocol.
67
        //
68
        // In order to communicate with a device using HCE, the discovered tag should be processed
69
        // using the IsoDep class.
70
        IsoDep isoDep = IsoDep.get(tag);
71
        if (isoDep != null) {
72
            try {
73
                // Connect to the remote NFC device
74
                isoDep.connect();
75
                // Build SELECT AID command for our loyalty card service.
76
                // This command tells the remote device which service we wish to communicate with.
77
                Log.i(TAG, "Requesting remote AID: " + SAMPLE_LOYALTY_CARD_AID);
78
                byte[] command = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID);
79
                // Send command to remote device
80
                Log.i(TAG, "Sending: " + ByteArrayToHexString(command));
81
                byte[] result = isoDep.transceive(command);
82
                // If AID is successfully selected, 0x9000 is returned as the status word (last 2
83
                // bytes of the result) by convention. Everything before the status word is
84
                // optional payload, which is used here to hold the account number.
85
                int resultLength = result.length;
86
                byte[] statusWord = {result[resultLength-2], result[resultLength-1]};
87
                byte[] payload = Arrays.copyOf(result, resultLength-2);
88
                if (Arrays.equals(SELECT_OK_SW, statusWord)) {
89
                    // The remote NFC device will immediately respond with its stored account number
90
                    String accountNumber = new String(payload, "UTF-8");
91
                    Log.i(TAG, "Received: " + accountNumber);
92
                    // Inform CardReaderFragment of received account number
93
                    mAccountCallback.get().onAccountReceived(accountNumber);
94
                }
95
            } catch (IOException e) {
96
                Log.e(TAG, "Error communicating with card: " + e.toString());
97
            }
98
        }
99
    }
100
 
101
    /**
102
     * Build APDU for SELECT AID command. This command indicates which service a reader is
103
     * interested in communicating with. See ISO 7816-4.
104
     *
105
     * @param aid Application ID (AID) to select
106
     * @return APDU for SELECT AID command
107
     */
108
    public static byte[] BuildSelectApdu(String aid) {
109
        // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
110
        return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", aid.length() / 2) + aid);
111
    }
112
 
113
    /**
114
     * Utility class to convert a byte array to a hexadecimal string.
115
     *
116
     * @param bytes Bytes to convert
117
     * @return String, containing hexadecimal representation.
118
     */
119
    public static String ByteArrayToHexString(byte[] bytes) {
120
        final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
121
        char[] hexChars = new char[bytes.length * 2];
122
        int v;
123
        for ( int j = 0; j < bytes.length; j++ ) {
124
            v = bytes[j] & 0xFF;
125
            hexChars[j * 2] = hexArray[v >>> 4];
126
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
127
        }
128
        return new String(hexChars);
129
    }
130
 
131
    /**
132
     * Utility class to convert a hexadecimal string to a byte string.
133
     *
134
     * <p>Behavior with input strings containing non-hexadecimal characters is undefined.
135
     *
136
     * @param s String containing hexadecimal characters to convert
137
     * @return Byte array generated from input
138
     */
139
    public static byte[] HexStringToByteArray(String s) {
140
        int len = s.length();
141
        byte[] data = new byte[len / 2];
142
        for (int i = 0; i < len; i += 2) {
143
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
144
                    + Character.digit(s.charAt(i+1), 16));
145
        }
146
        return data;
147
    }
148
 
149
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.