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