1 /* 2 * Copyright (C) 2014 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 package com.android.nfc.service; 17 18 import android.content.ComponentName; 19 import android.content.Intent; 20 import android.nfc.cardemulation.HostApduService; 21 import android.os.Bundle; 22 import android.util.Log; 23 24 import com.android.nfc.utils.HceUtils; 25 import com.android.nfc.utils.CommandApdu; 26 27 import java.util.Arrays; 28 29 public abstract class HceService extends HostApduService { 30 private static final String TAG = "HceService"; 31 32 // Intent action that's received when complete APDU sequence is received from an HCE service. 33 public static final String ACTION_APDU_SEQUENCE_COMPLETE = 34 "com.android.nfc.service.ACTION_APDU_SEQUENCE_COMPLETE"; 35 public static final String EXTRA_COMPONENT = "component"; 36 public static final String EXTRA_DURATION = "duration"; 37 38 private static final int STATE_IDLE = 0; 39 private static final int STATE_IN_PROGRESS = 1; 40 private static final int STATE_FAILED = 2; 41 42 // Variables below only used on main thread 43 CommandApdu[] mCommandApdus = null; 44 String[] mResponseApdus = null; 45 int mApduIndex = 0; 46 int mState = STATE_IDLE; 47 long mStartTime; 48 getComponent()49 public abstract ComponentName getComponent(); 50 51 /** 52 * Initializes the service 53 * 54 * @param commandApdus - list of expected command APDUs 55 * @param responseApdus - corresponding list of response APDUs to send 56 */ HceService(CommandApdu[] commandApdus, String[] responseApdus)57 public HceService(CommandApdu[] commandApdus, String[] responseApdus) { 58 mCommandApdus = commandApdus; 59 mResponseApdus = responseApdus; 60 } 61 62 /** Called when service is deactivated */ 63 @Override onDeactivated(int arg0)64 public void onDeactivated(int arg0) { 65 Log.d(TAG, "onDeactivated"); 66 mApduIndex = 0; 67 mState = STATE_IDLE; 68 } 69 70 /** Callback when entire apdu sequence is successfully completed. */ onApduSequenceComplete()71 public void onApduSequenceComplete() { 72 Intent completionIntent = new Intent(ACTION_APDU_SEQUENCE_COMPLETE); 73 completionIntent.putExtra(EXTRA_COMPONENT, getComponent()); 74 completionIntent.putExtra(EXTRA_DURATION, System.currentTimeMillis() - mStartTime); 75 sendBroadcast(completionIntent); 76 Log.d(TAG, "Successful APDU sequence. Sent broadcast"); 77 } 78 79 /** 80 * Implementation of processCommandApdu. Verifies correct APDU command is received and sends 81 * response. Triggers onApduSequenceComplete if all APDUs are received. 82 */ 83 @Override processCommandApdu(byte[] arg0, Bundle arg1)84 public byte[] processCommandApdu(byte[] arg0, Bundle arg1) { 85 Log.d(TAG, "processCommandApdu called"); 86 if (mState == STATE_FAILED) { 87 // Don't accept any more APDUs until deactivated 88 return null; 89 } 90 91 if (mState == STATE_IDLE) { 92 mState = STATE_IN_PROGRESS; 93 mStartTime = System.currentTimeMillis(); 94 } 95 96 if (mApduIndex >= mCommandApdus.length) { 97 // Skip all APDUs which aren't supposed to reach us 98 return null; 99 } 100 101 do { 102 if (!mCommandApdus[mApduIndex].isReachable()) { 103 mApduIndex++; 104 } else { 105 break; 106 } 107 } while (mApduIndex < mCommandApdus.length); 108 109 if (mApduIndex >= mCommandApdus.length) { 110 Log.d(TAG, "Ignoring command APDU; protocol complete."); 111 // Ignore new APDUs after completion 112 return null; 113 } else { 114 115 if (!Arrays.equals( 116 HceUtils.hexStringToBytes(mCommandApdus[mApduIndex].getApdu()), arg0)) { 117 Log.d(TAG, "Unexpected command APDU: " + HceUtils.getHexBytes("", arg0)); 118 return null; 119 } else { 120 // Send corresponding response APDU 121 byte[] responseApdu = HceUtils.hexStringToBytes(mResponseApdus[mApduIndex]); 122 mApduIndex++; 123 if (mApduIndex == mCommandApdus.length) { 124 onApduSequenceComplete(); 125 } 126 return responseApdu; 127 } 128 } 129 } 130 } 131