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 com.android.nfc.cardemulation; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.nfc.cardemulation.NfcFServiceInfo; 24 import android.nfc.cardemulation.HostNfcFService; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Message; 29 import android.os.Messenger; 30 import android.os.RemoteException; 31 import android.os.UserHandle; 32 import android.util.Log; 33 34 import com.android.nfc.NfcService; 35 36 import java.io.FileDescriptor; 37 import java.io.PrintWriter; 38 39 public class HostNfcFEmulationManager { 40 static final String TAG = "HostNfcFEmulationManager"; 41 static final boolean DBG = false; 42 43 static final int STATE_IDLE = 0; 44 static final int STATE_W4_SERVICE = 1; 45 static final int STATE_XFER = 2; 46 47 /** NFCID2 length */ 48 static final int NFCID2_LENGTH = 8; 49 50 /** Minimum NFC-F packets including length, command code and NFCID2 */ 51 static final int MINIMUM_NFCF_PACKET_LENGTH = 10; 52 53 final Context mContext; 54 final RegisteredT3tIdentifiersCache mT3tIdentifiersCache; 55 final Messenger mMessenger = new Messenger (new MessageHandler()); 56 final Object mLock; 57 58 // All variables below protected by mLock 59 ComponentName mEnabledFgServiceName; 60 61 Messenger mService; 62 boolean mServiceBound; 63 ComponentName mServiceName; 64 65 // mActiveService denotes the service interface 66 // that is the current active one, until a new packet 67 // comes in that may be resolved to a different service. 68 // On deactivation, mActiveService stops being valid. 69 Messenger mActiveService; 70 ComponentName mActiveServiceName; 71 72 int mState; 73 byte[] mPendingPacket; 74 HostNfcFEmulationManager(Context context, RegisteredT3tIdentifiersCache t3tIdentifiersCache)75 public HostNfcFEmulationManager(Context context, 76 RegisteredT3tIdentifiersCache t3tIdentifiersCache) { 77 mContext = context; 78 mLock = new Object(); 79 mEnabledFgServiceName = null; 80 mT3tIdentifiersCache = t3tIdentifiersCache; 81 mState = STATE_IDLE; 82 } 83 onEnabledForegroundNfcFServiceChanged(ComponentName service)84 public void onEnabledForegroundNfcFServiceChanged(ComponentName service) { 85 synchronized (mLock) { 86 mEnabledFgServiceName = service; 87 if (service == null) { 88 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 89 unbindServiceIfNeededLocked(); 90 } 91 } 92 } 93 onHostEmulationActivated()94 public void onHostEmulationActivated() { 95 if (DBG) Log.d(TAG, "notifyHostEmulationActivated"); 96 } 97 onHostEmulationData(byte[] data)98 public void onHostEmulationData(byte[] data) { 99 if (DBG) Log.d(TAG, "notifyHostEmulationData"); 100 String nfcid2 = findNfcid2(data); 101 ComponentName resolvedServiceName = null; 102 synchronized (mLock) { 103 if (nfcid2 != null) { 104 NfcFServiceInfo resolvedService = mT3tIdentifiersCache.resolveNfcid2(nfcid2); 105 if (resolvedService != null) { 106 resolvedServiceName = resolvedService.getComponent(); 107 } 108 } 109 if (resolvedServiceName == null) { 110 if (mActiveServiceName == null) { 111 return; 112 } 113 resolvedServiceName = mActiveServiceName; 114 } 115 // Check if resolvedService is actually currently enabled 116 if (mEnabledFgServiceName == null || 117 !mEnabledFgServiceName.equals(resolvedServiceName)) { 118 return; 119 } 120 if (DBG) Log.d(TAG, "resolvedServiceName: " + resolvedServiceName.toString() + 121 "mState: " + String.valueOf(mState)); 122 switch (mState) { 123 case STATE_IDLE: 124 Messenger existingService = bindServiceIfNeededLocked(resolvedServiceName); 125 if (existingService != null) { 126 Log.d(TAG, "Binding to existing service"); 127 mState = STATE_XFER; 128 sendDataToServiceLocked(existingService, data); 129 } else { 130 // Waiting for service to be bound 131 Log.d(TAG, "Waiting for new service."); 132 // Queue packet to be used 133 mPendingPacket = data; 134 mState = STATE_W4_SERVICE; 135 } 136 break; 137 case STATE_W4_SERVICE: 138 Log.d(TAG, "Unexpected packet in STATE_W4_SERVICE"); 139 break; 140 case STATE_XFER: 141 // Regular packet data 142 sendDataToServiceLocked(mActiveService, data); 143 break; 144 } 145 } 146 } 147 onHostEmulationDeactivated()148 public void onHostEmulationDeactivated() { 149 if (DBG) Log.d(TAG, "notifyHostEmulationDeactivated"); 150 synchronized (mLock) { 151 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 152 mActiveService = null; 153 mActiveServiceName = null; 154 unbindServiceIfNeededLocked(); 155 mState = STATE_IDLE; 156 } 157 } 158 onNfcDisabled()159 public void onNfcDisabled() { 160 synchronized (mLock) { 161 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 162 mEnabledFgServiceName = null; 163 mActiveService = null; 164 mActiveServiceName = null; 165 unbindServiceIfNeededLocked(); 166 mState = STATE_IDLE; 167 } 168 } 169 onUserSwitched()170 public void onUserSwitched() { 171 synchronized (mLock) { 172 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 173 mEnabledFgServiceName = null; 174 mActiveService = null; 175 mActiveServiceName = null; 176 unbindServiceIfNeededLocked(); 177 mState = STATE_IDLE; 178 } 179 } 180 sendDataToServiceLocked(Messenger service, byte[] data)181 void sendDataToServiceLocked(Messenger service, byte[] data) { 182 if (DBG) Log.d(TAG, "sendDataToServiceLocked"); 183 if (DBG) { 184 Log.d(TAG, "service: " + 185 (service != null ? service.toString() : "null")); 186 Log.d(TAG, "mActiveService: " + 187 (mActiveService != null ? mActiveService.toString() : "null")); 188 } 189 if (service != mActiveService) { 190 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 191 mActiveService = service; 192 mActiveServiceName = mServiceName; 193 } 194 Message msg = Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET); 195 Bundle dataBundle = new Bundle(); 196 dataBundle.putByteArray("data", data); 197 msg.setData(dataBundle); 198 msg.replyTo = mMessenger; 199 try { 200 Log.d(TAG, "Sending data to service"); 201 if (DBG) Log.d(TAG, "data: " + getByteDump(data)); 202 mActiveService.send(msg); 203 } catch (RemoteException e) { 204 Log.e(TAG, "Remote service has died, dropping packet"); 205 } 206 } 207 sendDeactivateToActiveServiceLocked(int reason)208 void sendDeactivateToActiveServiceLocked(int reason) { 209 if (DBG) Log.d(TAG, "sendDeactivateToActiveServiceLocked"); 210 if (mActiveService == null) return; 211 Message msg = Message.obtain(null, HostNfcFService.MSG_DEACTIVATED); 212 msg.arg1 = reason; 213 try { 214 mActiveService.send(msg); 215 } catch (RemoteException e) { 216 // Don't care 217 } 218 } 219 bindServiceIfNeededLocked(ComponentName service)220 Messenger bindServiceIfNeededLocked(ComponentName service) { 221 if (DBG) Log.d(TAG, "bindServiceIfNeededLocked"); 222 if (mServiceBound && mServiceName.equals(service)) { 223 Log.d(TAG, "Service already bound."); 224 return mService; 225 } else { 226 Log.d(TAG, "Binding to service " + service); 227 unbindServiceIfNeededLocked(); 228 Intent bindIntent = new Intent(HostNfcFService.SERVICE_INTERFACE); 229 bindIntent.setComponent(service); 230 if (mContext.bindServiceAsUser(bindIntent, mConnection, 231 Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) { 232 } else { 233 Log.e(TAG, "Could not bind service."); 234 } 235 return null; 236 } 237 } 238 unbindServiceIfNeededLocked()239 void unbindServiceIfNeededLocked() { 240 if (DBG) Log.d(TAG, "unbindServiceIfNeededLocked"); 241 if (mServiceBound) { 242 Log.d(TAG, "Unbinding from service " + mServiceName); 243 mContext.unbindService(mConnection); 244 mServiceBound = false; 245 mService = null; 246 mServiceName = null; 247 } 248 } 249 findNfcid2(byte[] data)250 String findNfcid2(byte[] data) { 251 if (DBG) Log.d(TAG, "findNfcid2"); 252 if (data == null || data.length < MINIMUM_NFCF_PACKET_LENGTH) { 253 if (DBG) Log.d(TAG, "Data size too small"); 254 return null; 255 } 256 int nfcid2Offset = 2; 257 return bytesToString(data, nfcid2Offset, NFCID2_LENGTH); 258 } 259 260 private ServiceConnection mConnection = new ServiceConnection() { 261 @Override 262 public void onServiceConnected(ComponentName name, IBinder service) { 263 synchronized (mLock) { 264 mService = new Messenger(service); 265 mServiceBound = true; 266 mServiceName = name; 267 Log.d(TAG, "Service bound"); 268 mState = STATE_XFER; 269 // Send pending packet 270 if (mPendingPacket != null) { 271 sendDataToServiceLocked(mService, mPendingPacket); 272 mPendingPacket = null; 273 } 274 } 275 } 276 277 @Override 278 public void onServiceDisconnected(ComponentName name) { 279 synchronized (mLock) { 280 Log.d(TAG, "Service unbound"); 281 mService = null; 282 mServiceBound = false; 283 mServiceName = null; 284 } 285 } 286 }; 287 288 class MessageHandler extends Handler { 289 @Override handleMessage(Message msg)290 public void handleMessage(Message msg) { 291 synchronized(mLock) { 292 if (mActiveService == null) { 293 Log.d(TAG, "Dropping service response message; service no longer active."); 294 return; 295 } else if (!msg.replyTo.getBinder().equals(mActiveService.getBinder())) { 296 Log.d(TAG, "Dropping service response message; service no longer bound."); 297 return; 298 } 299 } 300 if (msg.what == HostNfcFService.MSG_RESPONSE_PACKET) { 301 Bundle dataBundle = msg.getData(); 302 if (dataBundle == null) { 303 return; 304 } 305 byte[] data = dataBundle.getByteArray("data"); 306 if (data == null) { 307 return; 308 } 309 if (data.length == 0) { 310 Log.e(TAG, "Invalid response packet"); 311 return; 312 } 313 if (data.length != (data[0] & 0xff)) { 314 Log.e(TAG, "Invalid response packet"); 315 return; 316 } 317 int state; 318 synchronized(mLock) { 319 state = mState; 320 } 321 if (state == STATE_XFER) { 322 Log.d(TAG, "Sending data"); 323 if (DBG) Log.d(TAG, "data:" + getByteDump(data)); 324 NfcService.getInstance().sendData(data); 325 } else { 326 Log.d(TAG, "Dropping data, wrong state " + Integer.toString(state)); 327 } 328 } 329 } 330 } 331 bytesToString(byte[] bytes, int offset, int length)332 static String bytesToString(byte[] bytes, int offset, int length) { 333 final char[] hexChars = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 334 char[] chars = new char[length * 2]; 335 int byteValue; 336 for (int j = 0; j < length; j++) { 337 byteValue = bytes[offset + j] & 0xFF; 338 chars[j * 2] = hexChars[byteValue >>> 4]; 339 chars[j * 2 + 1] = hexChars[byteValue & 0x0F]; 340 } 341 return new String(chars); 342 } 343 getByteDump(final byte[] cmd)344 private String getByteDump(final byte[] cmd) { 345 StringBuffer str = new StringBuffer(""); 346 int letters = 8; 347 int i = 0; 348 349 if (cmd == null) { 350 str.append(" null\n"); 351 return str.toString(); 352 } 353 354 for (; i < cmd.length; i++) { 355 str.append(String.format(" %02X", cmd[i])); 356 if ((i % letters == letters - 1) || (i + 1 == cmd.length)) { 357 str.append("\n"); 358 } 359 } 360 361 return str.toString(); 362 } 363 dump(FileDescriptor fd, PrintWriter pw, String[] args)364 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 365 pw.println("Bound HCE-F services: "); 366 if (mServiceBound) { 367 pw.println(" service: " + mServiceName); 368 } 369 } 370 } 371