blob: 98df6f58f903a5791cecf869f2e4e73c10bad4d8 [file] [log] [blame]
nxpandroid64fd68c2015-09-23 16:45:15 +05301/*
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 */
nxpandroid1153eb32015-11-06 18:46:58 +053016
nxpandroid64fd68c2015-09-23 16:45:15 +053017package 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.net.Uri;
34import android.nfc.cardemulation.AidGroup;
35import android.nfc.cardemulation.ApduServiceInfo;
36import android.nfc.cardemulation.CardEmulation;
37import android.nfc.cardemulation.HostApduService;
38import android.nfc.cardemulation.OffHostApduService;
39import android.os.UserHandle;
40import android.util.AtomicFile;
41import android.util.Log;
42import android.util.SparseArray;
43import android.util.Xml;
nxpandroid34627bd2016-05-27 15:52:30 +053044import com.nxp.nfc.NxpConstants;
nxpandroid64fd68c2015-09-23 16:45:15 +053045
46import com.android.internal.util.FastXmlSerializer;
47import com.google.android.collect.Maps;
48
49import java.io.File;
50import java.io.FileDescriptor;
51import java.io.FileInputStream;
52import java.io.FileOutputStream;
53import java.io.IOException;
54import java.io.PrintWriter;
55import java.util.ArrayList;
56import java.util.Collections;
57import java.util.HashMap;
58import java.util.Iterator;
59import java.util.List;
60import java.util.Map;
61import java.util.concurrent.atomic.AtomicReference;
62import com.gsma.nfc.internal.RegisteredNxpServicesCache;
nxpandroida9a68ba2016-01-14 21:12:17 +053063import com.android.nfc.NfcService;
nxpandroid64fd68c2015-09-23 16:45:15 +053064
65/**
66 * This class is inspired by android.content.pm.RegisteredServicesCache
67 * That class was not re-used because it doesn't support dynamically
68 * registering additional properties, but generates everything from
69 * the manifest. Since we have some properties that are not in the manifest,
70 * it's less suited.
71 */
72public class RegisteredServicesCache {
73 static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output";
74 static final String TAG = "RegisteredServicesCache";
75 static final boolean DEBUG = true;
nxpandroid34627bd2016-05-27 15:52:30 +053076 static final String SERVICE_STATE_FILE_VERSION="1.0";
nxpandroid64fd68c2015-09-23 16:45:15 +053077
78 final Context mContext;
79 final AtomicReference<BroadcastReceiver> mReceiver;
80
81 final Object mLock = new Object();
82 // All variables below synchronized on mLock
83
84 // mUserServices holds the card emulation services that are running for each user
85 final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>();
86 final Callback mCallback;
87 final AtomicFile mDynamicAidsFile;
nxpandroida9a68ba2016-01-14 21:12:17 +053088 final AtomicFile mServiceStateFile;
nxpandroid64fd68c2015-09-23 16:45:15 +053089 //public ArrayList<ApduServiceInfo> mAllServices = new ArrayList<ApduServiceInfo>();
90 final HashMap<ComponentName, ApduServiceInfo> mAllServices = Maps.newHashMap();
nxpandroidfc2aab82017-04-10 18:19:30 +053091 /*Installed service will be used to load all the registered services available in the device
92 *key : UID corresponding to the service - owner of the service
93 *value : Hashmap of service component and corresponding state
94 * */
95 HashMap<String, HashMap<ComponentName, Integer>> installedServices = new HashMap<>();
nxpandroid64fd68c2015-09-23 16:45:15 +053096
97 private RegisteredNxpServicesCache mRegisteredNxpServicesCache;
98
99 public interface Callback {
100 void onServicesUpdated(int userId, final List<ApduServiceInfo> services);
101 };
102
103 static class DynamicAids {
104 public final int uid;
105 public final HashMap<String, AidGroup> aidGroups = Maps.newHashMap();
106
107 DynamicAids(int uid) {
108 this.uid = uid;
109 }
110 };
111
112 private static class UserServices {
113 /**
114 * All services that have registered
115 */
116 final HashMap<ComponentName, ApduServiceInfo> services =
117 Maps.newHashMap(); // Re-built at run-time
118 final HashMap<ComponentName, DynamicAids> dynamicAids =
119 Maps.newHashMap(); // In memory cache of dynamic AID store
120 };
121
122 private UserServices findOrCreateUserLocked(int userId) {
123 UserServices services = mUserServices.get(userId);
124 if (services == null) {
125 services = new UserServices();
126 mUserServices.put(userId, services);
127 }
128 return services;
129 }
130
131 public RegisteredServicesCache(Context context, Callback callback) {
132 mContext = context;
133 mCallback = callback;
134
135 final BroadcastReceiver receiver = new BroadcastReceiver() {
136 @Override
137 public void onReceive(Context context, Intent intent) {
138 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
139 String action = intent.getAction();
140 if (DEBUG) Log.d(TAG, "Intent action: " + action);
141 if (uid != -1) {
nxpandroid5d64ce92016-11-18 19:48:53 +0530142 int currentUser = ActivityManager.getCurrentUser();
143 if (currentUser == UserHandle.getUserId(uid)) {
nxpandroid64fd68c2015-09-23 16:45:15 +0530144 if(Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
145 Uri uri = intent.getData();
146 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
147 mRegisteredNxpServicesCache.onPackageRemoved(pkg); //GSMA changes
148 mRegisteredNxpServicesCache.writeDynamicApduService();
149 }
nxpandroid5d64ce92016-11-18 19:48:53 +0530150 boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) &&
151 (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
152 Intent.ACTION_PACKAGE_REMOVED.equals(action));
153 if (!replaced) {
154 invalidateCache(UserHandle.getUserId(uid));
nxpandroid64fd68c2015-09-23 16:45:15 +0530155 } else {
nxpandroid5d64ce92016-11-18 19:48:53 +0530156 if (DEBUG) Log.d(TAG, "Ignoring package intent due to package being replaced.");
nxpandroid64fd68c2015-09-23 16:45:15 +0530157 }
158 } else {
nxpandroid5d64ce92016-11-18 19:48:53 +0530159 // Cache will automatically be updated on user switch
nxpandroid64fd68c2015-09-23 16:45:15 +0530160 }
161 }
162 }
163 };
164 mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
165
166 IntentFilter intentFilter = new IntentFilter();
167 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
168 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
169 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
170 intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
171 intentFilter.addAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH);
172 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
173 intentFilter.addDataScheme("package");
174 mContext.registerReceiverAsUser(mReceiver.get(), UserHandle.ALL, intentFilter, null, null);
175
176 // Register for events related to sdcard operations
177 IntentFilter sdFilter = new IntentFilter();
178 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
179 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
180 mContext.registerReceiverAsUser(mReceiver.get(), UserHandle.ALL, sdFilter, null, null);
181
182 File dataDir = mContext.getFilesDir();
183 mDynamicAidsFile = new AtomicFile(new File(dataDir, "dynamic_aids.xml"));
nxpandroida9a68ba2016-01-14 21:12:17 +0530184 mServiceStateFile = new AtomicFile(new File(dataDir, "service_state.xml"));
nxpandroid64fd68c2015-09-23 16:45:15 +0530185 }
186
187 void initialize(RegisteredNxpServicesCache registeredNxpServicesCache) {
188 mRegisteredNxpServicesCache = registeredNxpServicesCache;
189 synchronized (mLock) {
190 readDynamicAidsLocked();
191 mRegisteredNxpServicesCache.readDynamicApduService();
192 }
193 invalidateCache(ActivityManager.getCurrentUser());
194 }
195
196 void dump(ArrayList<ApduServiceInfo> services) {
197 for (ApduServiceInfo service : services) {
198 if (DEBUG) Log.d(TAG, service.toString());
199 }
200 }
201
202 boolean containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName) {
203 for (ApduServiceInfo service : services) {
204 if (service.getComponent().equals(serviceName)) return true;
205 }
206 return false;
207 }
208
209 public boolean hasService(int userId, ComponentName service) {
210 return getService(userId, service) != null;
211 }
212
213 public ApduServiceInfo getService(int userId, ComponentName service) {
214 synchronized (mLock) {
215 UserServices userServices = findOrCreateUserLocked(userId);
216 return userServices.services.get(service);
217 }
218 }
219
220 public List<ApduServiceInfo> getServices(int userId) {
221 final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>();
222 synchronized (mLock) {
223 UserServices userServices = findOrCreateUserLocked(userId);
224 services.addAll(userServices.services.values());
225 }
226 return services;
227 }
228
229 public List<ApduServiceInfo> getServicesForCategory(int userId, String category) {
230 final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>();
231 synchronized (mLock) {
232 UserServices userServices = findOrCreateUserLocked(userId);
233 for (ApduServiceInfo service : userServices.services.values()) {
nxpandroidebf53fb2016-12-22 18:48:59 +0530234 if (service.hasCategory(category) &&
235 (service.getAidCacheSizeForCategory(category) > 0)) services.add(service);
nxpandroid64fd68c2015-09-23 16:45:15 +0530236 }
237 }
238 return services;
239 }
240
241 ArrayList<ApduServiceInfo> getInstalledServices(int userId) {
242 PackageManager pm;
243 try {
244 pm = mContext.createPackageContextAsUser("android", 0,
245 new UserHandle(userId)).getPackageManager();
246 } catch (NameNotFoundException e) {
247 Log.e(TAG, "Could not create user package context");
248 return null;
249 }
250 mAllServices.clear();
251 ArrayList<ApduServiceInfo> validServices = new ArrayList<ApduServiceInfo>();
252
253 List<ResolveInfo> resolvedServices = pm.queryIntentServicesAsUser(
254 new Intent(HostApduService.SERVICE_INTERFACE),
255 PackageManager.GET_META_DATA, userId);
256
257 List<ResolveInfo> resolvedOffHostServices = pm.queryIntentServicesAsUser(
258 new Intent(OffHostApduService.SERVICE_INTERFACE),
259 PackageManager.GET_META_DATA, userId);
260 resolvedServices.addAll(resolvedOffHostServices);
261
262 for (ResolveInfo resolvedService : resolvedServices) {
263 try {
264 boolean onHost = !resolvedOffHostServices.contains(resolvedService);
265 ServiceInfo si = resolvedService.serviceInfo;
266 ComponentName componentName = new ComponentName(si.packageName, si.name);
267 // Check if the package holds the NFC permission
268 if (pm.checkPermission(android.Manifest.permission.NFC, si.packageName) !=
269 PackageManager.PERMISSION_GRANTED) {
270 Log.e(TAG, "Skipping APDU service " + componentName +
271 ": it does not require the permission " +
272 android.Manifest.permission.NFC);
273 continue;
274 }
275 if (!android.Manifest.permission.BIND_NFC_SERVICE.equals(
276 si.permission)) {
277 Log.e(TAG, "Skipping APDU service " + componentName +
278 ": it does not require the permission " +
279 android.Manifest.permission.BIND_NFC_SERVICE);
280 continue;
281 }
282 ApduServiceInfo service = new ApduServiceInfo(pm, resolvedService, onHost);
283 if (service != null) {
284 validServices.add(service);
285 if(!onHost)
286 mAllServices.put(componentName, service);
287 }
288 } catch (XmlPullParserException e) {
289 Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e);
290 } catch (IOException e) {
291 Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e);
292 }
293 }
294 AddGsmaServices(validServices);
295 return validServices;
296 }
297
298 public ArrayList<ApduServiceInfo> getAllServices() {
299 return new ArrayList<ApduServiceInfo>(mAllServices.values());//mAllServices;
300 }
301
302 public HashMap<ComponentName, ApduServiceInfo> getAllStaticHashServices() {
303 return mAllServices;
304 }
305
306//Adding the GSMA Services to the Service List
307 private void AddGsmaServices(ArrayList<ApduServiceInfo> validServices){
308 validServices.addAll(mRegisteredNxpServicesCache.getApduservicesList());
309 }
310
311 public void invalidateCache(int userId) {
312 final ArrayList<ApduServiceInfo> validServices = getInstalledServices(userId);
313 if (validServices == null) {
314 return;
315 }
316 synchronized (mLock) {
317 UserServices userServices = findOrCreateUserLocked(userId);
318
319 // Find removed services
320 Iterator<Map.Entry<ComponentName, ApduServiceInfo>> it =
321 userServices.services.entrySet().iterator();
322 while (it.hasNext()) {
323 Map.Entry<ComponentName, ApduServiceInfo> entry =
324 (Map.Entry<ComponentName, ApduServiceInfo>) it.next();
325 if (!containsServiceLocked(validServices, entry.getKey())) {
326 Log.d(TAG, "Service removed: " + entry.getKey());
327 it.remove();
328 }
329 }
330 for (ApduServiceInfo service : validServices) {
331 if (DEBUG) Log.d(TAG, "Adding service: " + service.getComponent() +
332 " AIDs: " + service.getAids());
333 userServices.services.put(service.getComponent(), service);
334 }
335
336 // Apply dynamic AID mappings
337 ArrayList<ComponentName> toBeRemoved = new ArrayList<ComponentName>();
338 for (Map.Entry<ComponentName, DynamicAids> entry :
339 userServices.dynamicAids.entrySet()) {
340 // Verify component / uid match
341 ComponentName component = entry.getKey();
342 DynamicAids dynamicAids = entry.getValue();
343 ApduServiceInfo serviceInfo = userServices.services.get(component);
344 if (serviceInfo == null || (serviceInfo.getUid() != dynamicAids.uid)) {
345 toBeRemoved.add(component);
346 continue;
347 } else {
348 for (AidGroup group : dynamicAids.aidGroups.values()) {
349 serviceInfo.setOrReplaceDynamicAidGroup(group);
350 }
351 }
352 }
353
354 if (toBeRemoved.size() > 0) {
355 for (ComponentName component : toBeRemoved) {
356 Log.d(TAG, "Removing dynamic AIDs registered by " + component);
357 userServices.dynamicAids.remove(component);
358 }
359 // Persist to filesystem
360 writeDynamicAidsLocked();
361 }
nxpandroida9a68ba2016-01-14 21:12:17 +0530362 updateServiceStateFromFile(userId);
nxpandroid34627bd2016-05-27 15:52:30 +0530363 Log.e(TAG,"1"+Thread.currentThread().getStackTrace()[2].getMethodName()+":WriteServiceStateToFile");
nxpandroida9a68ba2016-01-14 21:12:17 +0530364 writeServiceStateToFile(userId);
nxpandroid64fd68c2015-09-23 16:45:15 +0530365 }
366
367 mCallback.onServicesUpdated(userId, Collections.unmodifiableList(validServices));
368 dump(validServices);
369 }
370
371 private void readDynamicAidsLocked() {
372 FileInputStream fis = null;
373 try {
374 if (!mDynamicAidsFile.getBaseFile().exists()) {
375 Log.d(TAG, "Dynamic AIDs file does not exist.");
376 return;
377 }
378 fis = mDynamicAidsFile.openRead();
379 XmlPullParser parser = Xml.newPullParser();
380 parser.setInput(fis, null);
381 int eventType = parser.getEventType();
382 while (eventType != XmlPullParser.START_TAG &&
383 eventType != XmlPullParser.END_DOCUMENT) {
384 eventType = parser.next();
385 }
386 String tagName = parser.getName();
387 if ("services".equals(tagName)) {
388 boolean inService = false;
389 ComponentName currentComponent = null;
390 int currentUid = -1;
391 ArrayList<AidGroup> currentGroups = new ArrayList<AidGroup>();
392 while (eventType != XmlPullParser.END_DOCUMENT) {
393 tagName = parser.getName();
394 if (eventType == XmlPullParser.START_TAG) {
395 if ("service".equals(tagName) && parser.getDepth() == 2) {
396 String compString = parser.getAttributeValue(null, "component");
397 String uidString = parser.getAttributeValue(null, "uid");
398 if (compString == null || uidString == null) {
399 Log.e(TAG, "Invalid service attributes");
400 } else {
401 try {
402 currentUid = Integer.parseInt(uidString);
403 currentComponent = ComponentName.unflattenFromString(compString);
404 inService = true;
405 } catch (NumberFormatException e) {
406 Log.e(TAG, "Could not parse service uid");
407 }
408 }
409 }
410 if ("aid-group".equals(tagName) && parser.getDepth() == 3 && inService) {
411 AidGroup group = AidGroup.createFromXml(parser);
412 if (group != null) {
413 currentGroups.add(group);
414 } else {
415 Log.e(TAG, "Could not parse AID group.");
416 }
417 }
418 } else if (eventType == XmlPullParser.END_TAG) {
419 if ("service".equals(tagName)) {
420 // See if we have a valid service
421 if (currentComponent != null && currentUid >= 0 &&
422 currentGroups.size() > 0) {
423 final int userId = UserHandle.getUserId(currentUid);
424 DynamicAids dynAids = new DynamicAids(currentUid);
425 for (AidGroup group : currentGroups) {
426 dynAids.aidGroups.put(group.getCategory(), group);
427 }
428 UserServices services = findOrCreateUserLocked(userId);
429 services.dynamicAids.put(currentComponent, dynAids);
430 }
431 currentUid = -1;
432 currentComponent = null;
433 currentGroups.clear();
434 inService = false;
435 }
436 }
437 eventType = parser.next();
438 };
439 }
440 } catch (Exception e) {
441 Log.e(TAG, "Could not parse dynamic AIDs file, trashing.");
442 mDynamicAidsFile.delete();
443 } finally {
444 if (fis != null) {
445 try {
446 fis.close();
447 } catch (IOException e) {
448 }
449 }
450 }
451 }
452
453 private boolean writeDynamicAidsLocked() {
454 FileOutputStream fos = null;
455 try {
456 fos = mDynamicAidsFile.startWrite();
457 XmlSerializer out = new FastXmlSerializer();
458 out.setOutput(fos, "utf-8");
459 out.startDocument(null, true);
460 out.setFeature(XML_INDENT_OUTPUT_FEATURE, true);
461 out.startTag(null, "services");
462 for (int i = 0; i < mUserServices.size(); i++) {
463 final UserServices user = mUserServices.valueAt(i);
464 for (Map.Entry<ComponentName, DynamicAids> service : user.dynamicAids.entrySet()) {
465 out.startTag(null, "service");
466 out.attribute(null, "component", service.getKey().flattenToString());
467 out.attribute(null, "uid", Integer.toString(service.getValue().uid));
468 for (AidGroup group : service.getValue().aidGroups.values()) {
469 group.writeAsXml(out);
470 }
471 out.endTag(null, "service");
472 }
473 }
474 out.endTag(null, "services");
475 out.endDocument();
476 mDynamicAidsFile.finishWrite(fos);
477 return true;
478 } catch (Exception e) {
479 Log.e(TAG, "Error writing dynamic AIDs", e);
480 if (fos != null) {
481 mDynamicAidsFile.failWrite(fos);
482 }
483 return false;
484 }
485 }
486
nxpandroida9a68ba2016-01-14 21:12:17 +0530487 private void updateServiceStateFromFile(int currUserId)
488 {
489 FileInputStream fis = null;
490 try {
nxpandroid34627bd2016-05-27 15:52:30 +0530491 /*if(NfcService.getInstance().getAidRoutingTableStatus() == 0x00) {
nxpandroida9a68ba2016-01-14 21:12:17 +0530492 Log.e(TAG, " Aid Routing Table still availble , No need to disable services");
493 return;
nxpandroid34627bd2016-05-27 15:52:30 +0530494 }*/
495 Log.d(TAG, " Reading service state data always from file");
nxpandroida9a68ba2016-01-14 21:12:17 +0530496 if(!mServiceStateFile.getBaseFile().exists()) {
497 Log.d(TAG,"mServiceStateFile does not exist");
498 return;
499 }
500 fis = mServiceStateFile.openRead();
501 XmlPullParser parser = Xml.newPullParser();
502 parser.setInput(fis , null);
503 int eventType = parser.getEventType();
504 int currUid = -1;
505 ComponentName currComponent = null;
506 HashMap<ComponentName ,ApduServiceInfo> nxpOffHostServiceMap = mRegisteredNxpServicesCache.getApduservicesMaps();
nxpandroid34627bd2016-05-27 15:52:30 +0530507 int state = NxpConstants.SERVICE_STATE_ENABLED;
nxpandroidfc2aab82017-04-10 18:19:30 +0530508
nxpandroida9a68ba2016-01-14 21:12:17 +0530509 while (eventType != XmlPullParser.START_TAG &&
510 eventType != XmlPullParser.END_DOCUMENT) {
511 eventType = parser.next();
512 }
513 String tagName = parser.getName();
nxpandroid34627bd2016-05-27 15:52:30 +0530514 String fileVersion = "null";
515 /**
516 * Get the version of the Service state file.
517 * if the version is 1.0, service states are stored as integers(0,1,2,3)
518 * or else service states are stored as boolean (true or false)
519 */
520 if("Version".equals(tagName)){
521 fileVersion = parser.getAttributeValue(null ,"FileVersion");
522 Log.d(TAG, "ServiceStateFileVersion="+fileVersion);
523 eventType = parser.next();
524 while (eventType != XmlPullParser.START_TAG &&
525 eventType != XmlPullParser.END_DOCUMENT) {
526 eventType = parser.next();
527 }
528 tagName = parser.getName();
529 Log.e(TAG, "Next Tag="+tagName);
530 }
nxpandroida9a68ba2016-01-14 21:12:17 +0530531 if ("services".equals(tagName)) {
532 while (eventType != XmlPullParser.END_DOCUMENT) {
533 tagName = parser.getName();
534 if (eventType == XmlPullParser.START_TAG) {
535 if("service".equals(tagName) && parser.getDepth() == 0x02) {
536 String compString = parser.getAttributeValue(null ,"component");
537 String uidString = parser.getAttributeValue(null ,"uid");
538 String stateString = parser.getAttributeValue(null ,"serviceState");
nxpandroidfc2aab82017-04-10 18:19:30 +0530539
nxpandroida9a68ba2016-01-14 21:12:17 +0530540 if(compString == null || uidString == null || stateString == null) {
541 Log.e(TAG, "Invalid service attributes");
542 } else {
543 try {
544 currUid = Integer.parseInt(uidString);
545 currComponent = ComponentName.unflattenFromString(compString);
546 Log.d(TAG, " curr component "+compString);
547 Log.d(TAG, " curr uid "+uidString);
548 Log.d(TAG, " curr state "+stateString);
nxpandroidfc2aab82017-04-10 18:19:30 +0530549
nxpandroid34627bd2016-05-27 15:52:30 +0530550 if(fileVersion.equals("null")){
551 if(stateString.equalsIgnoreCase("false"))
552 state = NxpConstants.SERVICE_STATE_DISABLED;
553 else
554 state = NxpConstants.SERVICE_STATE_ENABLED;
555 }else if(fileVersion.equals("1.0")){
556 state = Integer.parseInt(stateString);
557 if(state<NxpConstants.SERVICE_STATE_DISABLED || state > NxpConstants.SERVICE_STATE_DISABLING)
558 Log.e(TAG, "Invalid Service state");
559 }
nxpandroidfc2aab82017-04-10 18:19:30 +0530560 /*Load all the servies info into local memory from xml file and
561 *later update the xml file with updated information
562 *This way it can retain previous user's information even after switching to different user
563 * */
564 if(installedServices.containsKey(uidString))
565 {
566 Log.e(TAG, "installedServices contains uidString : " +uidString);
567 HashMap<ComponentName, Integer> componentStates;
568 componentStates = installedServices.get(uidString);
569 componentStates.put(currComponent,state);
570 }else
571 {
572 Log.e(TAG, "installedServices no uidString ");
573 HashMap<ComponentName, Integer> componentStates = new HashMap<>();
574 componentStates.put(currComponent,state);
575 installedServices.put(uidString,componentStates);
576 }
577
nxpandroida9a68ba2016-01-14 21:12:17 +0530578 } catch (NumberFormatException e) {
579 Log.e(TAG, "could not parse the service attributes");
580 }
581 }
582 }
583 } else if (eventType == XmlPullParser.END_TAG) {
584 if("service".equals(tagName)) {
585 final int userId = UserHandle.getUserId(currUid);
nxpandroidfc2aab82017-04-10 18:19:30 +0530586
nxpandroida9a68ba2016-01-14 21:12:17 +0530587 UserServices serviceCache = findOrCreateUserLocked(userId);
588 ApduServiceInfo serviceInfo = serviceCache.services.get(currComponent);
nxpandroidfc2aab82017-04-10 18:19:30 +0530589
nxpandroida9a68ba2016-01-14 21:12:17 +0530590 if(serviceInfo == null) {
591 // CHECK for GSMA related services also.
592 serviceInfo = nxpOffHostServiceMap.get(currComponent);
593 if(serviceInfo == null) {
594 Log.e(TAG, "could not find the required serviceInfo");
595 } else serviceInfo.setServiceState(CardEmulation.CATEGORY_OTHER ,state);
596 } else serviceInfo.setServiceState(CardEmulation.CATEGORY_OTHER ,state);
nxpandroida9a68ba2016-01-14 21:12:17 +0530597 }
598 currUid = -1;
599 currComponent = null;
nxpandroid34627bd2016-05-27 15:52:30 +0530600 state = NxpConstants.SERVICE_STATE_ENABLED;
nxpandroida9a68ba2016-01-14 21:12:17 +0530601 }
602
603 eventType = parser.next();
604 }
605 }
606 } catch(Exception e) {
607 mServiceStateFile.delete();
nxpandroidfc2aab82017-04-10 18:19:30 +0530608 Log.e(TAG, "could not parse the seriveState file , thrashing the file " + e);
nxpandroida9a68ba2016-01-14 21:12:17 +0530609 } finally {
610 try {
611 if(fis != null) {
612 fis.close();
613 }
614 } catch ( Exception e) {
615 }
616 }
617 }
618
619 private boolean writeServiceStateToFile(int currUserId) {
620 FileOutputStream fos = null;
621 ArrayList<ApduServiceInfo> nxpOffHostServiceCache = mRegisteredNxpServicesCache.getApduservicesList();
nxpandroid34627bd2016-05-27 15:52:30 +0530622 /*if(NfcService.getInstance().getAidRoutingTableStatus() == 0x00) {
nxpandroida9a68ba2016-01-14 21:12:17 +0530623 Log.e(TAG, " Aid Routing Table still availble , No need to disable services");
624 return false;
nxpandroid34627bd2016-05-27 15:52:30 +0530625 }*/
626 Log.e(TAG, " Writing service state Data Always");
nxpandroida9a68ba2016-01-14 21:12:17 +0530627 if(currUserId != ActivityManager.getCurrentUser()) {
628 return false;
629 }
nxpandroidfc2aab82017-04-10 18:19:30 +0530630 int state = NxpConstants.SERVICE_STATE_ENABLED;
nxpandroida9a68ba2016-01-14 21:12:17 +0530631 try {
632 fos = mServiceStateFile.startWrite();
633 XmlSerializer out = new FastXmlSerializer();
634 out.setOutput(fos, "utf-8");
635 out.startDocument(null , true);
636 out.setFeature(XML_INDENT_OUTPUT_FEATURE, true);
nxpandroid34627bd2016-05-27 15:52:30 +0530637 out.startTag(null ,"Version");
638 out.attribute(null, "FileVersion", SERVICE_STATE_FILE_VERSION);
639 out.endTag(null ,"Version");
nxpandroida9a68ba2016-01-14 21:12:17 +0530640 out.startTag(null ,"services");
641 for(int userId = 0; userId < mUserServices.size(); userId++) {
642 final UserServices userServices = mUserServices.valueAt(userId);
643 for (ApduServiceInfo serviceInfo : userServices.services.values()) {
644 if(!serviceInfo.hasCategory(CardEmulation.CATEGORY_OTHER)) {
645 continue;
646 }
647 out.startTag(null ,"service");
648 out.attribute(null, "component", serviceInfo.getComponent().flattenToString());
nxpandroid34627bd2016-05-27 15:52:30 +0530649 Log.e(TAG,"component name"+ serviceInfo.getComponent().flattenToString());
nxpandroida9a68ba2016-01-14 21:12:17 +0530650 out.attribute(null, "uid", Integer.toString(serviceInfo.getUid()));
nxpandroidfc2aab82017-04-10 18:19:30 +0530651
652 boolean isServiceInstalled = false;
653 if(installedServices.containsKey(Integer.toString(serviceInfo.getUid()))){
654 HashMap<ComponentName, Integer> componentStates = installedServices.get(Integer.toString(serviceInfo.getUid()));
655 if (componentStates.containsKey(serviceInfo.getComponent())) {
656 state = componentStates.get(serviceInfo.getComponent());
657 componentStates.remove(serviceInfo.getComponent());
658 if(componentStates.isEmpty())
659 {
660 installedServices.remove(Integer.toString(serviceInfo.getUid()));
661 }
662 isServiceInstalled = true;
663 }
664 }
665 if (!isServiceInstalled) {
666 state = serviceInfo.getServiceState(CardEmulation.CATEGORY_OTHER);
667 }
668 out.attribute(null, "serviceState", Integer.toString(state));
nxpandroida9a68ba2016-01-14 21:12:17 +0530669 out.endTag(null, "service");
670 }
671 }
672 dump(nxpOffHostServiceCache);
673 //ADD GSMA services Cache
674 for(ApduServiceInfo serviceInfo : nxpOffHostServiceCache) {
675 out.startTag(null ,"service");
676 out.attribute(null, "component", serviceInfo.getComponent().flattenToString());
677 Log.d(TAG,"component name"+ serviceInfo.getComponent().flattenToString());
678 out.attribute(null, "uid", Integer.toString(serviceInfo.getUid()));
679 Log.d(TAG,"uid name"+ Integer.toString(serviceInfo.getUid()));
nxpandroidfc2aab82017-04-10 18:19:30 +0530680
681 boolean isServiceInstalled = false;
682 if(installedServices.containsKey(Integer.toString(serviceInfo.getUid()))){
683 HashMap<ComponentName, Integer> componentStates = installedServices.get(Integer.toString(serviceInfo.getUid()));
684 if (componentStates.containsKey(serviceInfo.getComponent())) {
685 state = componentStates.get(serviceInfo.getComponent());
686 componentStates.remove(serviceInfo.getComponent());
687 if(componentStates.isEmpty())
688 {
689 installedServices.remove(Integer.toString(serviceInfo.getUid()));
690 }
691 isServiceInstalled = true;
692 }
693 }
694 if (!isServiceInstalled) {
695 state = serviceInfo.getServiceState(CardEmulation.CATEGORY_OTHER);
696 }
697 out.attribute(null, "serviceState", Integer.toString(state));
698 Log.d(TAG,"service State:"+ Integer.toString(state));
nxpandroida9a68ba2016-01-14 21:12:17 +0530699 out.endTag(null, "service");
700 }
701 out.endTag(null ,"services");
702 out.endDocument();
703 mServiceStateFile.finishWrite(fos);
704 return true;
705 } catch ( Exception e){
706 Log.e(TAG,"Failed to write serviceStateFile xml");
707 e.printStackTrace();
708 if (fos != null) {
709 mServiceStateFile.failWrite(fos);
710 }
711 return false;
712 }
713 }
714
715 public int updateServiceState(int userId , int uid,
716 Map<String , Boolean> serviceState) {
717 boolean success = false;
718 HashMap<ComponentName ,ApduServiceInfo> nxpOffHostServiceMap = mRegisteredNxpServicesCache.getApduservicesMaps();
719 if(NfcService.getInstance().getAidRoutingTableStatus() == 0x00) {
720 Log.e(TAG, " Aid Routing Table still availble , No need to disable services");
721 return 0xFF;
722 }
723 synchronized(mLock) {
724 Iterator<Map.Entry<String , Boolean>> it =
725 serviceState.entrySet().iterator();
726 while(it.hasNext()) {
727 Map.Entry<String , Boolean> entry =
728 (Map.Entry<String , Boolean>) it.next();
729 ComponentName componentName = ComponentName.unflattenFromString(entry.getKey());
730 ApduServiceInfo serviceInfo = getService(userId, componentName);
731 Log.e(TAG, "updateServiceState " + entry.getKey());
732 Log.e(TAG, "updateServiceState " + entry.getValue());
733 if (serviceInfo != null) {
nxpandroid34627bd2016-05-27 15:52:30 +0530734 serviceInfo.enableService(CardEmulation.CATEGORY_OTHER, entry.getValue());
nxpandroida9a68ba2016-01-14 21:12:17 +0530735 } else if ((serviceInfo = nxpOffHostServiceMap.get(componentName)) != null) {
736 // CHECK for GSMA cache
nxpandroid34627bd2016-05-27 15:52:30 +0530737 serviceInfo.enableService(CardEmulation.CATEGORY_OTHER, entry.getValue());
nxpandroida9a68ba2016-01-14 21:12:17 +0530738 } else {
739 Log.e(TAG, "Could not find service " + componentName);
740 return 0xFF;
741 }
742 }
nxpandroid34627bd2016-05-27 15:52:30 +0530743 Log.e(TAG,"2"+Thread.currentThread().getStackTrace()[2].getMethodName()+":WriteServiceStateToFile");
nxpandroida9a68ba2016-01-14 21:12:17 +0530744 success = writeServiceStateToFile(userId);
745 }
746 invalidateCache(ActivityManager.getCurrentUser());
747 return (success?0x00:0xFF);
748 }
749
nxpandroid64fd68c2015-09-23 16:45:15 +0530750 public boolean registerAidGroupForService(int userId, int uid,
751 ComponentName componentName, AidGroup aidGroup) {
752 ArrayList<ApduServiceInfo> newServices = null;
753 boolean success;
754 synchronized (mLock) {
755 UserServices services = findOrCreateUserLocked(userId);
756 // Check if we can find this service
757 ApduServiceInfo serviceInfo = getService(userId, componentName);
758 if (serviceInfo == null) {
759 Log.e(TAG, "Service " + componentName + " does not exist.");
760 return false;
761 }
762 if (serviceInfo.getUid() != uid) {
763 // This is probably a good indication something is wrong here.
764 // Either newer service installed with different uid (but then
765 // we should have known about it), or somebody calling us from
766 // a different uid.
767 Log.e(TAG, "UID mismatch.");
768 return false;
769 }
770 // Do another AID validation, since a caller could have thrown in a modified
771 // AidGroup object with invalid AIDs over Binder.
772 List<String> aids = aidGroup.getAids();
773 for (String aid : aids) {
774 if (!CardEmulation.isValidAid(aid)) {
775 Log.e(TAG, "AID " + aid + " is not a valid AID");
776 return false;
777 }
778 }
779 serviceInfo.setOrReplaceDynamicAidGroup(aidGroup);
780 DynamicAids dynAids = services.dynamicAids.get(componentName);
781 if (dynAids == null) {
782 dynAids = new DynamicAids(uid);
783 services.dynamicAids.put(componentName, dynAids);
784 }
785 dynAids.aidGroups.put(aidGroup.getCategory(), aidGroup);
786 success = writeDynamicAidsLocked();
787 if (success) {
788 newServices = new ArrayList<ApduServiceInfo>(services.services.values());
789 } else {
790 Log.e(TAG, "Failed to persist AID group.");
791 // Undo registration
792 dynAids.aidGroups.remove(aidGroup.getCategory());
793 }
794 }
795 if (success) {
796 // Make callback without the lock held
797 mCallback.onServicesUpdated(userId, newServices);
798 }
799 return success;
800 }
801
802 public AidGroup getAidGroupForService(int userId, int uid, ComponentName componentName,
803 String category) {
804 ApduServiceInfo serviceInfo = getService(userId, componentName);
805 if (serviceInfo != null) {
806 if (serviceInfo.getUid() != uid) {
807 Log.e(TAG, "UID mismatch");
808 return null;
809 }
810 return serviceInfo.getDynamicAidGroupForCategory(category);
811 } else {
812 Log.e(TAG, "Could not find service " + componentName);
813 return null;
814 }
815 }
816
817 public boolean removeAidGroupForService(int userId, int uid, ComponentName componentName,
818 String category) {
819 boolean success = false;
820 ArrayList<ApduServiceInfo> newServices = null;
821 synchronized (mLock) {
822 UserServices services = findOrCreateUserLocked(userId);
823 ApduServiceInfo serviceInfo = getService(userId, componentName);
824 if (serviceInfo != null) {
825 if (serviceInfo.getUid() != uid) {
826 // Calling from different uid
827 Log.e(TAG, "UID mismatch");
828 return false;
829 }
830 if (!serviceInfo.removeDynamicAidGroupForCategory(category)) {
831 Log.e(TAG," Could not find dynamic AIDs for category " + category);
832 return false;
833 }
834 // Remove from local cache
835 DynamicAids dynAids = services.dynamicAids.get(componentName);
836 if (dynAids != null) {
837 AidGroup deletedGroup = dynAids.aidGroups.remove(category);
838 success = writeDynamicAidsLocked();
839 if (success) {
840 newServices = new ArrayList<ApduServiceInfo>(services.services.values());
841 } else {
842 Log.e(TAG, "Could not persist deleted AID group.");
843 dynAids.aidGroups.put(category, deletedGroup);
844 return false;
845 }
846 } else {
847 Log.e(TAG, "Could not find aid group in local cache.");
848 }
849 } else {
850 Log.e(TAG, "Service " + componentName + " does not exist.");
851 }
852 }
853 if (success) {
854 mCallback.onServicesUpdated(userId, newServices);
855 }
856 return success;
857 }
858
859 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
860 pw.println("Registered HCE services for current user: ");
861 UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser());
862 for (ApduServiceInfo service : userServices.services.values()) {
863 service.dump(fd, pw, args);
864 pw.println("");
865 }
866 pw.println("");
867 }
868
nxpandroid34627bd2016-05-27 15:52:30 +0530869 public void updateStatusOfServices(boolean commitStatus) {
870 final UserServices userServices = mUserServices.get(ActivityManager.getCurrentUser());
871 for (ApduServiceInfo serviceInfo : userServices.services.values()) {
872 if(!serviceInfo.hasCategory(CardEmulation.CATEGORY_OTHER)) {
873 continue;
874 }
875 serviceInfo.updateServiceCommitStatus(CardEmulation.CATEGORY_OTHER,commitStatus);
876 }
877 Log.e(TAG,"3"+Thread.currentThread().getStackTrace()[2].getMethodName()+":WriteServiceStateToFile");
878 writeServiceStateToFile(ActivityManager.getCurrentUser());
879 }
nxpandroid64fd68c2015-09-23 16:45:15 +0530880}