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.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      */
     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      */
     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      */
     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      */
    115     public void formatReadOnly(NdefMessage firstMessage) throws IOException, FormatException {
    116         format(firstMessage, true);
    117     }
    118 
    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