Home | History | Annotate | Download | only in cardemulation
      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 org.xmlpull.v1.XmlPullParser;
     20 import org.xmlpull.v1.XmlPullParserException;
     21 import org.xmlpull.v1.XmlSerializer;
     22 
     23 import android.app.ActivityManager;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.PackageManager;
     30 import android.content.pm.ResolveInfo;
     31 import android.content.pm.ServiceInfo;
     32 import android.content.pm.PackageManager.NameNotFoundException;
     33 import android.nfc.cardemulation.NfcFServiceInfo;
     34 import android.nfc.cardemulation.NfcFCardEmulation;
     35 import android.nfc.cardemulation.HostNfcFService;
     36 import android.os.UserHandle;
     37 import android.util.AtomicFile;
     38 import android.util.Log;
     39 import android.util.SparseArray;
     40 import android.util.Xml;
     41 
     42 import com.android.internal.util.FastXmlSerializer;
     43 import com.google.android.collect.Maps;
     44 
     45 
     46 import java.io.File;
     47 import java.io.FileDescriptor;
     48 import java.io.FileInputStream;
     49 import java.io.FileOutputStream;
     50 import java.io.IOException;
     51 import java.io.PrintWriter;
     52 import java.util.ArrayList;
     53 import java.util.Collections;
     54 import java.util.HashMap;
     55 import java.util.Iterator;
     56 import java.util.List;
     57 import java.util.Map;
     58 import java.util.concurrent.atomic.AtomicReference;
     59 
     60 public class RegisteredNfcFServicesCache {
     61     static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output";
     62     static final String TAG = "RegisteredNfcFServicesCache";
     63     static final boolean DBG = false;
     64 
     65     final Context mContext;
     66     final AtomicReference<BroadcastReceiver> mReceiver;
     67 
     68     final Object mLock = new Object();
     69     // All variables below synchronized on mLock
     70 
     71     // mUserServices holds the card emulation services that are running for each user
     72     final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>();
     73     final Callback mCallback;
     74     final AtomicFile mDynamicSystemCodeNfcid2File;
     75     boolean mActivated = false;
     76     boolean mUserSwitched = false;
     77 
     78     public interface Callback {
     79         void onNfcFServicesUpdated(int userId, final List<NfcFServiceInfo> services);
     80     };
     81 
     82     static class DynamicSystemCode {
     83         public final int uid;
     84         public final String systemCode;
     85 
     86         DynamicSystemCode(int uid, String systemCode) {
     87             this.uid = uid;
     88             this.systemCode = systemCode;
     89         }
     90     };
     91 
     92     static class DynamicNfcid2 {
     93         public final int uid;
     94         public final String nfcid2;
     95 
     96         DynamicNfcid2(int uid, String nfcid2) {
     97             this.uid = uid;
     98             this.nfcid2 = nfcid2;
     99         }
    100     };
    101 
    102     private static class UserServices {
    103         /**
    104          * All services that have registered
    105          */
    106         final HashMap<ComponentName, NfcFServiceInfo> services =
    107                 Maps.newHashMap(); // Re-built at run-time
    108         final HashMap<ComponentName, DynamicSystemCode> dynamicSystemCode =
    109                 Maps.newHashMap(); // In memory cache of dynamic System Code store
    110         final HashMap<ComponentName, DynamicNfcid2> dynamicNfcid2 =
    111                 Maps.newHashMap(); // In memory cache of dynamic NFCID2 store
    112     };
    113 
    114     private UserServices findOrCreateUserLocked(int userId) {
    115         UserServices userServices = mUserServices.get(userId);
    116         if (userServices == null) {
    117             userServices = new UserServices();
    118             mUserServices.put(userId, userServices);
    119         }
    120         return userServices;
    121     }
    122 
    123     public RegisteredNfcFServicesCache(Context context, Callback callback) {
    124         mContext = context;
    125         mCallback = callback;
    126 
    127         final BroadcastReceiver receiver = new BroadcastReceiver() {
    128             @Override
    129             public void onReceive(Context context, Intent intent) {
    130                 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
    131                 String action = intent.getAction();
    132                 if (DBG) Log.d(TAG, "Intent action: " + action);
    133                 if (uid != -1) {
    134                     boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) &&
    135                             (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
    136                              Intent.ACTION_PACKAGE_REMOVED.equals(action));
    137                     if (!replaced) {
    138                         int currentUser = ActivityManager.getCurrentUser();
    139                         if (currentUser == UserHandle.getUserId(uid)) {
    140                             invalidateCache(UserHandle.getUserId(uid));
    141                         } else {
    142                             // Cache will automatically be updated on user switch
    143                         }
    144                     } else {
    145                         if (DBG) Log.d(TAG,
    146                                 "Ignoring package intent due to package being replaced.");
    147                     }
    148                 }
    149             }
    150         };
    151         mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
    152 
    153         IntentFilter intentFilter = new IntentFilter();
    154         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    155         intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    156         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    157         intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
    158         intentFilter.addAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH);
    159         intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
    160         intentFilter.addDataScheme("package");
    161         mContext.registerReceiverAsUser(mReceiver.get(), UserHandle.ALL, intentFilter, null, null);
    162 
    163         // Register for events related to sdcard operations
    164         IntentFilter sdFilter = new IntentFilter();
    165         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
    166         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    167         mContext.registerReceiverAsUser(mReceiver.get(), UserHandle.ALL, sdFilter, null, null);
    168 
    169         File dataDir = mContext.getFilesDir();
    170         mDynamicSystemCodeNfcid2File =
    171                 new AtomicFile(new File(dataDir, "dynamic_systemcode_nfcid2.xml"));
    172     }
    173 
    174     void initialize() {
    175         synchronized (mLock) {
    176             readDynamicSystemCodeNfcid2Locked();
    177         }
    178         invalidateCache(ActivityManager.getCurrentUser());
    179     }
    180 
    181     void dump(ArrayList<NfcFServiceInfo> services) {
    182         for (NfcFServiceInfo service : services) {
    183             Log.d(TAG, service.toString());
    184         }
    185     }
    186 
    187     boolean containsServiceLocked(ArrayList<NfcFServiceInfo> services,
    188             ComponentName componentName) {
    189         for (NfcFServiceInfo service : services) {
    190             if (service.getComponent().equals(componentName)) return true;
    191         }
    192         return false;
    193     }
    194 
    195     public boolean hasService(int userId, ComponentName componentName) {
    196         return getService(userId, componentName) != null;
    197     }
    198 
    199     public NfcFServiceInfo getService(int userId, ComponentName componentName) {
    200         synchronized (mLock) {
    201             UserServices userServices = findOrCreateUserLocked(userId);
    202             return userServices.services.get(componentName);
    203         }
    204     }
    205 
    206     public List<NfcFServiceInfo> getServices(int userId) {
    207         final ArrayList<NfcFServiceInfo> services = new ArrayList<NfcFServiceInfo>();
    208         synchronized (mLock) {
    209             UserServices userServices = findOrCreateUserLocked(userId);
    210             services.addAll(userServices.services.values());
    211         }
    212         return services;
    213     }
    214 
    215     ArrayList<NfcFServiceInfo> getInstalledServices(int userId) {
    216         if (DBG) Log.d(TAG, "getInstalledServices");
    217         PackageManager pm;
    218         try {
    219             pm = mContext.createPackageContextAsUser("android", 0,
    220                     new UserHandle(userId)).getPackageManager();
    221         } catch (NameNotFoundException e) {
    222             Log.e(TAG, "Could not create user package context");
    223             return null;
    224         }
    225 
    226         ArrayList<NfcFServiceInfo> validServices = new ArrayList<NfcFServiceInfo>();
    227 
    228         List<ResolveInfo> resolvedServices = pm.queryIntentServicesAsUser(
    229                 new Intent(HostNfcFService.SERVICE_INTERFACE),
    230                 PackageManager.GET_META_DATA, userId);
    231 
    232         for (ResolveInfo resolvedService : resolvedServices) {
    233             try {
    234                 ServiceInfo si = resolvedService.serviceInfo;
    235                 ComponentName componentName = new ComponentName(si.packageName, si.name);
    236                 // Check if the package holds the NFC permission
    237                 if (pm.checkPermission(android.Manifest.permission.NFC, si.packageName) !=
    238                         PackageManager.PERMISSION_GRANTED) {
    239                     Log.e(TAG, "Skipping NfcF service " + componentName +
    240                             ": it does not require the permission " +
    241                             android.Manifest.permission.NFC);
    242                     continue;
    243                 }
    244                 if (!android.Manifest.permission.BIND_NFC_SERVICE.equals(
    245                         si.permission)) {
    246                     Log.e(TAG, "Skipping NfcF service " + componentName +
    247                             ": it does not require the permission " +
    248                             android.Manifest.permission.BIND_NFC_SERVICE);
    249                     continue;
    250                 }
    251                 NfcFServiceInfo service = new NfcFServiceInfo(pm, resolvedService);
    252                 if (service != null) {
    253                     validServices.add(service);
    254                 }
    255             } catch (XmlPullParserException e) {
    256                 Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e);
    257             } catch (IOException e) {
    258                 Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e);
    259             }
    260         }
    261 
    262         return validServices;
    263     }
    264 
    265     public void invalidateCache(int userId) {
    266         if (DBG) Log.d(TAG, "invalidateCache");
    267         final ArrayList<NfcFServiceInfo> validServices = getInstalledServices(userId);
    268         if (validServices == null) {
    269             return;
    270         }
    271         ArrayList<NfcFServiceInfo> newServices = null;
    272         synchronized (mLock) {
    273             UserServices userServices = findOrCreateUserLocked(userId);
    274 
    275             // Check update
    276             ArrayList<NfcFServiceInfo> cachedServices =
    277                     new ArrayList<NfcFServiceInfo>(userServices.services.values());
    278             ArrayList<NfcFServiceInfo> toBeAdded = new ArrayList<NfcFServiceInfo>();
    279             ArrayList<NfcFServiceInfo> toBeRemoved = new ArrayList<NfcFServiceInfo>();
    280             boolean matched = false;
    281             for (NfcFServiceInfo validService : validServices) {
    282                 for (NfcFServiceInfo cachedService : cachedServices) {
    283                     if (validService.equals(cachedService)) {
    284                         matched = true;
    285                         break;
    286                     }
    287                 }
    288                 if (!matched) {
    289                     toBeAdded.add(validService);
    290                 }
    291                 matched = false;
    292             }
    293             for (NfcFServiceInfo cachedService : cachedServices) {
    294                 for (NfcFServiceInfo validService : validServices) {
    295                     if (cachedService.equals(validService)) {
    296                         matched = true;
    297                         break;
    298                     }
    299                 }
    300                 if (!matched) {
    301                     toBeRemoved.add(cachedService);
    302                 }
    303                 matched = false;
    304             }
    305             if (mUserSwitched) {
    306                 Log.d(TAG, "User switched, rebuild internal cache");
    307                 mUserSwitched = false;
    308             } else if (toBeAdded.size() == 0 && toBeRemoved.size() == 0) {
    309                 Log.d(TAG, "Service unchanged, not updating");
    310                 return;
    311             }
    312 
    313             // Update cache
    314             for (NfcFServiceInfo service : toBeAdded) {
    315                 userServices.services.put(service.getComponent(), service);
    316                 if (DBG) Log.d(TAG, "Added service: " + service.getComponent());
    317             }
    318             for (NfcFServiceInfo service : toBeRemoved) {
    319                 userServices.services.remove(service.getComponent());
    320                 if (DBG) Log.d(TAG, "Removed service: " + service.getComponent());
    321             }
    322             // Apply dynamic System Code mappings
    323             ArrayList<ComponentName> toBeRemovedDynamicSystemCode =
    324                     new ArrayList<ComponentName>();
    325             for (Map.Entry<ComponentName, DynamicSystemCode> entry :
    326                     userServices.dynamicSystemCode.entrySet()) {
    327                 // Verify component / uid match
    328                 ComponentName componentName = entry.getKey();
    329                 DynamicSystemCode dynamicSystemCode = entry.getValue();
    330                 NfcFServiceInfo service = userServices.services.get(componentName);
    331                 if (service == null || (service.getUid() != dynamicSystemCode.uid)) {
    332                     toBeRemovedDynamicSystemCode.add(componentName);
    333                     continue;
    334                 } else {
    335                     service.setOrReplaceDynamicSystemCode(dynamicSystemCode.systemCode);
    336                 }
    337             }
    338             // Apply dynamic NFCID2 mappings
    339             ArrayList<ComponentName> toBeRemovedDynamicNfcid2 =
    340                     new ArrayList<ComponentName>();
    341             for (Map.Entry<ComponentName, DynamicNfcid2> entry :
    342                     userServices.dynamicNfcid2.entrySet()) {
    343                 // Verify component / uid match
    344                 ComponentName componentName = entry.getKey();
    345                 DynamicNfcid2 dynamicNfcid2 = entry.getValue();
    346                 NfcFServiceInfo service = userServices.services.get(componentName);
    347                 if (service == null || (service.getUid() != dynamicNfcid2.uid)) {
    348                     toBeRemovedDynamicNfcid2.add(componentName);
    349                     continue;
    350                 } else {
    351                     service.setOrReplaceDynamicNfcid2(dynamicNfcid2.nfcid2);
    352                 }
    353             }
    354             for (ComponentName removedComponent : toBeRemovedDynamicSystemCode) {
    355                 Log.d(TAG, "Removing dynamic System Code registered by " +
    356                         removedComponent);
    357                 userServices.dynamicSystemCode.remove(removedComponent);
    358             }
    359             for (ComponentName removedComponent : toBeRemovedDynamicNfcid2) {
    360                 Log.d(TAG, "Removing dynamic NFCID2 registered by " +
    361                         removedComponent);
    362                 userServices.dynamicNfcid2.remove(removedComponent);
    363             }
    364             // Assign a NFCID2 for services requesting a random NFCID2, then apply
    365             boolean nfcid2Assigned = false;
    366             for (Map.Entry<ComponentName, NfcFServiceInfo> entry :
    367                 userServices.services.entrySet()) {
    368                 NfcFServiceInfo service = entry.getValue();
    369                 if (service.getNfcid2().equalsIgnoreCase("RANDOM")) {
    370                     String randomNfcid2 = generateRandomNfcid2();
    371                     service.setOrReplaceDynamicNfcid2(randomNfcid2);
    372                     DynamicNfcid2 dynamicNfcid2 =
    373                             new DynamicNfcid2(service.getUid(), randomNfcid2);
    374                     userServices.dynamicNfcid2.put(entry.getKey(), dynamicNfcid2);
    375                     nfcid2Assigned = true;
    376                 }
    377             }
    378 
    379             // Persist to filesystem
    380             if (toBeRemovedDynamicSystemCode.size() > 0 ||
    381                     toBeRemovedDynamicNfcid2.size() > 0 ||
    382                     nfcid2Assigned) {
    383                 writeDynamicSystemCodeNfcid2Locked();
    384             }
    385 
    386             newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values());
    387         }
    388         mCallback.onNfcFServicesUpdated(userId, Collections.unmodifiableList(newServices));
    389         if (DBG) dump(newServices);
    390     }
    391 
    392     private void readDynamicSystemCodeNfcid2Locked() {
    393         if (DBG) Log.d(TAG, "readDynamicSystemCodeNfcid2Locked");
    394         FileInputStream fis = null;
    395         try {
    396             if (!mDynamicSystemCodeNfcid2File.getBaseFile().exists()) {
    397                 Log.d(TAG, "Dynamic System Code, NFCID2 file does not exist.");
    398                 return;
    399             }
    400             fis = mDynamicSystemCodeNfcid2File.openRead();
    401             XmlPullParser parser = Xml.newPullParser();
    402             parser.setInput(fis, null);
    403             int eventType = parser.getEventType();
    404             while (eventType != XmlPullParser.START_TAG &&
    405                     eventType != XmlPullParser.END_DOCUMENT) {
    406                 eventType = parser.next();
    407             }
    408             String tagName = parser.getName();
    409             if ("services".equals(tagName)) {
    410                 ComponentName componentName = null;
    411                 int currentUid = -1;
    412                 String systemCode = null;
    413                 String nfcid2 = null;
    414                 String description = null;
    415                 while (eventType != XmlPullParser.END_DOCUMENT) {
    416                     tagName = parser.getName();
    417                     if (eventType == XmlPullParser.START_TAG) {
    418                         if ("service".equals(tagName) && parser.getDepth() == 2) {
    419                             String compString =
    420                                     parser.getAttributeValue(null, "component");
    421                             String uidString =
    422                                     parser.getAttributeValue(null, "uid");
    423                             String systemCodeString =
    424                                     parser.getAttributeValue(null, "system-code");
    425                             String descriptionString =
    426                                     parser.getAttributeValue(null, "description");
    427                             String nfcid2String =
    428                                     parser.getAttributeValue(null, "nfcid2");
    429                             if (compString == null || uidString == null) {
    430                                 Log.e(TAG, "Invalid service attributes");
    431                             } else {
    432                                 try {
    433                                     componentName = ComponentName.unflattenFromString(compString);
    434                                     currentUid = Integer.parseInt(uidString);
    435                                     systemCode = systemCodeString;
    436                                     description = descriptionString;
    437                                     nfcid2 = nfcid2String;
    438                                 } catch (NumberFormatException e) {
    439                                     Log.e(TAG, "Could not parse service uid");
    440                                 }
    441                             }
    442                         }
    443                     } else if (eventType == XmlPullParser.END_TAG) {
    444                         if ("service".equals(tagName)) {
    445                             // See if we have a valid service
    446                             if (componentName != null && currentUid >= 0) {
    447                                 final int userId = UserHandle.getUserId(currentUid);
    448                                 UserServices userServices = findOrCreateUserLocked(userId);
    449                                 if (systemCode != null) {
    450                                     DynamicSystemCode dynamicSystemCode =
    451                                             new DynamicSystemCode(currentUid, systemCode);
    452                                     userServices.dynamicSystemCode.put(
    453                                             componentName, dynamicSystemCode);
    454                                 }
    455                                 if (nfcid2 != null) {
    456                                     DynamicNfcid2 dynamicNfcid2 =
    457                                             new DynamicNfcid2(currentUid, nfcid2);
    458                                     userServices.dynamicNfcid2.put(
    459                                             componentName, dynamicNfcid2);
    460                                 }
    461                             }
    462                             componentName = null;
    463                             currentUid = -1;
    464                             systemCode = null;
    465                             description = null;
    466                             nfcid2 = null;
    467                         }
    468                     }
    469                     eventType = parser.next();
    470                 };
    471             }
    472         } catch (Exception e) {
    473             Log.e(TAG, "Could not parse dynamic System Code, NFCID2 file, trashing.");
    474             mDynamicSystemCodeNfcid2File.delete();
    475         } finally {
    476             if (fis != null) {
    477                 try {
    478                     fis.close();
    479                 } catch (IOException e) {
    480                 }
    481             }
    482         }
    483     }
    484 
    485     private boolean writeDynamicSystemCodeNfcid2Locked() {
    486         if (DBG) Log.d(TAG, "writeDynamicSystemCodeNfcid2Locked");
    487         FileOutputStream fos = null;
    488         try {
    489             fos = mDynamicSystemCodeNfcid2File.startWrite();
    490             XmlSerializer out = new FastXmlSerializer();
    491             out.setOutput(fos, "utf-8");
    492             out.startDocument(null, true);
    493             out.setFeature(XML_INDENT_OUTPUT_FEATURE, true);
    494             out.startTag(null, "services");
    495             for (int i = 0; i < mUserServices.size(); i++) {
    496                 final UserServices userServices = mUserServices.valueAt(i);
    497                 for (Map.Entry<ComponentName, DynamicSystemCode> entry :
    498                         userServices.dynamicSystemCode.entrySet()) {
    499                     out.startTag(null, "service");
    500                     out.attribute(null, "component", entry.getKey().flattenToString());
    501                     out.attribute(null, "uid", Integer.toString(entry.getValue().uid));
    502                     out.attribute(null, "system-code", entry.getValue().systemCode);
    503                     if (userServices.dynamicNfcid2.containsKey(entry.getKey())) {
    504                         out.attribute(null, "nfcid2",
    505                                 userServices.dynamicNfcid2.get(entry.getKey()).nfcid2);
    506                     }
    507                     out.endTag(null, "service");
    508                 }
    509                 for (Map.Entry<ComponentName, DynamicNfcid2> entry :
    510                         userServices.dynamicNfcid2.entrySet()) {
    511                     if (!userServices.dynamicSystemCode.containsKey(entry.getKey())) {
    512                         out.startTag(null, "service");
    513                         out.attribute(null, "component", entry.getKey().flattenToString());
    514                         out.attribute(null, "uid", Integer.toString(entry.getValue().uid));
    515                         out.attribute(null, "nfcid2", entry.getValue().nfcid2);
    516                         out.endTag(null, "service");
    517                     }
    518                 }
    519             }
    520             out.endTag(null, "services");
    521             out.endDocument();
    522             mDynamicSystemCodeNfcid2File.finishWrite(fos);
    523             return true;
    524         } catch (Exception e) {
    525             Log.e(TAG, "Error writing dynamic System Code, NFCID2", e);
    526             if (fos != null) {
    527                 mDynamicSystemCodeNfcid2File.failWrite(fos);
    528             }
    529             return false;
    530         }
    531     }
    532 
    533     public boolean registerSystemCodeForService(int userId, int uid,
    534             ComponentName componentName, String systemCode) {
    535         if (DBG) Log.d(TAG, "registerSystemCodeForService");
    536         ArrayList<NfcFServiceInfo> newServices = null;
    537         boolean success;
    538         synchronized (mLock) {
    539             if (mActivated) {
    540                 Log.d(TAG, "failed to register System Code during activation");
    541                 return false;
    542             }
    543             UserServices userServices = findOrCreateUserLocked(userId);
    544             // Check if we can find this service
    545             NfcFServiceInfo service = getService(userId, componentName);
    546             if (service == null) {
    547                 Log.e(TAG, "Service " + componentName + " does not exist.");
    548                 return false;
    549             }
    550             if (service.getUid() != uid) {
    551                 // This is probably a good indication something is wrong here.
    552                 // Either newer service installed with different uid (but then
    553                 // we should have known about it), or somebody calling us from
    554                 // a different uid.
    555                 Log.e(TAG, "UID mismatch.");
    556                 return false;
    557             }
    558             if (!systemCode.equalsIgnoreCase("NULL") &&
    559                     !NfcFCardEmulation.isValidSystemCode(systemCode)) {
    560                 Log.e(TAG, "System Code " + systemCode + " is not a valid System Code");
    561                 return false;
    562             }
    563             // Apply dynamic System Code mappings
    564             systemCode = systemCode.toUpperCase();
    565             DynamicSystemCode oldDynamicSystemCode =
    566                     userServices.dynamicSystemCode.get(componentName);
    567             DynamicSystemCode dynamicSystemCode = new DynamicSystemCode(uid, systemCode);
    568             userServices.dynamicSystemCode.put(componentName, dynamicSystemCode);
    569             success = writeDynamicSystemCodeNfcid2Locked();
    570             if (success) {
    571                 service.setOrReplaceDynamicSystemCode(systemCode);
    572                 newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values());
    573             } else {
    574                 Log.e(TAG, "Failed to persist System Code.");
    575                 // Undo registration
    576                 if (oldDynamicSystemCode == null) {
    577                     userServices.dynamicSystemCode.remove(componentName);
    578                 } else {
    579                     userServices.dynamicSystemCode.put(componentName, oldDynamicSystemCode);
    580                 }
    581             }
    582         }
    583         if (success) {
    584             // Make callback without the lock held
    585             mCallback.onNfcFServicesUpdated(userId, newServices);
    586         }
    587         return success;
    588     }
    589 
    590     public String getSystemCodeForService(int userId, int uid, ComponentName componentName) {
    591         if (DBG) Log.d(TAG, "getSystemCodeForService");
    592         NfcFServiceInfo service = getService(userId, componentName);
    593         if (service != null) {
    594             if (service.getUid() != uid) {
    595                 Log.e(TAG, "UID mismatch");
    596                 return null;
    597             }
    598             return service.getSystemCode();
    599         } else {
    600             Log.e(TAG, "Could not find service " + componentName);
    601             return null;
    602         }
    603     }
    604 
    605     public boolean removeSystemCodeForService(int userId, int uid, ComponentName componentName) {
    606         if (DBG) Log.d(TAG, "removeSystemCodeForService");
    607         return registerSystemCodeForService(userId, uid, componentName, "NULL");
    608     }
    609 
    610     public boolean setNfcid2ForService(int userId, int uid,
    611             ComponentName componentName, String nfcid2) {
    612         if (DBG) Log.d(TAG, "setNfcid2ForService");
    613         ArrayList<NfcFServiceInfo> newServices = null;
    614         boolean success;
    615         synchronized (mLock) {
    616             if (mActivated) {
    617                 Log.d(TAG, "failed to set NFCID2 during activation");
    618                 return false;
    619             }
    620             UserServices userServices = findOrCreateUserLocked(userId);
    621             // Check if we can find this service
    622             NfcFServiceInfo service = getService(userId, componentName);
    623             if (service == null) {
    624                 Log.e(TAG, "Service " + componentName + " does not exist.");
    625                 return false;
    626             }
    627             if (service.getUid() != uid) {
    628                 // This is probably a good indication something is wrong here.
    629                 // Either newer service installed with different uid (but then
    630                 // we should have known about it), or somebody calling us from
    631                 // a different uid.
    632                 Log.e(TAG, "UID mismatch.");
    633                 return false;
    634             }
    635             if (!NfcFCardEmulation.isValidNfcid2(nfcid2)) {
    636                 Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2");
    637                 return false;
    638             }
    639             // Apply dynamic NFCID2 mappings
    640             nfcid2 = nfcid2.toUpperCase();
    641             DynamicNfcid2 oldDynamicNfcid2 = userServices.dynamicNfcid2.get(componentName);
    642             DynamicNfcid2 dynamicNfcid2 = new DynamicNfcid2(uid, nfcid2);
    643             userServices.dynamicNfcid2.put(componentName, dynamicNfcid2);
    644             success = writeDynamicSystemCodeNfcid2Locked();
    645             if (success) {
    646                 service.setOrReplaceDynamicNfcid2(nfcid2);
    647                 newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values());
    648             } else {
    649                 Log.e(TAG, "Failed to persist NFCID2.");
    650                 // Undo registration
    651                 if (oldDynamicNfcid2 == null) {
    652                     userServices.dynamicNfcid2.remove(componentName);
    653                 } else {
    654                     userServices.dynamicNfcid2.put(componentName, oldDynamicNfcid2);
    655                 }
    656             }
    657         }
    658         if (success) {
    659             // Make callback without the lock held
    660             mCallback.onNfcFServicesUpdated(userId, newServices);
    661         }
    662         return success;
    663     }
    664 
    665     public String getNfcid2ForService(int userId, int uid, ComponentName componentName) {
    666         if (DBG) Log.d(TAG, "getNfcid2ForService");
    667         NfcFServiceInfo service = getService(userId, componentName);
    668         if (service != null) {
    669             if (service.getUid() != uid) {
    670                 Log.e(TAG, "UID mismatch");
    671                 return null;
    672             }
    673             return service.getNfcid2();
    674         } else {
    675             Log.e(TAG, "Could not find service " + componentName);
    676             return null;
    677         }
    678     }
    679 
    680     public void onHostEmulationActivated() {
    681         if (DBG) Log.d(TAG, "onHostEmulationActivated");
    682         synchronized (mLock) {
    683             mActivated = true;
    684         }
    685     }
    686 
    687     public void onHostEmulationDeactivated() {
    688         if (DBG) Log.d(TAG, "onHostEmulationDeactivated");
    689         synchronized (mLock) {
    690             mActivated = false;
    691         }
    692     }
    693 
    694     public void onNfcDisabled() {
    695         synchronized (mLock) {
    696             mActivated = false;
    697         }
    698     }
    699 
    700     public void onUserSwitched() {
    701         synchronized (mLock) {
    702             mUserSwitched = true;
    703         }
    704     }
    705 
    706     private String generateRandomNfcid2() {
    707         long min = 0L;
    708         long max = 0xFFFFFFFFFFFFL;
    709 
    710         long randomNfcid2 = (long)Math.floor(Math.random() * (max-min+1)) + min;
    711         return String.format("02FE%02X%02X%02X%02X%02X%02X",
    712                 (randomNfcid2 >>> 8 * 5) & 0xFF, (randomNfcid2 >>> 8 * 4) & 0xFF,
    713                 (randomNfcid2 >>> 8 * 3) & 0xFF, (randomNfcid2 >>> 8 * 2) & 0xFF,
    714                 (randomNfcid2 >>> 8 * 1) & 0xFF, (randomNfcid2 >>> 8 * 0) & 0xFF);
    715     }
    716 
    717     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    718         pw.println("Registered HCE-F services for current user: ");
    719         synchronized (mLock) {
    720             UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser());
    721             for (NfcFServiceInfo service : userServices.services.values()) {
    722                 service.dump(fd, pw, args);
    723                 pw.println("");
    724             }
    725             pw.println("");
    726         }
    727     }
    728 
    729 }
    730