Ndef.java revision 4e21e1d21a877cce4db5ec8c5786604cc10f2d7e
1/*
2 * Copyright (C) 2010 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 android.nfc.tech;
18
19import android.nfc.ErrorCodes;
20import android.nfc.FormatException;
21import android.nfc.INfcTag;
22import android.nfc.NdefMessage;
23import android.nfc.NfcAdapter;
24import android.nfc.Tag;
25import android.os.Bundle;
26import android.os.RemoteException;
27import android.util.Log;
28
29import java.io.IOException;
30
31/**
32 * A high-level connection to a {@link Tag} using one of the NFC type 1, 2, 3, or 4 technologies
33 * to interact with NDEF data. MiFare Classic cards that present NDEF data may also be used
34 * via this class. To determine the exact technology being used call {@link #getTechnologyId()}
35 *
36 * <p>You can acquire this kind of connection with {@link #get}.
37 *
38 * <p class="note"><strong>Note:</strong>
39 * Use of this class requires the {@link android.Manifest.permission#NFC}
40 * permission.
41 */
42public final class Ndef extends BasicTagTechnology {
43    private static final String TAG = "NFC";
44
45    /** @hide */
46    public static final int NDEF_MODE_READ_ONLY = 1;
47    /** @hide */
48    public static final int NDEF_MODE_READ_WRITE = 2;
49    /** @hide */
50    public static final int NDEF_MODE_UNKNOWN = 3;
51
52    /** @hide */
53    public static final String EXTRA_NDEF_MSG = "ndefmsg";
54
55    /** @hide */
56    public static final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength";
57
58    /** @hide */
59    public static final String EXTRA_NDEF_CARDSTATE = "ndefcardstate";
60
61    /** @hide */
62    public static final String EXTRA_NDEF_TYPE = "ndeftype";
63
64    public static final int OTHER = -1;
65    public static final int NFC_FORUM_TYPE_1 = 1;
66    public static final int NFC_FORUM_TYPE_2 = 2;
67    public static final int NFC_FORUM_TYPE_3 = 3;
68    public static final int NFC_FORUM_TYPE_4 = 4;
69    public static final int MIFARE_CLASSIC = 101;
70
71    private final int mMaxNdefSize;
72    private final int mCardState;
73    private final NdefMessage mNdefMsg;
74    private final int mNdefType;
75
76    /**
77     * Returns an instance of this tech for the given tag. If the tag doesn't support
78     * this tech type null is returned.
79     *
80     * @param tag The tag to get the tech from
81     */
82    public static Ndef get(Tag tag) {
83        if (!tag.hasTech(TagTechnology.NDEF)) return null;
84        try {
85            return new Ndef(tag);
86        } catch (RemoteException e) {
87            return null;
88        }
89    }
90
91    /**
92     * Internal constructor, to be used by NfcAdapter
93     * @hide
94     */
95    public Ndef(Tag tag) throws RemoteException {
96        super(tag, TagTechnology.NDEF);
97        Bundle extras = tag.getTechExtras(TagTechnology.NDEF);
98        if (extras != null) {
99            mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH);
100            mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE);
101            mNdefMsg = extras.getParcelable(EXTRA_NDEF_MSG);
102            mNdefType = extras.getInt(EXTRA_NDEF_TYPE);
103        } else {
104            throw new NullPointerException("NDEF tech extras are null.");
105        }
106
107    }
108
109    /**
110     * Get the primary NDEF message on this tag. This data is read at discovery time
111     * and does not require a connection.
112     */
113    public NdefMessage getCachedNdefMessage() {
114        return mNdefMsg;
115    }
116
117    /**
118     * Get NDEF tag type.
119     * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2},
120     * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4},
121     * {@link #MIFARE_CLASSIC} or {@link #OTHER}.
122     * <p>Platforms of this API revision will always return one of the above
123     * values. Platforms at future API revisions may return other values, which
124     * can be treated as {@link #OTHER} by applications targeting this API.
125     * <p>Android devices with NFC support must always correctly enumerate
126     * NFC Forum tag types, and may optionally enumerate
127     * {@link #MIFARE_CLASSIC} since it requires proprietary technology.
128     * Devices that cannot enumerate {@link #MIFARE_CLASSIC} will use
129     * {@link #OTHER} instead.
130     */
131    public int getType() {
132        return mNdefType;
133    }
134
135    /**
136     * Get maximum NDEF message size in bytes
137     */
138    public int getMaxSize() {
139        return mMaxNdefSize;
140    }
141
142    /**
143     * Provides a hint on whether writes are likely to succeed.
144     * <p>Requires {@link android.Manifest.permission#NFC} permission.
145     * @return true if write is likely to succeed
146     */
147    public boolean isWritable() {
148        return (mCardState == NDEF_MODE_READ_WRITE);
149    }
150
151    // Methods that require connect()
152    /**
153     * Get the primary NDEF message on this tag. This data is read actively
154     * and requires a connection.
155     */
156    public NdefMessage getNdefMessage() throws IOException, FormatException {
157        checkConnected();
158
159        try {
160            INfcTag tagService = mTag.getTagService();
161            int serviceHandle = mTag.getServiceHandle();
162            if (tagService.isNdef(serviceHandle)) {
163                NdefMessage msg = tagService.ndefRead(serviceHandle);
164                if (msg == null) {
165                    int errorCode = tagService.getLastError(serviceHandle);
166                    switch (errorCode) {
167                        case ErrorCodes.ERROR_IO:
168                            throw new IOException();
169                        case ErrorCodes.ERROR_INVALID_PARAM:
170                            throw new FormatException();
171                        default:
172                            // Should not happen
173                            throw new IOException();
174                    }
175                }
176                return msg;
177            } else {
178                return null;
179            }
180        } catch (RemoteException e) {
181            Log.e(TAG, "NFC service dead", e);
182            return null;
183        }
184    }
185
186    /**
187     * Overwrite the primary NDEF message
188     * @throws IOException
189     */
190    public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
191        checkConnected();
192
193        try {
194            INfcTag tagService = mTag.getTagService();
195            int serviceHandle = mTag.getServiceHandle();
196            if (tagService.isNdef(serviceHandle)) {
197                int errorCode = tagService.ndefWrite(serviceHandle, msg);
198                switch (errorCode) {
199                    case ErrorCodes.SUCCESS:
200                        break;
201                    case ErrorCodes.ERROR_IO:
202                        throw new IOException();
203                    case ErrorCodes.ERROR_INVALID_PARAM:
204                        throw new FormatException();
205                    default:
206                        // Should not happen
207                        throw new IOException();
208                }
209            }
210            else {
211                throw new IOException("Tag is not ndef");
212            }
213        } catch (RemoteException e) {
214            Log.e(TAG, "NFC service dead", e);
215        }
216    }
217
218    /**
219     * Indicates whether a tag can be made read-only with
220     * {@link #makeReadonly()}
221     */
222    public boolean canMakeReadonly() {
223        if (mNdefType == NFC_FORUM_TYPE_1 || mNdefType == NFC_FORUM_TYPE_2) {
224            return true;
225        } else {
226            return false;
227        }
228    }
229
230    /**
231     * Sets the CC field to indicate this tag is read-only
232     * and permanently sets the lock bits to prevent any further NDEF
233     * modifications.
234     * This is a one-way process and can not be reverted!
235     * @throws IOException
236     */
237    public boolean makeReadonly() throws IOException {
238        checkConnected();
239
240        try {
241            INfcTag tagService = mTag.getTagService();
242            if (tagService.isNdef(mTag.getServiceHandle())) {
243                int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle());
244                switch (errorCode) {
245                    case ErrorCodes.SUCCESS:
246                        return true;
247                    case ErrorCodes.ERROR_IO:
248                        throw new IOException();
249                    case ErrorCodes.ERROR_INVALID_PARAM:
250                        return false;
251                    default:
252                        // Should not happen
253                        throw new IOException();
254                }
255           }
256           else {
257               throw new IOException("Tag is not ndef");
258           }
259        } catch (RemoteException e) {
260            Log.e(TAG, "NFC service dead", e);
261            return false;
262        }
263    }
264}
265