Home | History | Annotate | Download | only in cardemulation
      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      * 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