More CTS tests for HCE.

This one checks protocol params. Also already
added code for default route test for upcoming
release - but commented out for now.

Bug: 10681671
Change-Id: Ica8769b93fa0b1c824063539c11b80db6691e3e2
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index cbd2168..23d9a3a 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -292,6 +292,15 @@
         <activity android:name=".nfc.TagVerifierActivity"
                 android:label="@string/nfc_tag_verifier"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.DefaultRouteEmulatorActivity"
+                android:label="@string/nfc_hce_default_route_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.ProtocolParamsEmulatorActivity"
+                android:label="@string/nfc_hce_protocol_params_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
         <activity android:name=".nfc.hce.SinglePaymentEmulatorActivity"
                 android:label="@string/nfc_hce_single_payment_emulator"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
@@ -300,6 +309,10 @@
                 android:label="@string/nfc_hce_single_payment_reader"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
 
+        <activity android:name=".nfc.hce.ProtocolParamsReaderActivity"
+                android:label="@string/nfc_hce_protocol_params_reader"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
         <activity android:name=".nfc.hce.DualPaymentEmulatorActivity"
                 android:label="@string/nfc_hce_dual_payment_emulator"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 5e0c3a9..503367e 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -267,7 +267,6 @@
     <string name="nfc_ndef_content">Id: %1$s\nMime: %2$s\nPayload: %3$s</string>
 
     <string name="nfc_hce">Host-based card emulation</string>
-    <string name="nfc_hce_default">Default route</string>
     <string name="nfc_hce_reader_tests">HCE reader tests</string>
     <string name="nfc_hce_emulator_tests">HCE emulator tests</string>
     <string name="nfc_hce_emulator_test_info">The host-based card emulation
@@ -288,6 +287,15 @@
     </string>
     <string name="nfc_hce_please_wait">Please wait</string>
     <string name="nfc_hce_setting_up">Setting up card emulation services...</string>
+
+    <string name="nfc_hce_default_route_emulator">Default route (Emulator)</string>
+    <string name="nfc_hce_default_route_reader">Default route (Reader)</string>
+    <string name="nfc_hce_default_route_emulator_help">This test verifies that the default route for ISO-DEP (ISO14443-4) frames is the host CPU. It does this by selecting an AID that any Android HCE phone will respond to if the select command is routed to the host. Please verify that there is no rule in the routing table that points this AID to the host. This test may be passed if the "PASS" button on the reader side lights up after tapping the devices together.</string>
+
+    <string name="nfc_hce_protocol_params_emulator">Protocol parameters (Emulator)</string>
+    <string name="nfc_hce_protocol_params_reader">Protocol parameters (Reader)</string>
+    <string name="nfc_hce_protocol_params_emulator_help">This test verifies that the Nfc-A and ISO-DEP protocol parameters are being set correctly. The test may be passed when the "PASS" button on the reader side lights up. \n\n Note that for each test there are 3 possible outcomes:\n1) OK -> test has passed;\n2) FAIL -> test has failed and this must be fixed;\n3) FAIL EMVCO -> this protocol parameter is deviating from the requirements in the EMV Contactless Communication Protocol specification.\n\nWhile it is allowed to ship a HCE implementation with EMVCo failures, it may not perform optimal when EMVco based payment HCE apps are run on the device.</string>
+
     <string name="nfc_hce_single_payment_emulator">Single payment (Emulator)</string>
     <string name="nfc_hce_single_payment_reader">Single payment (Reader)</string>
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteActivity.java
deleted file mode 100644
index dde77b6..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteActivity.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import com.android.cts.verifier.PassFailButtons;
-
-public class DefaultRouteActivity extends PassFailButtons.Activity {
-
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteEmulatorActivity.java
new file mode 100644
index 0000000..2de99e7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteEmulatorActivity.java
@@ -0,0 +1,49 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+public class DefaultRouteEmulatorActivity extends BaseEmulatorActivity {
+    public static final String[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu("A000000476416E64726F6964484345"),
+    };
+    public static final String[] APDU_RESPONSE_SEQUENCE = {
+        "148100009000"
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        NfcDialogs.createHceTapReaderDialog(this,
+                getString(R.string.nfc_hce_default_route_emulator_help)).show();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS, APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES, APDU_RESPONSE_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_default_route_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
index b0535a1..e85d387 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
@@ -39,6 +39,17 @@
 
         if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
             adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_emulator_tests));
+
+            /*
+             * Only add this test when supported in platform
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_default_route_emulator,
+                    DefaultRouteEmulatorActivity.class.getName(),
+                    new Intent(this, DefaultRouteEmulatorActivity.class), null));
+            */
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_protocol_params_emulator,
+                    ProtocolParamsEmulatorActivity.class.getName(),
+                    new Intent(this, ProtocolParamsEmulatorActivity.class), null));
+
             adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_payment_emulator,
                     SinglePaymentEmulatorActivity.class.getName(),
                     new Intent(this, SinglePaymentEmulatorActivity.class), null));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
