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 20 import android.annotation.FlaggedApi; 21 import android.annotation.NonNull; 22 import android.annotation.SdkConstant; 23 import android.annotation.SdkConstant.SdkConstantType; 24 import android.annotation.SuppressLint; 25 import android.app.Service; 26 import android.content.Intent; 27 import android.content.pm.PackageManager; 28 import android.nfc.NfcAdapter; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Message; 33 import android.os.Messenger; 34 import android.os.RemoteException; 35 import android.os.Trace; 36 import android.util.Log; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.nfc.module.flags.Flags; 40 41 import java.nio.ByteBuffer; 42 import java.util.ArrayList; 43 import java.util.List; 44 import java.util.Random; 45 46 /** 47 * <p>HostApduService is a convenience {@link Service} class that can be 48 * extended to emulate an NFC card inside an Android 49 * service component. 50 * 51 * <div class="special reference"> 52 * <h3>Developer Guide</h3> 53 * For a general introduction to card emulation, see 54 * <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html"> 55 * Host-based Card Emulation</a>.</p> 56 * </div> 57 * 58 * <h3>NFC Protocols</h3> 59 * <p>Cards emulated by this class are based on the NFC-Forum ISO-DEP 60 * protocol (based on ISO/IEC 14443-4) and support processing 61 * command Application Protocol Data Units (APDUs) as 62 * defined in the ISO/IEC 7816-4 specification. 63 * 64 * <h3>Service selection</h3> 65 * <p>When a remote NFC device wants to talk to your 66 * service, it sends a so-called 67 * "SELECT AID" APDU as defined in the ISO/IEC 7816-4 specification. 68 * The AID is an application identifier defined in ISO/IEC 7816-4. 69 * 70 * <p>The registration procedure for AIDs is defined in the 71 * ISO/IEC 7816-5 specification. If you don't want to register an 72 * AID, you are free to use AIDs in the proprietary range: 73 * bits 8-5 of the first byte must each be set to '1'. For example, 74 * "0xF00102030405" is a proprietary AID. If you do use proprietary 75 * AIDs, it is recommended to choose an AID of at least 6 bytes, 76 * to reduce the risk of collisions with other applications that 77 * might be using proprietary AIDs as well. 78 * 79 * <h3>AID groups</h3> 80 * <p>In some cases, a service may need to register multiple AIDs 81 * to implement a certain application, and it needs to be sure 82 * that it is the default handler for all of these AIDs (as opposed 83 * to some AIDs in the group going to another service). 84 * 85 * <p>An AID group is a list of AIDs that should be considered as 86 * belonging together by the OS. For all AIDs in an AID group, the 87 * OS will guarantee one of the following: 88 * <ul> 89 * <li>All AIDs in the group are routed to this service 90 * <li>No AIDs in the group are routed to this service 91 * </ul> 92 * In other words, there is no in-between state, where some AIDs 93 * in the group can be routed to this service, and some to another. 94 * <h3>AID groups and categories</h3> 95 * <p>Each AID group can be associated with a category. This allows 96 * the Android OS to classify services, and it allows the user to 97 * set defaults at the category level instead of the AID level. 98 * 99 * <p>You can use 100 * {@link CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String)} 101 * to determine if your service is the default handler for a category. 102 * 103 * <p>In this version of the platform, the only known categories 104 * are {@link CardEmulation#CATEGORY_PAYMENT} and {@link CardEmulation#CATEGORY_OTHER}. 105 * AID groups without a category, or with a category that is not recognized 106 * by the current platform version, will automatically be 107 * grouped into the {@link CardEmulation#CATEGORY_OTHER} category. 108 * <h3>Service AID registration</h3> 109 * <p>To tell the platform which AIDs groups 110 * are requested by this service, a {@link #SERVICE_META_DATA} 111 * entry must be included in the declaration of the service. An 112 * example of a HostApduService manifest declaration is shown below: 113 * <pre> <service android:name=".MyHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> 114 * <intent-filter> 115 * <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/> 116 * </intent-filter> 117 * <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apduservice"/> 118 * </service></pre> 119 * 120 * This meta-data tag points to an apduservice.xml file. 121 * An example of this file with a single AID group declaration is shown below: 122 * <pre> 123 * <host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" 124 * android:description="@string/servicedesc" android:requireDeviceUnlock="false"> 125 * <aid-group android:description="@string/aiddescription" android:category="other"> 126 * <aid-filter android:name="F0010203040506"/> 127 * <aid-filter android:name="F0394148148100"/> 128 * </aid-group> 129 * </host-apdu-service> 130 * </pre> 131 * 132 * <p>The {@link android.R.styleable#HostApduService <host-apdu-service>} is required 133 * to contain a 134 * {@link android.R.styleable#HostApduService_description <android:description>} 135 * attribute that contains a user-friendly description of the service that may be shown in UI. 136 * The 137 * {@link android.R.styleable#HostApduService_requireDeviceUnlock <requireDeviceUnlock>} 138 * attribute can be used to specify that the device must be unlocked before this service 139 * can be invoked to handle APDUs. 140 * <p>The {@link android.R.styleable#HostApduService <host-apdu-service>} must 141 * contain one or more {@link android.R.styleable#AidGroup <aid-group>} tags. 142 * Each {@link android.R.styleable#AidGroup <aid-group>} must contain one or 143 * more {@link android.R.styleable#AidFilter <aid-filter>} tags, each of which 144 * contains a single AID. The AID must be specified in hexadecimal format, and contain 145 * an even number of characters. 146 * <h3>AID conflict resolution</h3> 147 * Multiple HostApduServices may be installed on a single device, and the same AID 148 * can be registered by more than one service. The Android platform resolves AID 149 * conflicts depending on which category an AID belongs to. Each category may 150 * have a different conflict resolution policy. For example, for some categories 151 * the user may be able to select a default service in the Android settings UI. 152 * For other categories, to policy may be to always ask the user which service 153 * is to be invoked in case of conflict. 154 * 155 * To query the conflict resolution policy for a certain category, see 156 * {@link CardEmulation#getSelectionModeForCategory(String)}. 157 * 158 * <h3>Data exchange</h3> 159 * <p>Once the platform has resolved a "SELECT AID" command APDU to a specific 160 * service component, the "SELECT AID" command APDU and all subsequent 161 * command APDUs will be sent to that service through 162 * {@link #processCommandApdu(byte[], Bundle)}, until either: 163 * <ul> 164 * <li>The NFC link is broken</li> 165 * <li>A "SELECT AID" APDU is received which resolves to another service</li> 166 * </ul> 167 * These two scenarios are indicated by a call to {@link #onDeactivated(int)}. 168 * 169 * <p class="note">Use of this class requires the 170 * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present 171 * on the device. 172 * 173 */ 174 public abstract class HostApduService extends Service { 175 /** 176 * The {@link Intent} action that must be declared as handled by the service. 177 */ 178 @SdkConstant(SdkConstantType.SERVICE_ACTION) 179 public static final String SERVICE_INTERFACE = 180 "android.nfc.cardemulation.action.HOST_APDU_SERVICE"; 181 182 /** 183 * The name of the meta-data element that contains 184 * more information about this service. 185 */ 186 public static final String SERVICE_META_DATA = 187 "android.nfc.cardemulation.host_apdu_service"; 188 189 /** 190 * Reason for {@link #onDeactivated(int)}. 191 * Indicates deactivation was due to the NFC link 192 * being lost. 193 */ 194 public static final int DEACTIVATION_LINK_LOSS = 0; 195 196 /** 197 * Reason for {@link #onDeactivated(int)}. 198 * 199 * <p>Indicates deactivation was due to a different AID 200 * being selected (which implicitly deselects the AID 201 * currently active on the logical channel). 202 * 203 * <p>Note that this next AID may still be resolved to this 204 * service, in which case {@link #processCommandApdu(byte[], Bundle)} 205 * will be called again. 206 */ 207 public static final int DEACTIVATION_DESELECTED = 1; 208 209 static final String TAG = "ApduService"; 210 private static final String EVENT_HCE_RESPONSE_APDU = "hce_response_apdu"; 211 212 /** 213 * MSG_COMMAND_APDU is sent by NfcService when 214 * a 7816-4 command APDU has been received. 215 * 216 * @hide 217 */ 218 public static final int MSG_COMMAND_APDU = 0; 219 220 /** 221 * MSG_RESPONSE_APDU is sent to NfcService to send 222 * a response APDU back to the remote device. 223 * 224 * @hide 225 */ 226 public static final int MSG_RESPONSE_APDU = 1; 227 228 /** 229 * MSG_DEACTIVATED is sent by NfcService when 230 * the current session is finished; either because 231 * another AID was selected that resolved to 232 * another service, or because the NFC link 233 * was deactivated. 234 * 235 * @hide 236 */ 237 public static final int MSG_DEACTIVATED = 2; 238 239 /** 240 * 241 * @hide 242 */ 243 public static final int MSG_UNHANDLED = 3; 244 245 /** 246 * @hide 247 */ 248 public static final int MSG_POLLING_LOOP = 4; 249 250 /** 251 * @hide 252 */ 253 public static final int MSG_COMMAND_APDU_ACK = 5; 254 255 /** 256 * @hide 257 */ 258 public static final int MSG_RESPONSE_APDU_ACK = 6; 259 260 /** 261 * @hide 262 */ 263 public static final int MSG_POLLING_LOOP_ACK = 7; 264 265 /** 266 * @hide 267 */ 268 public static final String KEY_DATA = "data"; 269 270 /** 271 * @hide 272 */ 273 public static final String KEY_POLLING_LOOP_FRAMES_BUNDLE = 274 "android.nfc.cardemulation.POLLING_FRAMES"; 275 276 /** 277 * Messenger interface to NfcService for sending responses. 278 * Only accessed on main thread by the message handler. 279 * 280 * @hide 281 */ 282 Messenger mNfcService = null; 283 final Messenger mMessenger = new Messenger(new MsgHandler()); 284 285 private Random mCookieRandom = new Random(System.currentTimeMillis()); 286 287 /** 288 * @hide 289 */ 290 @VisibleForTesting getMsgHandler()291 public MsgHandler getMsgHandler() { 292 return new MsgHandler(); 293 } 294 295 /** 296 * @hide 297 */ getNfcService()298 public Messenger getNfcService() { 299 return mNfcService; 300 } 301 302 /** 303 * @hide 304 */ setNfcService(Messenger nfcService)305 public void setNfcService(Messenger nfcService) { 306 mNfcService = nfcService; 307 } 308 309 /** 310 * @hide 311 */ getMessenger()312 public Messenger getMessenger() { 313 return mMessenger; 314 } 315 316 /** 317 * @hide 318 */ getCookieRandom()319 public Random getCookieRandom() { 320 return mCookieRandom; 321 } 322 323 final class MsgHandler extends Handler { 324 @Override handleMessage(Message msg)325 public void handleMessage(Message msg) { 326 switch (msg.what) { 327 case MSG_COMMAND_APDU: 328 Bundle dataBundle = msg.getData(); 329 if (dataBundle == null) { 330 return; 331 } 332 if (getNfcService() == null) setNfcService(msg.replyTo); 333 334 byte[] apdu = dataBundle.getByteArray(KEY_DATA); 335 if (apdu != null) { 336 byte[] responseApdu = processCommandApdu(apdu, null); 337 338 if (Flags.nfcHceLatencyEvents()) { 339 try { 340 Message ackMsg = Message.obtain(null, MSG_COMMAND_APDU_ACK); 341 ackMsg.arg1 = msg.arg1; 342 ackMsg.replyTo = getMessenger(); 343 msg.replyTo.send(ackMsg); 344 } catch (RemoteException e) { 345 Log.e(TAG, "Failed to acknowledge MSG_COMMAND_APDU", e); 346 } 347 } 348 349 if (responseApdu != null) { 350 if (getNfcService() == null) { 351 Log.e(TAG, "Response not sent; service was deactivated."); 352 return; 353 } 354 355 Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU); 356 357 int ackCookie = 0; 358 if (Flags.nfcHceLatencyEvents()) { 359 ackCookie = generateApduAckCookie(); 360 msg.arg1 = ackCookie; 361 Trace.beginAsyncSection(EVENT_HCE_RESPONSE_APDU, ackCookie); 362 } 363 364 Bundle responseBundle = new Bundle(); 365 responseBundle.putByteArray(KEY_DATA, responseApdu); 366 responseMsg.setData(responseBundle); 367 responseMsg.replyTo = getMessenger(); 368 try { 369 getNfcService().send(responseMsg); 370 } catch (RemoteException e) { 371 if (Flags.nfcHceLatencyEvents()) { 372 Trace.endAsyncSection(EVENT_HCE_RESPONSE_APDU, ackCookie); 373 } 374 Log.e(TAG, "Response not sent; RemoteException calling into " + 375 "NfcService."); 376 } 377 } 378 } else { 379 Log.e(TAG, "Received MSG_COMMAND_APDU without data."); 380 } 381 break; 382 case MSG_RESPONSE_APDU: 383 if (getNfcService() == null) { 384 Log.e(TAG, "Response not sent; service was deactivated."); 385 return; 386 } 387 388 try { 389 msg.replyTo = getMessenger(); 390 getNfcService().send(msg); 391 } catch (RemoteException e) { 392 Log.e(TAG, "RemoteException calling into NfcService."); 393 } 394 break; 395 case MSG_RESPONSE_APDU_ACK: 396 Trace.endAsyncSection(EVENT_HCE_RESPONSE_APDU, msg.arg1); 397 Log.e(TAG, "Received response apdu ack for " + msg.arg1); 398 break; 399 case MSG_DEACTIVATED: 400 // Make sure we won't call into NfcService again 401 setNfcService(null); 402 onDeactivated(msg.arg1); 403 break; 404 case MSG_UNHANDLED: 405 if (getNfcService() == null) { 406 Log.e(TAG, "notifyUnhandled not sent; service was deactivated."); 407 return; 408 } 409 try { 410 msg.replyTo = getMessenger(); 411 getNfcService().send(msg); 412 } catch (RemoteException e) { 413 Log.e(TAG, "RemoteException calling into NfcService."); 414 } 415 break; 416 case MSG_POLLING_LOOP: 417 { 418 ArrayList<PollingFrame> pollingFrames = 419 msg.getData().getParcelableArrayList( 420 KEY_POLLING_LOOP_FRAMES_BUNDLE, PollingFrame.class); 421 processPollingFrames(pollingFrames); 422 423 if (Flags.nfcHceLatencyEvents()) { 424 try { 425 Message ackMsg = Message.obtain(null, MSG_POLLING_LOOP_ACK); 426 ackMsg.arg1 = msg.arg1; 427 ackMsg.replyTo = getMessenger(); 428 msg.replyTo.send(ackMsg); 429 } catch (RemoteException e) { 430 Log.e(TAG, "Failed to acknowledge MSG_POLLING_LOOP", e); 431 } 432 } 433 } 434 break; 435 default: 436 super.handleMessage(msg); 437 } 438 } 439 } 440 441 @Override onBind(Intent intent)442 public final IBinder onBind(Intent intent) { 443 return getMessenger().getBinder(); 444 } 445 446 /** 447 * Sends a response APDU back to the remote device. 448 * 449 * <p>Note: this method may be called from any thread and will not block. 450 * @param responseApdu A byte-array containing the reponse APDU. 451 */ sendResponseApdu(byte[] responseApdu)452 public final void sendResponseApdu(byte[] responseApdu) { 453 Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU); 454 Bundle dataBundle = new Bundle(); 455 dataBundle.putByteArray(KEY_DATA, responseApdu); 456 responseMsg.setData(dataBundle); 457 458 int ackCookie = 0; 459 if (Flags.nfcHceLatencyEvents()) { 460 ackCookie = generateApduAckCookie(); 461 responseMsg.arg1 = ackCookie; 462 463 Trace.beginAsyncSection(EVENT_HCE_RESPONSE_APDU, ackCookie); 464 } 465 466 try { 467 getMessenger().send(responseMsg); 468 } catch (RemoteException e) { 469 Log.e("TAG", "Local messenger has died."); 470 } 471 } 472 generateApduAckCookie()473 private int generateApduAckCookie() { 474 byte[] token = new byte[Integer.BYTES]; 475 getCookieRandom().nextBytes(token); 476 return ByteBuffer.wrap(token).getInt(); 477 } 478 479 /** 480 * Calling this method allows the service to tell the OS 481 * that it won't be able to complete this transaction - 482 * for example, because it requires data connectivity 483 * that is not present at that moment. 484 * 485 * The OS may use this indication to give the user a list 486 * of alternative applications that can handle the last 487 * AID that was selected. If the user would select an 488 * application from the list, that action by itself 489 * will not cause the default to be changed; the selected 490 * application will be invoked for the next tap only. 491 * 492 * If there are no other applications that can handle 493 * this transaction, the OS will show an error dialog 494 * indicating your service could not complete the 495 * transaction. 496 * 497 * <p>Note: this method may be called anywhere between 498 * the first {@link #processCommandApdu(byte[], Bundle)} 499 * call and a {@link #onDeactivated(int)} call. 500 */ notifyUnhandled()501 public final void notifyUnhandled() { 502 Message unhandledMsg = Message.obtain(null, MSG_UNHANDLED); 503 try { 504 getMessenger().send(unhandledMsg); 505 } catch (RemoteException e) { 506 Log.e("TAG", "Local messenger has died."); 507 } 508 } 509 510 /** 511 * This method is called when polling frames have been received from a 512 * remote device. If the device is in observe mode, the service should 513 * call {@link NfcAdapter#allowTransaction()} once it is ready to proceed 514 * with the transaction. If the device is not in observe mode, the service 515 * can use this polling frame information to determine how to proceed if it 516 * subsequently has {@link #processCommandApdu(byte[], Bundle)} called. The 517 * service must override this method inorder to receive polling frames, 518 * otherwise the base implementation drops the frame. 519 * 520 * @param frames A description of the polling frame. 521 */ 522 @SuppressLint("OnNameExpected") processPollingFrames(@onNull List<PollingFrame> frames)523 public void processPollingFrames(@NonNull List<PollingFrame> frames) { 524 } 525 526 /** 527 * <p>This method will be called when a command APDU has been received 528 * from a remote device. A response APDU can be provided directly 529 * by returning a byte-array in this method. Note that in general 530 * response APDUs must be sent as quickly as possible, given the fact 531 * that the user is likely holding their device over an NFC reader 532 * when this method is called. 533 * 534 * <p class="note">If there are multiple services that have registered for the same 535 * AIDs in their meta-data entry, you will only get called if the user has 536 * explicitly selected your service, either as a default or just for the next tap. 537 * 538 * <p class="note">This method is running on the main thread of your application. 539 * If you cannot return a response APDU immediately, return null 540 * and use the {@link #sendResponseApdu(byte[])} method later. 541 * 542 * @param commandApdu The APDU that was received from the remote device 543 * @param extras A bundle containing extra data. May be null. 544 * @return a byte-array containing the response APDU, or null if no 545 * response APDU can be sent at this point. 546 */ processCommandApdu(byte[] commandApdu, Bundle extras)547 public abstract byte[] processCommandApdu(byte[] commandApdu, Bundle extras); 548 549 /** 550 * This method will be called in two possible scenarios: 551 * <li>The NFC link has been deactivated or lost 552 * <li>A different AID has been selected and was resolved to a different 553 * service component 554 * @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED} 555 */ onDeactivated(int reason)556 public abstract void onDeactivated(int reason); 557 558 } 559