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; 18 19 import java.io.IOException; 20 21 import android.os.RemoteException; 22 import android.util.Log; 23 24 /** 25 * A low-level connection to a {@link Tag} target. 26 * <p>You can acquire this kind of connection with {@link NfcAdapter#createRawTagConnection 27 * createRawTagConnection()}. Use the connection to send and receive data with {@link #transceive 28 * transceive()}. 29 * <p> 30 * Applications must implement their own protocol stack on top of {@link #transceive transceive()}. 31 * 32 * <p class="note"><strong>Note:</strong> 33 * Use of this class requires the {@link android.Manifest.permission#NFC} 34 * permission. 35 * @hide 36 */ 37 public class RawTagConnection { 38 39 /*package*/ final Tag mTag; 40 /*package*/ boolean mIsConnected; 41 /*package*/ String mSelectedTarget; 42 private final NfcAdapter mAdapter; 43 44 // Following fields are final after construction, except for 45 // during attemptDeadServiceRecovery() when NFC crashes. 46 // Not locked - we accept a best effort attempt when NFC crashes. 47 /*package*/ INfcAdapter mService; 48 /*package*/ INfcTag mTagService; 49 50 private static final String TAG = "NFC"; 51 52 /*package*/ RawTagConnection(NfcAdapter adapter, Tag tag, String target) throws RemoteException { 53 String[] targets = tag.getRawTargets(); 54 int i; 55 56 // Check target validity 57 for (i=0;i<targets.length;i++) { 58 if (target.equals(targets[i])) { 59 break; 60 } 61 } 62 if (i >= targets.length) { 63 // Target not found 64 throw new IllegalArgumentException(); 65 } 66 67 mAdapter = adapter; 68 mService = mAdapter.mService; 69 mTagService = mService.getNfcTagInterface(); 70 mTag = tag; 71 mSelectedTarget = target; 72 } 73 74 /*package*/ RawTagConnection(NfcAdapter adapter, Tag tag) throws RemoteException { 75 this(adapter, tag, tag.getRawTargets()[0]); 76 } 77 78 /** NFC service dead - attempt best effort recovery */ 79 /*package*/ void attemptDeadServiceRecovery(Exception e) { 80 mAdapter.attemptDeadServiceRecovery(e); 81 /* assigning to mService is not thread-safe, but this is best-effort code 82 * and on a well-behaved system should never happen */ 83 mService = mAdapter.mService; 84 try { 85 mTagService = mService.getNfcTagInterface(); 86 } catch (RemoteException e2) { 87 Log.e(TAG, "second RemoteException trying to recover from dead NFC service", e2); 88 } 89 } 90 91 /** 92 * Get the {@link Tag} this connection is associated with. 93 * <p>Requires {@link android.Manifest.permission#NFC} permission. 94 */ 95 public Tag getTag() { 96 return mTag; 97 } 98 99 /** 100 * <p>Requires {@link android.Manifest.permission#NFC} permission. 101 */ 102 public String getTagTarget() { 103 return mSelectedTarget; 104 } 105 106 /** 107 * Helper to indicate if {@link #transceive transceive()} calls might succeed. 108 * <p> 109 * Does not cause RF activity, and does not block. 110 * <p>Requires {@link android.Manifest.permission#NFC} permission. 111 * @return true if {@link #connect} has completed successfully and the {@link Tag} is believed 112 * to be within range. Applications must still handle {@link java.io.IOException} 113 * while using {@link #transceive transceive()}, in case connection is lost after this method 114 * returns true. 115 */ 116 public boolean isConnected() { 117 if (!mIsConnected) { 118 return false; 119 } 120 121 try { 122 return mTagService.isPresent(mTag.mServiceHandle); 123 } catch (RemoteException e) { 124 attemptDeadServiceRecovery(e); 125 return false; 126 } 127 } 128 129 /** 130 * Connect to the {@link Tag} associated with this connection. 131 * <p> 132 * This method blocks until the connection is established. 133 * <p> 134 * {@link #close} can be called from another thread to cancel this connection 135 * attempt. 136 * <p>Requires {@link android.Manifest.permission#NFC} permission. 137 * @throws IOException if the target is lost, or connect canceled 138 */ 139 public void connect() throws IOException { 140 //TODO(nxp): enforce exclusivity 141 mIsConnected = true; 142 } 143 144 /** 145 * Close this connection. 146 * <p> 147 * Causes blocking operations such as {@link #transceive transceive()} or {@link #connect} to 148 * be canceled and immediately throw {@link java.io.IOException}. 149 * <p> 150 * Once this method is called, this object cannot be re-used and should be discarded. Further 151 * calls to {@link #transceive transceive()} or {@link #connect} will fail. 152 * <p>Requires {@link android.Manifest.permission#NFC} permission. 153 */ 154 public void close() { 155 mIsConnected = false; 156 try { 157 mTagService.close(mTag.mServiceHandle); 158 } catch (RemoteException e) { 159 attemptDeadServiceRecovery(e); 160 } 161 } 162 163 /** 164 * Send data to a tag and receive the response. 165 * <p> 166 * This method will block until the response is received. It can be canceled 167 * with {@link #close}. 168 * <p>Requires {@link android.Manifest.permission#NFC} permission. 169 * 170 * @param data bytes to send 171 * @return bytes received in response 172 * @throws IOException if the target is lost or connection closed 173 */ 174 public byte[] transceive(byte[] data) throws IOException { 175 try { 176 byte[] response = mTagService.transceive(mTag.mServiceHandle, data); 177 if (response == null) { 178 throw new IOException("transcieve failed"); 179 } 180 return response; 181 } catch (RemoteException e) { 182 attemptDeadServiceRecovery(e); 183 throw new IOException("NFC service died"); 184 } 185 } 186 } 187