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 17 package android.nfc.tech; 18 19 import android.nfc.ErrorCodes; 20 import android.nfc.FormatException; 21 import android.nfc.INfcTag; 22 import android.nfc.NdefMessage; 23 import android.nfc.Tag; 24 import android.nfc.TagLostException; 25 import android.os.RemoteException; 26 import android.util.Log; 27 28 import java.io.IOException; 29 30 /** 31 * Provide access to NDEF format operations on a {@link Tag}. 32 * 33 * <p>Acquire a {@link NdefFormatable} object using {@link #get}. 34 * 35 * <p>Android devices with NFC must only enumerate and implement this 36 * class for tags for which it can format to NDEF. 37 * 38 * <p>Unfortunately the procedures to convert unformated tags to NDEF formatted 39 * tags are not specified by NFC Forum, and are not generally well-known. So 40 * there is no mandatory set of tags for which all Android devices with NFC 41 * must support {@link NdefFormatable}. 42 * 43 * <p class="note"><strong>Note:</strong> Methods that perform I/O operations 44 * require the {@link android.Manifest.permission#NFC} permission. 45 */ 46 public final class NdefFormatable extends BasicTagTechnology { 47 private static final String TAG = "NFC"; 48 49 /** 50 * Get an instance of {@link NdefFormatable} for the given tag. 51 * <p>Does not cause any RF activity and does not block. 52 * <p>Returns null if {@link NdefFormatable} was not enumerated in {@link Tag#getTechList}. 53 * This indicates the tag is not NDEF formatable by this Android device. 54 * 55 * @param tag an NDEF formatable tag 56 * @return NDEF formatable object 57 */ get(Tag tag)58 public static NdefFormatable get(Tag tag) { 59 if (!tag.hasTech(TagTechnology.NDEF_FORMATABLE)) return null; 60 try { 61 return new NdefFormatable(tag); 62 } catch (RemoteException e) { 63 return null; 64 } 65 } 66 67 /** 68 * Internal constructor, to be used by NfcAdapter 69 * @hide 70 */ NdefFormatable(Tag tag)71 public NdefFormatable(Tag tag) throws RemoteException { 72 super(tag, TagTechnology.NDEF_FORMATABLE); 73 } 74 75 /** 76 * Format a tag as NDEF, and write a {@link NdefMessage}. 77 * 78 * <p>This is a multi-step process, an IOException is thrown 79 * if any one step fails. 80 * <p>The card is left in a read-write state after this operation. 81 * 82 * <p>This is an I/O operation and will block until complete. It must 83 * not be called from the main application thread. A blocked call will be canceled with 84 * {@link IOException} if {@link #close} is called from another thread. 85 * 86 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 87 * 88 * @param firstMessage the NDEF message to write after formatting, can be null 89 * @throws TagLostException if the tag leaves the field 90 * @throws IOException if there is an I/O failure, or the operation is canceled 91 * @throws FormatException if the NDEF Message to write is malformed 92 */ format(NdefMessage firstMessage)93 public void format(NdefMessage firstMessage) throws IOException, FormatException { 94 format(firstMessage, false); 95 } 96 97 /** 98 * Formats a tag as NDEF, write a {@link NdefMessage}, and make read-only. 99 * 100 * <p>This is a multi-step process, an IOException is thrown 101 * if any one step fails. 102 * <p>The card is left in a read-only state if this method returns successfully. 103 * 104 * <p>This is an I/O operation and will block until complete. It must 105 * not be called from the main application thread. A blocked call will be canceled with 106 * {@link IOException} if {@link #close} is called from another thread. 107 * 108 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 109 * 110 * @param firstMessage the NDEF message to write after formatting 111 * @throws TagLostException if the tag leaves the field 112 * @throws IOException if there is an I/O failure, or the operation is canceled 113 * @throws FormatException if the NDEF Message to write is malformed 114 */ formatReadOnly(NdefMessage firstMessage)115 public void formatReadOnly(NdefMessage firstMessage) throws IOException, FormatException { 116 format(firstMessage, true); 117 } 118 format(NdefMessage firstMessage, boolean makeReadOnly)119 /*package*/ void format(NdefMessage firstMessage, boolean makeReadOnly) throws IOException, 120 FormatException { 121 checkConnected(); 122 123 try { 124 int serviceHandle = mTag.getServiceHandle(); 125 INfcTag tagService = mTag.getTagService(); 126 int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT); 127 switch (errorCode) { 128 case ErrorCodes.SUCCESS: 129 break; 130 case ErrorCodes.ERROR_IO: 131 throw new IOException(); 132 case ErrorCodes.ERROR_INVALID_PARAM: 133 throw new FormatException(); 134 default: 135 // Should not happen 136 throw new IOException(); 137 } 138 // Now check and see if the format worked 139 if (!tagService.isNdef(serviceHandle)) { 140 throw new IOException(); 141 } 142 143 // Write a message, if one was provided 144 if (firstMessage != null) { 145 errorCode = tagService.ndefWrite(serviceHandle, firstMessage); 146 switch (errorCode) { 147 case ErrorCodes.SUCCESS: 148 break; 149 case ErrorCodes.ERROR_IO: 150 throw new IOException(); 151 case ErrorCodes.ERROR_INVALID_PARAM: 152 throw new FormatException(); 153 default: 154 // Should not happen 155 throw new IOException(); 156 } 157 } 158 159 // optionally make read-only 160 if (makeReadOnly) { 161 errorCode = tagService.ndefMakeReadOnly(serviceHandle); 162 switch (errorCode) { 163 case ErrorCodes.SUCCESS: 164 break; 165 case ErrorCodes.ERROR_IO: 166 throw new IOException(); 167 case ErrorCodes.ERROR_INVALID_PARAM: 168 throw new IOException(); 169 default: 170 // Should not happen 171 throw new IOException(); 172 } 173 } 174 } catch (RemoteException e) { 175 Log.e(TAG, "NFC service dead", e); 176 } 177 } 178 } 179