Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd23eca5 authored by Martijn Coenen's avatar Martijn Coenen Committed by Android (Google) Code Review
Browse files

Merge "First shot at (host-based) card emulation APIs."

parents 23425433 5eaa8970
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -259,6 +259,7 @@ package android {
    field public static final int addPrintersActivity = 16843747; // 0x10103e3
    field public static final int addStatesFromChildren = 16842992; // 0x10100f0
    field public static final int adjustViewBounds = 16843038; // 0x101011e
    field public static final int aid = 16843750; // 0x10103e6
    field public static final int alertDialogIcon = 16843605; // 0x1010355
    field public static final int alertDialogStyle = 16842845; // 0x101005d
    field public static final int alertDialogTheme = 16843529; // 0x1010309
@@ -793,6 +794,7 @@ package android {
    field public static final int path = 16842794; // 0x101002a
    field public static final int pathPattern = 16842796; // 0x101002c
    field public static final int pathPrefix = 16842795; // 0x101002b
    field public static final int paymentService = 16843749; // 0x10103e5
    field public static final int permission = 16842758; // 0x1010006
    field public static final int permissionFlags = 16843719; // 0x10103c7
    field public static final int permissionGroup = 16842762; // 0x101000a
@@ -14743,6 +14745,31 @@ package android.nfc {
}
package android.nfc.cardemulation {
  public abstract class HostApduService extends android.app.Service {
    ctor public HostApduService();
    method public final android.os.IBinder onBind(android.content.Intent);
    method public abstract void onDeactivated(int);
    method public abstract byte[] processCommandApdu(byte[], int);
    method public final void sendResponseApdu(byte[]);
    field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
    field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
    field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.HostApduService";
    field public static final java.lang.String SERVICE_META_DATA = "android.nfc.HostApduService";
  }
  public abstract class SeApduService extends android.app.Service {
    ctor public SeApduService();
    method public abstract void onAidSelected(byte[]);
    method public final android.os.IBinder onBind(android.content.Intent);
    method public abstract void onHciTransactionEvent(byte[], byte[]);
    field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.SeApduService";
    field public static final java.lang.String SERVICE_META_DATA = "android.nfc.SeApduService";
  }
}
package android.nfc.tech {
   abstract class BasicTagTechnology implements android.nfc.tech.TagTechnology {
+222 −0
Original line number Diff line number Diff line
package android.nfc.cardemulation;

import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

/**
 * <p>A convenience class that can be extended to implement
 * a service that processes ISO7816-4 commands on top of
 * the ISO14443-4 / IsoDep protocol (T=CL).
 *
 * <p>To tell the platform which ISO7816 application ID (AIDs)
 * are implemented by this service, a {@link #SERVICE_META_DATA}
 * entry must be included in the declaration of the service. An
 * example of such a service declaration is shown below:
 * <pre> &lt;service android:name=".MyHostApduService"&gt;
 *     &lt;intent-filter&gt;
 *         &lt;action android:name="android.nfc.HostApduService"/&gt;
 *     &lt;/intent-filter&gt;
 *     &lt;meta-data android:name="android.nfc.HostApduService" android:resource="@xml/apduservice.xml"/&gt;
 * &lt;/service&gt;</pre>
 * <p>For more details refer to {@link #SERVICE_META_DATA},
 * <code>&lt;{@link android.R.styleable#ApduService apdu-service}&gt;</code> and
 * <code>&lt;{@link android.R.styleable#AidFilter aid-filter}&gt;</code>.
 * <p class="note">The Android platform currently only supports a single
 * logical channel.
 */
public abstract class HostApduService extends Service {
    /**
     * The {@link Intent} that must be declared as handled by the service.
     */
    @SdkConstant(SdkConstantType.SERVICE_ACTION)
    public static final String SERVICE_INTERFACE =
            "android.nfc.HostApduService";

    /**
     * The name of the meta-data element that contains
     * more information about this service.
     */
    public static final String SERVICE_META_DATA = "android.nfc.HostApduService";

    /**
     * Reason for {@link #onDeactivated(int)}.
     * Indicates deactivation was due to the NFC link
     * being lost.
     */
    public static final int DEACTIVATION_LINK_LOSS = 0;

    /**
     * Reason for {@link #onDeactivated(int)}.
     *
     * <p>Indicates deactivation was due to a different AID
     * being selected (which implicitly deselects the AID
     * currently active on the logical channel).
     *
     * <p>Note that this next AID may still be resolved to this
     * service, in which case {@link #processCommandApdu(byte[], int)}
     * will be called again.
     */
    public static final int DEACTIVATION_DESELECTED = 1;

    static final String TAG = "ApduService";

    /**
     * MSG_COMMAND_APDU is sent by NfcService when
     * a 7816-4 command APDU has been received.
     *
     * @hide
     */
    public static final int MSG_COMMAND_APDU = 0;

    /**
     * MSG_RESPONSE_APDU is sent to NfcService to send
     * a response APDU back to the remote device.
     *
     * @hide
     */
    public static final int MSG_RESPONSE_APDU = 1;

    /**
     * MSG_DEACTIVATED is sent by NfcService when
     * the current session is finished; either because
     * another AID was selected that resolved to
     * another service, or because the NFC link
     * was deactivated.
     *
     * @hide
     */
    public static final int MSG_DEACTIVATED = 2;

    /**
     * @hide
     */
    public static final String KEY_DATA = "data";

    /**
     * Messenger interface to NfcService for sending responses.
     * Only accessed on main thread by the message handler.
     */
    Messenger mNfcService = null;

    final Messenger mMessenger = new Messenger(new MsgHandler());

    final class MsgHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_COMMAND_APDU:
                Bundle dataBundle = msg.getData();
                if (dataBundle == null) {
                    return;
                }
                if (mNfcService == null) mNfcService = msg.replyTo;

                byte[] apdu = dataBundle.getByteArray(KEY_DATA);
                if (apdu != null) {
                    byte[] responseApdu = processCommandApdu(apdu, 0);
                    if (responseApdu != null) {
                        if (mNfcService == null) {
                            Log.e(TAG, "Response not sent; service was deactivated.");
                            return;
                        }
                        Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU);
                        Bundle responseBundle = new Bundle();
                        responseBundle.putByteArray(KEY_DATA, responseApdu);
                        responseMsg.setData(responseBundle);
                        try {
                            mNfcService.send(responseMsg);
                        } catch (RemoteException e) {
                            Log.e("TAG", "Response not sent; RemoteException calling into " +
                                    "NfcService.");
                        }
                    }
                } else {
                    Log.e(TAG, "Received MSG_COMMAND_APDU without data.");
                }
                break;
            case MSG_RESPONSE_APDU:
                if (mNfcService == null) {
                    Log.e(TAG, "Response not sent; service was deactivated.");
                    return;
                }
                try {
                    mNfcService.send(msg);
                } catch (RemoteException e) {
                    Log.e(TAG, "RemoteException calling into NfcService.");
                }
                break;
            case MSG_DEACTIVATED:
                // Make sure we won't call into NfcService again
                mNfcService = null;
                onDeactivated(msg.arg1);
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

    @Override
    public final IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }

    /**
     * Sends a response APDU back to the remote device.
     *
     * <p>Note: this method may be called from any thread and will not block.
     * @param responseApdu A byte-array containing the reponse APDU.
     */
    public final void sendResponseApdu(byte[] responseApdu) {
        Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU);
        Bundle dataBundle = new Bundle();
        dataBundle.putByteArray(KEY_DATA, responseApdu);
        responseMsg.setData(dataBundle);
        try {
            mMessenger.send(responseMsg);
        } catch (RemoteException e) {
            Log.e("TAG", "Local messenger has died.");
        }
    }

    /**
     * <p>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. Note that 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
     * @param flags
     * @return a byte-array containing the response APDU, or null if no
     *         response APDU can be sent at this point.
     */
    public abstract byte[] processCommandApdu(byte[] commandApdu, int flags);

    /**
     * This method will be called in two possible scenarios:
     * <li>The NFC link has been deactivated or lost
     * <li>A different AID has been selected and was resolved to a different
     *     service component
     * @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED}
     */
    public abstract void onDeactivated(int reason);
}
+111 −0
Original line number Diff line number Diff line
package android.nfc.cardemulation;

import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;

/**
 * <p>A convenience class that can be extended to implement
 * a service that registers and deals with events for
 * ISO7814-4 AIDs that reside on an embedded secure element
 * or UICC.
 *
 * <p>To tell the platform which ISO7816 application ID (AIDs)
 * are present on the Secure Element and handled by this service,
 * a {@link #SERVICE_META_DATA} entry must be included in the declaration
 * of the service. An example of such a service declaration is shown below:
 * <pre> &lt;service android:name=".MySeApduService"&gt;
 *     &lt;intent-filter&gt;
 *         &lt;action android:name="android.nfc.SeApduService"/&gt;
 *     &lt;/intent-filter&gt;
 *     &lt;meta-data android:name="android.nfc.SeApduService" android:resource="@xml/apduservice.xml"/&gt;
 * &lt;/service&gt;</pre>
 * <p>For more details refer to {@link #SERVICE_META_DATA},
 * <code>&lt;{@link android.R.styleable#ApduService apdu-service}&gt;</code> and
 * <code>&lt;{@link android.R.styleable#AidFilter aid-filter}&gt;</code>.
 */
public abstract class SeApduService extends Service {
    /**
     * The {@link Intent} that must be declared as handled by the service.
     */
    @SdkConstant(SdkConstantType.SERVICE_ACTION)
    public static final String SERVICE_INTERFACE =
            "android.nfc.SeApduService";

    /**
     * The name of the meta-data element that contains
     * more information about this service.
     */
    public static final String SERVICE_META_DATA = "android.nfc.SeApduService";

    /**
     * @hide
     */
    public static final int MSG_AID_SELECTED = 0;

    /**
     * @hide
     */
    public static final int MSG_HCI_TRANSACTION_EVT = 1;

    /**
     * @hide
     */
    public static final String KEY_AID = "aid";

    /**
     * @hide
     */
    public static final String KEY_PARAMETERS = "parameters";

    final Messenger mMessenger = new Messenger(new MsgHandler());

    final class MsgHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MSG_AID_SELECTED: {
                    Bundle dataBundle = msg.getData();
                    byte[] aid = dataBundle.getByteArray(KEY_AID);
                    onAidSelected(aid);
                    break;
                }
                case MSG_HCI_TRANSACTION_EVT: {
                    Bundle dataBundle = msg.getData();
                    byte[] aid = dataBundle.getByteArray(KEY_AID);
                    byte[] parameters = dataBundle.getByteArray(KEY_PARAMETERS);
                    onHciTransactionEvent(aid, parameters);
                    break;
                }
            }
        }
    };

    @Override
    public final IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }

    /**
     * This method is called when an AID that has been registered
     * in the manifest of this service has been selected on a
     * eSE/UICC.
     * @param aid The AID that has been selected
     */
    public abstract void onAidSelected(byte[] aid);

    /**
     * This method is called when a HCI transaction event has
     * been received for an AID that has been registered
     * in the manifest of this service.
     * @param aid The AID of the application that generated the event
     * @param parameters Parameters according to ETSI-TS 102 622
     */
    public abstract void onHciTransactionEvent(byte[] aid, byte[] parameters);
}
 No newline at end of file
