Home | History | Annotate | Download | only in nfc_extras
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.nfc_extras;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.os.Binder;
     22 import android.os.Bundle;
     23 import android.os.RemoteException;
     24 
     25 import java.io.IOException;
     26 
     27 public class NfcExecutionEnvironment {
     28     private final NfcAdapterExtras mExtras;
     29     private final Binder mToken;
     30 
     31     // Exception types that can be thrown by NfcService
     32     // 1:1 mapped to EE_ERROR_ types in NfcService
     33     private static final int EE_ERROR_IO = -1;
     34     private static final int EE_ERROR_ALREADY_OPEN = -2;
     35     private static final int EE_ERROR_INIT = -3;
     36     private static final int EE_ERROR_LISTEN_MODE = -4;
     37     private static final int EE_ERROR_EXT_FIELD = -5;
     38     private static final int EE_ERROR_NFC_DISABLED = -6;
     39 
     40     /**
     41      * Broadcast Action: An ISO-DEP AID was selected.
     42      *
     43      * <p>This happens as the result of a 'SELECT AID' command from an
     44      * external NFC reader/writer.
     45      *
     46      * <p>Always contains the extra field {@link #EXTRA_AID}
     47      *
     48      * <p class="note">
     49      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
     50      * to receive.
     51      */
     52     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     53     public static final String ACTION_AID_SELECTED =
     54         "com.android.nfc_extras.action.AID_SELECTED";
     55 
     56     /**
     57      * Mandatory byte array extra field in {@link #ACTION_AID_SELECTED}.
     58      *
     59      * <p>Contains the AID selected.
     60      * @hide
     61      */
     62     public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID";
     63 
     64     /**
     65      * Broadcast action: A filtered APDU was received.
     66      *
     67      * <p>This happens when an APDU of interest was matched by the Nfc adapter,
     68      * for instance as the result of matching an externally-configured filter.
     69      *
     70      * <p>The filter configuration mechanism is not currently defined.
     71      *
     72      * <p>Always contains the extra field {@link EXTRA_APDU_BYTES}.
     73      *
     74      * @hide
     75      */
     76     public static final String ACTION_APDU_RECEIVED =
     77         "com.android.nfc_extras.action.APDU_RECEIVED";
     78 
     79     /**
     80      * Mandatory byte array extra field in {@link #ACTION_APDU_RECEIVED}.
     81      *
     82      * <p>Contains the bytes of the received APDU.
     83      *
     84      * @hide
     85      */
     86     public static final String EXTRA_APDU_BYTES =
     87         "com.android.nfc_extras.extra.APDU_BYTES";
     88 
     89     /**
     90      * Broadcast action: An EMV card removal event was detected.
     91      *
     92      * @hide
     93      */
     94     public static final String ACTION_EMV_CARD_REMOVAL =
     95         "com.android.nfc_extras.action.EMV_CARD_REMOVAL";
     96 
     97     /**
     98      * Broadcast action: An adapter implementing MIFARE Classic via card
     99      * emulation detected that a block has been accessed.
    100      *
    101      * <p>This may only be issued for the first block that the reader
    102      * authenticates to.
    103      *
    104      * <p>May contain the extra field {@link #EXTRA_MIFARE_BLOCK}.
    105      *
    106      * @hide
    107      */
    108     public static final String ACTION_MIFARE_ACCESS_DETECTED =
    109         "com.android.nfc_extras.action.MIFARE_ACCESS_DETECTED";
    110 
    111     /**
    112      * Optional integer extra field in {@link #ACTION_MIFARE_ACCESS_DETECTED}.
    113      *
    114      * <p>Provides the block number being accessed.  If not set, the block
    115      * number being accessed is unknown.
    116      *
    117      * @hide
    118      */
    119     public static final String EXTRA_MIFARE_BLOCK =
    120         "com.android.nfc_extras.extra.MIFARE_BLOCK";
    121 
    122     NfcExecutionEnvironment(NfcAdapterExtras extras) {
    123         mExtras = extras;
    124         mToken = new Binder();
    125     }
    126 
    127     /**
    128      * Open the NFC Execution Environment on its contact interface.
    129      *
    130      * <p>Opening a channel to the the secure element may fail
    131      * for a number of reasons:
    132      * <ul>
    133      * <li>NFC must be enabled for the connection to the SE to be opened.
    134      * If it is disabled at the time of this call, an {@link EeNfcDisabledException}
    135      * is thrown.
    136      *
    137      * <li>Only one process may open the secure element at a time. Additionally,
    138      * this method is not reentrant. If the secure element is already opened,
    139      * either by this process or by a different process, an {@link EeAlreadyOpenException}
    140      * is thrown.
    141      *
    142      * <li>If the connection to the secure element could not be initialized,
    143      * an {@link EeInitializationException} is thrown.
    144      *
    145      * <li>If the secure element or the NFC controller is activated in listen
    146      * mode - that is, it is talking over the contactless interface - an
    147      * {@link EeListenModeException} is thrown.
    148      *
    149      * <li>If the NFC controller is in a field powered by a remote device,
    150      * such as a payment terminal, an {@link EeExternalFieldException} is
    151      * thrown.
    152      * </ul>
    153      * <p>All other NFC functionality is disabled while the NFC-EE is open
    154      * on its contact interface, so make sure to call {@link #close} once complete.
    155      *
    156      * <p class="note">
    157      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
    158      *
    159      * @throws EeAlreadyOpenException if the NFC-EE is already open
    160      * @throws EeNfcDisabledException if NFC is disabled
    161      * @throws EeInitializationException if the Secure Element could not be initialized
    162      * @throws EeListenModeException if the NFCC or Secure Element is activated in listen mode
    163      * @throws EeExternalFieldException if the NFCC is in the presence of a remote-powered field
    164      * @throws EeIoException if an unknown error occurs
    165      */
    166     public void open() throws EeIOException {
    167         try {
    168             Bundle b = mExtras.getService().open(mExtras.mPackageName, mToken);
    169             throwBundle(b);
    170         } catch (RemoteException e) {
    171             mExtras.attemptDeadServiceRecovery(e);
    172             throw new EeIOException("NFC Service was dead, try again");
    173         }
    174     }
    175 
    176     /**
    177      * Close the NFC Execution Environment on its contact interface.
    178      *
    179      * <p class="note">
    180      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
    181      *
    182      * @throws IOException if the NFC-EE is already open, or some other error occurs
    183      */
    184     public void close() throws IOException {
    185         try {
    186             throwBundle(mExtras.getService().close(mExtras.mPackageName, mToken));
    187         } catch (RemoteException e) {
    188             mExtras.attemptDeadServiceRecovery(e);
    189             throw new IOException("NFC Service was dead");
    190         }
    191     }
    192 
    193     /**
    194      * Send raw commands to the NFC-EE and receive the response.
    195      *
    196      * <p class="note">
    197      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
    198      *
    199      * @throws IOException if the NFC-EE is not open, or some other error occurs
    200      */
    201     public byte[] transceive(byte[] in) throws IOException {
    202         Bundle b;
    203         try {
    204             b = mExtras.getService().transceive(mExtras.mPackageName, in);
    205         } catch (RemoteException e) {
    206             mExtras.attemptDeadServiceRecovery(e);
    207             throw new IOException("NFC Service was dead, need to re-open");
    208         }
    209         throwBundle(b);
    210         return b.getByteArray("out");
    211     }
    212 
    213     private static void throwBundle(Bundle b) throws EeIOException {
    214         switch (b.getInt("e")) {
    215             case EE_ERROR_NFC_DISABLED:
    216                 throw new EeNfcDisabledException(b.getString("m"));
    217             case EE_ERROR_IO:
    218                 throw new EeIOException(b.getString("m"));
    219             case EE_ERROR_INIT:
    220                 throw new EeInitializationException(b.getString("m"));
    221             case EE_ERROR_EXT_FIELD:
    222                 throw new EeExternalFieldException(b.getString("m"));
    223             case EE_ERROR_LISTEN_MODE:
    224                 throw new EeListenModeException(b.getString("m"));
    225             case EE_ERROR_ALREADY_OPEN:
    226                 throw new EeAlreadyOpenException(b.getString("m"));
    227         }
    228     }
    229 }
    230