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