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 java.io.IOException;
20
21import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
23import android.content.Context;
24import android.nfc.INfcAdapterExtras;
25import android.nfc.NfcAdapter;
26import android.os.Binder;
27import android.os.Bundle;
28import android.os.IBinder;
29import android.os.RemoteException;
30
31public class NfcExecutionEnvironment {
32    private final NfcAdapterExtras mExtras;
33
34    /**
35     * Broadcast Action: An ISO-DEP AID was selected.
36     *
37     * <p>This happens as the result of a 'SELECT AID' command from an
38     * external NFC reader/writer.
39     *
40     * <p>Always contains the extra field {@link #EXTRA_AID}
41     *
42     * <p class="note">
43     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
44     * to receive.
45     */
46    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
47    public static final String ACTION_AID_SELECTED =
48        "com.android.nfc_extras.action.AID_SELECTED";
49
50    /**
51     * Mandatory byte array extra field in {@link #ACTION_AID_SELECTED}.
52     *
53     * <p>Contains the AID selected.
54     * @hide
55     */
56    public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID";
57
58    NfcExecutionEnvironment(NfcAdapterExtras extras) {
59        mExtras = extras;
60    }
61
62    /**
63     * Open the NFC Execution Environment on its contact interface.
64     *
65     * <p>Only one process may open the secure element at a time. If it is
66     * already open, an {@link IOException} is thrown.
67     *
68     * <p>All other NFC functionality is disabled while the NFC-EE is open
69     * on its contact interface, so make sure to call {@link #close} once complete.
70     *
71     * <p class="note">
72     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
73     *
74     * @throws IOException if the NFC-EE is already open, or some other error occurs
75     */
76    public void open() throws IOException {
77        try {
78            Bundle b = mExtras.getService().open(new Binder());
79            throwBundle(b);
80        } catch (RemoteException e) {
81            mExtras.attemptDeadServiceRecovery(e);
82            throw new IOException("NFC Service was dead, try again");
83        }
84    }
85
86    /**
87     * Close the NFC Execution Environment on its contact interface.
88     *
89     * <p class="note">
90     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
91     *
92     * @throws IOException if the NFC-EE is already open, or some other error occurs
93     */
94    public void close() throws IOException {
95        try {
96            throwBundle(mExtras.getService().close());
97        } catch (RemoteException e) {
98            mExtras.attemptDeadServiceRecovery(e);
99            throw new IOException("NFC Service was dead");
100        }
101    }
102
103    /**
104     * Send raw commands to the NFC-EE and receive the response.
105     *
106     * <p class="note">
107     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
108     *
109     * @throws IOException if the NFC-EE is not open, or some other error occurs
110     */
111    public byte[] transceive(byte[] in) throws IOException {
112        Bundle b;
113        try {
114            b = mExtras.getService().transceive(in);
115        } catch (RemoteException e) {
116            mExtras.attemptDeadServiceRecovery(e);
117            throw new IOException("NFC Service was dead, need to re-open");
118        }
119        throwBundle(b);
120        return b.getByteArray("out");
121    }
122
123    private static void throwBundle(Bundle b) throws IOException {
124        if (b.getInt("e") == -1) {
125            throw new IOException(b.getString("m"));
126        }
127    }
128}
129