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