CTS tests for HCE.

Bug: 10681671
Change-Id: I881508a7226f9340ad37813de2593e8c16a2f684
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index b9f7ceb..cbd2168 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -263,6 +263,24 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.nfc" />
         </activity>
 
+        <activity android:name="com.android.cts.verifier.nfc.hce.HceReaderTestActivity"
+                android:label="@string/nfc_test"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="com.android.cts.verifier.nfc.hce.HceEmulatorTestActivity"
+                android:label="@string/nfc_test"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".nfc.NdefPushSenderActivity"
                 android:label="@string/nfc_ndef_push_sender"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
@@ -274,6 +292,115 @@
         <activity android:name=".nfc.TagVerifierActivity"
                 android:label="@string/nfc_tag_verifier"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
+        <activity android:name=".nfc.hce.SinglePaymentEmulatorActivity"
+                android:label="@string/nfc_hce_single_payment_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.SimpleReaderActivity"
+                android:label="@string/nfc_hce_single_payment_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" />
+
+        <activity android:name=".nfc.hce.ChangeDefaultEmulatorActivity"
+                android:label="@string/nfc_hce_change_default_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.SingleNonPaymentEmulatorActivity"
+                android:label="@string/nfc_hce_single_non_payment_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.DualNonPaymentEmulatorActivity"
+                android:label="@string/nfc_hce_dual_non_payment_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.ConflictingNonPaymentEmulatorActivity"
+                android:label="@string/nfc_hce_conflicting_non_payment_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.OffHostEmulatorActivity"
+                android:label="@string/nfc_hce_offhost_service_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.OnAndOffHostEmulatorActivity"
+                android:label="@string/nfc_hce_on_and_offhost_service_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.ThroughputEmulatorActivity"
+                android:label="@string/nfc_hce_throughput_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hce.TapTestEmulatorActivity"
+                android:label="@string/nfc_hce_tap_test_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <!-- services used for testing NFC host-based card emulation -->
+        <service android:name=".nfc.hce.PaymentService1" android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE"
+                 android:enabled="false">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_1"/>
+        </service>
+        <service android:name=".nfc.hce.PaymentService2" android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE"
+                 android:enabled="false">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_2"/>
+        </service>
+        <service android:name=".nfc.hce.TransportService1" android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE"
+                 android:enabled="false">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/transport_aid_list_1"/>
+        </service>
+        <service android:name=".nfc.hce.TransportService2" android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE"
+                 android:enabled="false">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/transport_aid_list_2"/>
+        </service>
+        <service android:name=".nfc.hce.AccessService" android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE"
+                 android:enabled="false">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/access_aid_list"/>
+        </service>
+        <service android:name=".nfc.hce.ThroughputService" android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE"
+                 android:enabled="false">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/throughput_aid_list"/>
+        </service>
+
+        <service android:name=".nfc.hce.OffHostService" android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE"
+                 android:enabled="false">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service" android:resource="@xml/offhost_aid_list"/>
+        </service>
 
         <activity android:name=".sensors.AccelerometerTestActivity" android:label="@string/snsr_accel_test"
                 android:screenOrientation="nosensor">
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 6df4b42..5e0c3a9 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -206,6 +206,8 @@
         known to be compatible.\n\nThe Tag Verification tests check that your
         device can properly read and write to tags of different technologies. The MIFARE
         Ultralight test is only applicable for devices that support it.
+        \n\nThe Host-based card emulation tests check that your device has properly implemented
+             host-based card emulation.
     </string>
 
     <string name="nfc_not_enabled">NFC is not enabled!</string>
@@ -264,6 +266,75 @@
     <string name="nfc_result_message">Written data:\n%1$s\n\nRead data:\n%2$s</string>
     <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
