1 /*
2  * Copyright (C) 2024 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.android.nfc.reader;
18 
19 import android.content.Intent;
20 import android.nfc.NfcAdapter;
21 import android.nfc.NfcAdapter.ReaderCallback;
22 import android.nfc.Tag;
23 import android.nfc.tech.IsoDep;
24 import android.os.Parcelable;
25 import android.util.Log;
26 
27 import com.android.nfc.utils.CommandApdu;
28 import com.android.nfc.utils.HceUtils;
29 
30 import java.io.IOException;
31 import java.util.Arrays;
32 
33 /** Basic reader activity that sends and receives APDUs to tag when discovered. */
34 public class SimpleReaderActivity extends BaseReaderActivity implements ReaderCallback {
35     public static final String EXTRA_APDUS = "apdus";
36     public static final String EXTRA_RESPONSES = "responses";
37 
38     private static final String TAG = "SimpleReaderActivity";
39     private static final String EXTRA_NFC_TECH = "nfc_tech";
40     public static final int NFC_TECH_A_POLLING_ON =
41             NfcAdapter.FLAG_READER_NFC_A
42                     | NfcAdapter.FLAG_READER_NFC_BARCODE
43                     | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;
44 
45 
46     private CommandApdu[] mApdus;
47     private String[] mResponses;
48 
49     @Override
onResume()50     protected void onResume() {
51         super.onResume();
52         Intent intent = getIntent();
53         setIntent(intent);
54         int nfcTech = intent.getIntExtra(EXTRA_NFC_TECH, NFC_TECH_A_POLLING_ON);
55         mAdapter.enableReaderMode(this, this, nfcTech, null);
56         Parcelable[] apdus = intent.getParcelableArrayExtra(EXTRA_APDUS);
57         if (apdus != null) {
58             mApdus = new CommandApdu[apdus.length];
59             for (int i = 0; i < apdus.length; i++) {
60                 mApdus[i] = (CommandApdu) apdus[i];
61             }
62         } else {
63             mApdus = null;
64         }
65 
66         mResponses = intent.getStringArrayExtra(EXTRA_RESPONSES);
67     }
68 
69     // Override the default setPollTech for this case since we have a specific reader activity.
70     @Override
setPollTech(int pollTech)71     public void setPollTech(int pollTech) {
72         Log.d(TAG, "setting polltech to " + pollTech);
73         mAdapter.enableReaderMode(this, this, pollTech, null);
74     }
75 
76     @Override
onPause()77     protected void onPause() {
78         super.onPause();
79         Log.d(TAG, "onPause");
80     }
81 
82     @Override
onTagDiscovered(Tag tag)83     public void onTagDiscovered(Tag tag) {
84         Log.d(TAG, "onTagDiscovered");
85         final StringBuilder sb = new StringBuilder();
86         IsoDep isoDep = IsoDep.get(tag);
87         if (isoDep == null) {
88             return;
89         }
90 
91         boolean success = true;
92         long startTime = System.currentTimeMillis();
93         try {
94             isoDep.connect();
95             isoDep.setTimeout(5000);
96             int count = 0;
97 
98             for (CommandApdu apdu : mApdus) {
99                 sb.append("Request APDU:\n");
100                 sb.append(apdu.getApdu()).append("\n\n");
101                 long apduStartTime = System.currentTimeMillis();
102                 byte[] response = isoDep.transceive(HceUtils.hexStringToBytes(apdu.getApdu()));
103                 long apduEndTime = System.currentTimeMillis();
104                 sb.append("Response APDU (in ")
105                         .append(apduEndTime - apduStartTime)
106                         .append(" ms):\n");
107                 sb.append(HceUtils.getHexBytes(null, response));
108 
109                 sb.append("\n\n\n");
110                 boolean wildCard = "*".equals(mResponses[count]);
111                 byte[] expectedResponse = HceUtils.hexStringToBytes(mResponses[count]);
112                 Log.d(TAG, HceUtils.getHexBytes("APDU response: ", response));
113                 if (!wildCard && !Arrays.equals(response, expectedResponse)) {
114                     Log.d(TAG, "Unexpected APDU response: " + HceUtils.getHexBytes("", response)
115                             + " expected: " + mResponses[count]);
116                     success = false;
117                     break;
118                 }
119                 count++;
120             }
121         } catch (IOException e) {
122             sb.insert(
123                     0,
124                     "Error while reading: (did you keep the devices in range?)\nPlease try "
125                             + "again\n.");
126             Log.e(TAG, sb.toString());
127         }
128         if (success) {
129             sb.insert(
130                     0,
131                     "Total APDU exchange time: "
132                             + (System.currentTimeMillis() - startTime)
133                             + " ms.\n\n");
134             Log.d(TAG, sb.toString());
135             setTestPassed();
136         } else {
137             sb.insert(
138                     0,
139                     "FAIL. Total APDU exchange time: "
140                             + (System.currentTimeMillis() - startTime)
141                             + " ms.\n\n");
142             Log.w(TAG, sb.toString());
143         }
144     }
145 }
146