+25 −0
Original line number Diff line number Diff line
@@ -2578,6 +2578,31 @@
        <attr name="vendor" format="string"/>
    </declare-styleable>

    <!-- Use <code>apdu-service</code> as the root tag of the XML resource that
         describes an {@link android.nfc.cardemulation.HostApduService} or
         {@link android.nfc.cardemulation.SeApduService} service, which is referenced
         from its SERVICE_META_DATA entry. -->
    <declare-styleable name="ApduService">
        <!-- Set to true to let the NFC subsystem know that this service implements
             a payment instrument. That will allow this service to be enumerated in
             a list of payment services, where the user can pick his preferred payment
             service. The preferred service will be bound to persistently, to make sure
             it can immediately process APDUs without service startup delay. This is vital
             for existing payment infrastructure that has very strict timing requirements. -->
        <attr name="paymentService" format="boolean" />
        <!-- Short description of the functionality the serivce implements.-->
        <attr name="description" />
    </declare-styleable>

    <!-- Specify one or more <code>aid-filter</code> elements inside a <code>apdu-service</code>
         element to list the ISO7816 Application ID (AIDs) your service can handle.-->
    <declare-styleable name="AidFilter">
        <!-- The ISO7816 Application ID -->
        <attr name="aid" format="string" />
        <!-- Short description of what the AID implements.-->
        <attr name="description" />
    </declare-styleable>

    <declare-styleable name="ActionMenuItemView">
        <attr name="minWidth" />
    </declare-styleable>
+2 −1
Original line number Diff line number Diff line
@@ -2068,5 +2068,6 @@
  <public type="attr" name="sspPattern" />
  <public type="attr" name="addPrintersActivity" />
  <public type="attr" name="vendor" />

  <public type="attr" name="paymentService" />
  <public type="attr" name="aid" />
</resources>