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