NFC host card emulation samples (reader + writer)
Demonstrates how to implement a virtual loyalty card using NFC HCE,
along with a virtual terminal for reading this loyalty card.
Change-Id: Ifb1a47208148ee3867980ea90a50b6df474f808c
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/.gitignore b/connectivity/nfc/CardEmulation/CardEmulationSample/.gitignore
new file mode 100644
index 0000000..6eb878d
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/.gitignore
@@ -0,0 +1,16 @@
+# Copyright 2013 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.
+src/template/
+src/common/
+build.gradle
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/README-fragmentview.txt b/connectivity/nfc/CardEmulation/CardEmulationSample/README-fragmentview.txt
new file mode 100644
index 0000000..38d903f
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/README-fragmentview.txt
@@ -0,0 +1,37 @@
+<!--
+ Copyright 2013 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.
+-->
+
+Steps to implement FragmentView template:
+-in template-params.xml.ftl:
+ -add the following line to common imports
+ <common src="activities"/>
+
+-Add a Fragment to show behavior. In your MainActivity.java class, it will reference a Fragment
+ called (yourProjectName)Fragment.java. Create that file in your project, using the "main" source
+ folder instead of "common" or "templates".
+ For instance, if your package name is com.example.foo, create the file
+ src/main/java/com/example/foo/FooFragment.java
+
+
+-Within this fragment, make sure that the onCreate method has the line
+ "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
+
+-In order to override menu events, override onOptionsItemSelected.
+
+-refer to sampleSamples/fragmentViewSample for a reference implementation of a
+project built on this template.
+
+
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/proguard-project.txt b/connectivity/nfc/CardEmulation/CardEmulationSample/proguard-project.txt
new file mode 100644
index 0000000..0d8f171
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/proguard-project.txt
@@ -0,0 +1,20 @@
+ To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/AndroidManifest.xml b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4a4af08
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/AndroidManifest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.cardemulation"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <!-- Card emulation was introduced in API 19. -->
+ <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
+ <uses-feature android:name="android.hardware.nfc.hce" android:required="true" />
+ <uses-permission android:name="android.permission.NFC" />
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme">
+
+ <!-- Basic UI for sample discoverability. -->
+ <activity android:name=".MainActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <!-- BEGIN_INCLUDE(CardEmulationManifest) -->
+ <!-- Service for handling communication with NFC terminal. -->
+ <service android:name=".CardService"
+ android:exported="true"
+ android:permission="android.permission.BIND_NFC_SERVICE">
+ <!-- Intent filter indicating that we support card emulation. -->
+ <intent-filter>
+ <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <!-- Required XML configuration file, listing the AIDs that we are emulating cards
+ for. This defines what protocols our card emulation service supports. -->
+ <meta-data android:name="android.nfc.cardemulation.host_apdu_service"
+ android:resource="@xml/aid_list"/>
+ </service>
+ <!-- END_INCLUDE(CardEmulationManifest) -->
+ </application>
+
+
+</manifest>
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/AccountStorage.java b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/AccountStorage.java
new file mode 100644
index 0000000..56a69a0
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/AccountStorage.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 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.example.android.cardemulation;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+/**
+ * Utility class for persisting account numbers to disk.
+ *
+ * <p>The default SharedPreferences instance is used as the backing storage. Values are cached
+ * in memory for performance.
+ *
+ * <p>This class is thread-safe.
+ */
+public class AccountStorage {
+ private static final String PREF_ACCOUNT_NUMBER = "account_number";
+ private static final String DEFAULT_ACCOUNT_NUMBER = "00000000";
+ private static final String TAG = "AccountStorage";
+ private static String sAccount = null;
+ private static final Object sAccountLock = new Object();
+
+ public static void SetAccount(Context c, String s) {
+ synchronized(sAccountLock) {
+ Log.i(TAG, "Setting account number: " + s);
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c);
+ prefs.edit().putString(PREF_ACCOUNT_NUMBER, s);
+ sAccount = s;
+ }
+ }
+
+ public static String GetAccount(Context c) {
+ synchronized (sAccountLock) {
+ if (sAccount == null) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c);
+ String account = prefs.getString(PREF_ACCOUNT_NUMBER, DEFAULT_ACCOUNT_NUMBER);
+ sAccount = account;
+ }
+ return sAccount;
+ }
+ }
+}
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/CardEmulationFragment.java b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/CardEmulationFragment.java
new file mode 100644
index 0000000..f26efda
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/CardEmulationFragment.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 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.example.android.cardemulation;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+/**
+ * Generic UI for sample discovery.
+ */
+public class CardEmulationFragment extends Fragment {
+
+ public static final String TAG = "CardEmulationFragment";
+
+ /** Called when sample is created. Displays generic UI with welcome text. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View v = inflater.inflate(R.layout.main_fragment, container, false);
+ EditText account = (EditText) v.findViewById(R.id.card_account_field);
+ account.setText(AccountStorage.GetAccount(getActivity()));
+ account.addTextChangedListener(new AccountUpdater());
+ return v;
+ }
+
+
+ private class AccountUpdater implements TextWatcher {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Not implemented.
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Not implemented.
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ String account = s.toString();
+ AccountStorage.SetAccount(getActivity(), account);
+ }
+ }
+}
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/CardService.java b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/CardService.java
new file mode 100644
index 0000000..a409d28
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/java/com/example/android/cardemulation/CardService.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 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.example.android.cardemulation;
+
+import android.nfc.cardemulation.HostApduService;
+import android.os.Bundle;
+import com.example.android.common.logger.Log;
+
+import java.util.Arrays;
+
+/**
+ * This is a sample APDU Service which demonstrates how to interface with the card emulation support
+ * added in Android 4.4, KitKat.
+ *
+ * <p>This sample replies to any requests sent with the string "Hello World". In real-world
+ * situations, you would need to modify this code to implement your desired communication
+ * protocol.
+ *
+ * <p>This sample will be invoked for any terminals selecting AIDs of 0xF11111111, 0xF22222222, or
+ * 0xF33333333. See src/main/res/xml/aid_list.xml for more details.
+ *
+ * <p class="note">Note: This is a low-level interface. Unlike the NdefMessage many developers
+ * are familiar with for implementing Android Beam in apps, card emulation only provides a
+ * byte-array based communication channel. It is left to developers to implement higher level
+ * protocol support as needed.
+ */
+public class CardService extends HostApduService {
+ private static final String TAG = "CardService";
+ // AID for our loyalty card service.
+ private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222";
+ // ISO-DEP command HEADER for selecting an AID.
+ // Format: [Class | Instruction | Parameter 1 | Parameter 2]
+ private static final String SELECT_APDU_HEADER = "00A40400";
+ // "OK" status word sent in response to SELECT AID command (0x9000)
+ private static final byte[] SELECT_OK_SW = HexStringToByteArray("9000");
+ // "UNKNOWN" status word sent in response to invalid APDU command (0x0000)
+ private static final byte[] UNKNOWN_CMD_SW = HexStringToByteArray("0000");
+ private static final byte[] SELECT_APDU = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID);
+
+ /**
+ * Called if the connection to the NFC card is lost, in order to let the application know the
+ * cause for the disconnection (either a lost link, or another AID being selected by the
+ * reader).
+ *
+ * @param reason Either DEACTIVATION_LINK_LOSS or DEACTIVATION_DESELECTED
+ */
+ @Override
+ public void onDeactivated(int reason) { }
+
+ /**
+ * This method will be called when a command APDU has been received from a remote device. A
+ * response APDU can be provided directly by returning a byte-array in this method. In general
+ * response APDUs must be sent as quickly as possible, given the fact that the user is likely
+ * holding his device over an NFC reader when this method is called.
+ *
+ * <p class="note">If there are multiple services that have registered for the same AIDs in
+ * their meta-data entry, you will only get called if the user has explicitly selected your
+ * service, either as a default or just for the next tap.
+ *
+ * <p class="note">This method is running on the main thread of your application. If you
+ * cannot return a response APDU immediately, return null and use the {@link
+ * #sendResponseApdu(byte[])} method later.
+ *
+ * @param commandApdu The APDU that received from the remote device
+ * @param extras A bundle containing extra data. May be null.
+ * @return a byte-array containing the response APDU, or null if no response APDU can be sent
+ * at this point.
+ */
+ // BEGIN_INCLUDE(processCommandApdu)
+ @Override
+ public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
+ Log.i(TAG, "Received APDU: " + ByteArrayToHexString(commandApdu));
+ // If the APDU matches the SELECT AID command for this service,
+ // send the loyalty card account number, followed by a SELECT_OK status trailer (0x9000).
+ if (Arrays.equals(SELECT_APDU, commandApdu)) {
+ String account = AccountStorage.GetAccount(this);
+ byte[] accountBytes = account.getBytes();
+ Log.i(TAG, "Sending account number: " + account);
+ return ConcatArrays(accountBytes, SELECT_OK_SW);
+ } else {
+ return UNKNOWN_CMD_SW;
+ }
+ }
+ // END_INCLUDE(processCommandApdu)
+
+ /**
+ * Build APDU for SELECT AID command. This command indicates which service a reader is
+ * interested in communicating with. See ISO 7816-4.
+ *
+ * @param aid Application ID (AID) to select
+ * @return APDU for SELECT AID command
+ */
+ public static byte[] BuildSelectApdu(String aid) {
+ // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
+ return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X",
+ aid.length() / 2) + aid);
+ }
+
+ /**
+ * Utility method to convert a byte array to a hexadecimal string.
+ *
+ * @param bytes Bytes to convert
+ * @return String, containing hexadecimal representation.
+ */
+ public static String ByteArrayToHexString(byte[] bytes) {
+ final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+ char[] hexChars = new char[bytes.length * 2]; // Each byte has two hex characters (nibbles)
+ int v;
+ for (int j = 0; j < bytes.length; j++) {
+ v = bytes[j] & 0xFF; // Cast bytes[j] to int, treating as unsigned value
+ hexChars[j * 2] = hexArray[v >>> 4]; // Select hex character from upper nibble
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F]; // Select hex character from lower nibble
+ }
+ return new String(hexChars);
+ }
+
+ /**
+ * Utility method to convert a hexadecimal string to a byte string.
+ *
+ * <p>Behavior with input strings containing non-hexadecimal characters is undefined.
+ *
+ * @param s String containing hexadecimal characters to convert
+ * @return Byte array generated from input
+ * @throws java.lang.IllegalArgumentException if input length is incorrect
+ */
+ public static byte[] HexStringToByteArray(String s) throws IllegalArgumentException {
+ int len = s.length();
+ if (len % 2 == 1) {
+ throw new IllegalArgumentException("Hex string must have even number of characters");
+ }
+ byte[] data = new byte[len / 2]; // Allocate 1 byte per 2 hex characters
+ for (int i = 0; i < len; i += 2) {
+ // Convert each character into a integer (base-16), then bit-shift into place
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ + Character.digit(s.charAt(i+1), 16));
+ }
+ return data;
+ }
+
+ /**
+ * Utility method to concatenate two byte arrays.
+ * @param first First array
+ * @param rest Any remaining arrays
+ * @return Concatenated copy of input arrays
+ */
+ public static byte[] ConcatArrays(byte[] first, byte[]... rest) {
+ int totalLength = first.length;
+ for (byte[] array : rest) {
+ totalLength += array.length;
+ }
+ byte[] result = Arrays.copyOf(first, totalLength);
+ int offset = first.length;
+ for (byte[] array : rest) {
+ System.arraycopy(array, 0, result, offset, array.length);
+ offset += array.length;
+ }
+ return result;
+ }
+}
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-hdpi/ic_launcher.png b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..0cebab5
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-mdpi/ic_launcher.png b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..bccac40
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xhdpi/ic_launcher.png b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..1a6a328
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xxhdpi/card_background.png b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xxhdpi/card_background.png
new file mode 100644
index 0000000..86a7ba7
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xxhdpi/card_background.png
Binary files differ
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xxhdpi/ic_launcher.png b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..063dd25
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/layout/main_fragment.xml b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/layout/main_fragment.xml
new file mode 100644
index 0000000..2c69743
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/layout/main_fragment.xml
@@ -0,0 +1,65 @@
+<!--
+Copyright (C) 2013 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="380dp"
+ android:layout_height="242.25dp"
+ android:layout_gravity="center"
+ android:layout_margin="20dp">
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/card_background"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="20dp"
+ android:layout_gravity="center"
+ android:clickable="true">
+ <TextView
+ android:id="@+id/card_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/card_title"
+ android:fontFamily="sans-serif-condensed"
+ android:textStyle="bold"
+ android:textSize="32dp"
+ />
+ <TextView
+ android:id="@+id/card_account_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/account_number"
+ android:fontFamily="sans-serif"
+ android:textStyle="bold"
+ android:textSize="18dp"
+ android:layout_marginTop="40dp"
+ />
+ <EditText
+ android:id="@+id/card_account_field"
+ android:width="360dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-condensed"
+ android:textStyle="bold"
+ android:textSize="42dp"
+ android:singleLine="true"
+ android:inputType="number" />
+ </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/values/strings.xml b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f708284
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2013 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.
+-->
+
+<resources>
+ <string name="service_name">CardEmulation Sample</string>
+ <string name="card_title">Sample Loyalty Card</string>
+ <string name="account_number">Account Number</string>
+</resources>
\ No newline at end of file
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/xml/aid_list.xml b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/xml/aid_list.xml
new file mode 100644
index 0000000..15ed754
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/src/main/res/xml/aid_list.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2013 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.
+-->
+
+<!-- This file defines which AIDs this application should emulate cards for.
+
+ Vendor-specific AIDs should always start with an "F", according to the ISO 7816 spec. We
+ recommended vendor-specific AIDs be at least 6 characters long, to provide sufficient
+ uniqueness. Note, however, that longer AIDs may impose a burden on non-Android NFC terminals.
+ AIDs may not exceed 32 characters (16 bytes).
+
+ Additionally, AIDs must always contain an even number of characters, in hexadecimal format.
+
+ In order to avoid prompting the user to select which service they want to use when the device
+ is scanned, this app must be selected as the default handler for an AID group by the user, or
+ the terminal must select *all* AIDs defined in the category simultaneously ("exact match").
+-->
+<!-- BEGIN_INCLUDE(CardEmulationXML) -->
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/service_name"
+ android:requireDeviceUnlock="false">
+ <!--
+ If category="payment" is used for any aid-groups, you must also add an android:apduServiceBanner
+ attribute above, like so:
+
+ android:apduServiceBanner="@drawable/settings_banner"
+
+ apduServiceBanner should be 260x96 dp. In pixels, that works out to...
+ - drawable-xxhdpi: 780x288 px
+ - drawable-xhdpi: 520x192 px
+ - drawable-hdpi: 390x144 px
+ - drawable-mdpi: 260x96 px
+
+ The apduServiceBanner is displayed in the "Tap & Pay" menu in the system Settings app, and
+ is only displayed for apps which implement the "payment" AID category.
+
+ Since this sample is implementing a non-standard card type (a loyalty card, specifically), we
+ do not need to define a banner.
+
+ Important: category="payment" should only be used for industry-standard payment cards. If you are
+ implementing a closed-loop payment system (e.g. stored value cards for a specific merchant
+ or transit system), use category="other". This is because only one "payment" card may be
+ active at a time, whereas all "other" cards are active simultaneously (subject to AID
+ dispatch).
+ -->
+
+ <aid-group android:description="@string/card_title" android:category="other">
+ <aid-filter android:name="F222222222"/>
+ </aid-group>
+<!-- END_INCLUDE(CardEmulationXML) -->
+</host-apdu-service>
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/tests/AndroidManifest.xml b/connectivity/nfc/CardEmulation/CardEmulationSample/tests/AndroidManifest.xml
new file mode 100644
index 0000000..447d041
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/tests/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2013 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 name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="${sample.package}.tests"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk
+ android:minSdkVersion="19"
+ android:targetSdkVersion="19" />
+
+ <!-- We add an application tag here just so that we can indicate that
+ this package needs to link against the android.test library,
+ which is needed when building test cases. -->
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!--
+ Specifies the instrumentation test runner used to run the tests.
+ -->
+ <instrumentation
+ android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.example.android.cardemulation"
+ android:label="Tests for com.example.android.cardemulation" />
+
+</manifest>
\ No newline at end of file
diff --git a/connectivity/nfc/CardEmulation/CardEmulationSample/tests/src/com/example/android/cardemulation/tests/SampleTests.java b/connectivity/nfc/CardEmulation/CardEmulationSample/tests/src/com/example/android/cardemulation/tests/SampleTests.java
new file mode 100644
index 0000000..b9828c2
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/CardEmulationSample/tests/src/com/example/android/cardemulation/tests/SampleTests.java
@@ -0,0 +1,169 @@
+/*
+* Copyright 2013 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.
+*/
+/*
+* Copyright (C) 2013 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.example.android.cardemulation.tests;
+
+import com.example.android.cardemulation.*;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+* Tests for CardEmulation sample.
+*/
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+ private MainActivity mTestActivity;
+ private CardEmulationFragment mTestFragment;
+
+ public SampleTests() {
+ super(MainActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // Starts the activity under test using the default Intent with:
+ // action = {@link Intent#ACTION_MAIN}
+ // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+ // All other fields are null or empty.
+ mTestActivity = getActivity();
+ mTestFragment = (CardEmulationFragment)
+ mTestActivity.getSupportFragmentManager().getFragments().get(1);
+ }
+
+ /**
+ * Test if the test fixture has been set up correctly.
+ */
+ public void testPreconditions() {
+ //Try to add a message to add context to your assertions. These messages will be shown if
+ //a tests fails and make it easy to understand why a test failed
+ assertNotNull("mTestActivity is null", mTestActivity);
+ assertNotNull("mTestFragment is null", mTestFragment);
+ }
+
+ /**
+ * Test converting from a hex string to binary.
+ */
+ public void testHexToBinary() {
+ final byte[] testData = {(byte) 0xc0, (byte) 0xff, (byte) 0xee};
+ final byte[] output = CardService.HexStringToByteArray("C0FFEE");
+ for (int i = 0; i < testData.length; i++) {
+ assertEquals(testData[i], output[i]);
+ }
+ }
+
+ /**
+ * Test converting from binary to a hex string
+ */
+ public void testBinaryToHex() {
+ final byte[] input = {(byte) 0xc0, (byte) 0xff, (byte) 0xee};
+ final String output = CardService.ByteArrayToHexString(input);
+ assertEquals("C0FFEE", output);
+ }
+
+ /**
+ * Verify that account number is returned in response to APDU.
+ */
+ public void testRequestAccoutNumber() {
+ AccountStorage.SetAccount(this.getActivity(), "1234");
+ final byte[] command = {(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x05,
+ (byte) 0xF2, (byte) 0x22, (byte) 0x22, (byte) 0x22, (byte) 0x22};
+ final byte[] expectedResponse = {(byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x34,
+ (byte) 0x90, (byte) 0x00};
+ final CardService cs = new CardService();
+ final byte[] output = cs.processCommandApdu(command, null);
+
+ assertEquals(expectedResponse.length, output.length);
+ for (int i = 0; i < expectedResponse.length; i++) {
+ assertEquals(expectedResponse[i], output[i]);
+ }
+
+ }
+
+
+ /**
+ * Verify that account number is returned in response to APDU.
+ */
+ public void testInvalidCommand() {
+ AccountStorage.SetAccount(this.getActivity(), "1234");
+ final byte[] command = {(byte) 0x01, (byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x05,
+ (byte) 0xF2, (byte) 0x22, (byte) 0x22, (byte) 0x22, (byte) 0x22};
+ final byte[] expectedResponse = {(byte) 0x00, (byte) 0x00};
+ final CardService cs = new CardService();
+ final byte[] output = cs.processCommandApdu(command, null);
+
+ assertEquals(expectedResponse.length, output.length);
+ for (int i = 0; i < expectedResponse.length; i++) {
+ assertEquals(expectedResponse[i], output[i]);
+ }
+ }
+
+ /**
+ * Test functionality fo ConcatArrays method.
+ */
+ public void testArrayConcat() {
+ final byte[] a = {(byte) 0x01, (byte) 0x02};
+ final byte[] b = {(byte) 0x03, (byte) 0x04};
+ final byte[] c = {(byte) 0x05, (byte) 0x06};
+ final byte[] expected = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ (byte) 0x06};
+ final byte[] result = CardService.ConcatArrays(a, b, c);
+
+ assertEquals(expected.length, result.length);
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(expected[i], result[i]);
+ }
+ }
+
+ /**
+ * Test setting and getting account number
+ */
+ public void testSetAccountNumber() {
+ final String account = "3456";
+ AccountStorage.SetAccount(getActivity(), account);
+ this.assertEquals(account, AccountStorage.GetAccount(getActivity()));
+ }
+
+ /**
+ * Test building SELECT APDU from AID string.
+ */
+ public void testBuildSelectApdu() {
+ final String aid = "1234";
+ final byte[] expectedResult = {(byte) 0x00, (byte) 0xA4, 04, (byte) 0x00, (byte) 0x02,
+ (byte) 0x12, (byte) 0x34};
+ final byte[] result = CardService.BuildSelectApdu(aid);
+
+ assertEquals(expectedResult.length, result.length);
+ for (int i = 0; i < expectedResult.length; i++) {
+ assertEquals(expectedResult[i], result[i]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/connectivity/nfc/CardEmulation/build.gradle b/connectivity/nfc/CardEmulation/build.gradle
new file mode 100644
index 0000000..c7f6491
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/build.gradle
@@ -0,0 +1,8 @@
+
+// BEGIN_EXCLUDE
+apply from: "../../../../../build/build.gradle"
+samplegen {
+ pathToBuild "../../../../../build"
+ pathToSamplesCommon "../../../common"
+}
+// END_EXCLUDE
diff --git a/connectivity/nfc/CardEmulation/buildSrc/build.gradle b/connectivity/nfc/CardEmulation/buildSrc/build.gradle
new file mode 100644
index 0000000..7cebf71
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+ mavenCentral()
+}
+dependencies {
+ compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+ main {
+ groovy {
+ srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+ }
+ }
+}
+
diff --git a/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.jar b/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties b/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..861eddc
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-bin.zip
diff --git a/connectivity/nfc/CardEmulation/gradlew b/connectivity/nfc/CardEmulation/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/connectivity/nfc/CardEmulation/gradlew.bat b/connectivity/nfc/CardEmulation/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/connectivity/nfc/CardEmulation/settings.gradle b/connectivity/nfc/CardEmulation/settings.gradle
new file mode 100644
index 0000000..32b4942
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/settings.gradle
@@ -0,0 +1 @@
+include 'CardEmulationSample'
diff --git a/connectivity/nfc/CardEmulation/template-params.xml b/connectivity/nfc/CardEmulation/template-params.xml
new file mode 100644
index 0000000..6613121
--- /dev/null
+++ b/connectivity/nfc/CardEmulation/template-params.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<sample>
+ <name>CardEmulation</name>
+ <group>Connectivity</group>
+ <package>com.example.android.cardemulation</package>
+
+ <!-- change minSdk if needed-->
+ <minSdk>19</minSdk>
+
+ <strings>
+ <intro>
+ <![CDATA[
+ This sample demonstrates how to emulate an NFC card, using the "host card emulation"
+ feature added in Android 4.4. This sample makes the device appear as a loyalty card
+ whenever the screen is on and the user taps their device on an appropriately configured
+ NFC reader.
+
+ The "CardReader" sample can be used to read the loyalty card implemented in this sample.
+ ]]>
+ </intro>
+ </strings>
+
+ <template src="base"/>
+ <template src="FragmentView"/>
+ <common src="activities"/>
+ <common src="logger"/>
+
+</sample>
diff --git a/connectivity/nfc/CardReader/CardReaderSample/.gitignore b/connectivity/nfc/CardReader/CardReaderSample/.gitignore
new file mode 100644
index 0000000..6eb878d
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/.gitignore
@@ -0,0 +1,16 @@
+# Copyright 2013 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.
+src/template/
+src/common/
+build.gradle
diff --git a/connectivity/nfc/CardReader/CardReaderSample/README-fragmentview.txt b/connectivity/nfc/CardReader/CardReaderSample/README-fragmentview.txt
new file mode 100644
index 0000000..38d903f
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/README-fragmentview.txt
@@ -0,0 +1,37 @@
+<!--
+ Copyright 2013 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.
+-->
+
+Steps to implement FragmentView template:
+-in template-params.xml.ftl:
+ -add the following line to common imports
+ <common src="activities"/>
+
+-Add a Fragment to show behavior. In your MainActivity.java class, it will reference a Fragment
+ called (yourProjectName)Fragment.java. Create that file in your project, using the "main" source
+ folder instead of "common" or "templates".
+ For instance, if your package name is com.example.foo, create the file
+ src/main/java/com/example/foo/FooFragment.java
+
+
+-Within this fragment, make sure that the onCreate method has the line
+ "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
+
+-In order to override menu events, override onOptionsItemSelected.
+
+-refer to sampleSamples/fragmentViewSample for a reference implementation of a
+project built on this template.
+
+
diff --git a/connectivity/nfc/CardReader/CardReaderSample/proguard-project.txt b/connectivity/nfc/CardReader/CardReaderSample/proguard-project.txt
new file mode 100644
index 0000000..0d8f171
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/proguard-project.txt
@@ -0,0 +1,20 @@
+ To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/AndroidManifest.xml b/connectivity/nfc/CardReader/CardReaderSample/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a8ebd13
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.cardreader"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <!-- NFC Reader Mode was added in API 19. -->
+ <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
+ <uses-permission android:name="android.permission.NFC" />
+ <uses-feature android:name="android.hardware.nfc" android:required="true" />
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme">
+ <activity android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <!-- NFC-related intent filter. Allows application to handle messages from any
+ NFC-A devices discovered. Other Android devices are required to support NFC-A.
+ See: res/xml/nfc_tech_filter.xml -->
+ <intent-filter>
+ <action android:name="android.nfc.action.TECH_DISCOVERED" />
+ </intent-filter>
+ <meta-data
+ android:name="android.nfc.action.TECH_DISCOVERED"
+ android:resource="@xml/nfc_tech_filter" />
+ </activity>
+ </application>
+
+
+</manifest>
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/java/com/example/android/cardreader/CardReaderFragment.java b/connectivity/nfc/CardReader/CardReaderSample/src/main/java/com/example/android/cardreader/CardReaderFragment.java
new file mode 100644
index 0000000..3f27e9b
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/java/com/example/android/cardreader/CardReaderFragment.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 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.example.android.cardreader;
+
+import android.app.Activity;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.common.logger.Log;
+
+/**
+ * Generic UI for sample discovery.
+ */
+public class CardReaderFragment extends Fragment implements LoyaltyCardReader.AccountCallback {
+
+ public static final String TAG = "CardReaderFragment";
+ // Recommend NfcAdapter flags for reading from other Android devices. Indicates that this
+ // activity is interested in NFC-A devices (including other Android devices), and that the
+ // system should not check for the presence of NDEF-formatted data (e.g. Android Beam).
+ public static int READER_FLAGS =
+ NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;
+ public LoyaltyCardReader mLoyaltyCardReader;
+ private TextView mAccountField;
+
+ /** Called when sample is created. Displays generic UI with welcome text. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View v = inflater.inflate(R.layout.main_fragment, container, false);
+ if (v != null) {
+ mAccountField = (TextView) v.findViewById(R.id.card_account_field);
+ mAccountField.setText("Waiting...");
+
+ mLoyaltyCardReader = new LoyaltyCardReader(this);
+
+ // Disable Android Beam and register our card reader callback
+ enableReaderMode();
+ }
+
+ return v;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ disableReaderMode();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ enableReaderMode();
+ }
+
+ private void enableReaderMode() {
+ Log.i(TAG, "Enabling reader mode");
+ Activity activity = getActivity();
+ NfcAdapter nfc = NfcAdapter.getDefaultAdapter(activity);
+ if (nfc != null) {
+ nfc.enableReaderMode(activity, mLoyaltyCardReader, READER_FLAGS, null);
+ }
+ }
+
+ private void disableReaderMode() {
+ Log.i(TAG, "Disabling reader mode");
+ Activity activity = getActivity();
+ NfcAdapter nfc = NfcAdapter.getDefaultAdapter(activity);
+ if (nfc != null) {
+ nfc.disableReaderMode(activity);
+ }
+ }
+
+ @Override
+ public void onAccountReceived(final String account) {
+ // This callback is run on a background thread, but updates to UI elements must be performed
+ // on the UI thread.
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mAccountField.setText(account);
+ }
+ });
+ }
+}
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/java/com/example/android/cardreader/LoyaltyCardReader.java b/connectivity/nfc/CardReader/CardReaderSample/src/main/java/com/example/android/cardreader/LoyaltyCardReader.java
new file mode 100644
index 0000000..c29bdfd
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/java/com/example/android/cardreader/LoyaltyCardReader.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 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.example.android.cardreader;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.nfc.tech.IsoDep;
+
+import com.example.android.common.logger.Log;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+
+/**
+ * Callback class, invoked when an NFC card is scanned while the device is running in reader mode.
+ *
+ * Reader mode can be invoked by calling NfcAdapter
+ */
+public class LoyaltyCardReader implements NfcAdapter.ReaderCallback {
+ private static final String TAG = "LoyaltyCardReader";
+ // AID for our loyalty card service.
+ private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222";
+ // ISO-DEP command HEADER for selecting an AID.
+ // Format: [Class | Instruction | Parameter 1 | Parameter 2]
+ private static final String SELECT_APDU_HEADER = "00A40400";
+ // "OK" status word sent in response to SELECT AID command (0x9000)
+ private static final byte[] SELECT_OK_SW = {(byte) 0x90, (byte) 0x00};
+
+ // Weak reference to prevent retain loop. mAccountCallback is responsible for exiting
+ // foreground mode before it becomes invalid (e.g. during onPause() or onStop()).
+ private WeakReference<AccountCallback> mAccountCallback;
+
+ public interface AccountCallback {
+ public void onAccountReceived(String account);
+ }
+
+ public LoyaltyCardReader(AccountCallback accountCallback) {
+ mAccountCallback = new WeakReference<AccountCallback>(accountCallback);
+ }
+
+ /**
+ * Callback when a new tag is discovered by the system.
+ *
+ * <p>Communication with the card should take place here.
+ *
+ * @param tag Discovered tag
+ */
+ @Override
+ public void onTagDiscovered(Tag tag) {
+ Log.i(TAG, "New tag discovered");
+ // Android's Host-based Card Emulation (HCE) feature implements the ISO-DEP (ISO 14443-4)
+ // protocol.
+ //
+ // In order to communicate with a device using HCE, the discovered tag should be processed
+ // using the IsoDep class.
+ IsoDep isoDep = IsoDep.get(tag);
+ if (isoDep != null) {
+ try {
+ // Connect to the remote NFC device
+ isoDep.connect();
+ // Build SELECT AID command for our loyalty card service.
+ // This command tells the remote device which service we wish to communicate with.
+ Log.i(TAG, "Requesting remote AID: " + SAMPLE_LOYALTY_CARD_AID);
+ byte[] command = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID);
+ // Send command to remote device
+ Log.i(TAG, "Sending: " + ByteArrayToHexString(command));
+ byte[] result = isoDep.transceive(command);
+ // If AID is successfully selected, 0x9000 is returned as the status word (last 2
+ // bytes of the result) by convention. Everything before the status word is
+ // optional payload, which is used here to hold the account number.
+ int resultLength = result.length;
+ byte[] statusWord = {result[resultLength-2], result[resultLength-1]};
+ byte[] payload = Arrays.copyOf(result, resultLength-2);
+ if (Arrays.equals(SELECT_OK_SW, statusWord)) {
+ // The remote NFC device will immediately respond with its stored account number
+ String accountNumber = new String(payload, "UTF-8");
+ Log.i(TAG, "Received: " + accountNumber);
+ // Inform CardReaderFragment of received account number
+ mAccountCallback.get().onAccountReceived(accountNumber);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error communicating with card: " + e.toString());
+ }
+ }
+ }
+
+ /**
+ * Build APDU for SELECT AID command. This command indicates which service a reader is
+ * interested in communicating with. See ISO 7816-4.
+ *
+ * @param aid Application ID (AID) to select
+ * @return APDU for SELECT AID command
+ */
+ public static byte[] BuildSelectApdu(String aid) {
+ // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
+ return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", aid.length() / 2) + aid);
+ }
+
+ /**
+ * Utility class to convert a byte array to a hexadecimal string.
+ *
+ * @param bytes Bytes to convert
+ * @return String, containing hexadecimal representation.
+ */
+ public static String ByteArrayToHexString(byte[] bytes) {
+ final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+ char[] hexChars = new char[bytes.length * 2];
+ int v;
+ for ( int j = 0; j < bytes.length; j++ ) {
+ v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ /**
+ * Utility class to convert a hexadecimal string to a byte string.
+ *
+ * <p>Behavior with input strings containing non-hexadecimal characters is undefined.
+ *
+ * @param s String containing hexadecimal characters to convert
+ * @return Byte array generated from input
+ */
+ public static byte[] HexStringToByteArray(String s) {
+ int len = s.length();
+ 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;
+ }
+
+}
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-hdpi/ic_launcher.png b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..b1efaf4
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-mdpi/ic_launcher.png b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..f5f9244
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xhdpi/ic_launcher.png b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5d07b3f
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xxhdpi/card_background.png b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xxhdpi/card_background.png
new file mode 100644
index 0000000..86a7ba7
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xxhdpi/card_background.png
Binary files differ
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xxhdpi/ic_launcher.png b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..6ef21e1
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/res/layout/main_fragment.xml b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/layout/main_fragment.xml
new file mode 100644
index 0000000..1e3886c
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/layout/main_fragment.xml
@@ -0,0 +1,64 @@
+<!--
+Copyright (C) 2013 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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="380dp"
+ android:layout_height="242.25dp"
+ android:layout_gravity="center"
+ android:layout_margin="20dp">
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/card_background"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="20dp"
+ android:layout_gravity="center"
+ android:clickable="true">
+ <TextView
+ android:id="@+id/card_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/card_title"
+ android:fontFamily="sans-serif-condensed"
+ android:textStyle="bold"
+ android:textSize="32dp"
+ />
+ <TextView
+ android:id="@+id/card_account_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/account_number"
+ android:fontFamily="sans-serif"
+ android:textStyle="bold"
+ android:textSize="18dp"
+ android:layout_marginTop="40dp"
+ />
+ <TextView
+ android:id="@+id/card_account_field"
+ android:width="360dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-condensed"
+ android:textStyle="bold"
+ android:textSize="42dp"
+ android:singleLine="true"
+ />
+ </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/res/values/strings.xml b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b1b1a49
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2013 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.
+-->
+
+<resources>
+ <string name="card_title">Sample Loyalty Card</string>
+ <string name="account_number">Account Number</string>
+</resources>
\ No newline at end of file
diff --git a/connectivity/nfc/CardReader/CardReaderSample/src/main/res/xml/nfc_tech_filter.xml b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/xml/nfc_tech_filter.xml
new file mode 100644
index 0000000..dcfc979
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/src/main/res/xml/nfc_tech_filter.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This file is used as part of the filter for incoming NFC TECH_DISCOVERED intents. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Android's host card emulation feature only supports the IsoDep protocol. -->
+ <tech-list>
+ <tech>android.nfc.tech.IsoDep</tech>
+ </tech-list>
+</resources>
\ No newline at end of file
diff --git a/connectivity/nfc/CardReader/CardReaderSample/tests/AndroidManifest.xml b/connectivity/nfc/CardReader/CardReaderSample/tests/AndroidManifest.xml
new file mode 100644
index 0000000..1098859
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/tests/AndroidManifest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
+
+
+
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2013 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 name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.cardreader.tests"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk
+ android:minSdkVersion="19"
+ android:targetSdkVersion="19" />
+
+ <!-- We add an application tag here just so that we can indicate that
+ this package needs to link against the android.test library,
+ which is needed when building test cases. -->
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!--
+ Specifies the instrumentation test runner used to run the tests.
+ -->
+ <instrumentation
+ android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.example.android.cardreader"
+ android:label="Tests for com.example.android.cardreader" />
+
+</manifest>
\ No newline at end of file
diff --git a/connectivity/nfc/CardReader/CardReaderSample/tests/src/com/example/android/cardreader/tests/SampleTests.java b/connectivity/nfc/CardReader/CardReaderSample/tests/src/com/example/android/cardreader/tests/SampleTests.java
new file mode 100644
index 0000000..d9d2cc5
--- /dev/null
+++ b/connectivity/nfc/CardReader/CardReaderSample/tests/src/com/example/android/cardreader/tests/SampleTests.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 2013 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.
+*/
+
+
+
+/*
+* Copyright (C) 2013 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.example.android.cardreader.tests;
+
+import com.example.android.cardreader.*;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+* Tests for CardReader sample.
+*/
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+ private MainActivity mTestActivity;
+ private CardReaderFragment mTestFragment;
+
+ public SampleTests() {
+ super(MainActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // Starts the activity under test using the default Intent with:
+ // action = {@link Intent#ACTION_MAIN}
+ // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+ // All other fields are null or empty.
+ mTestActivity = getActivity();
+ mTestFragment = (CardReaderFragment)
+ mTestActivity.getSupportFragmentManager().getFragments().get(1);
+ }
+
+ /**
+ * Test if the test fixture has been set up correctly.
+ */
+ public void testPreconditions() {
+ //Try to add a message to add context to your assertions. These messages will be shown if
+ //a tests fails and make it easy to understand why a test failed
+ assertNotNull("mTestActivity is null", mTestActivity);
+ assertNotNull("mTestFragment is null", mTestFragment);
+ }
+
+ /**
+ * Test building SELECT APDU from AID string.
+ */
+ public void testBuildSelectApdu() {
+ final String aid = "1234";
+ final byte[] expectedResult = {(byte) 0x00, (byte) 0xA4, 04, (byte) 0x00, (byte) 0x02,
+ (byte) 0x12, (byte) 0x34};
+ final byte[] result = LoyaltyCardReader.BuildSelectApdu(aid);
+
+ assertEquals(expectedResult.length, result.length);
+ for (int i = 0; i < expectedResult.length; i++) {
+ assertEquals(expectedResult[i], result[i]);
+ }
+ }
+
+ /**
+ * Test converting from a hex string to binary.
+ */
+ public void testHexToBinary() {
+ final byte[] testData = {(byte) 0xc0, (byte) 0xff, (byte) 0xee};
+ final byte[] output = LoyaltyCardReader.HexStringToByteArray("C0FFEE");
+ for (int i = 0; i < testData.length; i++) {
+ assertEquals(testData[i], output[i]);
+ }
+ }
+
+ /**
+ * Test converting from binary to a hex string
+ */
+ public void testBinaryToHex() {
+ final byte[] input = {(byte) 0xc0, (byte) 0xff, (byte) 0xee};
+ final String output = LoyaltyCardReader.ByteArrayToHexString(input);
+ assertEquals("C0FFEE", output);
+ }
+
+}
\ No newline at end of file
diff --git a/connectivity/nfc/CardReader/build.gradle b/connectivity/nfc/CardReader/build.gradle
new file mode 100644
index 0000000..be1fa82
--- /dev/null
+++ b/connectivity/nfc/CardReader/build.gradle
@@ -0,0 +1,14 @@
+
+
+
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+ pathToBuild "../../../../../build"
+ pathToSamplesCommon "../../../common"
+}
+apply from: "../../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/connectivity/nfc/CardReader/buildSrc/build.gradle b/connectivity/nfc/CardReader/buildSrc/build.gradle
new file mode 100644
index 0000000..e344a8c
--- /dev/null
+++ b/connectivity/nfc/CardReader/buildSrc/build.gradle
@@ -0,0 +1,18 @@
+
+
+
+repositories {
+ mavenCentral()
+}
+dependencies {
+ compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+ main {
+ groovy {
+ srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+ }
+ }
+}
+
diff --git a/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.jar b/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties b/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1cfd865
--- /dev/null
+++ b/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Jan 02 15:37:05 PST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-all.zip
diff --git a/connectivity/nfc/CardReader/gradlew b/connectivity/nfc/CardReader/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/connectivity/nfc/CardReader/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/connectivity/nfc/CardReader/gradlew.bat b/connectivity/nfc/CardReader/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/connectivity/nfc/CardReader/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/connectivity/nfc/CardReader/settings.gradle b/connectivity/nfc/CardReader/settings.gradle
new file mode 100644
index 0000000..d1d7333
--- /dev/null
+++ b/connectivity/nfc/CardReader/settings.gradle
@@ -0,0 +1,4 @@
+
+
+
+include 'CardReaderSample'
diff --git a/connectivity/nfc/CardReader/template-params.xml b/connectivity/nfc/CardReader/template-params.xml
new file mode 100644
index 0000000..bd79926
--- /dev/null
+++ b/connectivity/nfc/CardReader/template-params.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
+
+
+
+<sample>
+ <name>CardReader</name>
+ <group>Connectivity</group>
+ <package>com.example.android.cardreader</package>
+
+ <!-- change minSdk if needed-->
+ <minSdk>19</minSdk>
+
+ <strings>
+ <intro>
+ <![CDATA[
+ This sample demonstrates how to implement a low-level NFC card reader, for reading cards
+ that do not contain NDEF or Android Beam data. This sample is designed to read the virtual
+ loyalty card implemented in the "CardEmulation" sample.\n\n
+
+ In particular, this sample demonstrates how to disable Android Beam, select which AIDs the
+ reader is interested, and establish communication with the card
+ ]]>
+ </intro>
+ </strings>
+
+ <template src="base"/>
+ <template src="FragmentView"/>
+ <common src="activities"/>
+ <common src="logger"/>
+
+</sample>