Skip to content

Most visited

Recently visited

navigation
CardEmulation / src / com.example.android.cardemulation /

CardService.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
 
17
package com.example.android.cardemulation;
18
 
19
import android.nfc.cardemulation.HostApduService;
20
import android.os.Bundle;
21
import com.example.android.common.logger.Log;
22
 
23
import java.util.Arrays;
24
 
25
/**
26
 * This is a sample APDU Service which demonstrates how to interface with the card emulation support
27
 * added in Android 4.4, KitKat.
28
 *
29
 * <p>This sample replies to any requests sent with the string "Hello World". In real-world
30
 * situations, you would need to modify this code to implement your desired communication
31
 * protocol.
32
 *
33
 * <p>This sample will be invoked for any terminals selecting AIDs of 0xF11111111, 0xF22222222, or
34
 * 0xF33333333. See src/main/res/xml/aid_list.xml for more details.
35
 *
36
 * <p class="note">Note: This is a low-level interface. Unlike the NdefMessage many developers
37
 * are familiar with for implementing Android Beam in apps, card emulation only provides a
38
 * byte-array based communication channel. It is left to developers to implement higher level
39
 * protocol support as needed.
40
 */
41
public class CardService extends HostApduService {
42
    private static final String TAG = "CardService";
43
    // AID for our loyalty card service.
44
    private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222";
45
    // ISO-DEP command HEADER for selecting an AID.
46
    // Format: [Class | Instruction | Parameter 1 | Parameter 2]
47
    private static final String SELECT_APDU_HEADER = "00A40400";
48
    // "OK" status word sent in response to SELECT AID command (0x9000)
49
    private static final byte[] SELECT_OK_SW = HexStringToByteArray("9000");
50
    // "UNKNOWN" status word sent in response to invalid APDU command (0x0000)
51
    private static final byte[] UNKNOWN_CMD_SW = HexStringToByteArray("0000");
52
    private static final byte[] SELECT_APDU = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID);
53
 
54
    /**
55
     * Called if the connection to the NFC card is lost, in order to let the application know the
56
     * cause for the disconnection (either a lost link, or another AID being selected by the
57
     * reader).
58
     *
59
     * @param reason Either DEACTIVATION_LINK_LOSS or DEACTIVATION_DESELECTED
60
     */
61
    @Override
62
    public void onDeactivated(int reason) { }
63
 
64
    /**
65
     * This method will be called when a command APDU has been received from a remote device. A
66
     * response APDU can be provided directly by returning a byte-array in this method. In general
67
     * response APDUs must be sent as quickly as possible, given the fact that the user is likely
68
     * holding his device over an NFC reader when this method is called.
69
     *
70
     * <p class="note">If there are multiple services that have registered for the same AIDs in
71
     * their meta-data entry, you will only get called if the user has explicitly selected your
72
     * service, either as a default or just for the next tap.
73
     *
74
     * <p class="note">This method is running on the main thread of your application. If you
75
     * cannot return a response APDU immediately, return null and use the {@link
76
     * #sendResponseApdu(byte[])} method later.
77
     *
78
     * @param commandApdu The APDU that received from the remote device
79
     * @param extras A bundle containing extra data. May be null.
80
     * @return a byte-array containing the response APDU, or null if no response APDU can be sent
81
     * at this point.
82
     */
84
    @Override
85
    public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
86
        Log.i(TAG, "Received APDU: " + ByteArrayToHexString(commandApdu));
87
        // If the APDU matches the SELECT AID command for this service,
88
        // send the loyalty card account number, followed by a SELECT_OK status trailer (0x9000).
89
        if (Arrays.equals(SELECT_APDU, commandApdu)) {
90
            String account = AccountStorage.GetAccount(this);
91
            byte[] accountBytes = account.getBytes();
92
            Log.i(TAG, "Sending account number: " + account);
93
            return ConcatArrays(accountBytes, SELECT_OK_SW);
94
        } else {
95
            return UNKNOWN_CMD_SW;
96
        }
97
    }
99
 
100
    /**
101
     * Build APDU for SELECT AID command. This command indicates which service a reader is
102
     * interested in communicating with. See ISO 7816-4.
103
     *
104
     * @param aid Application ID (AID) to select
105
     * @return APDU for SELECT AID command
106
     */
107
    public static byte[] BuildSelectApdu(String aid) {
108
        // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
109
        return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X",
110
                aid.length() / 2) + aid);
111
    }
112
 
113
    /**
114
     * Utility method 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]; // Each byte has two hex characters (nibbles)
122
        int v;
123
        for (int j = 0; j < bytes.length; j++) {
124
            v = bytes[j] & 0xFF; // Cast bytes[j] to int, treating as unsigned value
125
            hexChars[j * 2] = hexArray[v >>> 4]; // Select hex character from upper nibble
126
            hexChars[j * 2 + 1] = hexArray[v & 0x0F]; // Select hex character from lower nibble
127
        }
128
        return new String(hexChars);
129
    }
130
 
131
    /**
132
     * Utility method 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
     * @throws java.lang.IllegalArgumentException if input length is incorrect
139
     */
140
    public static byte[] HexStringToByteArray(String s) throws IllegalArgumentException {
141
        int len = s.length();
142
        if (len % 2 == 1) {
143
            throw new IllegalArgumentException("Hex string must have even number of characters");
144
        }
145
        byte[] data = new byte[len / 2]; // Allocate 1 byte per 2 hex characters
146
        for (int i = 0; i < len; i += 2) {
147
            // Convert each character into a integer (base-16), then bit-shift into place
148
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
149
                    + Character.digit(s.charAt(i+1), 16));
150
        }
151
        return data;
152
    }
153
 
154
    /**
155
     * Utility method to concatenate two byte arrays.
156
     * @param first First array
157
     * @param rest Any remaining arrays
158
     * @return Concatenated copy of input arrays
159
     */
160
    public static byte[] ConcatArrays(byte[] first, byte[]... rest) {
161
        int totalLength = first.length;
162
        for (byte[] array : rest) {
163
            totalLength += array.length;
164
        }
165
        byte[] result = Arrays.copyOf(first, totalLength);
166
        int offset = first.length;
167
        for (byte[] array : rest) {
168
            System.arraycopy(array, 0, result, offset, array.length);
169
            offset += array.length;
170
        }
171
        return result;
172
    }
173
}
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.