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