index 4893cc3..704cc89 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
@@ -21,6 +21,7 @@
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.TestListAdapter.TestListItem;
 
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 
@@ -37,6 +38,17 @@
 
         if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
             adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_reader_tests));
+            /*
+             * Only add this test when supported in platform
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_default_route_reader,
+                    SimpleReaderActivity.class.getName(),
+                    DefaultRouteEmulatorActivity.buildReaderIntent(this), null));
+             */
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_protocol_params_reader,
+                    ProtocolParamsReaderActivity.class.getName(),
+                    new Intent(this, ProtocolParamsReaderActivity.class), null));
+
             adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_payment_reader,
                     SimpleReaderActivity.class.getName(),
                     SinglePaymentEmulatorActivity.buildReaderIntent(this), null));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsEmulatorActivity.java
new file mode 100644
index 0000000..70842a5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsEmulatorActivity.java
@@ -0,0 +1,32 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+public class ProtocolParamsEmulatorActivity extends BaseEmulatorActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(true);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        NfcDialogs.createHceTapReaderDialog(this,
+                getString(R.string.nfc_hce_protocol_params_emulator_help)).show();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsReaderActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsReaderActivity.java
new file mode 100644
index 0000000..986cdb9
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsReaderActivity.java
@@ -0,0 +1,219 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.ReaderCallback;
+import android.nfc.tech.IsoDep;
+import android.nfc.tech.NfcA;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+
+@TargetApi(19)
+public class ProtocolParamsReaderActivity extends PassFailButtons.Activity implements ReaderCallback {
+    public static final String TAG = "ProtocolParamsReaderActivity";
+
+    NfcAdapter mAdapter;
+
+    TextView mTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        setTitle(R.string.nfc_hce_protocol_params_reader);
+
+        mAdapter = NfcAdapter.getDefaultAdapter(this);
+        mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setTextSize(12.0f);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A |
+                NfcAdapter.FLAG_READER_NFC_BARCODE | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
+    }
+
+    public boolean parseProtocolParameters(StringBuilder sb, byte[] uid,
+            short sak, byte[] atqa, byte[] ats) {
+
+        boolean success = true;
+
+        sb.append("UID: " + HceUtils.getHexBytes(null, uid) + "\n\n");
+        sb.append("SAK: 0x" + Integer.toHexString(sak & 0xFF) + "\n");
+
+        if ((sak & 0x20) != 0) {
+            sb.append("    (OK) ISO-DEP bit (0x20) is set.\n");
+        } else {
+            success = false;
+            sb.append("    (FAIL) ISO-DEP bit (0x20) is NOT set.\n");
+        }
+
+        if ((sak & 0x40) != 0) {
+            sb.append("    (OK) P2P bit (0x40) is set.\n");
+        } else {
+            sb.append("    (WARN) P2P bit (0x40) is NOT set.\n");
+        }
+
+        sb.append("\n");
+        sb.append("ATQA: " + HceUtils.getHexBytes(null, atqa) + "\n");
+        sb.append("\n");
+
+        sb.append("ATS: " + HceUtils.getHexBytes(null, ats) + "\n");
+        sb.append("    TL: 0x" + Integer.toHexString(ats[0] & 0xFF) + "\n");
+        sb.append("    T0: 0x" + Integer.toHexString(ats[1] & 0xFF) + "\n");
+
+        boolean ta_present = false;
+        boolean tb_present = false;
+        boolean tc_present = false;
+        int atsIndex = 1;
+        if ((ats[atsIndex] & 0x40) != 0) {
+            sb.append("        (OK) T(C) is present (bit 7 is set).\n");
+            tc_present = true;
+        } else {
+            success = false;
+            sb.append("        (FAIL) T(C) is not present (bit 7 is NOT set).\n");
+        }
+        if ((ats[atsIndex] & 0x20) != 0) {
+            sb.append("        (OK) T(B) is present (bit 6 is set).\n");
+            tb_present = true;
+        } else {
+            success = false;
+            sb.append("        (FAIL) T(B) is not present (bit 6 is NOT set).\n");
+        }
+        if ((ats[atsIndex] & 0x10) != 0) {
+            sb.append("        (OK) T(A) is present (bit 5 is set).\n");
+            ta_present = true;
+        } else {
+            success = false;
+            sb.append("        (FAIL) T(A) is not present (bit 5 is NOT set).\n");
+        }
+        int fsc = ats[atsIndex] & 0x0F;
+        if (fsc > 8) {
+            success = false;
+            sb.append("        (FAIL) FSC " + Integer.toString(fsc) + " is > 8\n");
+        } else if (fsc < 2) {
+            sb.append("        (FAIL EMVCO) FSC " + Integer.toString(fsc) + " is < 2\n");
+        } else {
+            sb.append("        (OK) FSC = " + Integer.toString(fsc) + "\n");
+        }
+
+        atsIndex++;
+        if (ta_present) {
+            sb.append("    TA: 0x" + Integer.toHexString(ats[atsIndex] & 0xff) + "\n");
+            if ((ats[atsIndex] & 0x80) != 0) {
+                sb.append("        (OK) bit 8 set, indicating only same bit rate divisor.\n");
+            } else {
+                sb.append("        (FAIL EMVCO) bit 8 NOT set, indicating support for assymetric " +
+                        "bit rate divisors. EMVCo requires bit 8 set.\n");
+            }
+            if ((ats[atsIndex] & 0x70) != 0) {
+                sb.append("        (FAIL EMVCO) EMVCo requires bits 7 to 5 set to 0.\n");
+            } else {
+                sb.append("        (OK) bits 7 to 5 indicating only 106 kbit/s L->P supported.\n");
+            }
+            if ((ats[atsIndex] & 0x7) != 0) {
+                sb.append("        (FAIL EMVCO) EMVCo requires bits 3 to 1 set to 0.\n");
+            } else {
+                sb.append("        (OK) bits 3 to 1 indicating only 106 kbit/s P->L supported.\n");
+            }
+            atsIndex++;
+        }
+
+        if (tb_present) {
+            sb.append("    TB: 0x" + Integer.toHexString(ats[3] & 0xFF) + "\n");
+            int fwi = (ats[atsIndex] & 0xF0) >> 4;
+            if (fwi > 8) {
+                success = false;
+                sb.append("        (FAIL) FWI=" + Integer.toString(fwi) + ", should be <= 8\n");
+            } else if (fwi == 8) {
+                sb.append("        (FAIL EMVCO) FWI=" + Integer.toString(fwi) +
+                        ", EMVCo requires <= 7\n");
+            } else {
+                sb.append("        (OK) FWI=" + Integer.toString(fwi) + "\n");
+            }
+            int sfgi = ats[atsIndex] & 0x0F;
+            if (sfgi > 8) {
+                success = false;
+                sb.append("        (FAIL) SFGI=" + Integer.toString(sfgi) + ", should be <= 8\n");
+            } else {
+                sb.append("        (OK) SFGI=" + Integer.toString(sfgi) + "\n");
+            }
+            atsIndex++;
+        }
+
+        if (tc_present) {
+            sb.append("    TC: 0x" + Integer.toHexString(ats[atsIndex] & 0xFF) + "\n");
+            boolean apSupported = (ats[atsIndex] & 0x10) != 0;
+            boolean didSupported = (ats[atsIndex] & 0x02) != 0;
+            boolean nadSupported = (ats[atsIndex] & 0x01) != 0;
+            if (nadSupported) {
+                success = false;
+                sb.append("        (FAIL) NAD bit is not allowed to be set.\n");
+            } else {
+                sb.append("        (OK) NAD bit is not set.\n");
+            }
+            atsIndex++;
+            // See if there's any bytes left for general bytes
+            if (atsIndex + 1 < ats.length) {
+                int bytesToCopy = ats.length - atsIndex;
+                byte[] historical_bytes = new byte[bytesToCopy];
+                System.arraycopy(ats, atsIndex, historical_bytes, 0, bytesToCopy);
+                sb.append("\n(OK) Historical bytes: " +
+                        HceUtils.getHexBytes(null, historical_bytes));
+            }
+        }
+        return success;
+    }
+
+    @Override
+    public void onTagDiscovered(Tag tag) {
+        final StringBuilder sb = new StringBuilder();
+        IsoDep isoDep = IsoDep.get(tag);
+        NfcA nfcA = NfcA.get(tag);
+        boolean success = false;
+        if (nfcA == null || isoDep == null) {
+            return;
+        }
+        try {
+            nfcA.connect();
+            byte[] ats = nfcA.transceive(new byte[] { (byte) 0xE0, (byte)0xF0});
+            success = parseProtocolParameters(sb, tag.getId(), nfcA.getSak(), nfcA.getAtqa(), ats);
+        } catch (IOException e) {
+            sb.insert(0, "Test failed. IOException (did you keep the devices in range?)\n\n.");
+        } finally {
+            if (success) {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText(sb.toString());
+                        getPassButton().setEnabled(true);
+                    }
+                });
+            } else {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText(sb.toString());
+                        getPassButton().setEnabled(false);
+                    }
+                });
+            }
+            try {
+                nfcA.transceive(new byte[] {(byte) 0xC2});
+                nfcA.close();
+                isoDep.connect();
+            } catch (IOException e) {
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SimpleReaderActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SimpleReaderActivity.java
index ff98dd7..553f730 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SimpleReaderActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SimpleReaderActivity.java
@@ -66,6 +66,7 @@
 
         try {
             isoDep.connect();
+            isoDep.setTimeout(5000);
             int count = 0;
             boolean success = true;
             long startTime = System.currentTimeMillis();
@@ -117,7 +118,12 @@
             }
         } catch (IOException e) {
             sb.insert(0, "Test failed. IOException (did you keep the devices in range?)\n\n.");
-            mTextView.setText(sb.toString());
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mTextView.setText(sb.toString());
+                }
+            });
         } finally {
         }
     }