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