+        tests require two devices to be completed. The HCE emulator tests are used
+        to actually test the host-based card emulation feature of the device-under-test. So the
+        device running the emulator tests must be the candidate device running the software
+        to be tested. \n\nFor each emulator test, there is a corresponding "reader test"
+        in the "HCE reader tests" section. The "reader test" acts as a NFC terminal/POS
+        and makes sure the device-under-test implements card emulation correctly.
+    </string>
+    <string name="nfc_hce_reader_test_info">The host-based card emulation
+        tests require two devices to be completed. The HCE emulator tests are used
+        to actually test the host-based card emulation feature of the device-under-test. So the
+        device running the emulator tests must be the candidate device running the software
+        to be tested. \n\nFor each emulator test, there is a corresponding "reader test"
+        in the "HCE reader tests" section. The "reader test" acts as a NFC terminal/POS
+        and makes sure the device-under-test implements card emulation correctly.
+    </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_single_payment_emulator">Single payment (Emulator)</string>
+    <string name="nfc_hce_single_payment_reader">Single payment (Reader)</string>
+
+    <string name="nfc_hce_dual_payment_emulator">Two payment services (Emulator)</string>
+    <string name="nfc_hce_dual_payment_reader">Two payment services (Reader)</string>
+
+    <string name="nfc_hce_change_default_emulator">Change default payment service (Emulator)</string>
+    <string name="nfc_hce_change_default_reader">Change default payment service (Reader)</string>
+
+    <string name="nfc_hce_tap_reader_title">Tap reader</string>
+    <string name="nfc_hce_tap_reader_message">Select the corresponding reader test on the remote device, click OK on this dialog, and tap devices together. The pass button will be enabled if the test passes.</string>
+
+    <string name="nfc_hce_single_non_payment_emulator">Single non-payment (Emulator)</string>
+    <string name="nfc_hce_single_non_payment_reader">Single non-payment (Reader)</string>
+
+    <string name="nfc_hce_dual_non_payment_emulator">Two non-payment services (Emulator)</string>
+    <string name="nfc_hce_dual_non_payment_reader">Two non-payment services (Reader)</string>
+
+    <string name="nfc_hce_conflicting_non_payment_emulator">Two conflicting non-payment services (Emulator)</string>
+    <string name="nfc_hce_conflicting_non_payment_reader">Two conflicting non-payment services (Reader)</string>
+
+    <string name="nfc_hce_offhost_service_emulator">Off-host service (Emulator)</string>
+    <string name="nfc_hce_offhost_service_reader">Off-host service (Reader)</string>
+    <string name="nfc_hce_offhost_emulator_help">This tests enables a service that declares some AIDs to reside off-host. This test only needs to be passed if your device has a secure element (either embedded or UICC.). The responses from the secure element are not verified by the test - it is up to the tester to verify the responses are as expected.</string>
+
+    <string name="nfc_hce_on_and_offhost_service_emulator">On and off-host services (Emulator)</string>
+    <string name="nfc_hce_on_and_offhost_service_reader">On and off-host services (Reader)</string>
+    <string name="nfc_hce_on_and_offhost_emulator_help">This tests enables a service that declares some AIDs to reside off-host, and another service that runs on host. It then first sends an APDU sequence that goes off-host, and subsequently some APDUs that should go to the on-host service. This test only needs to be passed if your device has a secure element (either embedded or UICC.). The pass button will be enabled if the on-host sequence is performed as expected. The responses from the secure element are not verified by the test - it is up to the tester to verify the responses are as expected. </string>
+
+    <string name="nfc_hce_tap_test_emulator">50 successful taps test (Emulator)</string>
+    <string name="nfc_hce_tap_test_reader">50 successful taps test (Reader)</string>
+    <string name="nfc_hce_tap_test_emulator_help">This test requires you to complete at least 50 HCE taps, to ensure stability of the HCE feature. The NFC service and controller should not crash or hang during any of the 50 taps.</string>
+    <string name="nfc_hce_throughput_emulator">HCE throughput test (Emulator)</string>
+    <string name="nfc_hce_throughput_reader">HCE throughput test (Reader)</string>
+    <string name="nfc_hce_throughput_emulator_help">This tests verifies that your HCE implementation can reach a decent throughput. While Android does not have any requirements on HCE performance, many HCE applications such as transport and payment apps do. If the average APDU roundtrip time is more than 50ms, please take a look at your implementation to see where the delay is coming from.</string>
+    <string name="nfc_hce_change_default_help">You will now be asked whether you want to make Payment Service #1 the default app. Select yes.</string>
+    <string name="nfc_hce_conflicting_non_payment_help">When tapping the first time, you will be shown a dialog that asks you to choose between TransportService #1 and TransportService #2. Select TransportService #2. Verify a dialog is shown that asks you to tap again to complete. Now tap again, and if communication with TransportService #2 is successfull the pass button will be enabled."</string>
+    <string name="nfc_payment_service_desc">NFC Payment service</string>
+    <string name="ppse">PPSE</string>
+    <string name="mastercard">MasterCard</string>
+    <string name="paymentService1">Payment Service #1</string>
+    <string name="paymentService2">Payment Service #2</string>
+    <string name="transportService1">TransportService #1</string>
+    <string name="transportService2">TransportService #2</string>
+    <string name="accessService">AccessService</string>
+    <string name="offhostService">OffhostService</string>
+
     <!-- Strings for AccelerometerTestActivity and GyroscopeTestActivity -->
     <string name="snsr_accel_test">Accelerometer Test</string>
     <string name="snsr_accel_test_info">This test verifies that the accelerometer is working properly. As you move the device around through space, the triangle should always point down (i.e. in the direction of gravity.) If it does not, the accelerometer is improperly configured.</string>
diff --git a/apps/CtsVerifier/res/xml/access_aid_list.xml b/apps/CtsVerifier/res/xml/access_aid_list.xml
new file mode 100644
index 0000000..1303f13
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/access_aid_list.xml
@@ -0,0 +1,6 @@
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/transportService1">
+        <aid-group>
+            <aid-filter android:name="F005060708"/>
+        </aid-group>
+</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/offhost_aid_list.xml b/apps/CtsVerifier/res/xml/offhost_aid_list.xml
new file mode 100644
index 0000000..524e54d
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/offhost_aid_list.xml
@@ -0,0 +1,9 @@
+<offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/offhostService">
+    <aid-group>
+        <!-- OBTH card manager AID -->
+        <aid-filter android:name="A000000151000000"/>
+        <!-- NXP card manager AID -->
+        <aid-filter android:name="A000000003000000"/>
+    </aid-group>
+</offhost-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/payment_aid_list_1.xml b/apps/CtsVerifier/res/xml/payment_aid_list_1.xml
new file mode 100644
index 0000000..2036402
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/payment_aid_list_1.xml
@@ -0,0 +1,9 @@
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/paymentService1">
+        <aid-group android:description="@string/paymentService1" android:category="payment">
+            <aid-filter android:name="325041592E5359532E4444463031"
+                        android:description="@string/ppse"/>
+            <aid-filter android:name="A0000000041010"
+                        android:description="@string/mastercard"/>
+        </aid-group>
+</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/payment_aid_list_2.xml b/apps/CtsVerifier/res/xml/payment_aid_list_2.xml
new file mode 100644
index 0000000..36a5af0
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/payment_aid_list_2.xml
@@ -0,0 +1,9 @@
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/paymentService2">
+        <aid-group android:description="@string/paymentService2" android:category="payment">
+            <aid-filter android:name="325041592E5359532E4444463031"
+                        android:description="@string/ppse"/>
+            <aid-filter android:name="A0000000041010"
+                        android:description="@string/mastercard"/>
+        </aid-group>
+</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/throughput_aid_list.xml b/apps/CtsVerifier/res/xml/throughput_aid_list.xml
new file mode 100644
index 0000000..7f49eb3
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/throughput_aid_list.xml
@@ -0,0 +1,6 @@
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/transportService1">
+        <aid-group>
+            <aid-filter android:name="F0010203040607FF"/>
+        </aid-group>
+</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/transport_aid_list_1.xml b/apps/CtsVerifier/res/xml/transport_aid_list_1.xml
new file mode 100644
index 0000000..9396391
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/transport_aid_list_1.xml
@@ -0,0 +1,6 @@
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/transportService1">
+        <aid-group>
+            <aid-filter android:name="F001020304"/>
+        </aid-group>
+</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/transport_aid_list_2.xml b/apps/CtsVerifier/res/xml/transport_aid_list_2.xml
new file mode 100644
index 0000000..9e20ff3
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/transport_aid_list_2.xml
@@ -0,0 +1,6 @@
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/transportService2">
+        <aid-group>
+            <aid-filter android:name="F001020304"/>
+        </aid-group>
+</host-apdu-service>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java
index c78062b..40a12ae 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java
@@ -25,7 +25,7 @@
 import android.provider.Settings;
 
 /** Class containing methods to create common dialogs for NFC activities. */
