1 /* 2 * Copyright (C) 2015 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.cardemulation; 18 19 import android.annotation.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.app.Service; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.Message; 28 import android.os.Messenger; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 /** 33 * <p>HostApduService is a convenience {@link Service} class that can be 34 * extended to emulate an NFC card inside an Android 35 * service component. 36 * 37 * <div class="special reference"> 38 * <h3>Developer Guide</h3> 39 * For a general introduction to card emulation, see 40 * <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html"> 41 * Host-based Card Emulation</a>.</p> 42 * </div> 43 * 44 * <h3>NFC Protocols</h3> 45 * <p>Cards emulated by this class are based on the NFC-Forum ISO-DEP 46 * protocol (based on ISO/IEC 14443-4) and support processing 47 * command Application Protocol Data Units (APDUs) as 48 * defined in the ISO/IEC 7816-4 specification. 49 * 50 * <h3>Service selection</h3> 51 * <p>When a remote NFC device wants to talk to your 52 * service, it sends a so-called 53 * "SELECT AID" APDU as defined in the ISO/IEC 7816-4 specification. 54 * The AID is an application identifier defined in ISO/IEC 7816-4. 55 * 56 * <p>The registration procedure for AIDs is defined in the 57 * ISO/IEC 7816-5 specification. If you don't want to register an 58 * AID, you are free to use AIDs in the proprietary range: 59 * bits 8-5 of the first byte must each be set to '1'. For example, 60 * "0xF00102030405" is a proprietary AID. If you do use proprietary 61 * AIDs, it is recommended to choose an AID of at least 6 bytes, 62 * to reduce the risk of collisions with other applications that 63 * might be using proprietary AIDs as well. 64 * 65 * <h3>AID groups</h3> 66 * <p>In some cases, a service may need to register multiple AIDs 67 * to implement a certain application, and it needs to be sure 68 * that it is the default handler for all of these AIDs (as opposed 69 * to some AIDs in the group going to another service). 70 * 71 * <p>An AID group is a list of AIDs that should be considered as 72 * belonging together by the OS. For all AIDs in an AID group, the 73 * OS will guarantee one of the following: 74 * <ul> 75 * <li>All AIDs in the group are routed to this service 76 * <li>No AIDs in the group are routed to this service 77 * </ul> 78 * In other words, there is no in-between state, where some AIDs 79 * in the group can be routed to this service, and some to another. 80 * <h3>AID groups and categories</h3> 81 * <p>Each AID group can be associated with a category. This allows 82 * the Android OS to classify services, and it allows the user to 83 * set defaults at the category level instead of the AID level. 84 * 85 * <p>You can use 86 * {@link CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String)} 87 * to determine if your service is the default handler for a category. 88 * 89 * <p>In this version of the platform, the only known categories 90 * are {@link CardEmulation#CATEGORY_PAYMENT} and {@link CardEmulation#CATEGORY_OTHER}. 91 * AID groups without a category, or with a category that is not recognized 92 * by the current platform version, will automatically be 93 * grouped into the {@link CardEmulation#CATEGORY_OTHER} category. 94 * <h3>Service AID registration</h3> 95 * <p>To tell the platform which AIDs groups 96 * are requested by this service, a {@link #SERVICE_META_DATA} 97 * entry must be included in the declaration of the service. An 98 * example of a HostApduService manifest declaration is shown below: 99 * <pre> <service android:name=".MyHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> 100 * <intent-filter> 101 * <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/> 102 * </intent-filter> 103 * <meta-data android:name="android.nfc.cardemulation.host_apdu_ervice" android:resource="@xml/apduservice"/> 104 * </service></pre> 105 * 106 * This meta-data tag points to an apduservice.xml file. 107 * An example of this file with a single AID group declaration is shown below: 108 * <pre> 109 * <host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" 110 * android:description="@string/servicedesc" android:requireDeviceUnlock="false"> 111 * <aid-group android:description="@string/aiddescription" android:category="other"> 112 * <aid-filter android:name="F0010203040506"/> 113 * <aid-filter android:name="F0394148148100"/> 114 * </aid-group> 115 * </host-apdu-service> 116 * </pre> 117 * 118 * <p>The {@link android.R.styleable#HostApduService <host-apdu-service>} is required 119 * to contain a 120 * {@link android.R.styleable#HostApduService_description <android:description>} 121 * attribute that contains a user-friendly description of the service that may be shown in UI. 122 * The 123 * {@link android.R.styleable#HostApduService_requireDeviceUnlock <requireDeviceUnlock>} 124 * attribute can be used to specify that the device must be unlocked before this service 125 * can be invoked to handle APDUs. 126 * <p>The {@link android.R.styleable#HostApduService <host-apdu-service>} must 127 * contain one or more {@link android.R.styleable#AidGroup <aid-group>} tags. 128 * Each {@link android.R.styleable#AidGroup <aid-group>} must contain one or 129 * more {@link android.R.styleable#AidFilter <aid-filter>} tags, each of which 130 * contains a single AID. The AID must be specified in hexadecimal format, and contain 131 * an even number of characters. 132 * <h3>AID conflict resolution</h3> 133 * Multiple HostApduServices may be installed on a single device, and the same AID 134 * can be registered by more than one service. The Android platform resolves AID 135 * conflicts depending on which category an AID belongs to. Each category may 136 * have a different conflict resolution policy. For example, for some categories 137 * the user may be able to select a default service in the Android settings UI. 138 * For other categories, to policy may be to always ask the user which service 139 * is to be invoked in case of conflict. 140 * 141 * To query the conflict resolution policy for a certain category, see 142 * {@link CardEmulation#getSelectionModeForCategory(String)}. 143 * 144 * <h3>Data exchange</h3> 145 * <p>Once the platform has resolved a "SELECT AID" command APDU to a specific 146 * service component, the "SELECT AID" command APDU and all subsequent 147 * command APDUs will be sent to that service through 148 * {@link #processCommandApdu(byte[], Bundle)}, until either: 149 * <ul> 150 * <li>The NFC link is broken</li> 151 * <li>A "SELECT AID" APDU is received which resolves to another service</li> 152 * </ul> 153 * These two scenarios are indicated by a call to {@link #onDeactivated(int)}. 154 * 155 * <p class="note">Use of this class requires the 156 * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present 157 * on the device. 158 * 159 */ 160 public abstract class HostApduService extends Service { 161 /** 162 * The {@link Intent} action that must be declared as handled by the service. 163 */ 164 @SdkConstant(SdkConstantType.SERVICE_ACTION) 165 public static final String SERVICE_INTERFACE = 166 "android.nfc.cardemulation.action.HOST_APDU_SERVICE"; 167 168 /** 169 * The name of the meta-data element that contains 170 * more information about this service. 171 */ 172 public static final String SERVICE_META_DATA = 173 "android.nfc.cardemulation.host_apdu_service"; 174 175 /** 176 * Reason for {@link #onDeactivated(int)}. 177 * Indicates deactivation was due to the NFC link 178 * being lost. 179 */ 180 public static final int DEACTIVATION_LINK_LOSS = 0; 181 182 /** 183 * Reason for {@link #onDeactivated(int)}. 184 * 185 * <p>Indicates deactivation was due to a different AID 186 * being selected (which implicitly deselects the AID 187 * currently active on the logical channel). 188 * 189 * <p>Note that this next AID may still be resolved to this 190 * service, in which case {@link #processCommandApdu(byte[], Bundle)} 191 * will be called again. 192 */ 193 public static final int DEACTIVATION_DESELECTED = 1; 194 195 static final String TAG = "ApduService"; 196 197 /** 198 * MSG_COMMAND_APDU is sent by NfcService when 199 * a 7816-4 command APDU has been received. 200 * 201 * @hide 202 */ 203 public static final int MSG_COMMAND_APDU = 0; 204 205 /** 206 * MSG_RESPONSE_APDU is sent to NfcService to send 207 * a response APDU back to the remote device. 208 * 209 * @hide 210 */ 211 public static final int MSG_RESPONSE_APDU = 1; 212 213 /** 214 * MSG_DEACTIVATED is sent by NfcService when 215 * the current session is finished; either because 216 * another AID was selected that resolved to 217 * another service, or because the NFC link 218 * was deactivated. 219 * 220 * @hide 221 */ 222 public static final int MSG_DEACTIVATED = 2; 223 224 /** 225 * 226 * @hide 227 */ 228 public static final int MSG_UNHANDLED = 3; 229 230 /** 231 * @hide 232 */ 233 public static final String KEY_DATA = "data"; 234 235 /** 236 * Messenger interface to NfcService for sending responses. 237 * Only accessed on main thread by the message handler. 238 * 239 * @hide 240 */ 241 Messenger mNfcService = null; 242 243 final Messenger mMessenger = new Messenger(new MsgHandler()); 244 245 final class MsgHandler extends Handler { 246 @Override handleMessage(Message msg)247 public void handleMessage(Message msg) { 248 switch (msg.what) { 249 case MSG_COMMAND_APDU: 250 Bundle dataBundle = msg.getData(); 251 if (dataBundle == null) { 252 return; 253 } 254 if (mNfcService == null) mNfcService = msg.replyTo; 255 256 byte[] apdu = dataBundle.getByteArray(KEY_DATA); 257 if (apdu != null) { 258 byte[] responseApdu = processCommandApdu(apdu, null); 259 if (responseApdu != null) { 260 if (mNfcService == null) { 261 Log.e(TAG, "Response not sent; service was deactivated."); 262 return; 263 } 264 Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU); 265 Bundle responseBundle = new Bundle(); 266 responseBundle.putByteArray(KEY_DATA, responseApdu); 267 responseMsg.setData(responseBundle); 268 responseMsg.replyTo = mMessenger; 269 try { 270 mNfcService.send(responseMsg); 271 } catch (RemoteException e) { 272 Log.e("TAG", "Response not sent; RemoteException calling into " + 273 "NfcService."); 274 } 275 } 276 } else { 277 Log.e(TAG, "Received MSG_COMMAND_APDU without data."); 278 } 279 break; 280 case MSG_RESPONSE_APDU: 281 if (mNfcService == null) { 282 Log.e(TAG, "Response not sent; service was deactivated."); 283 return; 284 } 285 try { 286 msg.replyTo = mMessenger; 287 mNfcService.send(msg); 288 } catch (RemoteException e) { 289 Log.e(TAG, "RemoteException calling into NfcService."); 290 } 291 break; 292 case MSG_DEACTIVATED: 293 // Make sure we won't call into NfcService again 294 mNfcService = null; 295 onDeactivated(msg.arg1); 296 break; 297 case MSG_UNHANDLED: 298 if (mNfcService == null) { 299 Log.e(TAG, "notifyUnhandled not sent; service was deactivated."); 300 return; 301 } 302 try { 303 msg.replyTo = mMessenger; 304 mNfcService.send(msg); 305 } catch (RemoteException e) { 306 Log.e(TAG, "RemoteException calling into NfcService."); 307 } 308 break; 309 default: 310 super.handleMessage(msg); 311 } 312 } 313 } 314 315 @Override onBind(Intent intent)316 public final IBinder onBind(Intent intent) { 317 return mMessenger.getBinder(); 318 } 319 320 /** 321 * Sends a response APDU back to the remote device. 322 * 323 * <p>Note: this method may be called from any thread and will not block. 324 * @param responseApdu A byte-array containing the reponse APDU. 325 */ sendResponseApdu(byte[] responseApdu)326 public final void sendResponseApdu(byte[] responseApdu) { 327 Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU); 328 Bundle dataBundle = new Bundle(); 329 dataBundle.putByteArray(KEY_DATA, responseApdu); 330 responseMsg.setData(dataBundle); 331 try { 332 mMessenger.send(responseMsg); 333 } catch (RemoteException e) { 334 Log.e("TAG", "Local messenger has died."); 335 } 336 } 337 338 /** 339 * Calling this method allows the service to tell the OS 340 * that it won't be able to complete this transaction - 341 * for example, because it requires data connectivity 342 * that is not present at that moment. 343 * 344 * The OS may use this indication to give the user a list 345 * of alternative applications that can handle the last 346 * AID that was selected. If the user would select an 347 * application from the list, that action by itself 348 * will not cause the default to be changed; the selected 349 * application will be invoked for the next tap only. 350 * 351 * If there are no other applications that can handle 352 * this transaction, the OS will show an error dialog 353 * indicating your service could not complete the 354 * transaction. 355 * 356 * <p>Note: this method may be called anywhere between 357 * the first {@link #processCommandApdu(byte[], Bundle)} 358 * call and a {@link #onDeactivated(int)} call. 359 */ notifyUnhandled()360 public final void notifyUnhandled() { 361 Message unhandledMsg = Message.obtain(null, MSG_UNHANDLED); 362 try { 363 mMessenger.send(unhandledMsg); 364 } catch (RemoteException e) { 365 Log.e("TAG", "Local messenger has died."); 366 } 367 } 368 369 370 /** 371 * <p>This method will be called when a command APDU has been received 372 * from a remote device. A response APDU can be provided directly 373 * by returning a byte-array in this method. Note that in general 374 * response APDUs must be sent as quickly as possible, given the fact 375 * that the user is likely holding his device over an NFC reader 376 * when this method is called. 377 * 378 * <p class="note">If there are multiple services that have registered for the same 379 * AIDs in their meta-data entry, you will only get called if the user has 380 * explicitly selected your service, either as a default or just for the next tap. 381 * 382 * <p class="note">This method is running on the main thread of your application. 383 * If you cannot return a response APDU immediately, return null 384 * and use the {@link #sendResponseApdu(byte[])} method later. 385 * 386 * @param commandApdu The APDU that was received from the remote device 387 * @param extras A bundle containing extra data. May be null. 388 * @return a byte-array containing the response APDU, or null if no 389 * response APDU can be sent at this point. 390 */ processCommandApdu(byte[] commandApdu, Bundle extras)391 public abstract byte[] processCommandApdu(byte[] commandApdu, Bundle extras); 392 393 /** 394 * This method will be called in two possible scenarios: 395 * <li>The NFC link has been deactivated or lost 396 * <li>A different AID has been selected and was resolved to a different 397 * service component 398 * @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED} 399 */ onDeactivated(int reason)400 public abstract void onDeactivated(int reason); 401 } 402