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