• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.nfc.cardemulation;
18 
19 import static android.nfc.cardemulation.CardEmulation.CATEGORY_OTHER;
20 import static android.nfc.cardemulation.CardEmulation.SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET;
21 import static android.nfc.cardemulation.CardEmulation.SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE;
22 import static android.nfc.cardemulation.CardEmulation.SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR;
23 import static android.nfc.cardemulation.CardEmulation.SET_SERVICE_ENABLED_STATUS_OK;
24 
25 import android.app.ActivityManager;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.PackageManager;
33 import android.content.pm.PackageManager.NameNotFoundException;
34 import android.content.pm.PackageManager.ResolveInfoFlags;
35 import android.content.pm.ResolveInfo;
36 import android.content.pm.ServiceInfo;
37 import android.nfc.cardemulation.AidGroup;
38 import android.nfc.cardemulation.ApduServiceInfo;
39 import android.nfc.cardemulation.CardEmulation;
40 import android.nfc.cardemulation.HostApduService;
41 import android.nfc.cardemulation.OffHostApduService;
42 import android.os.ParcelFileDescriptor;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.sysprop.NfcProperties;
46 import android.text.TextUtils;
47 import android.util.AtomicFile;
48 import android.util.Log;
49 import android.util.Pair;
50 import android.util.SparseArray;
51 import android.util.Xml;
52 import android.util.proto.ProtoOutputStream;
53 
54 import androidx.annotation.VisibleForTesting;
55 
56 import com.android.internal.annotations.GuardedBy;
57 import com.android.internal.util.FastXmlSerializer;
58 import com.android.nfc.NfcInjector;
59 import com.android.nfc.NfcService;
60 import com.android.nfc.R;
61 import com.android.nfc.Utils;
62 import com.android.nfc.cardemulation.util.NfcFileUtils;
63 
64 import org.xmlpull.v1.XmlPullParser;
65 import org.xmlpull.v1.XmlPullParserException;
66 import org.xmlpull.v1.XmlSerializer;
67 
68 import java.io.File;
69 import java.io.FileDescriptor;
70 import java.io.FileNotFoundException;
71 import java.io.FileOutputStream;
72 import java.io.IOException;
73 import java.io.InputStream;
74 import java.io.PrintWriter;
75 import java.util.ArrayList;
76 import java.util.Collections;
77 import java.util.HashMap;
78 import java.util.Iterator;
79 import java.util.List;
80 import java.util.Map;
81 import java.util.concurrent.atomic.AtomicReference;
82 
83 /**
84  * This class is inspired by android.content.pm.RegisteredServicesCache
85  * That class was not re-used because it doesn't support dynamically
86  * registering additional properties, but generates everything from
87  * the manifest. Since we have some properties that are not in the manifest,
88  * it's less suited.
89  */
90 public class RegisteredServicesCache {
91     static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output";
92     static final String TAG = "RegisteredServicesCache";
93     static final String AID_XML_PATH = "dynamic_aids.xml";
94     static final String OTHER_STATUS_PATH = "other_status.xml";
95     static final String PACKAGE_DATA = "package";
96     static final boolean DEBUG = NfcProperties.debug_enabled().orElse(true);
97     private static final boolean VDBG = false; // turn on for local testing.
98 
99     final Context mContext;
100     final AtomicReference<BroadcastReceiver> mReceiver;
101 
102     final Object mLock = new Object();
103     // All variables below synchronized on mLock
104 
105     // mUserHandles holds the UserHandles of all the profiles that belong to current user
106     @GuardedBy("mLock")
107     List<UserHandle> mUserHandles;
108 
109     // mUserServices holds the card emulation services that are running for each user
110     final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>();
111     final Callback mCallback;
112     SettingsFile mDynamicSettingsFile;
113     SettingsFile mOthersFile;
114     final ServiceParser mServiceParser;
115     final RoutingOptionManager mRoutingOptionManager;
116 
117     final Intent mHostApduServiceIntent = new Intent(HostApduService.SERVICE_INTERFACE);
118     final Intent mOffHostApduServiceIntent = new Intent(OffHostApduService.SERVICE_INTERFACE);
119 
120     static final String DEFAULT_T4T_NFCEE_AID = "D2760000850101";
121 
122     public interface Callback {
123         /**
124          * ServicesUpdated for specific userId.
125          */
onServicesUpdated(int userId, List<ApduServiceInfo> services, boolean validateInstalled)126         void onServicesUpdated(int userId, List<ApduServiceInfo> services,
127                 boolean validateInstalled);
128     };
129 
130     static class DynamicSettings {
131         public final int uid;
132         public final Map<String, Boolean> pollingLoopFilters = new HashMap<>();
133         public final Map<String, Boolean> pollingLoopPatternFilters = new HashMap<>();
134         public final HashMap<String, AidGroup> aidGroups = new HashMap<>();
135         public String offHostSE;
136         public String shouldDefaultToObserveModeStr;
137 
DynamicSettings(int uid)138         DynamicSettings(int uid) {
139             this.uid = uid;
140         }
141     };
142 
143     static class OtherServiceStatus {
144         public final int uid;
145         public boolean checked;
146 
OtherServiceStatus(int uid, boolean checked)147         OtherServiceStatus(int uid, boolean checked) {
148             this.uid = uid;
149             this.checked = checked;
150         }
151     };
152 
153     @VisibleForTesting
154     static class UserServices {
155         /**
156          * All services that have registered
157          */
158         final HashMap<ComponentName, ApduServiceInfo> services =
159                 new HashMap<>(); // Re-built at run-time
160         final HashMap<ComponentName, DynamicSettings> dynamicSettings =
161                 new HashMap<>(); // In memory cache of dynamic settings
162         final HashMap<ComponentName, OtherServiceStatus> others =
163                 new HashMap<>();
164     };
165 
166     @VisibleForTesting
167     static class SettingsFile {
168         final AtomicFile mFile;
SettingsFile(Context context, String path)169         SettingsFile(Context context, String path) {
170             File dir = context.getFilesDir();
171             mFile = new AtomicFile(new File(dir, path));
172         }
173 
exists()174         boolean exists() {
175             return mFile.getBaseFile().exists();
176         }
177 
openRead()178         InputStream openRead() throws FileNotFoundException {
179             return mFile.openRead();
180         }
181 
delete()182         void delete() {
183             mFile.delete();
184         }
185 
startWrite()186         FileOutputStream startWrite() throws IOException {
187             return mFile.startWrite();
188         }
189 
finishWrite(FileOutputStream fileOutputStream)190         void finishWrite(FileOutputStream fileOutputStream) {
191             mFile.finishWrite(fileOutputStream);
192         }
193 
failWrite(FileOutputStream fileOutputStream)194         void failWrite(FileOutputStream fileOutputStream) {
195             mFile.failWrite(fileOutputStream);
196         }
197 
getBaseFile()198         File getBaseFile() {
199             return mFile.getBaseFile();
200         }
201     }
202 
203     @VisibleForTesting
204     interface ServiceParser {
parseApduService(PackageManager packageManager, ResolveInfo resolveInfo, boolean onHost)205         ApduServiceInfo parseApduService(PackageManager packageManager,
206                                          ResolveInfo resolveInfo,
207                                          boolean onHost) throws XmlPullParserException, IOException;
208     }
209 
210     private static class RealServiceParser implements ServiceParser {
211 
212         @Override
parseApduService(PackageManager packageManager, ResolveInfo resolveInfo, boolean onHost)213         public ApduServiceInfo parseApduService(PackageManager packageManager,
214                                                 ResolveInfo resolveInfo, boolean onHost)
215                 throws XmlPullParserException, IOException {
216             return new ApduServiceInfo(packageManager, resolveInfo, onHost);
217         }
218     }
219 
findOrCreateUserLocked(int userId)220     private UserServices findOrCreateUserLocked(int userId) {
221         UserServices services = mUserServices.get(userId);
222         if (services == null) {
223             services = new UserServices();
224             mUserServices.put(userId, services);
225         }
226         return services;
227     }
228 
getProfileParentId(Context context, int userId)229     private int getProfileParentId(Context context, int userId) {
230         UserManager um = context.getSystemService(UserManager.class);
231         UserHandle uh = um.getProfileParent(UserHandle.of(userId));
232         return uh == null ? userId : uh.getIdentifier();
233     }
234 
getProfileParentId(int userId)235     private int getProfileParentId(int userId) {
236         return getProfileParentId(mContext.createContextAsUser(
237                 UserHandle.of(userId), /*flags=*/0), userId);
238     }
239 
RegisteredServicesCache(Context context, Callback callback)240     public RegisteredServicesCache(Context context, Callback callback) {
241         this(context, callback, new SettingsFile(context, AID_XML_PATH),
242                 new SettingsFile(context, OTHER_STATUS_PATH), new RealServiceParser(),
243                 RoutingOptionManager.getInstance());
244     }
245 
246     @VisibleForTesting
RegisteredServicesCache(Context context, Callback callback, RoutingOptionManager routingOptionManager)247     RegisteredServicesCache(Context context, Callback callback,
248                                    RoutingOptionManager routingOptionManager) {
249         this(context, callback, new SettingsFile(context, AID_XML_PATH),
250                 new SettingsFile(context, OTHER_STATUS_PATH), new RealServiceParser(),
251                 routingOptionManager);
252     }
253 
254     @VisibleForTesting
RegisteredServicesCache(Context context, Callback callback, SettingsFile dynamicSettings, SettingsFile otherSettings, ServiceParser serviceParser, RoutingOptionManager routingOptionManager)255     RegisteredServicesCache(Context context, Callback callback, SettingsFile dynamicSettings,
256                             SettingsFile otherSettings, ServiceParser serviceParser,
257                             RoutingOptionManager routingOptionManager) {
258         mContext = context;
259         mCallback = callback;
260         mServiceParser = serviceParser;
261         mRoutingOptionManager = routingOptionManager;
262 
263         synchronized (mLock) {
264             refreshUserProfilesLocked(false);
265         }
266 
267         final BroadcastReceiver receiver = new BroadcastReceiver() {
268             @Override
269             public void onReceive(Context context, Intent intent) {
270                 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
271                 String action = intent.getAction();
272                 if (VDBG) Log.d(TAG, "onReceive: Intent action: " + action);
273 
274                 if (mRoutingOptionManager.isRoutingTableOverrided()) {
275                     if (DEBUG) {
276                         Log.d(TAG, "onReceive: Routing table overrided. Skip invalidateCache()");
277                     }
278                 }
279                 if (uid == -1) return;
280                 int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
281                 int currentUser = ActivityManager.getCurrentUser();
282                 if (currentUser != getProfileParentId(context, userId)) {
283                     // Cache will automatically be updated on user switch
284                     if (VDBG) {
285                         Log.d(TAG,
286                                 "onReceive: Ignoring package change intent from non-current user");
287                     }
288                     return;
289                 }
290                 // If app not removed, check if the app has any valid CE services.
291                 if (!Intent.ACTION_PACKAGE_REMOVED.equals(action) &&
292                         !Utils.hasCeServicesWithValidPermissions(mContext, intent, userId)) {
293                     if (VDBG) {
294                         Log.d(TAG, "onReceive: Ignoring package change intent from non-CE app");
295                     }
296                     return;
297                 }
298                 boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
299                         && (Intent.ACTION_PACKAGE_ADDED.equals(action)
300                         || Intent.ACTION_PACKAGE_REMOVED.equals(action));
301                 if (!replaced) {
302                     if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
303                         invalidateCache(UserHandle.
304                                 getUserHandleForUid(uid).getIdentifier(), true);
305                     } else {
306                         invalidateCache(UserHandle.
307                                 getUserHandleForUid(uid).getIdentifier(), false);
308                     }
309                 } else {
310                     if (DEBUG) {
311                         Log.d(TAG,
312                                 "onReceive: Ignoring package intent due to package "
313                                         + "being replaced");
314                     }
315                 }
316             }
317         };
318         mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
319 
320         IntentFilter intentFilter = new IntentFilter();
321         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
322         intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
323         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
324         intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
325         intentFilter.addAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH);
326         intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
327         intentFilter.addDataScheme(PACKAGE_DATA);
328         mContext.registerReceiverForAllUsers(mReceiver.get(), intentFilter, null, null);
329 
330         // Register for events related to sdcard operations
331         IntentFilter sdFilter = new IntentFilter();
332         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
333         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
334         mContext.registerReceiverForAllUsers(mReceiver.get(), sdFilter, null, null);
335 
336         mDynamicSettingsFile = dynamicSettings;
337         mOthersFile = otherSettings;
338     }
339 
initialize()340     void initialize() {
341         synchronized (mLock) {
342             readDynamicSettingsLocked();
343             readOthersLocked();
344             if (NfcInjector.getInstance().isBootCompleted()) {
345                 for (UserHandle uh : mUserHandles) {
346                     invalidateCache(uh.getIdentifier(), false);
347                 }
348             }
349         }
350     }
351 
onBootCompleted()352     public void onBootCompleted() {
353         synchronized (mLock) {
354             refreshUserProfilesLocked(true);
355         }
356     }
357 
onUserSwitched()358     public void onUserSwitched() {
359         synchronized (mLock) {
360             refreshUserProfilesLocked(true);
361         }
362     }
363 
onManagedProfileChanged()364     public void onManagedProfileChanged() {
365         synchronized (mLock) {
366             refreshUserProfilesLocked(true);
367         }
368     }
369 
migrateFromCe(Context ceContext)370     void migrateFromCe(Context ceContext) {
371         File ceFilesDir = ceContext.getFilesDir();
372         File deFilesDir = mContext.getFilesDir();
373         if (NfcFileUtils.isEmptyDir(ceFilesDir)) {
374             Log.d(TAG, "migrateFromCe: Nothing to migrate from CE data");
375             return;
376         }
377         if (NfcFileUtils.moveFiles(ceFilesDir, deFilesDir) < 0) {
378             Log.e(TAG, "migrateFromCe: Failed to move directory from " + ceFilesDir + " to "
379                     + deFilesDir);
380             return;
381         }
382         Log.i(TAG, "migrateFromCe: Moved directory from " + ceFilesDir + " to " + deFilesDir
383             + ". Reinitializing cache.");
384         mDynamicSettingsFile = new SettingsFile(mContext, AID_XML_PATH);
385         mOthersFile = new SettingsFile(mContext, OTHER_STATUS_PATH);
386     }
387 
migrateSettingsFilesFromCe(Context ceContext)388     public void migrateSettingsFilesFromCe(Context ceContext) {
389         synchronized (mLock) {
390             migrateFromCe(ceContext);
391             initialize();
392         }
393     }
394 
refreshUserProfilesLocked(boolean invalidateCache)395     private void refreshUserProfilesLocked(boolean invalidateCache) {
396         UserManager um = mContext.createContextAsUser(
397                 UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
398                 .getSystemService(UserManager.class);
399         mUserHandles = um.getEnabledProfiles();
400         List<UserHandle> removeUserHandles = new ArrayList<UserHandle>();
401 
402         for (UserHandle uh : mUserHandles) {
403             if (um.isQuietModeEnabled(uh)) {
404                 removeUserHandles.add(uh);
405             }
406         }
407         mUserHandles.removeAll(removeUserHandles);
408         if (invalidateCache) {
409             for (UserHandle uh : mUserHandles) {
410                 invalidateCache(uh.getIdentifier(), false);
411             }
412         }
413     }
414 
dump(List<ApduServiceInfo> services)415     void dump(List<ApduServiceInfo> services) {
416         for (ApduServiceInfo service : services) {
417             if (DEBUG) Log.d(TAG, service.toString());
418         }
419     }
420 
dump(ArrayList<ComponentName> services)421     void dump(ArrayList<ComponentName> services) {
422         for (ComponentName service : services) {
423             if (DEBUG) Log.d(TAG, service.toString());
424         }
425     }
426 
containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName)427     boolean containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName) {
428         for (ApduServiceInfo service : services) {
429             if (service.getComponent().equals(serviceName)) return true;
430         }
431         return false;
432     }
433 
hasService(int userId, ComponentName service)434     public boolean hasService(int userId, ComponentName service) {
435         return getService(userId, service) != null;
436     }
437 
getService(int userId, ComponentName service)438     public ApduServiceInfo getService(int userId, ComponentName service) {
439         synchronized (mLock) {
440             UserServices userServices = findOrCreateUserLocked(userId);
441             return userServices.services.get(service);
442         }
443     }
444 
getServices(int userId)445     public List<ApduServiceInfo> getServices(int userId) {
446         final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>();
447         synchronized (mLock) {
448             UserServices userServices = findOrCreateUserLocked(userId);
449             services.addAll(userServices.services.values());
450         }
451         return services;
452     }
453 
getServicesForCategory(int userId, String category)454     public List<ApduServiceInfo> getServicesForCategory(int userId, String category) {
455         final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>();
456         synchronized (mLock) {
457             UserServices userServices = findOrCreateUserLocked(userId);
458             for (ApduServiceInfo service : userServices.services.values()) {
459                 if (service.hasCategory(category)) services.add(service);
460             }
461         }
462         return services;
463     }
464 
getInstalledServices(int userId)465     ArrayList<ApduServiceInfo> getInstalledServices(int userId) {
466         PackageManager pm;
467         try {
468             pm = mContext.createPackageContextAsUser("android", 0,
469                     UserHandle.of(userId)).getPackageManager();
470         } catch (NameNotFoundException e) {
471             Log.e(TAG, "getInstalledServices: Could not create user package context");
472             return null;
473         }
474 
475         ArrayList<ApduServiceInfo> validServices = new ArrayList<ApduServiceInfo>();
476 
477         List<ResolveInfo> resolvedServices = new ArrayList<>(pm.queryIntentServicesAsUser(
478                 mHostApduServiceIntent,
479                 ResolveInfoFlags.of(PackageManager.GET_META_DATA), UserHandle.of(userId)));
480 
481         List<ResolveInfo> resolvedOffHostServices = pm.queryIntentServicesAsUser(
482                 mOffHostApduServiceIntent,
483                 ResolveInfoFlags.of(PackageManager.GET_META_DATA), UserHandle.of(userId));
484         resolvedServices.addAll(resolvedOffHostServices);
485         for (ResolveInfo resolvedService : resolvedServices) {
486             try {
487                 boolean onHost = !resolvedOffHostServices.contains(resolvedService);
488                 ServiceInfo si = resolvedService.serviceInfo;
489                 ComponentName componentName = new ComponentName(si.packageName, si.name);
490                 // Check if the package exported the service in manifest
491                 if (!si.exported) {
492                     Log.e(TAG, "getInstalledServices: Skipping application component "
493                             + componentName + ": it must configured as exported");
494                     continue;
495                 }
496                 // Check if the package holds the NFC permission
497                 if (pm.checkPermission(android.Manifest.permission.NFC, si.packageName) !=
498                         PackageManager.PERMISSION_GRANTED) {
499                     Log.e(TAG,
500                             "getInstalledServices: Skipping application component " + componentName
501                                     + ": it must request the permission "
502                                     + android.Manifest.permission.NFC);
503                     continue;
504                 }
505                 if (!android.Manifest.permission.BIND_NFC_SERVICE.equals(
506                         si.permission)) {
507                     Log.e(TAG,
508                             "getInstalledServices: Skipping APDU service " + componentName
509                                     + ": it does not require the permission "
510                                     + android.Manifest.permission.BIND_NFC_SERVICE);
511                     continue;
512                 }
513                 ApduServiceInfo service = mServiceParser.parseApduService(pm, resolvedService,
514                         onHost);
515                 if (service != null) {
516                     validServices.add(service);
517                 }
518             } catch (XmlPullParserException e) {
519                 Log.w(TAG, "getInstalledServices: Unable to load component info "
520                         + resolvedService.toString(), e);
521             } catch (IOException e) {
522                 Log.w(TAG, "getInstalledServices: Unable to load component info "
523                         + resolvedService.toString(), e);
524             }
525         }
526 
527         // Add NDEF-NFCEE AID - Only if NDEF-NFCEE feature supported
528         // And only for user 0 to avoid adding several times (if multiple profiles)
529         if (userId == UserHandle.SYSTEM.getIdentifier()
530                 && NfcService.getInstance().isNdefNfceefeatureEnabled()) {
531             ResolveInfo ndefNfceeAppInfo = new ResolveInfo();
532             ndefNfceeAppInfo.resolvePackageName = "NdefNfceeAidRoute";
533             ndefNfceeAppInfo.serviceInfo = new ServiceInfo();
534             ndefNfceeAppInfo.serviceInfo.packageName = "com.android.nfc.ndef_nfcee";
535             ndefNfceeAppInfo.serviceInfo.name = "com.android.nfc.ndef_nfcee.NdefNfceeService";
536             ndefNfceeAppInfo.serviceInfo.applicationInfo = new ApplicationInfo();
537             List<String> ndefNfceeAid = new ArrayList<String>();
538             ndefNfceeAid.add(DEFAULT_T4T_NFCEE_AID);
539             AidGroup ndefNfceeAidGroup = new AidGroup(ndefNfceeAid, CATEGORY_OTHER);
540             ArrayList<AidGroup> ndefNfceeAidStaticGroups = new ArrayList<>();
541             ndefNfceeAidStaticGroups.add(ndefNfceeAidGroup);
542             ArrayList<AidGroup> ndefNfceeAidDynamicGroups = new ArrayList<>();
543             ApduServiceInfo ndefNfceeAidService = new ApduServiceInfo(
544                     ndefNfceeAppInfo,
545                     false,
546                     mContext.getResources().getString(R.string.device_ndef_nfcee_service_name),
547                     ndefNfceeAidStaticGroups,
548                     ndefNfceeAidDynamicGroups,
549                     false,
550                     0,
551                     userId,
552                     mContext.getResources().getString(R.string.device_ndef_nfcee_service_name),
553                     RoutingOptionManager.SE_NDEF_NFCEE,
554                     RoutingOptionManager.SE_NDEF_NFCEE);
555             validServices.add(ndefNfceeAidService);
556         }
557 
558         return validServices;
559     }
560 
561     /**
562      * invalidateCache for specific userId.
563      */
invalidateCache(int userId, boolean validateInstalled)564     public void invalidateCache(int userId, boolean validateInstalled) {
565         final ArrayList<ApduServiceInfo> validServices = getInstalledServices(userId);
566         if (validServices == null) {
567             return;
568         }
569         ArrayList<ApduServiceInfo> toBeAdded = new ArrayList<>();
570         ArrayList<ApduServiceInfo> toBeRemoved = new ArrayList<>();
571         synchronized (mLock) {
572             UserServices userServices = findOrCreateUserLocked(userId);
573 
574             // Find removed services
575             Iterator<Map.Entry<ComponentName, ApduServiceInfo>> it =
576                     userServices.services.entrySet().iterator();
577             while (it.hasNext()) {
578                 Map.Entry<ComponentName, ApduServiceInfo> entry =
579                         (Map.Entry<ComponentName, ApduServiceInfo>) it.next();
580                 if (!containsServiceLocked(validServices, entry.getKey())) {
581                     toBeRemoved.add(entry.getValue());
582                     it.remove();
583                 }
584             }
585             for (ApduServiceInfo service : validServices) {
586                 toBeAdded.add(service);
587                 userServices.services.put(service.getComponent(), service);
588             }
589 
590             // Apply dynamic settings mappings
591             ArrayList<ComponentName> toBeRemovedComponent = new ArrayList<ComponentName>();
592             for (Map.Entry<ComponentName, DynamicSettings> entry :
593                     userServices.dynamicSettings.entrySet()) {
594                 // Verify component / uid match
595                 ComponentName component = entry.getKey();
596                 DynamicSettings dynamicSettings = entry.getValue();
597                 ApduServiceInfo serviceInfo = userServices.services.get(component);
598                 if (serviceInfo == null) {
599                     toBeRemovedComponent.add(component);
600                     continue;
601                 } else {
602                     for (AidGroup group : dynamicSettings.aidGroups.values()) {
603                         serviceInfo.setDynamicAidGroup(group);
604                     }
605                     for (Map.Entry<String, Boolean> filter : dynamicSettings.pollingLoopFilters
606                             .entrySet()) {
607                         serviceInfo.addPollingLoopFilter(filter.getKey(), filter.getValue());
608                     }
609                     for (Map.Entry<String, Boolean> filter : dynamicSettings
610                             .pollingLoopPatternFilters
611                             .entrySet()) {
612                         serviceInfo.addPollingLoopPatternFilter(filter.getKey(), filter.getValue());
613                     }
614                     if (dynamicSettings.offHostSE != null) {
615                         serviceInfo.setOffHostSecureElement(dynamicSettings.offHostSE);
616                     }
617                     if (dynamicSettings.shouldDefaultToObserveModeStr != null) {
618                         serviceInfo.setShouldDefaultToObserveMode(
619                                 convertValueToBoolean(dynamicSettings.shouldDefaultToObserveModeStr,
620                                 false));
621                     }
622                 }
623             }
624             if (toBeRemoved.size() > 0) {
625                 for (ComponentName component : toBeRemovedComponent) {
626                     Log.d(TAG, "invalidateCache: Removing dynamic AIDs registered by " + component);
627                     userServices.dynamicSettings.remove(component);
628                 }
629                 // Persist to filesystem
630                 writeDynamicSettingsLocked();
631             }
632         }
633 
634         List<ApduServiceInfo> otherServices = getServicesForCategory(userId,
635                 CardEmulation.CATEGORY_OTHER);
636         invalidateOther(userId, otherServices);
637 
638         mCallback.onServicesUpdated(userId, Collections.unmodifiableList(validServices),
639                 validateInstalled);
640         if (VDBG) {
641             Log.i(TAG, "invalidateCache: Services => ");
642             dump(validServices);
643         } else {
644             // dump only new services added or removed
645             Log.i(TAG, "invalidateCache: New Services => ");
646             dump(toBeAdded);
647             Log.i(TAG, "invalidateCache: Removed Services => ");
648             dump(toBeRemoved);
649         }
650     }
651 
invalidateOther(int userId, List<ApduServiceInfo> validOtherServices)652     private void invalidateOther(int userId, List<ApduServiceInfo> validOtherServices) {
653         Log.d(TAG, "invalidateOther : " + userId);
654         ArrayList<ComponentName> toBeAdded = new ArrayList<>();
655         ArrayList<ComponentName> toBeRemoved = new ArrayList<>();
656         // remove services
657         synchronized (mLock) {
658             UserServices userServices = findOrCreateUserLocked(userId);
659             boolean needToWrite = false;
660             Iterator<Map.Entry<ComponentName, OtherServiceStatus>> it =
661                     userServices.others.entrySet().iterator();
662 
663             while (it.hasNext()) {
664                 Map.Entry<ComponentName, OtherServiceStatus> entry = it.next();
665                 if (!containsServiceLocked((ArrayList<ApduServiceInfo>) validOtherServices,
666                         entry.getKey())) {
667                     toBeRemoved.add(entry.getKey());
668                     needToWrite = true;
669                     it.remove();
670                 }
671             }
672 
673             UserManager um = mContext.createContextAsUser(
674                             UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
675                     .getSystemService(UserManager.class);
676             boolean isManagedProfile = um.isManagedProfile(userId);
677             Log.i(TAG, "invalidateOther: current user: " + ActivityManager.getCurrentUser()
678                     + ", is managed profile : " + isManagedProfile);
679             boolean isChecked = true;
680             if (NfcInjector.getInstance().getDeviceConfigFacade().getCeDisableOtherServicesOnManagedProfiles()) {
681                 isChecked = !(isManagedProfile);
682             }
683             for (ApduServiceInfo service : validOtherServices) {
684                 if (VDBG) {
685                     Log.d(TAG, "invalidateOther: update valid otherService: "
686                             + service.getComponent() + " AIDs: " + service.getAids());
687                 }
688                 if (!service.hasCategory(CardEmulation.CATEGORY_OTHER)) {
689                     Log.e(TAG, "invalidateOther: service does not have other category");
690                     continue;
691                 }
692 
693                 ComponentName component = service.getComponent();
694                 OtherServiceStatus status = userServices.others.get(component);
695 
696                 if (status == null) {
697                     toBeAdded.add(service.getComponent());
698                     status = new OtherServiceStatus(service.getUid(), isChecked);
699                     needToWrite = true;
700                 }
701                 service.setCategoryOtherServiceEnabled(status.checked);
702                 userServices.others.put(component, status);
703             }
704 
705             if (needToWrite) {
706                 writeOthersLocked();
707             }
708         }
709         if (VDBG) {
710             Log.i(TAG, "invalidateOther: Services => ");
711             dump(validOtherServices);
712         } else {
713             // dump only new services added or removed
714             Log.i(TAG, "invalidateOther: New Services => ");
715             dump(toBeAdded);
716             Log.i(TAG, "invalidateOther: Removed Services => ");
717             dump(toBeRemoved);
718         }
719     }
720 
convertValueToBoolean(CharSequence value, boolean defaultValue)721     private static final boolean convertValueToBoolean(CharSequence value, boolean defaultValue) {
722        boolean result = false;
723 
724         if (TextUtils.isEmpty(value)) {
725             return defaultValue;
726         }
727 
728         if (value.equals("1")
729         ||  value.equals("true")
730         ||  value.equals("TRUE"))
731             result = true;
732 
733         return result;
734     }
735 
736     @VisibleForTesting
737     static Map<Integer, List<Pair<ComponentName, DynamicSettings>>>
readDynamicSettingsFromFile(SettingsFile settingsFile)738     readDynamicSettingsFromFile(SettingsFile settingsFile) {
739         Log.d(TAG, "readDynamicSettingsFromFile");
740         Map<Integer, List<Pair<ComponentName, DynamicSettings>>> readSettingsMap =
741                 new HashMap<>();
742         InputStream fis = null;
743         try {
744             if (!settingsFile.exists()) {
745                 Log.d(TAG, "readDynamicSettingsFromFile: Dynamic AIDs file does not exist");
746                 return new HashMap<>();
747             }
748             fis = settingsFile.openRead();
749             XmlPullParser parser = Xml.newPullParser();
750             parser.setInput(fis, null);
751             int eventType = parser.getEventType();
752             while (eventType != XmlPullParser.START_TAG &&
753                     eventType != XmlPullParser.END_DOCUMENT) {
754                 eventType = parser.next();
755             }
756             String tagName = parser.getName();
757             if ("services".equals(tagName)) {
758                 boolean inService = false;
759                 ComponentName currentComponent = null;
760                 int currentUid = -1;
761                 String currentOffHostSE = null;
762                 String shouldDefaultToObserveModeStr = null;
763                 ArrayList<AidGroup> currentGroups = new ArrayList<AidGroup>();
764                 Map<String, Boolean> plFilters = new HashMap<>();
765                 Map<String, Boolean> plPatternFilters = new HashMap<>();
766                 while (eventType != XmlPullParser.END_DOCUMENT) {
767                     tagName = parser.getName();
768                     if (eventType == XmlPullParser.START_TAG) {
769                         if ("service".equals(tagName) && parser.getDepth() == 2) {
770                             String compString = parser.getAttributeValue(null, "component");
771                             String uidString = parser.getAttributeValue(null, "uid");
772                             String offHostString
773                                     = parser.getAttributeValue(null, "offHostSE");
774                             shouldDefaultToObserveModeStr =
775                                     parser.getAttributeValue(null, "shouldDefaultToObserveMode");
776                             if (compString == null || uidString == null) {
777                                 Log.e(TAG,
778                                         "readDynamicSettingsFromFile: Invalid service attributes");
779                             } else {
780                                 try {
781                                     currentUid = Integer.parseInt(uidString);
782                                     currentComponent = ComponentName
783                                             .unflattenFromString(compString);
784                                     currentOffHostSE = offHostString;
785                                     inService = true;
786                                 } catch (NumberFormatException e) {
787                                     Log.e(TAG,
788                                             "readDynamicSettingsFromFile: "
789                                                     + "Could not parse service uid");
790                                 }
791                             }
792                         }
793                         if ("aid-group".equals(tagName) && parser.getDepth() == 3 && inService) {
794                             AidGroup group = AidGroup.createFromXml(parser);
795                             if (group != null) {
796                                 currentGroups.add(group);
797                             } else {
798                                 Log.e(TAG,
799                                         "readDynamicSettingsFromFile: Could not parse AID group");
800                             }
801                         }
802                         if ("pl_filter".equals(tagName) && parser.getDepth() == 4 && inService) {
803                             String filter = parser.getAttributeValue(null, "value");
804                             String autoTransact = parser
805                                     .getAttributeValue(null, "auto_transact");
806                             plFilters.put(filter, Boolean.parseBoolean(autoTransact));
807                         }
808                         if ("pl_pattern_filter".equals(tagName) && parser.getDepth() == 4
809                                 && inService) {
810                             String pattern = parser.getAttributeValue(null, "value");
811                             String autoTransact = parser
812                                     .getAttributeValue(null, "auto_transact");
813                             plPatternFilters.put(pattern, Boolean.parseBoolean(autoTransact));
814                         }
815                     } else if (eventType == XmlPullParser.END_TAG) {
816                         if ("service".equals(tagName)) {
817                             // See if we have a valid service
818                             if (currentComponent != null && currentUid >= 0 &&
819                                     (currentGroups.size() > 0 || currentOffHostSE != null)) {
820                                 final int userId = UserHandle.
821                                         getUserHandleForUid(currentUid).getIdentifier();
822                                 Log.d(TAG, "readDynamicSettingsFromFile: ## user id - " + userId);
823                                 DynamicSettings dynSettings = new DynamicSettings(currentUid);
824                                 for (AidGroup group : currentGroups) {
825                                     dynSettings.aidGroups.put(group.getCategory(), group);
826                                 }
827                                 dynSettings.pollingLoopFilters.putAll(plFilters);
828                                 dynSettings.pollingLoopPatternFilters.putAll(plPatternFilters);
829                                 dynSettings.offHostSE = currentOffHostSE;
830                                 dynSettings.shouldDefaultToObserveModeStr
831                                         = shouldDefaultToObserveModeStr;
832                                 if (!readSettingsMap.containsKey(userId)) {
833                                     readSettingsMap.put(userId, new ArrayList<>());
834                                 }
835                                 readSettingsMap.get(userId)
836                                         .add(new Pair<>(currentComponent, dynSettings));
837                             }
838                             currentUid = -1;
839                             currentComponent = null;
840                             plFilters.clear();
841                             plPatternFilters.clear();
842                             currentGroups.clear();
843                             inService = false;
844                             currentOffHostSE = null;
845                         }
846                     }
847                     eventType = parser.next();
848                 };
849             }
850         } catch (Exception e) {
851             Log.e(TAG, "readDynamicSettingsFromFile: Could not parse dynamic AIDs file, trashing",
852                     e);
853             settingsFile.delete();
854         } finally {
855             if (fis != null) {
856                 try {
857                     fis.close();
858                 } catch (IOException e) {
859                 }
860             }
861         }
862         return readSettingsMap;
863     }
864 
readDynamicSettingsLocked()865     private void readDynamicSettingsLocked() {
866         Map<Integer, List<Pair<ComponentName, DynamicSettings>>> readSettingsMap
867                 = readDynamicSettingsFromFile(mDynamicSettingsFile);
868         for (Integer userId: readSettingsMap.keySet()) {
869             UserServices services = findOrCreateUserLocked(userId);
870             List<Pair<ComponentName, DynamicSettings>> componentNameDynamicServiceStatusPairs
871                     = readSettingsMap.get(userId);
872             int pairsSize = componentNameDynamicServiceStatusPairs.size();
873             for (int i = 0; i < pairsSize; i++) {
874                 Pair<ComponentName, DynamicSettings> pair
875                         = componentNameDynamicServiceStatusPairs.get(i);
876                 services.dynamicSettings.put(pair.first, pair.second);
877             }
878         }
879     }
880 
881     @VisibleForTesting
882     static Map<Integer, List<Pair<ComponentName, OtherServiceStatus>>>
readOtherFromFile(SettingsFile settingsFile)883     readOtherFromFile(SettingsFile settingsFile) {
884         Map<Integer, List<Pair<ComponentName, OtherServiceStatus>>> readSettingsMap =
885                 new HashMap<>();
886         Log.d(TAG, "readOtherFromFile");
887 
888         InputStream fis = null;
889         try {
890             if (!settingsFile.exists()) {
891                 Log.d(TAG, "readOtherFromFile: Other settings file does not exist.");
892                 return new HashMap<>();
893             }
894             fis = settingsFile.openRead();
895             XmlPullParser parser = Xml.newPullParser();
896             parser.setInput(fis, null);
897             int eventType = parser.getEventType();
898             while (eventType != XmlPullParser.START_TAG &&
899                     eventType != XmlPullParser.END_DOCUMENT) {
900                 eventType = parser.next();
901             }
902             String tagName = parser.getName();
903             if ("services".equals(tagName)) {
904                 boolean checked = false;
905                 ComponentName currentComponent = null;
906                 int currentUid = -1;
907 
908                 while (eventType != XmlPullParser.END_DOCUMENT) {
909                     tagName = parser.getName();
910                     if (eventType == XmlPullParser.START_TAG) {
911                         if ("service".equals(tagName) && parser.getDepth() == 2) {
912                             String compString = parser.getAttributeValue(null, "component");
913                             String uidString = parser.getAttributeValue(null, "uid");
914                             String checkedString = parser.getAttributeValue(null, "checked");
915                             if (compString == null || uidString == null || checkedString == null) {
916                                 Log.e(TAG, "readOtherFromFile: Invalid service attributes");
917                             } else {
918                                 try {
919                                     currentUid = Integer.parseInt(uidString);
920                                     currentComponent =
921                                             ComponentName.unflattenFromString(compString);
922                                     checked = checkedString.equals("true") ? true : false;
923                                 } catch (NumberFormatException e) {
924                                     Log.e(TAG, "readOtherFromFile: Could not parse service uid");
925                                 }
926                             }
927                         }
928                     } else if (eventType == XmlPullParser.END_TAG) {
929                         if ("service".equals(tagName)) {
930                             // See if we have a valid service
931                             if (currentComponent != null && currentUid >= 0) {
932                                 Log.d(TAG, "readOtherFromFile: end of service tag");
933                                 final int userId =
934                                         UserHandle.getUserHandleForUid(currentUid).getIdentifier();
935                                 OtherServiceStatus status =
936                                         new OtherServiceStatus(currentUid, checked);
937                                 Log.d(TAG, "readOtherFromFile: ## user id - " + userId);
938                                 if (!readSettingsMap.containsKey(userId)) {
939                                     readSettingsMap.put(userId, new ArrayList<>());
940                                 }
941                                 readSettingsMap.get(userId)
942                                         .add(new Pair<>(currentComponent, status));
943                             }
944                             currentUid = -1;
945                             currentComponent = null;
946                             checked = false;
947                         }
948                     }
949                     eventType = parser.next();
950                 }
951             }
952         } catch (Exception e) {
953             Log.e(TAG, "readOtherFromFile: Could not parse others AIDs file, trashing", e);
954             settingsFile.delete();
955         } finally {
956             if (fis != null) {
957                 try {
958                     fis.close();
959                 } catch (IOException e) {
960                     // It is safe to ignore I/O exceptions when closing FileInputStream
961                 }
962             }
963         }
964         return readSettingsMap;
965     }
966 
readOthersLocked()967     private void readOthersLocked() {
968         Map<Integer, List<Pair<ComponentName, OtherServiceStatus>>> readSettingsMap
969                 = readOtherFromFile(mOthersFile);
970         for (Integer userId: readSettingsMap.keySet()) {
971             UserServices services = findOrCreateUserLocked(userId);
972             List<Pair<ComponentName, OtherServiceStatus>> componentNameOtherServiceStatusPairs
973                     = readSettingsMap.get(userId);
974             int pairsSize = componentNameOtherServiceStatusPairs.size();
975             for (int i = 0; i < pairsSize; i++) {
976                 Pair<ComponentName, OtherServiceStatus> pair
977                         = componentNameOtherServiceStatusPairs.get(i);
978                 services.others.put(pair.first,
979                         pair.second);
980             }
981         }
982     }
983 
writeDynamicSettingsLocked()984     private boolean writeDynamicSettingsLocked() {
985         FileOutputStream fos = null;
986         try {
987             fos = mDynamicSettingsFile.startWrite();
988             XmlSerializer out = Xml.newSerializer();
989             out.setOutput(fos, "utf-8");
990             out.startDocument(null, true);
991             out.setFeature(XML_INDENT_OUTPUT_FEATURE, true);
992             out.startTag(null, "services");
993             for (int i = 0; i < mUserServices.size(); i++) {
994                 final UserServices user = mUserServices.valueAt(i);
995                 for (Map.Entry<ComponentName, DynamicSettings> service :
996                         user.dynamicSettings.entrySet()) {
997                     out.startTag(null, "service");
998                     out.attribute(null, "component", service.getKey().flattenToString());
999                     out.attribute(null, "uid", Integer.toString(service.getValue().uid));
1000                     if (service.getValue().offHostSE != null) {
1001                         out.attribute(null, "offHostSE", service.getValue().offHostSE);
1002                     }
1003                     if (service.getValue().shouldDefaultToObserveModeStr != null) {
1004                         out.attribute(null, "shouldDefaultToObserveMode",
1005                                 service.getValue().shouldDefaultToObserveModeStr);
1006                     }
1007                     for (AidGroup group : service.getValue().aidGroups.values()) {
1008                         group.writeAsXml(out);
1009                     }
1010                     out.startTag(null , "pl_filters");
1011                     for (Map.Entry<String, Boolean> filter
1012                             : service.getValue().pollingLoopFilters.entrySet()) {
1013                         out.startTag(null, "pl_filter");
1014                         out.attribute(null, "value", filter.getKey());
1015                         out.attribute(null, "auto_transact",
1016                                 Boolean.toString(filter.getValue()));
1017                         out.endTag(null, "pl_filter");
1018                     }
1019                     out.endTag(null, "pl_filters");
1020                     out.startTag(null , "pl_pattern_filters");
1021                     for (Map.Entry<String, Boolean> filter
1022                             : service.getValue().pollingLoopPatternFilters.entrySet()) {
1023                         out.startTag(null, "pl_pattern_filter");
1024                         out.attribute(null, "value", filter.getKey());
1025                         out.attribute(null, "auto_transact",
1026                                 Boolean.toString(filter.getValue()));
1027                         out.endTag(null, "pl_pattern_filter");
1028                     }
1029                     out.endTag(null, "pl_pattern_filters");
1030                     out.endTag(null, "service");
1031                 }
1032             }
1033             out.endTag(null, "services");
1034             out.endDocument();
1035             mDynamicSettingsFile.finishWrite(fos);
1036             return true;
1037         } catch (Exception e) {
1038             Log.e(TAG, "writeDynamicSettingsLocked: Error writing dynamic AIDs", e);
1039             if (fos != null) {
1040                 mDynamicSettingsFile.failWrite(fos);
1041             }
1042             return false;
1043         }
1044     }
1045 
writeOthersLocked()1046     private boolean writeOthersLocked() {
1047         Log.d(TAG, "writeOthersLocked");
1048 
1049         FileOutputStream fos = null;
1050         try {
1051             fos = mOthersFile.startWrite();
1052             XmlSerializer out = new FastXmlSerializer();
1053             out.setOutput(fos, "utf-8");
1054             out.startDocument(null, true);
1055             out.setFeature(XML_INDENT_OUTPUT_FEATURE, true);
1056             out.startTag(null, "services");
1057 
1058             Log.d(TAG, "writeOthersLocked: userServices.size: " + mUserServices.size());
1059             for (int i = 0; i < mUserServices.size(); i++) {
1060                 final UserServices user = mUserServices.valueAt(i);
1061                 int userId = mUserServices.keyAt(i);
1062                 // Checking for 1 times
1063                 Log.d(TAG, "writeOthersLocked: userId: " + userId);
1064                 Log.d(TAG, "writeOthersLocked: others size: " + user.others.size());
1065                 ArrayList<ComponentName> currentService = new ArrayList<ComponentName>();
1066                 for (Map.Entry<ComponentName, OtherServiceStatus> service : user.others
1067                         .entrySet()) {
1068                     Log.d(TAG, "writeOthersLocked: component: " + service.getKey().flattenToString()
1069                             + ", checked: " + service.getValue().checked);
1070 
1071                     boolean hasDupe = false;
1072                     for (ComponentName cn : currentService) {
1073                         if (cn.equals(service.getKey())) {
1074                             hasDupe = true;
1075                             break;
1076                         }
1077                     }
1078                     if (hasDupe) {
1079                         continue;
1080                     } else {
1081                         Log.d(TAG, "writeOthersLocked: Already written");
1082                         currentService.add(service.getKey());
1083                     }
1084 
1085                     out.startTag(null, "service");
1086                     out.attribute(null, "component", service.getKey().flattenToString());
1087                     out.attribute(null, "uid", Integer.toString(service.getValue().uid));
1088                     out.attribute(null, "checked", Boolean.toString(service.getValue().checked));
1089                     out.endTag(null, "service");
1090                 }
1091             }
1092             out.endTag(null, "services");
1093             out.endDocument();
1094             mOthersFile.finishWrite(fos);
1095             return true;
1096         } catch (Exception e) {
1097             Log.e(TAG, "writeOthersLocked: Error writing other status", e);
1098             if (fos != null) {
1099                 mOthersFile.failWrite(fos);
1100             }
1101             return false;
1102         }
1103     }
1104 
setOffHostSecureElement(int userId, int uid, ComponentName componentName, String offHostSE)1105     public boolean setOffHostSecureElement(int userId, int uid, ComponentName componentName,
1106             String offHostSE) {
1107         ArrayList<ApduServiceInfo> newServices = null;
1108         synchronized (mLock) {
1109             UserServices services = findOrCreateUserLocked(userId);
1110             // Check if we can find this service
1111             ApduServiceInfo serviceInfo = getService(userId, componentName);
1112             if (serviceInfo == null) {
1113                 Log.e(TAG, "setOffHostSecureElement: Service " + componentName + " does not exist");
1114                 return false;
1115             }
1116             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1117                 // This is probably a good indication something is wrong here.
1118                 // Either newer service installed with different uid (but then
1119                 // we should have known about it), or somebody calling us from
1120                 // a different uid.
1121                 Log.e(TAG, "setOffHostSecureElement: UID mismatch");
1122                 return false;
1123             }
1124             if (offHostSE == null || serviceInfo.isOnHost()) {
1125                 Log.e(TAG, "setOffHostSecureElement: OffHostSE mismatch with Service type");
1126                 return false;
1127             }
1128 
1129             DynamicSettings dynSettings = services.dynamicSettings.get(componentName);
1130             if (dynSettings == null) {
1131                 dynSettings = new DynamicSettings(serviceInfo.getUid());
1132             }
1133             dynSettings.offHostSE = offHostSE;
1134             boolean success = writeDynamicSettingsLocked();
1135             if (!success) {
1136                 Log.e(TAG, "setOffHostSecureElement: Failed to persist AID group");
1137                 dynSettings.offHostSE = null;
1138                 return false;
1139             }
1140 
1141             serviceInfo.setOffHostSecureElement(offHostSE);
1142             newServices = new ArrayList<ApduServiceInfo>(services.services.values());
1143         }
1144         // Make callback without the lock held
1145         mCallback.onServicesUpdated(userId, newServices, true);
1146         return true;
1147     }
1148 
resetOffHostSecureElement(int userId, int uid, ComponentName componentName)1149     public boolean resetOffHostSecureElement(int userId, int uid, ComponentName componentName) {
1150         ArrayList<ApduServiceInfo> newServices = null;
1151         synchronized (mLock) {
1152             UserServices services = findOrCreateUserLocked(userId);
1153             // Check if we can find this service
1154             ApduServiceInfo serviceInfo = getService(userId, componentName);
1155             if (serviceInfo == null) {
1156                 Log.e(TAG,
1157                         "resetOffHostSecureElement: Service " + componentName + " does not exist");
1158                 return false;
1159             }
1160             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1161                 // This is probably a good indication something is wrong here.
1162                 // Either newer service installed with different uid (but then
1163                 // we should have known about it), or somebody calling us from
1164                 // a different uid.
1165                 Log.e(TAG, "resetOffHostSecureElement: UID mismatch");
1166                 return false;
1167             }
1168             if (serviceInfo.isOnHost() || serviceInfo.getOffHostSecureElement() == null) {
1169                 Log.e(TAG, "resetOffHostSecureElement: OffHostSE is not set");
1170                 return false;
1171             }
1172 
1173             DynamicSettings dynSettings = services.dynamicSettings.get(componentName);
1174             String offHostSE = dynSettings.offHostSE;
1175             dynSettings.offHostSE = null;
1176             boolean success = writeDynamicSettingsLocked();
1177             if (!success) {
1178                 Log.e(TAG, "resetOffHostSecureElement: Failed to persist AID group");
1179                 dynSettings.offHostSE = offHostSE;
1180                 return false;
1181             }
1182 
1183             serviceInfo.resetOffHostSecureElement();
1184             newServices = new ArrayList<ApduServiceInfo>(services.services.values());
1185         }
1186         // Make callback without the lock held
1187         mCallback.onServicesUpdated(userId, newServices, true);
1188         return true;
1189     }
1190 
setShouldDefaultToObserveModeForService(int userId, int uid, ComponentName componentName, boolean enable)1191     public boolean setShouldDefaultToObserveModeForService(int userId, int uid,
1192             ComponentName componentName, boolean enable) {
1193         synchronized (mLock) {
1194             UserServices services = findOrCreateUserLocked(userId);
1195             // Check if we can find this service
1196             ApduServiceInfo serviceInfo = getService(userId, componentName);
1197             if (serviceInfo == null) {
1198                 Log.e(TAG, "setShouldDefaultToObserveModeForService: Service " + componentName
1199                         + " does not exist");
1200                 return false;
1201             }
1202             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1203                 // This is probably a good indication something is wrong here.
1204                 // Either newer service installed with different uid (but then
1205                 // we should have known about it), or somebody calling us from
1206                 // a different uid.
1207                 Log.e(TAG, "setShouldDefaultToObserveModeForService UID mismatch");
1208                 return false;
1209             }
1210             serviceInfo.setShouldDefaultToObserveMode(enable);
1211             DynamicSettings dynSettings = services.dynamicSettings.get(componentName);
1212             if (dynSettings == null) {
1213                 dynSettings = new DynamicSettings(serviceInfo.getUid());
1214                 dynSettings.offHostSE = null;
1215                 services.dynamicSettings.put(componentName, dynSettings);
1216             }
1217             dynSettings.shouldDefaultToObserveModeStr =  Boolean.toString(enable);
1218         }
1219         return true;
1220     }
1221 
registerPollingLoopFilterForService(int userId, int uid, ComponentName componentName, String pollingLoopFilter, boolean autoTransact)1222     public boolean registerPollingLoopFilterForService(int userId, int uid,
1223             ComponentName componentName, String pollingLoopFilter,
1224             boolean autoTransact) {
1225         ArrayList<ApduServiceInfo> newServices = null;
1226         synchronized (mLock) {
1227             UserServices services = findOrCreateUserLocked(userId);
1228             // Check if we can find this service
1229             ApduServiceInfo serviceInfo = getService(userId, componentName);
1230             if (serviceInfo == null) {
1231                 Log.e(TAG, "registerPollingLoopFilterForService: Service " + componentName
1232                         + " does not exist");
1233                 return false;
1234             }
1235             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1236                 // This is probably a good indication something is wrong here.
1237                 // Either newer service installed with different uid (but then
1238                 // we should have known about it), or somebody calling us from
1239                 // a different uid.
1240                 Log.e(TAG, "registerPollingLoopFilterForService: UID mismatch");
1241                 return false;
1242             }
1243             if (!serviceInfo.isOnHost() && !autoTransact) {
1244                 return false;
1245             }
1246             DynamicSettings dynamicSettings =
1247                 getOrCreateSettings(services, componentName, serviceInfo.getUid());
1248             dynamicSettings.pollingLoopFilters.put(pollingLoopFilter,
1249                     autoTransact);
1250             serviceInfo.addPollingLoopFilter(pollingLoopFilter, autoTransact);
1251             newServices = new ArrayList<ApduServiceInfo>(services.services.values());
1252         }
1253         mCallback.onServicesUpdated(userId, newServices, true);
1254         return true;
1255     }
1256 
removePollingLoopFilterForService(int userId, int uid, ComponentName componentName, String pollingLoopFilter)1257     public boolean removePollingLoopFilterForService(int userId, int uid,
1258             ComponentName componentName, String pollingLoopFilter) {
1259         ArrayList<ApduServiceInfo> newServices = null;
1260         synchronized (mLock) {
1261             UserServices services = findOrCreateUserLocked(userId);
1262             // Check if we can find this service
1263             ApduServiceInfo serviceInfo = getService(userId, componentName);
1264             if (serviceInfo == null) {
1265                 Log.e(TAG, "removePollingLoopFilterForService: Service " + componentName
1266                         + " does not exist");
1267                 return false;
1268             }
1269             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1270                 // This is probably a good indication something is wrong here.
1271                 // Either newer service installed with different uid (but then
1272                 // we should have known about it), or somebody calling us from
1273                 // a different uid.
1274                 Log.e(TAG, "removePollingLoopFilterForService: UID mismatch");
1275                 return false;
1276             }
1277             DynamicSettings dynamicSettings =
1278                     getOrCreateSettings(services, componentName, serviceInfo.getUid());
1279             dynamicSettings.pollingLoopFilters.remove(pollingLoopFilter);
1280             serviceInfo.removePollingLoopFilter(pollingLoopFilter);
1281             newServices = new ArrayList<ApduServiceInfo>(services.services.values());
1282         }
1283         mCallback.onServicesUpdated(userId, newServices, true);
1284         return true;
1285     }
1286 
registerPollingLoopPatternFilterForService(int userId, int uid, ComponentName componentName, String pollingLoopPatternFilter, boolean autoTransact)1287     public boolean registerPollingLoopPatternFilterForService(int userId, int uid,
1288             ComponentName componentName, String pollingLoopPatternFilter,
1289             boolean autoTransact) {
1290         ArrayList<ApduServiceInfo> newServices = null;
1291         synchronized (mLock) {
1292             UserServices services = findOrCreateUserLocked(userId);
1293             // Check if we can find this service
1294             ApduServiceInfo serviceInfo = getService(userId, componentName);
1295             if (serviceInfo == null) {
1296                 Log.e(TAG, "removePollingLoopFilterForService: Service " + componentName
1297                         + " does not exist");
1298                 return false;
1299             }
1300             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1301                 // This is probably a good indication something is wrong here.
1302                 // Either newer service installed with different uid (but then
1303                 // we should have known about it), or somebody calling us from
1304                 // a different uid.
1305                 Log.e(TAG, "removePollingLoopFilterForService: UID mismatch");
1306                 return false;
1307             }
1308             if (!serviceInfo.isOnHost() && !autoTransact) {
1309                 return false;
1310             }
1311             DynamicSettings dynamicSettings =
1312                 getOrCreateSettings(services, componentName, serviceInfo.getUid());
1313             dynamicSettings.pollingLoopPatternFilters
1314                     .put(pollingLoopPatternFilter, autoTransact);
1315             serviceInfo.addPollingLoopPatternFilter(pollingLoopPatternFilter, autoTransact);
1316             newServices = new ArrayList<ApduServiceInfo>(services.services.values());
1317         }
1318         mCallback.onServicesUpdated(userId, newServices, true);
1319         return true;
1320     }
1321 
removePollingLoopPatternFilterForService(int userId, int uid, ComponentName componentName, String pollingLoopPatternFilter)1322     public boolean removePollingLoopPatternFilterForService(int userId, int uid,
1323             ComponentName componentName, String pollingLoopPatternFilter) {
1324         ArrayList<ApduServiceInfo> newServices = null;
1325         synchronized (mLock) {
1326             UserServices services = findOrCreateUserLocked(userId);
1327             // Check if we can find this service
1328             ApduServiceInfo serviceInfo = getService(userId, componentName);
1329             if (serviceInfo == null) {
1330                 Log.e(TAG, "removePollingLoopPatternFilterForService: Service " + componentName
1331                         + " does not exist");
1332                 return false;
1333             }
1334             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1335                 // This is probably a good indication something is wrong here.
1336                 // Either newer service installed with different uid (but then
1337                 // we should have known about it), or somebody calling us from
1338                 // a different uid.
1339                 Log.e(TAG, "removePollingLoopPatternFilterForService: UID mismatch");
1340                 return false;
1341             }
1342             DynamicSettings dynamicSettings =
1343                     getOrCreateSettings(services, componentName, serviceInfo.getUid());
1344             dynamicSettings.pollingLoopPatternFilters.remove(pollingLoopPatternFilter);
1345             serviceInfo.removePollingLoopPatternFilter(pollingLoopPatternFilter);
1346             newServices = new ArrayList<ApduServiceInfo>(services.services.values());
1347         }
1348         mCallback.onServicesUpdated(userId, newServices, true);
1349         return true;
1350     }
1351 
1352 
1353 
registerAidGroupForService(int userId, int uid, ComponentName componentName, AidGroup aidGroup)1354     public boolean registerAidGroupForService(int userId, int uid,
1355             ComponentName componentName, AidGroup aidGroup) {
1356         ArrayList<ApduServiceInfo> newServices = null;
1357         boolean success;
1358         synchronized (mLock) {
1359             UserServices services = findOrCreateUserLocked(userId);
1360             // Check if we can find this service
1361             ApduServiceInfo serviceInfo = getService(userId, componentName);
1362             if (serviceInfo == null) {
1363                 Log.e(TAG,
1364                         "registerAidGroupForService: Service " + componentName + " does not exist");
1365                 return false;
1366             }
1367             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1368                 // This is probably a good indication something is wrong here.
1369                 // Either newer service installed with different uid (but then
1370                 // we should have known about it), or somebody calling us from
1371                 // a different uid.
1372                 Log.e(TAG, "registerAidGroupForService: UID mismatch");
1373                 return false;
1374             }
1375             // Do another AID validation, since a caller could have thrown in a
1376             // modified AidGroup object with invalid AIDs over Binder.
1377             List<String> aids = aidGroup.getAids();
1378             for (String aid : aids) {
1379                 if (!CardEmulation.isValidAid(aid)) {
1380                     Log.e(TAG, "registerAidGroupForService: AID " + aid + " is not a valid AID");
1381                     return false;
1382                 }
1383             }
1384             serviceInfo.setDynamicAidGroup(aidGroup);
1385             DynamicSettings dynSettings = services.dynamicSettings.get(componentName);
1386             if (dynSettings == null) {
1387                 dynSettings = new DynamicSettings(serviceInfo.getUid());
1388                 dynSettings.offHostSE = null;
1389                 services.dynamicSettings.put(componentName, dynSettings);
1390             }
1391             dynSettings.aidGroups.put(aidGroup.getCategory(), aidGroup);
1392             success = writeDynamicSettingsLocked();
1393             if (success) {
1394                 newServices =
1395                     new ArrayList<ApduServiceInfo>(services.services.values());
1396             } else {
1397                 Log.e(TAG, "registerAidGroupForService: Failed to persist AID group");
1398                 // Undo registration
1399                 dynSettings.aidGroups.remove(aidGroup.getCategory());
1400             }
1401         }
1402         if (success) {
1403             List<ApduServiceInfo> otherServices = getServicesForCategory(userId,
1404                     CardEmulation.CATEGORY_OTHER);
1405             invalidateOther(userId, otherServices);
1406             // Make callback without the lock held
1407             mCallback.onServicesUpdated(userId, newServices, true);
1408         }
1409         return success;
1410     }
1411 
registerOtherForService(int userId, ComponentName componentName, boolean checked)1412     public int registerOtherForService(int userId,
1413             ComponentName componentName, boolean checked) {
1414         if (DEBUG) {
1415             Log.d(TAG, "registerOtherForService: checked:" + checked + ", " + componentName);
1416         }
1417 
1418         ArrayList<ApduServiceInfo> newServices = null;
1419         int success = SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR;
1420 
1421         synchronized (mLock) {
1422 
1423             Log.d(TAG, "registerOtherForService: / ComponentName" + componentName);
1424             ApduServiceInfo serviceInfo = getService(userId, componentName);
1425 
1426             if (serviceInfo == null) {
1427                 Log.e(TAG, "registerOtherForService: Service " + componentName + "does not exist");
1428                 return SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE;
1429             }
1430 
1431             success = updateOtherServiceStatus(userId, serviceInfo, checked)
1432                     ? SET_SERVICE_ENABLED_STATUS_OK
1433                     : SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET;
1434 
1435             if (success == SET_SERVICE_ENABLED_STATUS_OK) {
1436                 UserServices userService = findOrCreateUserLocked(userId);
1437                 newServices = new ArrayList<ApduServiceInfo>(userService.services.values());
1438             } else {
1439                 Log.e(TAG, "registerOtherForService: Fail to other checked");
1440             }
1441         }
1442 
1443         if (success == SET_SERVICE_ENABLED_STATUS_OK) {
1444             if (DEBUG) {
1445                 Log.d(TAG, "registerOtherForService: other list update due to User Select "
1446                         + componentName);
1447             }
1448             mCallback.onServicesUpdated(userId, Collections.unmodifiableList(newServices),false);
1449         }
1450 
1451         return success;
1452     }
1453 
getAidGroupForService(int userId, int uid, ComponentName componentName, String category)1454     public AidGroup getAidGroupForService(int userId, int uid, ComponentName componentName,
1455             String category) {
1456         ApduServiceInfo serviceInfo = getService(userId, componentName);
1457         if (serviceInfo != null) {
1458             if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1459                 Log.e(TAG, "getAidGroupForService: UID mismatch");
1460                 return null;
1461             }
1462             return serviceInfo.getDynamicAidGroupForCategory(category);
1463         } else {
1464             Log.e(TAG, "getAidGroupForService: Could not find service " + componentName);
1465             return null;
1466         }
1467     }
1468 
removeAidGroupForService(int userId, int uid, ComponentName componentName, String category)1469     public boolean removeAidGroupForService(int userId, int uid, ComponentName componentName,
1470             String category) {
1471         boolean success = false;
1472         ArrayList<ApduServiceInfo> newServices = null;
1473         synchronized (mLock) {
1474             UserServices services = findOrCreateUserLocked(userId);
1475             ApduServiceInfo serviceInfo = getService(userId, componentName);
1476             if (serviceInfo != null) {
1477                 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) {
1478                     // Calling from different uid
1479                     Log.e(TAG, "removeAidGroupForService: UID mismatch");
1480                     return false;
1481                 }
1482                 if (!serviceInfo.removeDynamicAidGroupForCategory(category)) {
1483                     Log.e(TAG, "removeAidGroupForService: Could not find dynamic AIDs for category "
1484                             + category);
1485                     return false;
1486                 }
1487                 // Remove from local cache
1488                 DynamicSettings dynSettings = services.dynamicSettings.get(componentName);
1489                 if (dynSettings != null) {
1490                     AidGroup deletedGroup = dynSettings.aidGroups.remove(category);
1491                     success = writeDynamicSettingsLocked();
1492                     if (success) {
1493                         newServices = new ArrayList<ApduServiceInfo>(services.services.values());
1494                     } else {
1495                         Log.e(TAG, "removeAidGroupForService: Could not persist deleted AID group");
1496                         dynSettings.aidGroups.put(category, deletedGroup);
1497                         return false;
1498                     }
1499                 } else {
1500                     Log.e(TAG, "removeAidGroupForService: Could not find aid group in "
1501                             + "local cache");
1502                 }
1503             } else {
1504                 Log.e(TAG,
1505                         "removeAidGroupForService: Service " + componentName + " does not exist");
1506             }
1507         }
1508         if (success) {
1509             List<ApduServiceInfo> otherServices = getServicesForCategory(userId,
1510                     CardEmulation.CATEGORY_OTHER);
1511             invalidateOther(userId, otherServices);
1512             mCallback.onServicesUpdated(userId, newServices, true);
1513         }
1514         return success;
1515     }
1516 
doesServiceShouldDefaultToObserveMode(int userId, ComponentName service)1517     boolean doesServiceShouldDefaultToObserveMode(int userId, ComponentName service) {
1518         UserServices services = findOrCreateUserLocked(userId);
1519         ApduServiceInfo serviceInfo = services.services.get(service);
1520         if (serviceInfo == null) {
1521             Log.d(TAG, "removeAidGroupForService: serviceInfo is null");
1522             return false;
1523         }
1524         return serviceInfo.shouldDefaultToObserveMode();
1525     }
1526 
updateOtherServiceStatus(int userId, ApduServiceInfo service, boolean checked)1527     private boolean updateOtherServiceStatus(int userId, ApduServiceInfo service, boolean checked) {
1528         UserServices userServices = findOrCreateUserLocked(userId);
1529 
1530         OtherServiceStatus status = userServices.others.get(service.getComponent());
1531         // This is Error handling code if otherServiceStatus is null
1532         if (status == null) {
1533             Log.d(TAG, "updateOtherServiceStatus: " + service.getComponent() + " status is null");
1534             return false;
1535         }
1536 
1537         if (service.isCategoryOtherServiceEnabled() == checked) {
1538             Log.d(TAG, "updateOtherServiceStatus: already same status: " + checked);
1539             return false;
1540         }
1541 
1542         service.setCategoryOtherServiceEnabled(checked);
1543         status.checked = checked;
1544 
1545         return writeOthersLocked();
1546     }
1547 
getOrCreateSettings(UserServices services, ComponentName componentName, int uid)1548     private DynamicSettings getOrCreateSettings(UserServices services, ComponentName componentName,
1549             int uid) {
1550         if (!services.dynamicSettings.containsKey(componentName)) {
1551             services.dynamicSettings.put(componentName, new DynamicSettings(uid));
1552         }
1553         return services.dynamicSettings.get(componentName);
1554     }
1555 
dump(FileDescriptor fd, PrintWriter pw, String[] args)1556     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1557         pw.println("Registered HCE services for current user: ");
1558         ParcelFileDescriptor pFd;
1559         try {
1560             pFd = ParcelFileDescriptor.dup(fd);
1561             synchronized (mLock) {
1562                 for (UserHandle uh : mUserHandles) {
1563                     UserManager um = mContext.createContextAsUser(
1564                             uh, /*flags=*/0).getSystemService(UserManager.class);
1565                     pw.println("User " + Utils.maskSubstring(um.getUserName(), 3));
1566                     UserServices userServices = findOrCreateUserLocked(uh.getIdentifier());
1567                     for (ApduServiceInfo service : userServices.services.values()) {
1568                         service.dump(pFd, pw, args);
1569                         pw.println("");
1570                     }
1571                     pw.println("");
1572                 }
1573             }
1574             pFd.close();
1575         } catch (IOException e) {
1576             pw.println("Failed to dump HCE services: " + e);
1577         }
1578     }
1579 
1580     /**
1581      * Dump debugging information as a RegisteredServicesCacheProto
1582      *
1583      * Note:
1584      * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
1585      * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
1586      * {@link ProtoOutputStream#end(long)} after.
1587      * Never reuse a proto field number. When removing a field, mark it as reserved.
1588      */
dumpDebug(ProtoOutputStream proto)1589     void dumpDebug(ProtoOutputStream proto) {
1590         UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser());
1591         for (ApduServiceInfo service : userServices.services.values()) {
1592             long token = proto.start(RegisteredServicesCacheProto.APDU_SERVICE_INFOS);
1593             service.dumpDebug(proto);
1594             proto.end(token);
1595         }
1596     }
1597 }
1598