-class NfcDialogs {
+public class NfcDialogs {
 
     static AlertDialog createNotEnabledDialog(final Context context) {
         return new AlertDialog.Builder(context)
@@ -57,6 +57,15 @@
                 .create();
     }
 
+    public static AlertDialog createHceTapReaderDialog(final Context context, String message) {
+        String baseString = context.getString(R.string.nfc_hce_tap_reader_message);
+        return new AlertDialog.Builder(context)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setTitle(R.string.nfc_hce_tap_reader_title)
+                .setMessage(message != null ? message + "\n\n" + baseString : baseString)
+                .setPositiveButton("OK", null)
+                .create();
+    }
     private NfcDialogs() {
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
index b158ad3..cb90241 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
@@ -20,6 +20,8 @@
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.TestListAdapter.TestListItem;
+import com.android.cts.verifier.nfc.hce.HceEmulatorTestActivity;
+import com.android.cts.verifier.nfc.hce.HceReaderTestActivity;
 
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -64,6 +66,16 @@
                     MIFARE_ULTRALIGHT_ID, getTagIntent(MifareUltralight.class), null));
         }
 
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_hce));
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_reader_tests,
+                    HceReaderTestActivity.class.getName(),
+                    new Intent(this, HceReaderTestActivity.class), null));
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_emulator_tests,
+                    HceEmulatorTestActivity.class.getName(),
+                    new Intent(this, HceEmulatorTestActivity.class), null));
+        }
+
         setTestListAdapter(adapter);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/AccessService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/AccessService.java
