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