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.cardemulation; 17 18 import java.io.FileDescriptor; 19 import java.io.PrintWriter; 20 import java.util.List; 21 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.nfc.INfcCardEmulation; 26 import android.nfc.cardemulation.AidGroup; 27 import android.nfc.cardemulation.ApduServiceInfo; 28 import android.nfc.cardemulation.CardEmulation; 29 import android.os.Binder; 30 import android.os.RemoteException; 31 import android.os.UserHandle; 32 import android.provider.Settings; 33 import android.util.Log; 34 35 import com.android.nfc.NfcPermissions; 36 import 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 */ 48 public 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); 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 * This class implements the application-facing APIs 252 * and are called from binder. All calls must be 253 * permission-checked. 254 * 255 */ 256 final class CardEmulationInterface extends INfcCardEmulation.Stub { 257 @Override 258 public boolean isDefaultServiceForCategory(int userId, ComponentName service, 259 String category) { 260 NfcPermissions.enforceUserPermissions(mContext); 261 NfcPermissions.validateUserId(userId); 262 if (!isServiceRegistered(userId, service)) { 263 return false; 264 } 265 ComponentName defaultService = 266 getDefaultServiceForCategory(userId, category, true); 267 return (defaultService != null && defaultService.equals(service)); 268 } 269 270 @Override 271 public boolean isDefaultServiceForAid(int userId, 272 ComponentName service, String aid) throws RemoteException { 273 NfcPermissions.validateUserId(userId); 274 NfcPermissions.enforceUserPermissions(mContext); 275 if (!isServiceRegistered(userId, service)) { 276 return false; 277 } 278 return mAidCache.isDefaultServiceForAid(userId, service, aid); 279 } 280 281 @Override 282 public boolean setDefaultServiceForCategory(int userId, 283 ComponentName service, String category) throws RemoteException { 284 NfcPermissions.validateUserId(userId); 285 NfcPermissions.enforceAdminPermissions(mContext); 286 if (!isServiceRegistered(userId, service)) { 287 return false; 288 } 289 return setDefaultServiceForCategoryChecked(userId, service, category); 290 } 291 292 @Override 293 public boolean setDefaultForNextTap(int userId, ComponentName service) 294 throws RemoteException { 295 NfcPermissions.validateUserId(userId); 296 NfcPermissions.enforceAdminPermissions(mContext); 297 if (!isServiceRegistered(userId, service)) { 298 return false; 299 } 300 return mPreferredServices.setDefaultForNextTap(service); 301 } 302 303 @Override 304 public boolean registerAidGroupForService(int userId, 305 ComponentName service, AidGroup aidGroup) throws RemoteException { 306 NfcPermissions.validateUserId(userId); 307 NfcPermissions.enforceUserPermissions(mContext); 308 if (!isServiceRegistered(userId, service)) { 309 return false; 310 } 311 return mServiceCache.registerAidGroupForService(userId, Binder.getCallingUid(), service, 312 aidGroup); 313 } 314 315 @Override 316 public AidGroup getAidGroupForService(int userId, 317 ComponentName service, String category) throws RemoteException { 318 NfcPermissions.validateUserId(userId); 319 NfcPermissions.enforceUserPermissions(mContext); 320 if (!isServiceRegistered(userId, service)) { 321 return null; 322 } 323 return mServiceCache.getAidGroupForService(userId, Binder.getCallingUid(), service, 324 category); 325 } 326 327 @Override 328 public boolean removeAidGroupForService(int userId, 329 ComponentName service, String category) throws RemoteException { 330 NfcPermissions.validateUserId(userId); 331 NfcPermissions.enforceUserPermissions(mContext); 332 if (!isServiceRegistered(userId, service)) { 333 return false; 334 } 335 return mServiceCache.removeAidGroupForService(userId, Binder.getCallingUid(), service, 336 category); 337 } 338 339 @Override 340 public List<ApduServiceInfo> getServices(int userId, String category) 341 throws RemoteException { 342 NfcPermissions.validateUserId(userId); 343 NfcPermissions.enforceAdminPermissions(mContext); 344 return mServiceCache.getServicesForCategory(userId, category); 345 } 346 347 @Override 348 public boolean setPreferredService(ComponentName service) 349 throws RemoteException { 350 NfcPermissions.enforceUserPermissions(mContext); 351 if (!isServiceRegistered(UserHandle.getCallingUserId(), service)) { 352 Log.e(TAG, "setPreferredService: unknown component."); 353 return false; 354 } 355 return mPreferredServices.registerPreferredForegroundService(service, 356 Binder.getCallingUid()); 357 } 358 359 @Override 360 public boolean unsetPreferredService() throws RemoteException { 361 NfcPermissions.enforceUserPermissions(mContext); 362 return mPreferredServices.unregisteredPreferredForegroundService( 363 Binder.getCallingUid()); 364 365 } 366 367 @Override 368 public boolean supportsAidPrefixRegistration() throws RemoteException { 369 return mAidCache.supportsAidPrefixRegistration(); 370 } 371 } 372 373 @Override 374 public void onPreferredPaymentServiceChanged(ComponentName service) { 375 mAidCache.onPreferredPaymentServiceChanged(service); 376 mHostEmulationManager.onPreferredPaymentServiceChanged(service); 377 } 378 379 @Override 380 public void onPreferredForegroundServiceChanged(ComponentName service) { 381 mAidCache.onPreferredForegroundServiceChanged(service); 382 mHostEmulationManager.onPreferredForegroundServiceChanged(service); 383 }; 384 } 385