new file mode 100644
index 0000000..969f621
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/AccessService.java
@@ -0,0 +1,28 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class AccessService extends HceService {
+    static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+            AccessService.class.getName());
+
+    public static final String[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu(HceUtils.ACCESS_AID),
+        "80CA01F000"
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "123456789000",
+        "1481148114819000"
+    };
+
+    public AccessService() {
+        initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
+    }
+
+    @Override
+    public ComponentName getComponent() {
+        return COMPONENT;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
new file mode 100644
index 0000000..bcd2b8d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
@@ -0,0 +1,131 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.hce.PaymentService1;
+
+@TargetApi(19)
+public abstract class BaseEmulatorActivity extends PassFailButtons.Activity {
+    static final String TAG = "BaseEmulatorActivity";
+    NfcAdapter mAdapter;
+    CardEmulation mCardEmulation;
+    ProgressDialog mSetupDialog;
+
+    final ArrayList<ComponentName> SERVICES = new ArrayList<ComponentName>(
+            Arrays.asList(
+            PaymentService1.COMPONENT,
+            PaymentService2.COMPONENT,
+            TransportService1.COMPONENT,
+            TransportService2.COMPONENT,
+            AccessService.COMPONENT,
+            ThroughputService.COMPONENT,
+            OffHostService.COMPONENT)
+    );
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mAdapter = NfcAdapter.getDefaultAdapter(this);
+        mCardEmulation = CardEmulation.getInstance(mAdapter);
+    }
+
+    abstract void onServicesSetup(boolean result);
+
+    abstract void onApduSequenceComplete(ComponentName component, long duration);
+
+    void onApduSequenceError() {
+
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter(HceUtils.ACTION_APDU_SEQUENCE_COMPLETE);
+        registerReceiver(mReceiver, filter);
+    }
+
+    final void setupServices(Context context, ComponentName... components) {
+        mSetupDialog = new ProgressDialog(context);
+        new SetupServicesTask().execute(components);
+    }
+
+    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (HceUtils.ACTION_APDU_SEQUENCE_COMPLETE.equals(action)) {
+                // Get component whose sequence was completed
+                ComponentName component = intent.getParcelableExtra(HceUtils.EXTRA_COMPONENT);
+                long duration = intent.getLongExtra(HceUtils.EXTRA_DURATION, 0);
+                if (component != null) {
+                    onApduSequenceComplete(component, duration);
+                }
+            } else if (HceUtils.ACTION_APDU_SEQUENCE_ERROR.equals(action)) {
+                onApduSequenceError();
+            }
+        }
+    };
+
+    private class SetupServicesTask extends AsyncTask<ComponentName, Void, Boolean> {
+        @Override
+        protected void onPostExecute(Boolean result) {
+            super.onPostExecute(result);
+            mSetupDialog.dismiss();
+            onServicesSetup(result);
+        }
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+            mSetupDialog.setTitle(R.string.nfc_hce_please_wait);
+            mSetupDialog.setMessage(getString(R.string.nfc_hce_setting_up));
+            mSetupDialog.setCancelable(false);
+            mSetupDialog.show();
+        }
+
+        @Override
+        protected Boolean doInBackground(ComponentName... components) {
+            List<ComponentName> enableComponents = Arrays.asList(components);
+            for (ComponentName component : SERVICES) {
+                if (enableComponents.contains(component)) {
+                    Log.d(TAG, "Enabling component " + component);
+                    HceUtils.enableComponent(getPackageManager(), component);
+                } else {
+                    Log.d(TAG, "Disabling component " + component);
+                    HceUtils.disableComponent(getPackageManager(), component);
+                }
+            }
+            // This is a trick to invalidate the HCE cache and avoid
+            // having to wait for PackageManager broadcasts to NFCService.
+            ComponentName bogusComponent = new ComponentName("com.android.cts.verifier",
+                    "com.android.cts.verifier.nfc.hce.BogusService");
+            mCardEmulation.isDefaultServiceForCategory(bogusComponent,
+                    CardEmulation.CATEGORY_PAYMENT);
+            return true;
+        }
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ChangeDefaultEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ChangeDefaultEmulatorActivity.java
new file mode 100644
index 0000000..f5584ec
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ChangeDefaultEmulatorActivity.java
@@ -0,0 +1,109 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+@TargetApi(19)
+public class ChangeDefaultEmulatorActivity extends BaseEmulatorActivity implements OnClickListener {
+    final static int STATE_IDLE = 0;
+    final static int STATE_SERVICE1_SETTING_UP = 1;
+    final static int STATE_SERVICE2_SETTING_UP = 2;
+    final static int STATE_DEFAULT_CHANGED = 3;
+
+    boolean mReceiverRegistered = false;
+    int mState = STATE_IDLE;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mState = STATE_SERVICE2_SETTING_UP;
+        setupServices(this, PaymentService2.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        if (mState == STATE_SERVICE2_SETTING_UP) {
+            mState = STATE_SERVICE1_SETTING_UP;
+            setupServices(this, PaymentService1.COMPONENT, PaymentService2.COMPONENT);
+            return;
+        }
+        // Verify HCE service 2 is the default
+        if (!mCardEmulation.isDefaultServiceForCategory(
+                PaymentService2.COMPONENT, CardEmulation.CATEGORY_PAYMENT)) {
+            // Popup dialog-box, fail test
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle("Test failed.");
+            builder.setMessage("PaymentService2 is not the default service according " +
+                    "to CardEmulation.getDefaultServiceForCategory(). Do you have" +
+                    "another Payment application installed?");
+            builder.setPositiveButton("OK", null);
+            builder.show();
+        } else {
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle("Note");
+            builder.setMessage(R.string.nfc_hce_change_default_help);
+            builder.setPositiveButton("OK", this);
+            builder.show();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mReceiverRegistered) {
+            unregisterReceiver(mReceiver);
+        }
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                PaymentService1.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                PaymentService1.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_change_default_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(PaymentService1.COMPONENT)) {
+            getPassButton().setEnabled(true);
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        Intent changeDefault = new Intent(CardEmulation.ACTION_CHANGE_DEFAULT);
+        changeDefault.putExtra(CardEmulation.EXTRA_CATEGORY, CardEmulation.CATEGORY_PAYMENT);
+        changeDefault.putExtra(CardEmulation.EXTRA_SERVICE_COMPONENT, PaymentService1.COMPONENT);
+        startActivityForResult(changeDefault, 0);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        mState = STATE_DEFAULT_CHANGED;
+        NfcDialogs.createHceTapReaderDialog(this, null).show();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentEmulatorActivity.java
new file mode 100644
index 0000000..37d5207
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentEmulatorActivity.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 ConflictingNonPaymentEmulatorActivity extends BaseEmulatorActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        setupServices(this, TransportService1.COMPONENT, TransportService2.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        NfcDialogs.createHceTapReaderDialog(this,
+                getString(R.string.nfc_hce_conflicting_non_payment_help)).show();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                TransportService2.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                TransportService2.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_conflicting_non_payment_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(TransportService2.COMPONENT)) {
+            getPassButton().setEnabled(true);
+        }
+    }
+}
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
new file mode 100644
index 0000000..dde77b6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteActivity.java
@@ -0,0 +1,7 @@
+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/DualNonPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualNonPaymentEmulatorActivity.java
new file mode 100644
index 0000000..27b063c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualNonPaymentEmulatorActivity.java
@@ -0,0 +1,63 @@
+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 DualNonPaymentEmulatorActivity extends BaseEmulatorActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        setupServices(this, TransportService2.COMPONENT, AccessService.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        NfcDialogs.createHceTapReaderDialog(this, null).show();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        // Combine command/response APDU arrays
+        String[] commandSequences = new String[TransportService2.APDU_COMMAND_SEQUENCE.length +
+                                               AccessService.APDU_COMMAND_SEQUENCE.length];
+        System.arraycopy(TransportService2.APDU_COMMAND_SEQUENCE, 0, commandSequences, 0,
+                TransportService2.APDU_COMMAND_SEQUENCE.length);
+        System.arraycopy(AccessService.APDU_COMMAND_SEQUENCE, 0, commandSequences,
+                TransportService2.APDU_COMMAND_SEQUENCE.length,
+                AccessService.APDU_COMMAND_SEQUENCE.length);
+
+        String[] responseSequences = new String[TransportService2.APDU_RESPOND_SEQUENCE.length +
+                                               AccessService.APDU_RESPOND_SEQUENCE.length];
+        System.arraycopy(TransportService2.APDU_RESPOND_SEQUENCE, 0, responseSequences, 0,
+                TransportService2.APDU_RESPOND_SEQUENCE.length);
+        System.arraycopy(AccessService.APDU_RESPOND_SEQUENCE, 0, responseSequences,
+                TransportService2.APDU_RESPOND_SEQUENCE.length,
+                AccessService.APDU_RESPOND_SEQUENCE.length);
+
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS, commandSequences);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES, responseSequences);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_dual_non_payment_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(TransportService2.COMPONENT)) {
+            getPassButton().setEnabled(true);
+        }
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualPaymentEmulatorActivity.java
new file mode 100644
index 0000000..eaba131
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualPaymentEmulatorActivity.java
@@ -0,0 +1,87 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+@TargetApi(19)
+public class DualPaymentEmulatorActivity extends BaseEmulatorActivity {
+    final static int STATE_IDLE = 0;
+    final static int STATE_SERVICE1_SETTING_UP = 1;
+    final static int STATE_SERVICE2_SETTING_UP = 2;
+
+    boolean mReceiverRegistered = false;
+    int mState = STATE_IDLE;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        mState = STATE_SERVICE2_SETTING_UP;
+        setupServices(this, PaymentService2.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        if (mState == STATE_SERVICE2_SETTING_UP) {
+            mState = STATE_SERVICE1_SETTING_UP;
+            setupServices(this, PaymentService1.COMPONENT, PaymentService2.COMPONENT);
+            return;
+        }
+        // Verify HCE service 2 is the default
+        if (!mCardEmulation.isDefaultServiceForCategory(
+                PaymentService2.COMPONENT, CardEmulation.CATEGORY_PAYMENT)) {
+            // Popup dialog-box, fail test
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle("Test failed.");
+            builder.setMessage("PaymentService2 is not the default service according " +
+                    "to CardEmulation.getDefaultServiceForCategory()");
+            builder.setPositiveButton("OK", null);
+            builder.show();
+        } else {
+            NfcDialogs.createHceTapReaderDialog(this,null).show();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mReceiverRegistered) {
+            unregisterReceiver(mReceiver);
+        }
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                PaymentService2.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                PaymentService2.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_dual_payment_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(PaymentService2.COMPONENT)) {
+            getPassButton().setEnabled(true);
+        }
+    }
+
+
+}
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
new file mode 100644
index 0000000..b0535a1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.nfc.hce;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+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;
+
+/** Activity that lists all the NFC HCE emulator tests. */
+public class HceEmulatorTestActivity extends PassFailButtons.TestListActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setInfoResources(R.string.nfc_test, R.string.nfc_hce_emulator_test_info, 0);
+        setPassFailButtonClickListeners();
+
+        ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_emulator_tests));
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_payment_emulator,
+                    SinglePaymentEmulatorActivity.class.getName(),
+                    new Intent(this, SinglePaymentEmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_dual_payment_emulator,
+                    DualPaymentEmulatorActivity.class.getName(),
+                    new Intent(this, DualPaymentEmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_change_default_emulator,
+                    ChangeDefaultEmulatorActivity.class.getName(),
+                    new Intent(this, ChangeDefaultEmulatorActivity.class), null));
+
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_non_payment_emulator,
+                    SingleNonPaymentEmulatorActivity.class.getName(),
+                    new Intent(this, SingleNonPaymentEmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_dual_non_payment_emulator,
+                    DualNonPaymentEmulatorActivity.class.getName(),
+                    new Intent(this, DualNonPaymentEmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_conflicting_non_payment_emulator,
+                    ConflictingNonPaymentEmulatorActivity.class.getName(),
+                    new Intent(this, ConflictingNonPaymentEmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_throughput_emulator,
+                    ThroughputEmulatorActivity.class.getName(),
+                    new Intent(this, ThroughputEmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_tap_test_emulator,
+                    TapTestEmulatorActivity.class.getName(),
+                    new Intent(this, TapTestEmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_offhost_service_emulator,
+                    OffHostEmulatorActivity.class.getName(),
+                    new Intent(this, OffHostEmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_on_and_offhost_service_emulator,
+                    OnAndOffHostEmulatorActivity.class.getName(),
+                    new Intent(this, OnAndOffHostEmulatorActivity.class), null));
+
+        }
+
+        setTestListAdapter(adapter);
+    }
+}
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
new file mode 100644
index 0000000..4893cc3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.nfc.hce;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+/** Activity that lists all the NFC HCE reader tests. */
+public class HceReaderTestActivity extends PassFailButtons.TestListActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setInfoResources(R.string.nfc_test, R.string.nfc_hce_reader_test_info, 0);
+        setPassFailButtonClickListeners();
+
+        ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_reader_tests));
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_payment_reader,
+                    SimpleReaderActivity.class.getName(),
+                    SinglePaymentEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_dual_payment_reader,
+                    SimpleReaderActivity.class.getName(),
+                    DualPaymentEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_change_default_reader,
+                    SimpleReaderActivity.class.getName(),
+                    ChangeDefaultEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_non_payment_reader,
+                    SimpleReaderActivity.class.getName(),
+                    SingleNonPaymentEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_dual_non_payment_reader,
+                    SimpleReaderActivity.class.getName(),
+                    DualNonPaymentEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_conflicting_non_payment_reader,
+                    SimpleReaderActivity.class.getName(),
+                    ConflictingNonPaymentEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_throughput_reader,
+                    SimpleReaderActivity.class.getName(),
+                    ThroughputEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_tap_test_reader,
+                    SimpleReaderActivity.class.getName(),
+                    TapTestEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_offhost_service_reader,
+                    SimpleReaderActivity.class.getName(),
+                    OffHostEmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_on_and_offhost_service_reader,
+                    SimpleReaderActivity.class.getName(),
+                    OnAndOffHostEmulatorActivity.buildReaderIntent(this), null));
+        }
+
+        setTestListAdapter(adapter);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceService.java
new file mode 100644
index 0000000..20b34df
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceService.java
@@ -0,0 +1,86 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.nfc.cardemulation.HostApduService;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.Arrays;
+
+@TargetApi(19)
+public abstract class HceService extends HostApduService {
+    final static String TAG = "HceService";
+
+    final static int STATE_IDLE = 0;
+    final static int STATE_IN_PROGRESS = 1;
+    final static int STATE_FAILED = 2;
+
+    // Variables below only used on main thread
+    String[] mCommandApdus = null;
+    String[] mResponseApdus = null;
+    int mApduIndex = 0;
+    int mState = STATE_IDLE;
+    long mStartTime;
+
+    public void initialize(String[] commandApdus, String[] responseApdus) {
+       mCommandApdus = commandApdus;
+       mResponseApdus = responseApdus;
+    }
+
+    @Override
+    public void onDeactivated(int arg0) {
+        mApduIndex = 0;
+        mState = STATE_IDLE;
+    }
+
+    public abstract ComponentName getComponent();
+
+    public void onApduSequenceComplete() {
+        Intent completionIntent = new Intent(HceUtils.ACTION_APDU_SEQUENCE_COMPLETE);
+        completionIntent.putExtra(HceUtils.EXTRA_COMPONENT, getComponent());
+        completionIntent.putExtra(HceUtils.EXTRA_DURATION,
+                System.currentTimeMillis() - mStartTime);
+        sendBroadcast(completionIntent);
+    }
+
+    public void onApduSequenceError() {
+        Intent errorIntent = new Intent(HceUtils.ACTION_APDU_SEQUENCE_ERROR);
+        sendBroadcast(errorIntent);
+    }
+
+    @Override
+    public byte[] processCommandApdu(byte[] arg0, Bundle arg1) {
+        if (mState == STATE_FAILED) {
+            // Don't accept any more APDUs until deactivated
+            return null;
+        }
+
+        if (mState == STATE_IDLE) {
+            mState = STATE_IN_PROGRESS;
+            mStartTime = System.currentTimeMillis();
+        }
+
+        if (mApduIndex >= mCommandApdus.length) {
+            Log.d(TAG, "Ignoring command APDU; protocol complete.");
+            // Ignore new APDUs after completion
+            return null;
+        } else {
+            if (!Arrays.equals(HceUtils.hexStringToBytes(mCommandApdus[mApduIndex]), arg0)) {
+                Log.d(TAG, "Unexpected command APDU: " + HceUtils.getHexBytes("", arg0));
+                onApduSequenceError();
+                return null;
+            } else {
+                // Send corresponding response APDU
+                byte[] responseApdu = HceUtils.hexStringToBytes(mResponseApdus[mApduIndex]);
+                mApduIndex++;
+                if (mApduIndex == mCommandApdus.length) {
+                    // Test passed
+                    onApduSequenceComplete();
+                }
+                return responseApdu;
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
new file mode 100644
index 0000000..3bab53b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
@@ -0,0 +1,68 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+
+public final class HceUtils {
+    public static final String ACTION_APDU_SEQUENCE_COMPLETE =
+            "com.android.cts.verifier.nfc.hce.ACTION_APDU_SEQUENCE_COMPLETE";
+    public static final String ACTION_APDU_SEQUENCE_ERROR =
+            "com.android.cts.verifier.nfc.hce.ACTION_APDU_SEQUENCE_ERROR";
+
+    public static final String EXTRA_COMPONENT = "component";
+    public static final String EXTRA_DURATION = "duration";
+
+    public static final String PPSE_AID = "325041592E5359532E4444463031";
+    public static final String MC_AID = "A0000000041010";
+
+    public static final String TRANSPORT_AID = "F001020304";
+    public static final String ACCESS_AID = "F005060708";
+
+    public static void enableComponent(PackageManager pm, ComponentName component) {
+        pm.setComponentEnabledSetting(
+                component,
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                PackageManager.DONT_KILL_APP);
+    }
+
+    public static void disableComponent(PackageManager pm, ComponentName component) {
+        pm.setComponentEnabledSetting(
+                component,
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+    }
+
+    public static String getHexBytes(String header, byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        if (header != null) {
+            sb.append(header + ": ");
+        }
+        for (byte b : bytes) {
+            sb.append(String.format("%02X ", b));
+        }
+        return sb.toString();
+    }
+
+    public static byte[] hexStringToBytes(String s) {
+        if (s == null || s.length() == 0) return null;
+        int len = s.length();
+        if (len % 2 != 0) {
+            s = '0' + s;
+            len++;
+        }
+        byte[] data = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+                                 + Character.digit(s.charAt(i+1), 16));
+        }
+        return data;
+    }
+
+    public static final String buildSelectApdu(String aid) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("00A40400");
+        sb.append(String.format("%02X", aid.length() / 2));
+        sb.append(aid);
+        return sb.toString();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostEmulatorActivity.java
new file mode 100644
index 0000000..216f35a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostEmulatorActivity.java
@@ -0,0 +1,46 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+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;
+
+@TargetApi(19)
+public class OffHostEmulatorActivity extends BaseEmulatorActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        setupServices(this, OffHostService.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_offhost_emulator_help)).show();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                OffHostService.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                OffHostService.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_offhost_service_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java
new file mode 100644
index 0000000..c087c99
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java
@@ -0,0 +1,23 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class OffHostService {
+    public static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+                    OffHostService.class.getName());
+
+    public static final String[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu("A000000151000000"),
+        "80CA9F7F00",
+        HceUtils.buildSelectApdu("A000000003000000"),
+        "80CA9F7F00"
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "*",
+        "*",
+        "*",
+        "*"
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OnAndOffHostEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OnAndOffHostEmulatorActivity.java
new file mode 100644
index 0000000..3d5190f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OnAndOffHostEmulatorActivity.java
@@ -0,0 +1,67 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+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;
+
+@TargetApi(19)
+public class OnAndOffHostEmulatorActivity extends BaseEmulatorActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        setupServices(this, OffHostService.COMPONENT, AccessService.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_on_and_offhost_emulator_help)).show();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        // Combine command/response APDU arrays
+        String[] commandSequences = new String[OffHostService.APDU_COMMAND_SEQUENCE.length +
+                                               AccessService.APDU_COMMAND_SEQUENCE.length];
+        System.arraycopy(OffHostService.APDU_COMMAND_SEQUENCE, 0, commandSequences, 0,
+                OffHostService.APDU_COMMAND_SEQUENCE.length);
+        System.arraycopy(AccessService.APDU_COMMAND_SEQUENCE, 0, commandSequences,
+                OffHostService.APDU_COMMAND_SEQUENCE.length,
+                AccessService.APDU_COMMAND_SEQUENCE.length);
+
+        String[] responseSequences = new String[OffHostService.APDU_RESPOND_SEQUENCE.length +
+                                               AccessService.APDU_RESPOND_SEQUENCE.length];
+        System.arraycopy(OffHostService.APDU_RESPOND_SEQUENCE, 0, responseSequences, 0,
+                OffHostService.APDU_RESPOND_SEQUENCE.length);
+        System.arraycopy(AccessService.APDU_RESPOND_SEQUENCE, 0, responseSequences,
+                OffHostService.APDU_RESPOND_SEQUENCE.length,
+                AccessService.APDU_RESPOND_SEQUENCE.length);
+
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                commandSequences);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                responseSequences);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_on_and_offhost_service_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(AccessService.COMPONENT)) {
+            getPassButton().setEnabled(true);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService1.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService1.java
new file mode 100644
index 0000000..f6119eb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService1.java
@@ -0,0 +1,32 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class PaymentService1 extends HceService {
+    static final String TAG = "PaymentService1";
+
+    static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+            PaymentService1.class.getName());
+
+    public static final String[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu(HceUtils.PPSE_AID),
+        HceUtils.buildSelectApdu(HceUtils.MC_AID),
+        "80CA01F000"
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "FFFF9000",
+        "FFEF9000",
+        "FFDFFFAABB9000"
+    };
+
+    public PaymentService1() {
+        initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
+    }
+
+    @Override
+    public ComponentName getComponent() {
+        return COMPONENT;
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService2.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService2.java
new file mode 100644
index 0000000..23664b9
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService2.java
@@ -0,0 +1,28 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class PaymentService2 extends HceService {
+    static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+            PaymentService2.class.getName());
+
+    public static final String[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu(HceUtils.PPSE_AID),
+        HceUtils.buildSelectApdu(HceUtils.MC_AID)
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "12349000",
+        "56789000"
+    };
+
+    public PaymentService2() {
+        initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
+    }
+
+    @Override
+    public ComponentName getComponent() {
+        return COMPONENT;
+    }
+}
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
new file mode 100644
index 0000000..ff98dd7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SimpleReaderActivity.java
@@ -0,0 +1,124 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.ReaderCallback;
+import android.nfc.tech.IsoDep;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+@TargetApi(19)
+public class SimpleReaderActivity extends PassFailButtons.Activity implements ReaderCallback {
+    public static final String TAG = "SimpleReaderActivity";
+    public static final String EXTRA_APDUS = "apdus";
+    public static final String EXTRA_RESPONSES = "responses";
+    public static final String EXTRA_LABEL = "label";
+
+    NfcAdapter mAdapter;
+    String[] mApdus;
+    String[] mResponses;
+
+    TextView mTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        String label = getIntent().getStringExtra(EXTRA_LABEL);
+        setTitle(label);
+
+        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);
+        Intent intent = getIntent();
+        mApdus = intent.getStringArrayExtra(EXTRA_APDUS);
+        mResponses = intent.getStringArrayExtra(EXTRA_RESPONSES);
+    }
+
+    @Override
+    public void onTagDiscovered(Tag tag) {
+        final StringBuilder sb = new StringBuilder();
+        IsoDep isoDep = IsoDep.get(tag);
+        if (isoDep == null) {
+            // TODO dialog box
+            return;
+        }
+
+        try {
+            isoDep.connect();
+            int count = 0;
+            boolean success = true;
+            long startTime = System.currentTimeMillis();
+            for (String apdu: mApdus) {
+                sb.append("Request APDU:\n");
+                sb.append(apdu + "\n\n");
+                long apduStartTime = System.currentTimeMillis();
+                byte[] response = isoDep.transceive(HceUtils.hexStringToBytes(apdu));
+                long apduEndTime = System.currentTimeMillis();
+                sb.append("Response APDU (in " + Long.toString(apduEndTime - apduStartTime) +
+                        " ms):\n");
+                sb.append(HceUtils.getHexBytes(null, response));
+
+                sb.append("\n\n\n");
+                boolean wildCard = "*".equals(mResponses[count]);
+                byte[] expectedResponse = HceUtils.hexStringToBytes(mResponses[count]);
+                Log.d(TAG, HceUtils.getHexBytes("APDU response: ", response));
+                if (!wildCard && !Arrays.equals(response, expectedResponse)) {
+                    Log.d(TAG, "Unexpected APDU response: " + HceUtils.getHexBytes("", response));
+                    success = false;
+                    break;
+                }
+                count++;
+            }
+            if (success) {
+                sb.insert(0, "Total APDU exchange time: " +
+                        Long.toString(System.currentTimeMillis() - startTime) + " ms.\n\n");
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText(sb.toString());
+                        getPassButton().setEnabled(true);
+                    }
+                });
+            } else {
+                sb.insert(0, "FAIL. Total APDU exchange time: " +
+                        Long.toString(System.currentTimeMillis() - startTime) + " ms.\n\n");
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText(sb.toString());
+                        AlertDialog.Builder builder = new AlertDialog.Builder(SimpleReaderActivity.this);
+                        builder.setTitle("Test failed");
+                        builder.setMessage("An unexpected response APDU was received, or no APDUs were received at all.");
+                        builder.setPositiveButton("OK", null);
+                        builder.show();
+                    }
+                });
+            }
+        } catch (IOException e) {
+            sb.insert(0, "Test failed. IOException (did you keep the devices in range?)\n\n.");
+            mTextView.setText(sb.toString());
+        } finally {
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SingleNonPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SingleNonPaymentEmulatorActivity.java
new file mode 100644
index 0000000..694199b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SingleNonPaymentEmulatorActivity.java
@@ -0,0 +1,48 @@
+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 SingleNonPaymentEmulatorActivity extends BaseEmulatorActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        setupServices(this, TransportService1.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        NfcDialogs.createHceTapReaderDialog(this, null).show();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                TransportService1.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                TransportService1.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_single_non_payment_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(TransportService1.COMPONENT)) {
+            getPassButton().setEnabled(true);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SinglePaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SinglePaymentEmulatorActivity.java
new file mode 100644
index 0000000..e5f9f17
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SinglePaymentEmulatorActivity.java
@@ -0,0 +1,65 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+@TargetApi(19)
+public class SinglePaymentEmulatorActivity extends BaseEmulatorActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        setupServices(this, PaymentService1.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        // Verify HCE service 1 is the default
+        if (!mCardEmulation.isDefaultServiceForCategory(
+                PaymentService1.COMPONENT, CardEmulation.CATEGORY_PAYMENT)) {
+            // Popup dialog-box, fail test
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle("Test failed.");
+            builder.setMessage("PaymentService1 is not the default service according " +
+                    "to CardEmulation.getDefaultServiceForCategory(), verify no other " +
+                    "payment HCE apps are installed.");
+            builder.setPositiveButton("OK", null);
+            builder.show();
+        } else {
+	        NfcDialogs.createHceTapReaderDialog(this, null).show();
+        }
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                PaymentService1.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                PaymentService1.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_single_payment_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(PaymentService1.COMPONENT)) {
+            getPassButton().setEnabled(true);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TapTestEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TapTestEmulatorActivity.java
new file mode 100644
index 0000000..2eea89c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TapTestEmulatorActivity.java
@@ -0,0 +1,65 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+@TargetApi(19)
+public class TapTestEmulatorActivity extends BaseEmulatorActivity {
+    TextView mTextView;
+    int mNumTaps;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        mTextView = (TextView) findViewById(R.id.text);
+        setupServices(this, TransportService1.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mNumTaps = 0;
+        mTextView.setText("Number of successful taps: 0/50.");
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        NfcDialogs.createHceTapReaderDialog(this,
+                getString(R.string.nfc_hce_tap_test_emulator_help)).show();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                TransportService1.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                TransportService1.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_tap_test_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(TransportService1.COMPONENT)) {
+            mNumTaps++;
+            if (mNumTaps <= 50) {
+                mTextView.setText("Number of successful taps: " + Integer.toString(mNumTaps) +
+                        "/50.");
+            }
+            if (mNumTaps >= 50) {
+                getPassButton().setEnabled(true);
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputEmulatorActivity.java
new file mode 100644
index 0000000..f050d77
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputEmulatorActivity.java
@@ -0,0 +1,64 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.NfcDialogs;
+
+@TargetApi(19)
+public class ThroughputEmulatorActivity extends BaseEmulatorActivity {
+    TextView mTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        mTextView = (TextView) findViewById(R.id.text);
+        setupServices(this, ThroughputService.COMPONENT);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    void onServicesSetup(boolean result) {
+        NfcDialogs.createHceTapReaderDialog(this,
+                getString(R.string.nfc_hce_throughput_emulator_help)).show();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
+                ThroughputService.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
+                ThroughputService.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_hce_throughput_reader));
+        return readerIntent;
+    }
+
+    @Override
+    void onApduSequenceComplete(ComponentName component, long duration) {
+        if (component.equals(ThroughputService.COMPONENT)) {
+            long timePerApdu = duration / ThroughputService.APDU_COMMAND_SEQUENCE.length;
+            if (duration < 1000) {
+                mTextView.setText("PASS. Total duration: " + Long.toString(duration) + " ms " +
+                        "( " + Long.toString(timePerApdu) + " ms per APDU roundtrip).");
+                getPassButton().setEnabled(true);
+            } else {
+                mTextView.setText("FAIL. Total duration: " + Long.toString(duration) + " ms " +
+                        "(" + Long.toString(timePerApdu) + " ms per APDU roundtrip)." +
+                        " Require <= 60ms per APDU roundtrip.");
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputService.java
new file mode 100644
index 0000000..8f826ff
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputService.java
@@ -0,0 +1,58 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class ThroughputService extends HceService {
+    static final String TAG = "PaymentService1";
+
+    static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+            ThroughputService.class.getName());
+
+    public static final String[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu("F0010203040607FF"),
+        "80CA010000",
+        "80CA010100",
+        "80CA010200",
+        "80CA010300",
+        "80CA010400",
+        "80CA010500",
+        "80CA010600",
+        "80CA010700",
+        "80CA010800",
+        "80CA010900",
+        "80CA010A00",
+        "80CA010B00",
+        "80CA010C00",
+        "80CA010D00",
+        "80CA010E00",
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "9000",
+        "0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0002FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "0009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "000AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+        "000EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
+    };
+
+    public ThroughputService() {
+        initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
+    }
+
+    @Override
+    public ComponentName getComponent() {
+        return COMPONENT;
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService1.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService1.java
new file mode 100644
index 0000000..c751e76
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService1.java
@@ -0,0 +1,28 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class TransportService1 extends HceService {
+    static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+            TransportService1.class.getName());
+
+    public static final String[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu(HceUtils.TRANSPORT_AID),
+        "80CA01E000"
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "80CA9000",
+        "83947102829000"
+    };
+
+    public TransportService1() {
+        initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
+    }
+
+    @Override
+    public ComponentName getComponent() {
+        return COMPONENT;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService2.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService2.java
new file mode 100644
index 0000000..0815b9c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService2.java
@@ -0,0 +1,28 @@
+package com.android.cts.verifier.nfc.hce;
+
+import android.content.ComponentName;
+
+public class TransportService2 extends HceService {
+    static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+            TransportService2.class.getName());
+
+    public static final String[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu(HceUtils.TRANSPORT_AID),
+        "80CA01E100"
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "81CA9000",
+        "7483624748FEFE9000"
+    };
+
+    public TransportService2() {
+        initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
+    }
+
+    @Override
+    public ComponentName getComponent() {
+        return COMPONENT;
+    }
+}