• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
onNfcFServicesUpdated(int userId, final List<NfcFServiceInfo> services)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 
DynamicSystemCode(int uid, String systemCode)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 
DynamicNfcid2(int uid, String nfcid2)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 
findOrCreateUserLocked(int userId)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 
RegisteredNfcFServicesCache(Context context, Callback callback)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 
initialize()173     void initialize() {
174         synchronized (mLock) {
175             readDynamicSystemCodeNfcid2Locked();
176         }
177         invalidateCache(ActivityManager.getCurrentUser());
178     }
179 
dump(ArrayList<NfcFServiceInfo> services)180     void dump(ArrayList<NfcFServiceInfo> services) {
181         for (NfcFServiceInfo service : services) {
182             Log.d(TAG, service.toString());
183         }
184     }
185 
containsServiceLocked(ArrayList<NfcFServiceInfo> services, ComponentName componentName)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 
hasService(int userId, ComponentName componentName)194     public boolean hasService(int userId, ComponentName componentName) {
195         return getService(userId, componentName) != null;
196     }
197 
getService(int userId, ComponentName componentName)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 
getServices(int userId)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 
getInstalledServices(int userId)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 
invalidateCache(int userId)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 
readDynamicSystemCodeNfcid2Locked()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 
writeDynamicSystemCodeNfcid2Locked()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 
registerSystemCodeForService(int userId, int uid, ComponentName componentName, String systemCode)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 
getSystemCodeForService(int userId, int uid, ComponentName componentName)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 
removeSystemCodeForService(int userId, int uid, ComponentName componentName)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 
setNfcid2ForService(int userId, int uid, ComponentName componentName, String nfcid2)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 
getNfcid2ForService(int userId, int uid, ComponentName componentName)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 
onHostEmulationActivated()676     public void onHostEmulationActivated() {
677         if (DBG) Log.d(TAG, "onHostEmulationActivated");
678         synchronized (mLock) {
679             mActivated = true;
680         }
681     }
682 
onHostEmulationDeactivated()683     public void onHostEmulationDeactivated() {
684         if (DBG) Log.d(TAG, "onHostEmulationDeactivated");
685         synchronized (mLock) {
686             mActivated = false;
687         }
688     }
689 
onNfcDisabled()690     public void onNfcDisabled() {
691         synchronized (mLock) {
692             mActivated = false;
693         }
694     }
695 
generateRandomNfcid2()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 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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