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 */ 16package com.android.nfc.cardemulation; 17 18import java.io.FileDescriptor; 19import java.io.PrintWriter; 20import java.util.List; 21 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.Intent; 25import android.nfc.INfcCardEmulation; 26import android.nfc.cardemulation.AidGroup; 27import android.nfc.cardemulation.ApduServiceInfo; 28import android.nfc.cardemulation.CardEmulation; 29import android.os.Binder; 30import android.os.RemoteException; 31import android.os.UserHandle; 32import android.provider.Settings; 33import android.util.Log; 34 35import com.android.nfc.NfcPermissions; 36import com.android.nfc.cardemulation.RegisteredServicesCache; 37 38/** 39 * CardEmulationManager is the central entity 40 * responsible for delegating to individual components 41 * implementing card emulation: 42 * - RegisteredServicesCache keeping track of HCE and SE services on the device 43 * - RegisteredAidCache keeping track of AIDs registered by those services and manages 44 * the routing table in the NFCC. 45 * - HostEmulationManager handles incoming APDUs for the host and forwards to HCE 46 * services as necessary. 47 */ 48public class CardEmulationManager implements RegisteredServicesCache.Callback, 49 PreferredServices.Callback { 50 static final String TAG = "CardEmulationManager"; 51 static final boolean DBG = false; 52 53 final RegisteredAidCache mAidCache; 54 final RegisteredServicesCache mServiceCache; 55 final HostEmulationManager mHostEmulationManager; 56 final PreferredServices mPreferredServices; 57 final Context mContext; 58 final CardEmulationInterface mCardEmulationInterface; 59 60 public CardEmulationManager(Context context) { 61 mContext = context; 62 mCardEmulationInterface = new CardEmulationInterface(); 63 mAidCache = new RegisteredAidCache(context); 64 mHostEmulationManager = new HostEmulationManager(context, mAidCache); 65 mServiceCache = new RegisteredServicesCache(context, this); 66 mPreferredServices = new PreferredServices(context, mServiceCache, mAidCache, this); 67 68 mServiceCache.initialize(); 69 } 70 71 public INfcCardEmulation getNfcCardEmulationInterface() { 72 return mCardEmulationInterface; 73 } 74 75 public void onHostCardEmulationActivated() { 76 mHostEmulationManager.onHostEmulationActivated(); 77 mPreferredServices.onHostEmulationActivated(); 78 } 79 80 public void onHostCardEmulationData(byte[] data) { 81 mHostEmulationManager.onHostEmulationData(data); 82 } 83 84 public void onHostCardEmulationDeactivated() { 85 mHostEmulationManager.onHostEmulationDeactivated(); 86 mPreferredServices.onHostEmulationDeactivated(); 87 } 88 89 public void onOffHostAidSelected() { 90 mHostEmulationManager.onOffHostAidSelected(); 91 } 92 93 public void onUserSwitched(int userId) { 94 mServiceCache.invalidateCache(userId); 95 mPreferredServices.onUserSwitched(userId); 96 } 97 98 public void onNfcEnabled() { 99 mAidCache.onNfcEnabled(); 100 } 101 102 public void onNfcDisabled() { 103 mAidCache.onNfcDisabled(); 104 } 105 106 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 107 mServiceCache.dump(fd, pw, args); 108 mPreferredServices.dump(fd, pw, args); 109 mAidCache.dump(fd, pw, args); 110 mHostEmulationManager.dump(fd, pw, args); 111 } 112 113 @Override 114 public void onServicesUpdated(int userId, List<ApduServiceInfo> services) { 115 // Verify defaults are still sane 116 verifyDefaults(userId, services); 117 // Update the AID cache 118 mAidCache.onServicesUpdated(userId, services); 119 // Update the preferred services list 120 mPreferredServices.onServicesUpdated(); 121 } 122 123 void verifyDefaults(int userId, List<ApduServiceInfo> services) { 124 ComponentName defaultPaymentService = 125 getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, false); 126 if (DBG) Log.d(TAG, "Current default: " + defaultPaymentService); 127 if (defaultPaymentService != null) { 128 // Validate the default is still installed and handling payment 129 ApduServiceInfo serviceInfo = mServiceCache.getService(userId, defaultPaymentService); 130 if (serviceInfo == null || !serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) { 131 if (serviceInfo == null) { 132 Log.e(TAG, "Default payment service unexpectedly removed."); 133 } else if (!serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) { 134 if (DBG) Log.d(TAG, "Default payment service had payment category removed"); 135 } 136 int numPaymentServices = 0; 137 ComponentName lastFoundPaymentService = null; 138 for (ApduServiceInfo service : services) { 139 if (service.hasCategory(CardEmulation.CATEGORY_PAYMENT)) { 140 numPaymentServices++; 141 lastFoundPaymentService = service.getComponent(); 142 } 143 } 144 if (DBG) Log.d(TAG, "Number of payment services is " + 145 Integer.toString(numPaymentServices)); 146 if (numPaymentServices == 0) { 147 if (DBG) Log.d(TAG, "Default removed, no services left."); 148 // No payment services left, unset default and don't ask the user 149 setDefaultServiceForCategoryChecked(userId, null, CardEmulation.CATEGORY_PAYMENT); 150 } else if (numPaymentServices == 1) { 151 // Only one left, automatically make it the default 152 if (DBG) Log.d(TAG, "Default removed, making remaining service default."); 153 setDefaultServiceForCategoryChecked(userId, lastFoundPaymentService, 154 CardEmulation.CATEGORY_PAYMENT); 155 } else if (numPaymentServices > 1) { 156 // More than one left, unset default and ask the user if he wants 157 // to set a new one 158 if (DBG) Log.d(TAG, "Default removed, asking user to pick."); 159 setDefaultServiceForCategoryChecked(userId, null, 160 CardEmulation.CATEGORY_PAYMENT); 161 Intent intent = new Intent(mContext, DefaultRemovedActivity.class); 162 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 163 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 164 } 165 } else { 166 // Default still exists and handles the category, nothing do 167 if (DBG) Log.d(TAG, "Default payment service still ok."); 168 } 169 } else { 170 // A payment service may have been removed, leaving only one; 171 // in that case, automatically set that app as default. 172 int numPaymentServices = 0; 173 ComponentName lastFoundPaymentService = null; 174 for (ApduServiceInfo service : services) { 175 if (service.hasCategory(CardEmulation.CATEGORY_PAYMENT)) { 176 numPaymentServices++; 177 lastFoundPaymentService = service.getComponent(); 178 } 179 } 180 if (numPaymentServices > 1) { 181 // More than one service left, leave default unset 182 if (DBG) Log.d(TAG, "No default set, more than one service left."); 183 } else if (numPaymentServices == 1) { 184 // Make single found payment service the default 185 if (DBG) Log.d(TAG, "No default set, making single service default."); 186 setDefaultServiceForCategoryChecked(userId, lastFoundPaymentService, 187 CardEmulation.CATEGORY_PAYMENT); 188 } else { 189 // No payment services left, leave default at null 190 if (DBG) Log.d(TAG, "No default set, last payment service removed."); 191 } 192 } 193 } 194 195 ComponentName getDefaultServiceForCategory(int userId, String category, 196 boolean validateInstalled) { 197 if (!CardEmulation.CATEGORY_PAYMENT.equals(category)) { 198 Log.e(TAG, "Not allowing defaults for category " + category); 199 return null; 200 } 201 // Load current payment default from settings 202 String name = Settings.Secure.getStringForUser( 203 mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 204 userId); 205 if (name != null) { 206 ComponentName service = ComponentName.unflattenFromString(name); 207 if (!validateInstalled || service == null) { 208 return service; 209 } else { 210 return mServiceCache.hasService(userId, service) ? service : null; 211 } 212 } else { 213 return null; 214 } 215 } 216 217 boolean setDefaultServiceForCategoryChecked(int userId, ComponentName service, 218 String category) { 219 if (!CardEmulation.CATEGORY_PAYMENT.equals(category)) { 220 Log.e(TAG, "Not allowing defaults for category " + category); 221 return false; 222 } 223 // TODO Not really nice to be writing to Settings.Secure here... 224 // ideally we overlay our local changes over whatever is in 225 // Settings.Secure 226 if (service == null || mServiceCache.hasService(userId, service)) { 227 Settings.Secure.putStringForUser(mContext.getContentResolver(), 228 Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 229 service != null ? service.flattenToString() : null, userId); 230 } else { 231 Log.e(TAG, "Could not find default service to make default: " + service); 232 } 233 return true; 234 } 235 236 boolean isServiceRegistered(int userId, ComponentName service) { 237 boolean serviceFound = mServiceCache.hasService(userId, service); 238 if (!serviceFound) { 239 // If we don't know about this service yet, it may have just been enabled 240 // using PackageManager.setComponentEnabledSetting(). The PackageManager 241 // broadcasts are delayed by 10 seconds in that scenario, which causes 242 // calls to our APIs referencing that service to fail. 243 // Hence, update the cache in case we don't know about the service. 244 if (DBG) Log.d(TAG, "Didn't find passed in service, invalidating cache."); 245 mServiceCache.invalidateCache(userId); 246 } 247 return mServiceCache.hasService(userId, service); 248 } 249 250 /** 251 * Returns whether a service in this package is preferred, 252 * either because it's the default payment app or it's running 253 * in the foreground. 254 */ 255 public boolean packageHasPreferredService(String packageName) { 256 return mPreferredServices.packageHasPreferredService(packageName); 257 } 258 259 /** 260 * This class implements the application-facing APIs 261 * and are called from binder. All calls must be 262 * permission-checked. 263 * 264 */ 265 final class CardEmulationInterface extends INfcCardEmulation.Stub { 266 @Override 267 public boolean isDefaultServiceForCategory(int userId, ComponentName service, 268 String category) { 269 NfcPermissions.enforceUserPermissions(mContext); 270 NfcPermissions.validateUserId(userId); 271 if (!isServiceRegistered(userId, service)) { 272 return false; 273 } 274 ComponentName defaultService = 275 getDefaultServiceForCategory(userId, category, true); 276 return (defaultService != null && defaultService.equals(service)); 277 } 278 279 @Override 280 public boolean isDefaultServiceForAid(int userId, 281 ComponentName service, String aid) throws RemoteException { 282 NfcPermissions.validateUserId(userId); 283 NfcPermissions.enforceUserPermissions(mContext); 284 if (!isServiceRegistered(userId, service)) { 285 return false; 286 } 287 return mAidCache.isDefaultServiceForAid(userId, service, aid); 288 } 289 290 @Override 291 public boolean setDefaultServiceForCategory(int userId, 292 ComponentName service, String category) throws RemoteException { 293 NfcPermissions.validateUserId(userId); 294 NfcPermissions.enforceAdminPermissions(mContext); 295 if (!isServiceRegistered(userId, service)) { 296 return false; 297 } 298 return setDefaultServiceForCategoryChecked(userId, service, category); 299 } 300 301 @Override 302 public boolean setDefaultForNextTap(int userId, ComponentName service) 303 throws RemoteException { 304 NfcPermissions.validateUserId(userId); 305 NfcPermissions.enforceAdminPermissions(mContext); 306 if (!isServiceRegistered(userId, service)) { 307 return false; 308 } 309 return mPreferredServices.setDefaultForNextTap(service); 310 } 311 312 @Override 313 public boolean registerAidGroupForService(int userId, 314 ComponentName service, AidGroup aidGroup) throws RemoteException { 315 NfcPermissions.validateUserId(userId); 316 NfcPermissions.enforceUserPermissions(mContext); 317 if (!isServiceRegistered(userId, service)) { 318 return false; 319 } 320 return mServiceCache.registerAidGroupForService(userId, Binder.getCallingUid(), service, 321 aidGroup); 322 } 323 324 @Override 325 public AidGroup getAidGroupForService(int userId, 326 ComponentName service, String category) throws RemoteException { 327 NfcPermissions.validateUserId(userId); 328 NfcPermissions.enforceUserPermissions(mContext); 329 if (!isServiceRegistered(userId, service)) { 330 return null; 331 } 332 return mServiceCache.getAidGroupForService(userId, Binder.getCallingUid(), service, 333 category); 334 } 335 336 @Override 337 public boolean removeAidGroupForService(int userId, 338 ComponentName service, String category) throws RemoteException { 339 NfcPermissions.validateUserId(userId); 340 NfcPermissions.enforceUserPermissions(mContext); 341 if (!isServiceRegistered(userId, service)) { 342 return false; 343 } 344 return mServiceCache.removeAidGroupForService(userId, Binder.getCallingUid(), service, 345 category); 346 } 347 348 @Override 349 public List<ApduServiceInfo> getServices(int userId, String category) 350 throws RemoteException { 351 NfcPermissions.validateUserId(userId); 352 NfcPermissions.enforceAdminPermissions(mContext); 353 return mServiceCache.getServicesForCategory(userId, category); 354 } 355 356 @Override 357 public boolean setPreferredService(ComponentName service) 358 throws RemoteException { 359 NfcPermissions.enforceUserPermissions(mContext); 360 if (!isServiceRegistered(UserHandle.getCallingUserId(), service)) { 361 Log.e(TAG, "setPreferredService: unknown component."); 362 return false; 363 } 364 return mPreferredServices.registerPreferredForegroundService(service, 365 Binder.getCallingUid()); 366 } 367 368 @Override 369 public boolean unsetPreferredService() throws RemoteException { 370 NfcPermissions.enforceUserPermissions(mContext); 371 return mPreferredServices.unregisteredPreferredForegroundService( 372 Binder.getCallingUid()); 373 374 } 375 376 @Override 377 public boolean supportsAidPrefixRegistration() throws RemoteException { 378 return mAidCache.supportsAidPrefixRegistration(); 379 } 380 } 381 382 @Override 383 public void onPreferredPaymentServiceChanged(ComponentName service) { 384 mAidCache.onPreferredPaymentServiceChanged(service); 385 mHostEmulationManager.onPreferredPaymentServiceChanged(service); 386 } 387 388 @Override 389 public void onPreferredForegroundServiceChanged(ComponentName service) { 390 mAidCache.onPreferredForegroundServiceChanged(service); 391 mHostEmulationManager.onPreferredForegroundServiceChanged(service); 392 }; 393} 394