blob: 05d3678a60e9586beead12924e4bb0f4333e6937 [file] [log] [blame]
nxpandroid34627bd2016-05-27 15:52:30 +05301/*
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
17package com.android.nfc.cardemulation;
18
19import org.xmlpull.v1.XmlPullParser;
20import org.xmlpull.v1.XmlPullParserException;
21import org.xmlpull.v1.XmlSerializer;
22
23import android.app.ActivityManager;
24import android.content.BroadcastReceiver;
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.PackageManager;
30import android.content.pm.ResolveInfo;
31import android.content.pm.ServiceInfo;
32import android.content.pm.PackageManager.NameNotFoundException;
33import android.nfc.cardemulation.NfcFServiceInfo;
34import android.nfc.cardemulation.NfcFCardEmulation;
35import android.nfc.cardemulation.HostNfcFService;
36import android.os.UserHandle;
37import android.util.AtomicFile;
38import android.util.Log;
39import android.util.SparseArray;
40import android.util.Xml;
41
42import com.android.internal.util.FastXmlSerializer;
43import com.google.android.collect.Maps;
44
45
46import java.io.File;
47import java.io.FileDescriptor;
48import java.io.FileInputStream;
49import java.io.FileOutputStream;
50import java.io.IOException;
51import java.io.PrintWriter;
52import java.util.ArrayList;
53import java.util.Collections;
54import java.util.HashMap;
55import java.util.Iterator;
56import java.util.List;
57import java.util.Map;
58import java.util.concurrent.atomic.AtomicReference;
nxf500513a018e72019-04-23 17:11:41 +053059import android.os.SystemProperties;
nxpandroid34627bd2016-05-27 15:52:30 +053060
61public 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";
nxf500513a018e72019-04-23 17:11:41 +053064 static final boolean DBG = ((SystemProperties.get("persist.nfc.ce_debug").equals("1")) ? true : false);
nxpandroid34627bd2016-05-27 15:52:30 +053065
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;
nxpandroid6fd9cdb2017-07-12 18:25:41 +053077 boolean mUserSwitched = false;
nxpandroid34627bd2016-05-27 15:52:30 +053078
79 public interface Callback {
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
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
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
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
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
175 void initialize() {
176 synchronized (mLock) {
177 readDynamicSystemCodeNfcid2Locked();
178 }
179 invalidateCache(ActivityManager.getCurrentUser());
180 }
181
182 void dump(ArrayList<NfcFServiceInfo> services) {
183 for (NfcFServiceInfo service : services) {
184 Log.d(TAG, service.toString());
185 }
186 }
187
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
196 public boolean hasService(int userId, ComponentName componentName) {
197 return getService(userId, componentName) != null;
198 }
199
200 public NfcFServiceInfo getService(int userId, ComponentName componentName) {
201 synchronized (mLock) {
202 UserServices userServices = findOrCreateUserLocked(userId);
203 return userServices.services.get(componentName);
204 }
nxpandroid34627bd2016-05-27 15:52:30 +0530205 }
206
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
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
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 }
nxpandroid6fd9cdb2017-07-12 18:25:41 +0530306 if (mUserSwitched) {
307 Log.d(TAG, "User switched, rebuild internal cache");
308 mUserSwitched = false;
309 } else if (toBeAdded.size() == 0 && toBeRemoved.size() == 0) {
nxpandroid34627bd2016-05-27 15:52:30 +0530310 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
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
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 }
nxpandroid34627bd2016-05-27 15:52:30 +0530532 }
533
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
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
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
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
666 public String getNfcid2ForService(int userId, int uid, ComponentName componentName) {
667 if (DBG) Log.d(TAG, "getNfcid2ForService");
nxpandroid34627bd2016-05-27 15:52:30 +0530668 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 }
nxpandroid34627bd2016-05-27 15:52:30 +0530679 }
680
681 public void onHostEmulationActivated() {
682 if (DBG) Log.d(TAG, "onHostEmulationActivated");
683 synchronized (mLock) {
684 mActivated = true;
685 }
686 }
687
688 public void onHostEmulationDeactivated() {
689 if (DBG) Log.d(TAG, "onHostEmulationDeactivated");
690 synchronized (mLock) {
691 mActivated = false;
692 }
693 }
694
695 public void onNfcDisabled() {
696 synchronized (mLock) {
697 mActivated = false;
698 }
699 }
700
nxpandroid6fd9cdb2017-07-12 18:25:41 +0530701 public void onUserSwitched() {
702 synchronized (mLock) {
703 mUserSwitched = true;
704 }
705 }
706
nxpandroid34627bd2016-05-27 15:52:30 +0530707 private String generateRandomNfcid2() {
708 long min = 0L;
709 long max = 0xFFFFFFFFFFFFL;
nxpandroid281eb922016-08-25 20:27:46 +0530710
nxpandroid34627bd2016-05-27 15:52:30 +0530711 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
718 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
nxpandroid53038072017-09-14 11:14:26 +0530719 pw.println("Registered HCE-F services for current user: ");
nxpandroid34627bd2016-05-27 15:52:30 +0530720 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}