summaryrefslogtreecommitdiffstats
path: root/core/java/android/nfc/NdefTagConnection.java
blob: 4e9865c4a2aeb26d13347d45caf3d57e5dccf138 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * Copyright (C) 2010 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 android.nfc;

import java.io.IOException;

import android.os.RemoteException;
import android.util.Log;

/**
 * A connection to an NDEF target on an {@link NdefTag}.
 * <p>You can acquire this kind of connection with {@link NfcAdapter#createNdefTagConnection
 * createNdefTagConnection()}. Use the connection to read or write {@link NdefMessage}s.
 */
public class NdefTagConnection extends RawTagConnection {
    public static final int NDEF_MODE_READ_ONCE = 1;
    public static final int NDEF_MODE_READ_ONLY = 2;
    public static final int NDEF_MODE_WRITE_ONCE = 3;
    public static final int NDEF_MODE_WRITE_MANY = 4;
    public static final int NDEF_MODE_UNKNOWN = 5;

    private static final String TAG = "NFC";

    /**
     * Internal constructor, to be used by NfcAdapter
     * @hide
     */
    /* package private */ NdefTagConnection(INfcAdapter service, NdefTag tag, String target) throws RemoteException {
        super(service, tag);
        String[] targets = tag.getNdefTargets();
        int i;

        // Check target validity
        for (i=0; i<targets.length; i++) {
            if (target.equals(targets[i])) {
                break;
            }
        }
        if (i >= targets.length) {
            // Target not found
            throw new IllegalArgumentException();
        }
    }

    /**
     * Internal constructor, to be used by NfcAdapter
     * @hide
     */
    /* package private */ NdefTagConnection(INfcAdapter service, NdefTag tag) throws RemoteException {
        this(service, tag, tag.getNdefTargets()[0]);
    }

    /**
     * Read NDEF message(s).
     * This will always return the most up to date payload, and can block.
     * It can be canceled with {@link RawTagConnection#close}.
     * Most NDEF tags will contain just one NDEF message.
     * <p>
     * @throws FormatException if the tag is not NDEF formatted
     * @throws IOException if the target is lost or connection closed
     * @throws FormatException
     */
    public NdefMessage[] readNdefMessages() throws IOException, FormatException {
        //TODO(nxp): do not use getLastError(), it is racy
        try {
            NdefMessage[] msgArray = new NdefMessage[1];
            NdefMessage msg = mTagService.read(mTag.mNativeHandle);
            if (msg == null) {
                int errorCode = mTagService.getLastError(mTag.mNativeHandle);
                switch (errorCode) {
                    case ErrorCodes.ERROR_IO:
                        throw new IOException();
                    case ErrorCodes.ERROR_INVALID_PARAM:
                        throw new FormatException();
                    default:
                        // Should not happen
                        throw new IOException();
                }
            }
            msgArray[0] = msg;
            return msgArray;
        } catch (RemoteException e) {
            Log.e(TAG, "NFC service died");
            return null;
        }
    }

    /**
     * Attempt to write an NDEF message to a tag.
     * This method will block until the data is written. It can be canceled
     * with {@link RawTagConnection#close}.
     * Many tags are write-once, so use this method carefully.
     * Specification allows for multiple NDEF messages per NDEF tag, but it is
     * encourage to only write one message, this so API only takes a single
     * message. Use {@link NdefRecord} to write several records to a single tag.
     * For write-many tags, use {@link #makeReadOnly} after this method to attempt
     * to prevent further modification. For write-once tags this is not
     * neccesary.
     * Requires NFC_WRITE permission.
     * @throws FormatException if the tag is not suitable for NDEF messages
     * @throws IOException if the target is lost or connection closed or the
     *                     write failed
     */
    public void writeNdefMessage(NdefMessage message) throws IOException, FormatException {
        try {
            int errorCode = mTagService.write(mTag.mNativeHandle, message);
            switch (errorCode) {
                case ErrorCodes.SUCCESS:
                    break;
                case ErrorCodes.ERROR_IO:
                    throw new IOException();
                case ErrorCodes.ERROR_INVALID_PARAM:
                    throw new FormatException();
                default:
                    // Should not happen
                    throw new IOException();
            }
        } catch (RemoteException e) {
            Log.e(TAG, "NFC service died");
        }
    }

    /**
     * Attempts to make the NDEF data in this tag read-only.
     * This method will block until the action is complete. It can be canceled
     * with {@link RawTagConnection#close}.
     * Requires NFC_WRITE permission.
     * @return true if the tag is now read-only
     * @throws IOException if the target is lost, or connection closed
     */
    public boolean makeReadOnly() throws IOException {
        try {
            int errorCode = mTagService.makeReadOnly(mTag.mNativeHandle);
            switch (errorCode) {
                case ErrorCodes.SUCCESS:
                    return true;
                case ErrorCodes.ERROR_IO:
                    throw new IOException();
                case ErrorCodes.ERROR_INVALID_PARAM:
                    return false;
                default:
                    // Should not happen
                    throw new IOException();
            }
        } catch (RemoteException e) {
            Log.e(TAG, "NFC service died");
            return false;
        }
    }

    /**
     * Read/Write mode hint.
     * Provides a hint if further reads or writes are likely to suceed.
     * @return one of NDEF_MODE
     * @throws IOException if the target is lost or connection closed
     */
    public int getModeHint() throws IOException {
        try {
            int result = mTagService.getModeHint(mTag.mNativeHandle);
            if (ErrorCodes.isError(result)) {
                switch (result) {
                    case ErrorCodes.ERROR_IO:
                        throw new IOException();
                    default:
                        // Should not happen
                        throw new IOException();
                }
            }
            return result;

        } catch (RemoteException e) {
            Log.e(TAG, "NFC service died");
            return NDEF_MODE_UNKNOWN;
        }
    }
}