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