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      * 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