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.annotation.Nullable; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.ServiceConnection; 24 import android.nfc.cardemulation.HostNfcFService; 25 import android.nfc.cardemulation.NfcFServiceInfo; 26 import android.nfc.cardemulation.Utils; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Message; 31 import android.os.Messenger; 32 import android.os.RemoteException; 33 import android.os.UserHandle; 34 import android.sysprop.NfcProperties; 35 import android.util.Log; 36 import android.util.proto.ProtoOutputStream; 37 38 import androidx.annotation.VisibleForTesting; 39 40 import com.android.nfc.NfcService; 41 import com.android.nfc.NfcStatsLog; 42 import com.android.nfc.cardemulation.util.StatsdUtils; 43 import com.android.nfc.cardemulation.util.StatsdUtilsContext; 44 import com.android.nfc.flags.Flags; 45 46 import java.io.FileDescriptor; 47 import java.io.PrintWriter; 48 49 public class HostNfcFEmulationManager { 50 static final String TAG = "HostNfcFEmulationManager"; 51 static final boolean DBG = NfcProperties.debug_enabled().orElse(true); 52 53 static final int STATE_IDLE = 0; 54 static final int STATE_W4_SERVICE = 1; 55 static final int STATE_XFER = 2; 56 57 /** NFCID2 length */ 58 static final int NFCID2_LENGTH = 8; 59 60 /** Minimum NFC-F packets including length, command code and NFCID2 */ 61 static final int MINIMUM_NFCF_PACKET_LENGTH = 10; 62 63 final Context mContext; 64 final RegisteredT3tIdentifiersCache mT3tIdentifiersCache; 65 final Messenger mMessenger = new Messenger (new MessageHandler()); 66 final Object mLock; 67 68 @Nullable 69 private final StatsdUtils mStatsdUtils; 70 71 // All variables below protected by mLock 72 ComponentName mEnabledFgServiceName; 73 int mEnabledFgServiceUserId; 74 75 Messenger mService; 76 boolean mServiceBound; 77 ComponentName mServiceName; 78 int mServiceUserId; 79 80 // mActiveService denotes the service interface 81 // that is the current active one, until a new packet 82 // comes in that may be resolved to a different service. 83 // On deactivation, mActiveService stops being valid. 84 Messenger mActiveService; 85 ComponentName mActiveServiceName; 86 87 int mState; 88 byte[] mPendingPacket; 89 HostNfcFEmulationManager(Context context, RegisteredT3tIdentifiersCache t3tIdentifiersCache)90 public HostNfcFEmulationManager(Context context, 91 RegisteredT3tIdentifiersCache t3tIdentifiersCache) { 92 mContext = context; 93 mLock = new Object(); 94 mEnabledFgServiceName = null; 95 mT3tIdentifiersCache = t3tIdentifiersCache; 96 mState = STATE_IDLE; 97 mStatsdUtils = 98 Flags.statsdCeEventsFlag() ? new StatsdUtils(StatsdUtils.SE_NAME_HCEF, 99 new StatsdUtilsContext()) : null; 100 } 101 102 /** 103 * Enabled Foreground NfcF service changed 104 */ onEnabledForegroundNfcFServiceChanged(int userId, ComponentName service)105 public void onEnabledForegroundNfcFServiceChanged(int userId, ComponentName service) { 106 synchronized (mLock) { 107 mEnabledFgServiceUserId = userId; 108 mEnabledFgServiceName = service; 109 if (service == null) { 110 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 111 unbindServiceIfNeededLocked(); 112 } 113 } 114 } 115 onHostEmulationActivated()116 public void onHostEmulationActivated() { 117 if (DBG) Log.d(TAG, "onHostEmulationActivated"); 118 } 119 onHostEmulationData(byte[] data)120 public void onHostEmulationData(byte[] data) { 121 if (DBG) Log.d(TAG, "onHostEmulationData"); 122 String nfcid2 = findNfcid2(data); 123 ComponentName resolvedServiceName = null; 124 NfcFServiceInfo resolvedService = null; 125 synchronized (mLock) { 126 if (nfcid2 != null) { 127 resolvedService = mT3tIdentifiersCache.resolveNfcid2(nfcid2); 128 if (resolvedService != null) { 129 resolvedServiceName = resolvedService.getComponent(); 130 } 131 } 132 if (resolvedServiceName == null) { 133 if (mActiveServiceName == null) { 134 return; 135 } 136 resolvedServiceName = mActiveServiceName; 137 } 138 // Check if resolvedService is actually currently enabled 139 if (mEnabledFgServiceName == null || 140 !mEnabledFgServiceName.equals(resolvedServiceName)) { 141 if (mStatsdUtils != null) { 142 mStatsdUtils.logCardEmulationWrongSettingEvent(); 143 } 144 return; 145 } 146 if (DBG) { 147 Log.d(TAG, "onHostEmulationData: resolvedServiceName: " 148 + resolvedServiceName.toString() + "mState: " + String.valueOf(mState)); 149 } 150 switch (mState) { 151 case STATE_IDLE: 152 int userId; 153 int uid = resolvedService != null ? resolvedService.getUid() : -1; 154 155 if (resolvedService == null) { 156 userId = mEnabledFgServiceUserId; 157 } else { 158 userId = UserHandle.getUserHandleForUid(uid) 159 .getIdentifier(); 160 } 161 Messenger existingService = 162 bindServiceIfNeededLocked(userId, resolvedServiceName); 163 if (existingService != null) { 164 Log.d(TAG, "onHostEmulationData: Binding to existing service"); 165 mState = STATE_XFER; 166 sendDataToServiceLocked(existingService, data); 167 } else { 168 // Waiting for service to be bound 169 Log.d(TAG, "onHostEmulationData: Waiting for new service."); 170 // Queue packet to be used 171 mPendingPacket = data; 172 mState = STATE_W4_SERVICE; 173 } 174 if (mStatsdUtils != null) { 175 mStatsdUtils.setCardEmulationEventUid(uid); 176 mStatsdUtils.notifyCardEmulationEventWaitingForResponse(); 177 } else { 178 NfcStatsLog.write(NfcStatsLog.NFC_CARDEMULATION_OCCURRED, 179 NfcStatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_PAYMENT, 180 "HCEF", 181 uid); 182 } 183 break; 184 case STATE_W4_SERVICE: 185 Log.d(TAG, "onHostEmulationData: Unexpected packet in STATE_W4_SERVICE"); 186 break; 187 case STATE_XFER: 188 // Regular packet data 189 sendDataToServiceLocked(mActiveService, data); 190 break; 191 } 192 } 193 } 194 onHostEmulationDeactivated()195 public void onHostEmulationDeactivated() { 196 if (DBG) Log.d(TAG, "onHostEmulationDeactivated"); 197 synchronized (mLock) { 198 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 199 mActiveService = null; 200 mActiveServiceName = null; 201 unbindServiceIfNeededLocked(); 202 mState = STATE_IDLE; 203 204 if (mStatsdUtils != null) { 205 mStatsdUtils.logCardEmulationDeactivatedEvent(); 206 } 207 } 208 } 209 onNfcDisabled()210 public void onNfcDisabled() { 211 synchronized (mLock) { 212 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 213 mEnabledFgServiceName = null; 214 mActiveService = null; 215 mActiveServiceName = null; 216 unbindServiceIfNeededLocked(); 217 mState = STATE_IDLE; 218 } 219 } 220 onUserSwitched()221 public void onUserSwitched() { 222 synchronized (mLock) { 223 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 224 mEnabledFgServiceName = null; 225 mActiveService = null; 226 mActiveServiceName = null; 227 unbindServiceIfNeededLocked(); 228 mState = STATE_IDLE; 229 } 230 } 231 sendDataToServiceLocked(Messenger service, byte[] data)232 void sendDataToServiceLocked(Messenger service, byte[] data) { 233 if (DBG) Log.d(TAG, "sendDataToServiceLocked"); 234 if (DBG) { 235 Log.d(TAG, "sendDataToServiceLocked: service: " 236 + (service != null ? service.toString() : "null")); 237 Log.d(TAG, "sendDataToServiceLocked: mActiveService: " 238 + (mActiveService != null ? mActiveService.toString() : "null")); 239 } 240 if (service != mActiveService) { 241 sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS); 242 mActiveService = service; 243 mActiveServiceName = mServiceName; 244 } 245 Message msg = Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET); 246 Bundle dataBundle = new Bundle(); 247 dataBundle.putByteArray("data", data); 248 msg.setData(dataBundle); 249 msg.replyTo = mMessenger; 250 try { 251 Log.d(TAG, "sendDataToServiceLocked: Sending data to service"); 252 if (DBG) Log.d(TAG, "sendDataToServiceLocked: data: " + getByteDump(data)); 253 mActiveService.send(msg); 254 } catch (RemoteException e) { 255 Log.e(TAG, "sendDataToServiceLocked: Remote service has died, dropping packet"); 256 } 257 } 258 sendDeactivateToActiveServiceLocked(int reason)259 void sendDeactivateToActiveServiceLocked(int reason) { 260 if (DBG) Log.d(TAG, "sendDeactivateToActiveServiceLocked"); 261 if (mActiveService == null) return; 262 Message msg = Message.obtain(null, HostNfcFService.MSG_DEACTIVATED); 263 msg.arg1 = reason; 264 try { 265 mActiveService.send(msg); 266 } catch (RemoteException e) { 267 // Don't care 268 } 269 } 270 bindServiceIfNeededLocked(int userId, ComponentName service)271 Messenger bindServiceIfNeededLocked(int userId, ComponentName service) { 272 if (DBG) Log.d(TAG, "bindServiceIfNeededLocked"); 273 if (mServiceBound && mServiceName.equals(service) && mServiceUserId == userId) { 274 Log.d(TAG, "bindServiceIfNeededLocked: Service already bound"); 275 return mService; 276 } else { 277 Log.d(TAG, "bindServiceIfNeededLocked: service " + service); 278 if (mStatsdUtils != null) { 279 mStatsdUtils.notifyCardEmulationEventWaitingForService(); 280 } 281 unbindServiceIfNeededLocked(); 282 Intent bindIntent = new Intent(HostNfcFService.SERVICE_INTERFACE); 283 bindIntent.setComponent(service); 284 try { 285 mServiceBound = mContext.bindServiceAsUser(bindIntent, mConnection, 286 Context.BIND_AUTO_CREATE, UserHandle.of(userId)); 287 if (!mServiceBound) { 288 Log.e(TAG, "bindServiceIfNeededLocked: Could not bind service"); 289 } else { 290 mServiceUserId = userId; 291 } 292 } catch (SecurityException e) { 293 Log.e(TAG, "bindServiceIfNeededLocked: Could not bind service due " 294 + "to security exception"); 295 } 296 return null; 297 } 298 } 299 unbindServiceIfNeededLocked()300 void unbindServiceIfNeededLocked() { 301 if (DBG) Log.d(TAG, "unbindServiceIfNeededLocked"); 302 if (mServiceBound) { 303 Log.d(TAG, "unbindServiceIfNeededLocked: service " + mServiceName); 304 mContext.unbindService(mConnection); 305 mServiceBound = false; 306 mService = null; 307 mServiceName = null; 308 mServiceUserId = -1; 309 } 310 } 311 findNfcid2(byte[] data)312 String findNfcid2(byte[] data) { 313 if (DBG) Log.d(TAG, "findNfcid2"); 314 if (data == null || data.length < MINIMUM_NFCF_PACKET_LENGTH) { 315 if (DBG) Log.d(TAG, "findNfcid2: Data size too small"); 316 return null; 317 } 318 int nfcid2Offset = 2; 319 return bytesToString(data, nfcid2Offset, NFCID2_LENGTH); 320 } 321 322 private ServiceConnection mConnection = new ServiceConnection() { 323 @Override 324 public void onServiceConnected(ComponentName name, IBinder service) { 325 synchronized (mLock) { 326 mService = new Messenger(service); 327 mServiceBound = true; 328 mServiceName = name; 329 Log.d(TAG, "onServiceConnected: Service bound"); 330 mState = STATE_XFER; 331 // Send pending packet 332 if (mPendingPacket != null) { 333 if (mStatsdUtils != null) { 334 mStatsdUtils.notifyCardEmulationEventServiceBound(); 335 } 336 sendDataToServiceLocked(mService, mPendingPacket); 337 mPendingPacket = null; 338 } 339 } 340 } 341 342 @Override 343 public void onServiceDisconnected(ComponentName name) { 344 synchronized (mLock) { 345 Log.d(TAG, "onServiceDisconnected"); 346 mService = null; 347 mServiceBound = false; 348 mServiceName = null; 349 } 350 } 351 }; 352 353 class MessageHandler extends Handler { 354 @Override handleMessage(Message msg)355 public void handleMessage(Message msg) { 356 synchronized(mLock) { 357 if (mActiveService == null) { 358 Log.d(TAG, "handleMessage: Dropping service response message; " 359 + "service no longer active."); 360 return; 361 } else if (!msg.replyTo.getBinder().equals(mActiveService.getBinder())) { 362 Log.d(TAG, "handleMessage: Dropping service response message; " 363 + "service no longer bound."); 364 return; 365 } 366 } 367 if (msg.what == HostNfcFService.MSG_RESPONSE_PACKET) { 368 Bundle dataBundle = msg.getData(); 369 if (dataBundle == null) { 370 return; 371 } 372 byte[] data = dataBundle.getByteArray("data"); 373 if (data == null) { 374 Log.e(TAG, "handleMessage: Data is null"); 375 return; 376 } 377 if (data.length != 0 && (data.length != (data[0] & 0xff))) { 378 Log.e(TAG, "handleMessage: Invalid response packet"); 379 return; 380 } 381 int state; 382 synchronized(mLock) { 383 state = mState; 384 } 385 if (state == STATE_XFER) { 386 Log.d(TAG, "handleMessage: Sending data"); 387 if (DBG) Log.d(TAG, "handleMessage: data:" + getByteDump(data)); 388 NfcService.getInstance().sendData(data); 389 if (mStatsdUtils != null) { 390 mStatsdUtils.notifyCardEmulationEventResponseReceived(); 391 } 392 } else { 393 Log.d(TAG, 394 "handleMessage: Dropping data, wrong state " + Integer.toString(state)); 395 } 396 } 397 } 398 } 399 bytesToString(byte[] bytes, int offset, int length)400 static String bytesToString(byte[] bytes, int offset, int length) { 401 final char[] hexChars = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 402 char[] chars = new char[length * 2]; 403 int byteValue; 404 for (int j = 0; j < length; j++) { 405 byteValue = bytes[offset + j] & 0xFF; 406 chars[j * 2] = hexChars[byteValue >>> 4]; 407 chars[j * 2 + 1] = hexChars[byteValue & 0x0F]; 408 } 409 return new String(chars); 410 } 411 getByteDump(final byte[] cmd)412 private String getByteDump(final byte[] cmd) { 413 StringBuffer str = new StringBuffer(""); 414 int letters = 8; 415 int i = 0; 416 417 if (cmd == null) { 418 str.append(" null\n"); 419 return str.toString(); 420 } 421 422 for (; i < cmd.length; i++) { 423 str.append(String.format(" %02X", cmd[i])); 424 if ((i % letters == letters - 1) || (i + 1 == cmd.length)) { 425 str.append("\n"); 426 } 427 } 428 429 return str.toString(); 430 } 431 dump(FileDescriptor fd, PrintWriter pw, String[] args)432 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 433 pw.println("Bound HCE-F services: "); 434 if (mServiceBound) { 435 pw.println(" service: " + mServiceName); 436 } 437 } 438 439 /** 440 * Dump debugging information as a HostNfcFEmulationManagerProto 441 * 442 * Note: 443 * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto 444 * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and 445 * {@link ProtoOutputStream#end(long)} after. 446 * Never reuse a proto field number. When removing a field, mark it as reserved. 447 */ dumpDebug(ProtoOutputStream proto)448 void dumpDebug(ProtoOutputStream proto) { 449 if (mServiceBound) { 450 Utils.dumpDebugComponentName( 451 mServiceName, proto, HostNfcFEmulationManagerProto.SERVICE_NAME); 452 } 453 } 454 @VisibleForTesting getEnabledFgServiceName()455 public String getEnabledFgServiceName() { 456 if (mEnabledFgServiceName != null) { 457 return mEnabledFgServiceName.getPackageName(); 458 } 459 return null; 460 } 461 462 @VisibleForTesting isUserSwitched()463 public boolean isUserSwitched() { 464 if (mEnabledFgServiceName == null && mActiveService == null && mState == STATE_IDLE) 465 return true; 466 return false; 467 } 468 469 @VisibleForTesting getServiceUserId()470 public int getServiceUserId() { 471 return mServiceUserId; 472 } 473 474 @VisibleForTesting getServiceConnection()475 public ServiceConnection getServiceConnection() { 476 return mConnection; 477 } 478 479 @VisibleForTesting getServiceName()480 public ComponentName getServiceName() { 481 return mServiceName; 482 } 483 484 } 485