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
17package com.android.nfc_extras;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.os.Binder;
22import android.os.Bundle;
23import android.os.RemoteException;
24
25import java.io.IOException;
26
27public class NfcExecutionEnvironment {
28    private final NfcAdapterExtras mExtras;
29    private final Binder mToken;
30
31    /**
32     * Broadcast Action: An ISO-DEP AID was selected.
33     *
34     * <p>This happens as the result of a 'SELECT AID' command from an
35     * external NFC reader/writer.
36     *
37     * <p>Always contains the extra field {@link #EXTRA_AID}
38     *
39     * <p class="note">
40     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
41     * to receive.
42     */
43    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
44    public static final String ACTION_AID_SELECTED =
45        "com.android.nfc_extras.action.AID_SELECTED";
46
47    /**
48     * Mandatory byte array extra field in {@link #ACTION_AID_SELECTED}.
49     *
50     * <p>Contains the AID selected.
51     * @hide
52     */
53    public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID";
54
55    /**
56     * Broadcast action: A filtered APDU was received.
57     *
58     * <p>This happens when an APDU of interest was matched by the Nfc adapter,
59     * for instance as the result of matching an externally-configured filter.
60     *
61     * <p>The filter configuration mechanism is not currently defined.
62     *
63     * <p>Always contains the extra field {@link EXTRA_APDU_BYTES}.
64     *
65     * @hide
66     */
67    public static final String ACTION_APDU_RECEIVED =
68        "com.android.nfc_extras.action.APDU_RECEIVED";
69
70    /**
71     * Mandatory byte array extra field in {@link #ACTION_APDU_RECEIVED}.
72     *
73     * <p>Contains the bytes of the received APDU.
74     *
75     * @hide
76     */
77    public static final String EXTRA_APDU_BYTES =
78        "com.android.nfc_extras.extra.APDU_BYTES";
79
80    /**
81     * Broadcast action: An EMV card removal event was detected.
82     *
83     * @hide
84     */
85    public static final String ACTION_EMV_CARD_REMOVAL =
86        "com.android.nfc_extras.action.EMV_CARD_REMOVAL";
87
88    /**
89     * Broadcast action: An adapter implementing MIFARE Classic via card
90     * emulation detected that a block has been accessed.
91     *
92     * <p>This may only be issued for the first block that the reader
93     * authenticates to.
94     *
95     * <p>May contain the extra field {@link #EXTRA_MIFARE_BLOCK}.
96     *
97     * @hide
98     */
99    public static final String ACTION_MIFARE_ACCESS_DETECTED =
100        "com.android.nfc_extras.action.MIFARE_ACCESS_DETECTED";
101
102    /**
103     * Optional integer extra field in {@link #ACTION_MIFARE_ACCESS_DETECTED}.
104     *
105     * <p>Provides the block number being accessed.  If not set, the block
106     * number being accessed is unknown.
107     *
108     * @hide
109     */
110    public static final String EXTRA_MIFARE_BLOCK =
111        "com.android.nfc_extras.extra.MIFARE_BLOCK";
112
113    NfcExecutionEnvironment(NfcAdapterExtras extras) {
114        mExtras = extras;
115        mToken = new Binder();
116    }
117
118    /**
119     * Open the NFC Execution Environment on its contact interface.
120     *
121     * <p>Only one process may open the secure element at a time. If it is
122     * already open, an {@link IOException} is thrown.
123     *
124     * <p>All other NFC functionality is disabled while the NFC-EE is open
125     * on its contact interface, so make sure to call {@link #close} once complete.
126     *
127     * <p class="note">
128     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
129     *
130     * @throws IOException if the NFC-EE is already open, or some other error occurs
131     */
132    public void open() throws IOException {
133        try {
134            Bundle b = mExtras.getService().open(mExtras.mPackageName, mToken);
135            throwBundle(b);
136        } catch (RemoteException e) {
137            mExtras.attemptDeadServiceRecovery(e);
138            throw new IOException("NFC Service was dead, try again");
139        }
140    }
141
142    /**
143     * Close the NFC Execution Environment on its contact interface.
144     *
145     * <p class="note">
146     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
147     *
148     * @throws IOException if the NFC-EE is already open, or some other error occurs
149     */
150    public void close() throws IOException {
151        try {
152            throwBundle(mExtras.getService().close(mExtras.mPackageName, mToken));
153        } catch (RemoteException e) {
154            mExtras.attemptDeadServiceRecovery(e);
155            throw new IOException("NFC Service was dead");
156        }
157    }
158
159    /**
160     * Send raw commands to the NFC-EE and receive the response.
161     *
162     * <p class="note">
163     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
164     *
165     * @throws IOException if the NFC-EE is not open, or some other error occurs
166     */
167    public byte[] transceive(byte[] in) throws IOException {
168        Bundle b;
169        try {
170            b = mExtras.getService().transceive(mExtras.mPackageName, in);
171        } catch (RemoteException e) {
172            mExtras.attemptDeadServiceRecovery(e);
173            throw new IOException("NFC Service was dead, need to re-open");
174        }
175        throwBundle(b);
176        return b.getByteArray("out");
177    }
178
179    private static void throwBundle(Bundle b) throws IOException {
180        if (b.getInt("e") == -1) {
181            throw new IOException(b.getString("m"));
182        }
183    }
184}
185