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