Home | History | Annotate | Download | only in tech
      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