Ndef.java revision f003e26df96067b4b136f0859012cb7ec3ed930f
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 #getType()} 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 /** @hide */ 65 public static final int TYPE_OTHER = -1; 66 /** @hide */ 67 public static final int TYPE_1 = 1; 68 /** @hide */ 69 public static final int TYPE_2 = 2; 70 /** @hide */ 71 public static final int TYPE_3 = 3; 72 /** @hide */ 73 public static final int TYPE_4 = 4; 74 /** @hide */ 75 public static final int TYPE_MIFARE_CLASSIC = 101; 76 77 /** @hide */ 78 public static final String UNKNOWN = "android.ndef.unknown"; 79 80 public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1"; 81 82 public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2"; 83 84 public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3"; 85 86 public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4"; 87 88 public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic"; 89 90 private final int mMaxNdefSize; 91 private final int mCardState; 92 private final NdefMessage mNdefMsg; 93 private final int mNdefType; 94 95 /** 96 * Returns an instance of this tech for the given tag. If the tag doesn't support 97 * this tech type null is returned. 98 * 99 * @param tag The tag to get the tech from 100 */ 101 public static Ndef get(Tag tag) { 102 if (!tag.hasTech(TagTechnology.NDEF)) return null; 103 try { 104 return new Ndef(tag); 105 } catch (RemoteException e) { 106 return null; 107 } 108 } 109 110 /** 111 * Internal constructor, to be used by NfcAdapter 112 * @hide 113 */ 114 public Ndef(Tag tag) throws RemoteException { 115 super(tag, TagTechnology.NDEF); 116 Bundle extras = tag.getTechExtras(TagTechnology.NDEF); 117 if (extras != null) { 118 mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH); 119 mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE); 120 mNdefMsg = extras.getParcelable(EXTRA_NDEF_MSG); 121 mNdefType = extras.getInt(EXTRA_NDEF_TYPE); 122 } else { 123 throw new NullPointerException("NDEF tech extras are null."); 124 } 125 126 } 127 128 /** 129 * Get the primary NDEF message on this tag. This data is read at discovery time 130 * and does not require a connection. 131 */ 132 public NdefMessage getCachedNdefMessage() { 133 return mNdefMsg; 134 } 135 136 /** 137 * Get NDEF tag type. 138 * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2}, 139 * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4}, 140 * {@link #MIFARE_CLASSIC} or another NDEF tag type that is not yet in the 141 * Android API. 142 * <p>Android devices with NFC support must always correctly enumerate 143 * NFC Forum tag types, and may optionally enumerate 144 * {@link #MIFARE_CLASSIC} since it requires proprietary technology. 145 */ 146 public String getType() { 147 switch (mNdefType) { 148 case TYPE_1: 149 return NFC_FORUM_TYPE_1; 150 case TYPE_2: 151 return NFC_FORUM_TYPE_2; 152 case TYPE_3: 153 return NFC_FORUM_TYPE_3; 154 case TYPE_4: 155 return NFC_FORUM_TYPE_4; 156 case TYPE_MIFARE_CLASSIC: 157 return MIFARE_CLASSIC; 158 default: 159 return UNKNOWN; 160 } 161 } 162 163 /** 164 * Get maximum NDEF message size in bytes 165 */ 166 public int getMaxSize() { 167 return mMaxNdefSize; 168 } 169 170 /** 171 * Provides a hint on whether writes are likely to succeed. 172 * <p>Requires {@link android.Manifest.permission#NFC} permission. 173 * @return true if write is likely to succeed 174 */ 175 public boolean isWritable() { 176 return (mCardState == NDEF_MODE_READ_WRITE); 177 } 178 179 // Methods that require connect() 180 /** 181 * Get the primary NDEF message on this tag. This data is read actively 182 * and requires a connection. 183 */ 184 public NdefMessage getNdefMessage() throws IOException, FormatException { 185 checkConnected(); 186 187 try { 188 INfcTag tagService = mTag.getTagService(); 189 int serviceHandle = mTag.getServiceHandle(); 190 if (tagService.isNdef(serviceHandle)) { 191 NdefMessage msg = tagService.ndefRead(serviceHandle); 192 if (msg == null) { 193 int errorCode = tagService.getLastError(serviceHandle); 194 switch (errorCode) { 195 case ErrorCodes.ERROR_IO: 196 throw new IOException(); 197 case ErrorCodes.ERROR_INVALID_PARAM: 198 throw new FormatException(); 199 default: 200 // Should not happen 201 throw new IOException(); 202 } 203 } 204 return msg; 205 } else { 206 return null; 207 } 208 } catch (RemoteException e) { 209 Log.e(TAG, "NFC service dead", e); 210 return null; 211 } 212 } 213 214 /** 215 * Overwrite the primary NDEF message 216 * @throws IOException 217 */ 218 public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException { 219 checkConnected(); 220 221 try { 222 INfcTag tagService = mTag.getTagService(); 223 int serviceHandle = mTag.getServiceHandle(); 224 if (tagService.isNdef(serviceHandle)) { 225 int errorCode = tagService.ndefWrite(serviceHandle, msg); 226 switch (errorCode) { 227 case ErrorCodes.SUCCESS: 228 break; 229 case ErrorCodes.ERROR_IO: 230 throw new IOException(); 231 case ErrorCodes.ERROR_INVALID_PARAM: 232 throw new FormatException(); 233 default: 234 // Should not happen 235 throw new IOException(); 236 } 237 } 238 else { 239 throw new IOException("Tag is not ndef"); 240 } 241 } catch (RemoteException e) { 242 Log.e(TAG, "NFC service dead", e); 243 } 244 } 245 246 /** 247 * Indicates whether a tag can be made read-only with 248 * {@link #makeReadOnly()} 249 */ 250 public boolean canMakeReadOnly() { 251 if (mNdefType == TYPE_1 || mNdefType == TYPE_2) { 252 return true; 253 } else { 254 return false; 255 } 256 } 257 258 /** 259 * Sets the CC field to indicate this tag is read-only 260 * and permanently sets the lock bits to prevent any further NDEF 261 * modifications. 262 * This is a one-way process and can not be reverted! 263 * @throws IOException 264 */ 265 public boolean makeReadOnly() throws IOException { 266 checkConnected(); 267 268 try { 269 INfcTag tagService = mTag.getTagService(); 270 if (tagService.isNdef(mTag.getServiceHandle())) { 271 int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle()); 272 switch (errorCode) { 273 case ErrorCodes.SUCCESS: 274 return true; 275 case ErrorCodes.ERROR_IO: 276 throw new IOException(); 277 case ErrorCodes.ERROR_INVALID_PARAM: 278 return false; 279 default: 280 // Should not happen 281 throw new IOException(); 282 } 283 } 284 else { 285 throw new IOException("Tag is not ndef"); 286 } 287 } catch (RemoteException e) { 288 Log.e(TAG, "NFC service dead", e); 289 return false; 290 } 291 } 292} 293