blob: 64c2bc2bb9769dbd410e3c781200d1b6863409cd [file] [log] [blame]
Martijn Coenen52246082013-08-30 11:14:46 -07001/*
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
17package android.nfc.cardemulation;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
Martijn Coenen2f6f3a012014-04-25 17:00:21 -070021import android.app.Activity;
Martijn Coenen52246082013-08-30 11:14:46 -070022import android.app.ActivityThread;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.pm.IPackageManager;
26import android.content.pm.PackageManager;
27import android.nfc.INfcCardEmulation;
28import android.nfc.NfcAdapter;
29import android.os.RemoteException;
30import android.os.UserHandle;
31import android.provider.Settings;
Martijn Coenen2f6f3a012014-04-25 17:00:21 -070032import android.provider.Settings.SettingNotFoundException;
Martijn Coenen52246082013-08-30 11:14:46 -070033import android.util.Log;
34
35import java.util.HashMap;
36import java.util.List;
37
Martijn Coenen35bf6282013-10-14 20:39:59 +020038/**
39 * This class can be used to query the state of
40 * NFC card emulation services.
41 *
42 * For a general introduction into NFC card emulation,
43 * please read the <a href="{@docRoot}guide/topics/nfc/ce.html">
44 * NFC card emulation developer guide</a>.</p>
45 *
46 * <p class="note">Use of this class requires the
47 * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present
48 * on the device.
49 */
Martijn Coenen52246082013-08-30 11:14:46 -070050public final class CardEmulation {
51 static final String TAG = "CardEmulation";
52
53 /**
54 * Activity action: ask the user to change the default
55 * card emulation service for a certain category. This will
56 * show a dialog that asks the user whether he wants to
57 * replace the current default service with the service
58 * identified with the ComponentName specified in
59 * {@link #EXTRA_SERVICE_COMPONENT}, for the category
60 * specified in {@link #EXTRA_CATEGORY}
61 */
62 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
63 public static final String ACTION_CHANGE_DEFAULT =
64 "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
65
66 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +020067 * The category extra for {@link #ACTION_CHANGE_DEFAULT}.
Martijn Coenen52246082013-08-30 11:14:46 -070068 *
69 * @see #ACTION_CHANGE_DEFAULT
70 */
71 public static final String EXTRA_CATEGORY = "category";
72
73 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +020074 * The service {@link ComponentName} object passed in as an
75 * extra for {@link #ACTION_CHANGE_DEFAULT}.
Martijn Coenen52246082013-08-30 11:14:46 -070076 *
77 * @see #ACTION_CHANGE_DEFAULT
78 */
79 public static final String EXTRA_SERVICE_COMPONENT = "component";
80
81 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +020082 * Category used for NFC payment services.
Martijn Coenen52246082013-08-30 11:14:46 -070083 */
84 public static final String CATEGORY_PAYMENT = "payment";
85
86 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +020087 * Category that can be used for all other card emulation
88 * services.
Martijn Coenen52246082013-08-30 11:14:46 -070089 */
90 public static final String CATEGORY_OTHER = "other";
91
92 /**
93 * Return value for {@link #getSelectionModeForCategory(String)}.
94 *
95 * <p>In this mode, the user has set a default service for this
Martijn Coenen35bf6282013-10-14 20:39:59 +020096 * category.
97 *
98 * <p>When using ISO-DEP card emulation with {@link HostApduService}
99 * or {@link OffHostApduService}, if a remote NFC device selects
100 * any of the Application IDs (AIDs)
Martijn Coenen52246082013-08-30 11:14:46 -0700101 * that the default service has registered in this category,
102 * that service will automatically be bound to to handle
103 * the transaction.
Martijn Coenen52246082013-08-30 11:14:46 -0700104 */
105 public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
106
107 /**
108 * Return value for {@link #getSelectionModeForCategory(String)}.
109 *
Martijn Coenen35bf6282013-10-14 20:39:59 +0200110 * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
111 * or {@link OffHostApduService}, whenever an Application ID (AID) of this category
112 * is selected, the user is asked which service he wants to use to handle
Martijn Coenen52246082013-08-30 11:14:46 -0700113 * the transaction, even if there is only one matching service.
114 */
115 public static final int SELECTION_MODE_ALWAYS_ASK = 1;
116
117 /**
118 * Return value for {@link #getSelectionModeForCategory(String)}.
119 *
Martijn Coenen35bf6282013-10-14 20:39:59 +0200120 * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
121 * or {@link OffHostApduService}, the user will only be asked to select a service
122 * if the Application ID (AID) selected by the reader has been registered by multiple
123 * services. If there is only one service that has registered for the AID,
124 * that service will be invoked directly.
Martijn Coenen52246082013-08-30 11:14:46 -0700125 */
126 public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
127
128 static boolean sIsInitialized = false;
Martijn Coenen35bf6282013-10-14 20:39:59 +0200129 static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
Martijn Coenen52246082013-08-30 11:14:46 -0700130 static INfcCardEmulation sService;
131
132 final Context mContext;
133
134 private CardEmulation(Context context, INfcCardEmulation service) {
135 mContext = context.getApplicationContext();
136 sService = service;
137 }
138
Martijn Coenen35bf6282013-10-14 20:39:59 +0200139 /**
140 * Helper to get an instance of this class.
141 *
142 * @param adapter A reference to an NfcAdapter object.
143 * @return
144 */
Martijn Coenen52246082013-08-30 11:14:46 -0700145 public static synchronized CardEmulation getInstance(NfcAdapter adapter) {
146 if (adapter == null) throw new NullPointerException("NfcAdapter is null");
147 Context context = adapter.getContext();
148 if (context == null) {
149 Log.e(TAG, "NfcAdapter context is null.");
150 throw new UnsupportedOperationException();
151 }
152 if (!sIsInitialized) {
153 IPackageManager pm = ActivityThread.getPackageManager();
154 if (pm == null) {
155 Log.e(TAG, "Cannot get PackageManager");
156 throw new UnsupportedOperationException();
157 }
158 try {
159 if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
160 Log.e(TAG, "This device does not support card emulation");
161 throw new UnsupportedOperationException();
162 }
163 } catch (RemoteException e) {
164 Log.e(TAG, "PackageManager query failed.");
165 throw new UnsupportedOperationException();
166 }
167 sIsInitialized = true;
168 }
169 CardEmulation manager = sCardEmus.get(context);
170 if (manager == null) {
171 // Get card emu service
172 INfcCardEmulation service = adapter.getCardEmulationService();
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700173 if (service == null) {
174 Log.e(TAG, "This device does not implement the INfcCardEmulation interface.");
175 throw new UnsupportedOperationException();
176 }
Martijn Coenen52246082013-08-30 11:14:46 -0700177 manager = new CardEmulation(context, service);
178 sCardEmus.put(context, manager);
179 }
180 return manager;
181 }
182
183 /**
184 * Allows an application to query whether a service is currently
185 * the default service to handle a card emulation category.
186 *
187 * <p>Note that if {@link #getSelectionModeForCategory(String)}
Martijn Coenen35bf6282013-10-14 20:39:59 +0200188 * returns {@link #SELECTION_MODE_ALWAYS_ASK} or {@link #SELECTION_MODE_ASK_IF_CONFLICT},
189 * this method will always return false. That is because in these
190 * selection modes a default can't be set at the category level. For categories where
191 * the selection mode is {@link #SELECTION_MODE_ALWAYS_ASK} or
192 * {@link #SELECTION_MODE_ASK_IF_CONFLICT}, use
193 * {@link #isDefaultServiceForAid(ComponentName, String)} to determine whether a service
194 * is the default for a specific AID.
Martijn Coenen52246082013-08-30 11:14:46 -0700195 *
196 * @param service The ComponentName of the service
197 * @param category The category
198 * @return whether service is currently the default service for the category.
Martijn Coenen35bf6282013-10-14 20:39:59 +0200199 *
200 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
Martijn Coenen52246082013-08-30 11:14:46 -0700201 */
202 public boolean isDefaultServiceForCategory(ComponentName service, String category) {
203 try {
204 return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
205 } catch (RemoteException e) {
206 // Try one more time
207 recoverService();
208 if (sService == null) {
209 Log.e(TAG, "Failed to recover CardEmulationService.");
210 return false;
211 }
212 try {
213 return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
214 category);
215 } catch (RemoteException ee) {
216 Log.e(TAG, "Failed to recover CardEmulationService.");
217 return false;
218 }
219 }
220 }
221
222 /**
223 *
224 * Allows an application to query whether a service is currently
225 * the default handler for a specified ISO7816-4 Application ID.
226 *
227 * @param service The ComponentName of the service
228 * @param aid The ISO7816-4 Application ID
Martijn Coenen35bf6282013-10-14 20:39:59 +0200229 * @return whether the service is the default handler for the specified AID
230 *
231 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
Martijn Coenen52246082013-08-30 11:14:46 -0700232 */
233 public boolean isDefaultServiceForAid(ComponentName service, String aid) {
234 try {
235 return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
236 } catch (RemoteException e) {
237 // Try one more time
238 recoverService();
239 if (sService == null) {
240 Log.e(TAG, "Failed to recover CardEmulationService.");
241 return false;
242 }
243 try {
244 return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
245 } catch (RemoteException ee) {
246 Log.e(TAG, "Failed to reach CardEmulationService.");
247 return false;
248 }
249 }
250 }
251
252 /**
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700253 * Returns whether the user has allowed AIDs registered in the
254 * specified category to be handled by a service that is preferred
255 * by the foreground application, instead of by a pre-configured default.
256 *
257 * Foreground applications can set such preferences using the
258 * {@link #setPreferredService(Activity, ComponentName)} method.
259 *
260 * @param category The category, e.g. {@link #CATEGORY_PAYMENT}
261 * @return whether AIDs in the category can be handled by a service
262 * specified by the foreground app.
263 */
264 public boolean categoryAllowsForegroundPreference(String category) {
265 if (CATEGORY_PAYMENT.equals(category)) {
266 boolean preferForeground = false;
267 try {
268 preferForeground = Settings.Secure.getInt(mContext.getContentResolver(),
269 Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
270 } catch (SettingNotFoundException e) {
271 }
272 return preferForeground;
273 } else {
274 // Allowed for all other categories
275 return true;
276 }
277 }
278
279 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +0200280 * Returns the service selection mode for the passed in category.
Martijn Coenen52246082013-08-30 11:14:46 -0700281 * Valid return values are:
282 * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
Martijn Coenen35bf6282013-10-14 20:39:59 +0200283 * service for this category, which will be preferred.
Martijn Coenen52246082013-08-30 11:14:46 -0700284 * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
Martijn Coenen35bf6282013-10-14 20:39:59 +0200285 * every time what service he would like to use in this category.
Martijn Coenen52246082013-08-30 11:14:46 -0700286 * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
287 * to pick a service if there is a conflict.
288 * @param category The category, for example {@link #CATEGORY_PAYMENT}
Martijn Coenen35bf6282013-10-14 20:39:59 +0200289 * @return the selection mode for the passed in category
Martijn Coenen52246082013-08-30 11:14:46 -0700290 */
291 public int getSelectionModeForCategory(String category) {
292 if (CATEGORY_PAYMENT.equals(category)) {
293 String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
294 Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
295 if (defaultComponent != null) {
296 return SELECTION_MODE_PREFER_DEFAULT;
297 } else {
298 return SELECTION_MODE_ALWAYS_ASK;
299 }
300 } else {
Martijn Coenen52246082013-08-30 11:14:46 -0700301 return SELECTION_MODE_ASK_IF_CONFLICT;
302 }
303 }
304
305 /**
Martijn Coenendf48db32014-05-20 13:52:14 -0700306 * Registers a list of AIDs for a specific category for the
307 * specified service.
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700308 *
Martijn Coenendf48db32014-05-20 13:52:14 -0700309 * <p>If a list of AIDs for that category was previously
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700310 * registered for this service (either statically
311 * through the manifest, or dynamically by using this API),
Martijn Coenendf48db32014-05-20 13:52:14 -0700312 * that list of AIDs will be replaced with this one.
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700313 *
314 * <p>Note that you can only register AIDs for a service that
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700315 * is running under the same UID as the caller of this API. Typically
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700316 * this means you need to call this from the same
317 * package as the service itself, though UIDs can also
318 * be shared between packages using shared UIDs.
319 *
320 * @param service The component name of the service
Martijn Coenendf48db32014-05-20 13:52:14 -0700321 * @param category The category of AIDs to be registered
322 * @param aids A list containing the AIDs to be registered
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700323 * @return whether the registration was successful.
324 */
Martijn Coenendf48db32014-05-20 13:52:14 -0700325 public boolean registerAidsForService(ComponentName service, String category,
326 List<String> aids) {
327 AidGroup aidGroup = new AidGroup(aids, category);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700328 try {
329 return sService.registerAidGroupForService(UserHandle.myUserId(), service, aidGroup);
330 } catch (RemoteException e) {
331 // Try one more time
332 recoverService();
333 if (sService == null) {
334 Log.e(TAG, "Failed to recover CardEmulationService.");
335 return false;
336 }
337 try {
338 return sService.registerAidGroupForService(UserHandle.myUserId(), service,
339 aidGroup);
340 } catch (RemoteException ee) {
341 Log.e(TAG, "Failed to reach CardEmulationService.");
342 return false;
343 }
344 }
345 }
346
347 /**
Martijn Coenendf48db32014-05-20 13:52:14 -0700348 * Retrieves the currently registered AIDs for the specified
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700349 * category for a service.
350 *
Martijn Coenendf48db32014-05-20 13:52:14 -0700351 * <p>Note that this will only return AIDs that were dynamically
352 * registered using {@link #registerAidsForService(ComponentName, String, List)}
353 * method. It will *not* return AIDs that were statically registered
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700354 * in the manifest.
355 *
356 * @param service The component name of the service
Martijn Coenendf48db32014-05-20 13:52:14 -0700357 * @param category The category for which the AIDs were registered,
358 * e.g. {@link #CATEGORY_PAYMENT}
359 * @return The list of AIDs registered for this category, or null if it couldn't be found.
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700360 */
Martijn Coenendf48db32014-05-20 13:52:14 -0700361 public List<String> getAidsForService(ComponentName service, String category) {
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700362 try {
Martijn Coenendf48db32014-05-20 13:52:14 -0700363 AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
364 category);
365 return (group != null ? group.getAids() : null);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700366 } catch (RemoteException e) {
367 recoverService();
368 if (sService == null) {
369 Log.e(TAG, "Failed to recover CardEmulationService.");
370 return null;
371 }
372 try {
Martijn Coenendf48db32014-05-20 13:52:14 -0700373 AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
374 category);
375 return (group != null ? group.getAids() : null);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700376 } catch (RemoteException ee) {
377 Log.e(TAG, "Failed to recover CardEmulationService.");
378 return null;
379 }
380 }
381 }
382
383 /**
Martijn Coenendf48db32014-05-20 13:52:14 -0700384 * Removes a previously registered list of AIDs for the specified category for the
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700385 * service provided.
386 *
Martijn Coenendf48db32014-05-20 13:52:14 -0700387 * <p>Note that this will only remove AIDs that were dynamically
388 * registered using the {@link #registerAidsForService(ComponentName, String, List)}
389 * method. It will *not* remove AIDs that were statically registered in
390 * the manifest. If dynamically registered AIDs are removed using
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700391 * this method, and a statically registered AID group for the same category
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700392 * exists in the manifest, the static AID group will become active again.
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700393 *
394 * @param service The component name of the service
Martijn Coenendf48db32014-05-20 13:52:14 -0700395 * @param category The category of the AIDs to be removed, e.g. {@link #CATEGORY_PAYMENT}
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700396 * @return whether the group was successfully removed.
397 */
Martijn Coenendf48db32014-05-20 13:52:14 -0700398 public boolean removeAidsForService(ComponentName service, String category) {
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700399 try {
400 return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
401 } catch (RemoteException e) {
402 // Try one more time
403 recoverService();
404 if (sService == null) {
405 Log.e(TAG, "Failed to recover CardEmulationService.");
406 return false;
407 }
408 try {
409 return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
410 } catch (RemoteException ee) {
411 Log.e(TAG, "Failed to reach CardEmulationService.");
412 return false;
413 }
414 }
415 }
416
417 /**
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700418 * Allows a foreground application to specify which card emulation service
419 * should be preferred while a specific Activity is in the foreground.
420 *
421 * <p>The specified Activity must currently be in resumed state. A good
422 * paradigm is to call this method in your {@link Activity#onResume}, and to call
423 * {@link #unsetPreferredService(Activity)} in your {@link Activity#onPause}.
424 *
425 * <p>This method call will fail in two specific scenarios:
426 * <ul>
427 * <li> If the service registers one or more AIDs in the {@link #CATEGORY_PAYMENT}
428 * category, but the user has indicated that foreground apps are not allowed
429 * to override the default payment service.
430 * <li> If the service registers one or more AIDs in the {@link #CATEGORY_OTHER}
431 * category that are also handled by the default payment service, and the
432 * user has indicated that foreground apps are not allowed to override the
433 * default payment service.
434 * </ul>
435 *
436 * <p> Use {@link #categoryAllowsForegroundPreference(String)} to determine
437 * whether foreground apps can override the default payment service.
438 *
439 * <p>Note that this preference is not persisted by the OS, and hence must be
440 * called every time the Activity is resumed.
441 *
442 * @param activity The activity which prefers this service to be invoked
443 * @param service The service to be preferred while this activity is in the foreground
444 * @return whether the registration was successful
445 */
446 public boolean setPreferredService(Activity activity, ComponentName service) {
447 // Verify the activity is in the foreground before calling into NfcService
448 if (activity == null || service == null) {
449 throw new NullPointerException("activity or service or category is null");
450 }
451 if (!activity.isResumed()) {
452 throw new IllegalArgumentException("Activity must be resumed.");
453 }
454 try {
455 return sService.setPreferredService(service);
456 } catch (RemoteException e) {
457 // Try one more time
458 recoverService();
459 if (sService == null) {
460 Log.e(TAG, "Failed to recover CardEmulationService.");
461 return false;
462 }
463 try {
464 return sService.setPreferredService(service);
465 } catch (RemoteException ee) {
466 Log.e(TAG, "Failed to reach CardEmulationService.");
467 return false;
468 }
469 }
470 }
471
472 /**
473 * Unsets the preferred service for the specified Activity.
474 *
475 * <p>Note that the specified Activity must still be in resumed
476 * state at the time of this call. A good place to call this method
477 * is in your {@link Activity#onPause} implementation.
478 *
479 * @param activity The activity which the service was registered for
480 * @return true when successful
481 */
482 public boolean unsetPreferredService(Activity activity) {
483 if (activity == null) {
484 throw new NullPointerException("activity is null");
485 }
486 if (!activity.isResumed()) {
487 throw new IllegalArgumentException("Activity must be resumed.");
488 }
489 try {
490 return sService.unsetPreferredService();
491 } catch (RemoteException e) {
492 // Try one more time
493 recoverService();
494 if (sService == null) {
495 Log.e(TAG, "Failed to recover CardEmulationService.");
496 return false;
497 }
498 try {
499 return sService.unsetPreferredService();
500 } catch (RemoteException ee) {
501 Log.e(TAG, "Failed to reach CardEmulationService.");
502 return false;
503 }
504 }
505 }
506
507 /**
Martijn Coenend92c1682014-07-02 16:30:06 -0700508 * Some devices may allow an application to register all
509 * AIDs that starts with a certain prefix, e.g.
510 * "A000000004*" to register all MasterCard AIDs.
511 *
512 * Use this method to determine whether this device
513 * supports registering AID prefixes.
514 *
515 * @return whether AID prefix registering is supported on this device.
516 */
517 public boolean supportsAidPrefixRegistration() {
Martijn Coenen826a73b2014-08-05 07:51:58 -0700518 try {
519 return sService.supportsAidPrefixRegistration();
520 } catch (RemoteException e) {
521 recoverService();
522 if (sService == null) {
523 Log.e(TAG, "Failed to recover CardEmulationService.");
524 return false;
525 }
526 try {
527 return sService.supportsAidPrefixRegistration();
528 } catch (RemoteException ee) {
529 Log.e(TAG, "Failed to reach CardEmulationService.");
530 return false;
531 }
532 }
Martijn Coenend92c1682014-07-02 16:30:06 -0700533 }
534
535 /**
Martijn Coenen52246082013-08-30 11:14:46 -0700536 * @hide
537 */
538 public boolean setDefaultServiceForCategory(ComponentName service, String category) {
539 try {
540 return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
541 } catch (RemoteException e) {
542 // Try one more time
543 recoverService();
544 if (sService == null) {
545 Log.e(TAG, "Failed to recover CardEmulationService.");
546 return false;
547 }
548 try {
549 return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
550 category);
551 } catch (RemoteException ee) {
552 Log.e(TAG, "Failed to reach CardEmulationService.");
553 return false;
554 }
555 }
556 }
557
558 /**
559 * @hide
560 */
561 public boolean setDefaultForNextTap(ComponentName service) {
562 try {
563 return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
564 } catch (RemoteException e) {
565 // Try one more time
566 recoverService();
567 if (sService == null) {
568 Log.e(TAG, "Failed to recover CardEmulationService.");
569 return false;
570 }
571 try {
572 return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
573 } catch (RemoteException ee) {
574 Log.e(TAG, "Failed to reach CardEmulationService.");
575 return false;
576 }
577 }
578 }
Martijn Coenen35bf6282013-10-14 20:39:59 +0200579
Martijn Coenen52246082013-08-30 11:14:46 -0700580 /**
581 * @hide
582 */
583 public List<ApduServiceInfo> getServices(String category) {
584 try {
585 return sService.getServices(UserHandle.myUserId(), category);
586 } catch (RemoteException e) {
587 // Try one more time
588 recoverService();
589 if (sService == null) {
590 Log.e(TAG, "Failed to recover CardEmulationService.");
591 return null;
592 }
593 try {
594 return sService.getServices(UserHandle.myUserId(), category);
595 } catch (RemoteException ee) {
596 Log.e(TAG, "Failed to reach CardEmulationService.");
597 return null;
598 }
599 }
600 }
601
Martijn Coenenb5144112014-07-02 12:44:33 -0700602 /**
603 * A valid AID according to ISO/IEC 7816-4:
604 * <ul>
605 * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
606 * <li>Consist of only hex characters
607 * <li>Additionally, we allow an asterisk at the end, to indicate
608 * a prefix
609 * </ul>
610 *
611 * @hide
612 */
613 public static boolean isValidAid(String aid) {
614 if (aid == null)
615 return false;
616
617 // If a prefix AID, the total length must be odd (even # of AID chars + '*')
618 if (aid.endsWith("*") && ((aid.length() % 2) == 0)) {
619 Log.e(TAG, "AID " + aid + " is not a valid AID.");
620 return false;
621 }
622
623 // If not a prefix AID, the total length must be even (even # of AID chars)
624 if (!aid.endsWith("*") && ((aid.length() % 2) != 0)) {
625 Log.e(TAG, "AID " + aid + " is not a valid AID.");
626 return false;
627 }
628
629 // Verify hex characters
630 if (!aid.matches("[0-9A-Fa-f]{10,32}\\*?")) {
631 Log.e(TAG, "AID " + aid + " is not a valid AID.");
632 return false;
633 }
634
635 return true;
636 }
637
Martijn Coenen52246082013-08-30 11:14:46 -0700638 void recoverService() {
639 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
640 sService = adapter.getCardEmulationService();
641 }
Martijn Coenenb5144112014-07-02 12:44:33 -0700642
Martijn Coenen52246082013-08-30 11:14:46 -0700643}