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