blob: 01932abfaa9be0ced0ef2a197fd0aec0984b6032 [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;
Martijn Coenen52246082013-08-30 11:14:46 -070030import android.provider.Settings;
Martijn Coenen2f6f3a012014-04-25 17:00:21 -070031import android.provider.Settings.SettingNotFoundException;
Martijn Coenen52246082013-08-30 11:14:46 -070032import android.util.Log;
33
34import java.util.HashMap;
35import java.util.List;
36
Martijn Coenen35bf6282013-10-14 20:39:59 +020037/**
38 * This class can be used to query the state of
39 * NFC card emulation services.
40 *
41 * For a general introduction into NFC card emulation,
Robert Schaubf64c80a2015-06-29 17:21:20 -070042 * please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">
Martijn Coenen35bf6282013-10-14 20:39:59 +020043 * NFC card emulation developer guide</a>.</p>
44 *
45 * <p class="note">Use of this class requires the
46 * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present
47 * on the device.
48 */
Martijn Coenen52246082013-08-30 11:14:46 -070049public final class CardEmulation {
50 static final String TAG = "CardEmulation";
51
52 /**
53 * Activity action: ask the user to change the default
54 * card emulation service for a certain category. This will
55 * show a dialog that asks the user whether he wants to
56 * replace the current default service with the service
57 * identified with the ComponentName specified in
58 * {@link #EXTRA_SERVICE_COMPONENT}, for the category
59 * specified in {@link #EXTRA_CATEGORY}
60 */
61 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
62 public static final String ACTION_CHANGE_DEFAULT =
63 "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
64
65 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +020066 * The category extra for {@link #ACTION_CHANGE_DEFAULT}.
Martijn Coenen52246082013-08-30 11:14:46 -070067 *
68 * @see #ACTION_CHANGE_DEFAULT
69 */
70 public static final String EXTRA_CATEGORY = "category";
71
72 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +020073 * The service {@link ComponentName} object passed in as an
74 * extra for {@link #ACTION_CHANGE_DEFAULT}.
Martijn Coenen52246082013-08-30 11:14:46 -070075 *
76 * @see #ACTION_CHANGE_DEFAULT
77 */
78 public static final String EXTRA_SERVICE_COMPONENT = "component";
79
80 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +020081 * Category used for NFC payment services.
Martijn Coenen52246082013-08-30 11:14:46 -070082 */
83 public static final String CATEGORY_PAYMENT = "payment";
84
85 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +020086 * Category that can be used for all other card emulation
87 * services.
Martijn Coenen52246082013-08-30 11:14:46 -070088 */
89 public static final String CATEGORY_OTHER = "other";
90
91 /**
92 * Return value for {@link #getSelectionModeForCategory(String)}.
93 *
94 * <p>In this mode, the user has set a default service for this
Martijn Coenen35bf6282013-10-14 20:39:59 +020095 * category.
96 *
97 * <p>When using ISO-DEP card emulation with {@link HostApduService}
98 * or {@link OffHostApduService}, if a remote NFC device selects
99 * any of the Application IDs (AIDs)
Martijn Coenen52246082013-08-30 11:14:46 -0700100 * that the default service has registered in this category,
101 * that service will automatically be bound to to handle
102 * the transaction.
Martijn Coenen52246082013-08-30 11:14:46 -0700103 */
104 public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
105
106 /**
107 * Return value for {@link #getSelectionModeForCategory(String)}.
108 *
Martijn Coenen35bf6282013-10-14 20:39:59 +0200109 * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
110 * or {@link OffHostApduService}, whenever an Application ID (AID) of this category
111 * is selected, the user is asked which service he wants to use to handle
Martijn Coenen52246082013-08-30 11:14:46 -0700112 * the transaction, even if there is only one matching service.
113 */
114 public static final int SELECTION_MODE_ALWAYS_ASK = 1;
115
116 /**
117 * Return value for {@link #getSelectionModeForCategory(String)}.
118 *
Martijn Coenen35bf6282013-10-14 20:39:59 +0200119 * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
120 * or {@link OffHostApduService}, the user will only be asked to select a service
121 * if the Application ID (AID) selected by the reader has been registered by multiple
122 * services. If there is only one service that has registered for the AID,
123 * that service will be invoked directly.
Martijn Coenen52246082013-08-30 11:14:46 -0700124 */
125 public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
126
127 static boolean sIsInitialized = false;
Martijn Coenen35bf6282013-10-14 20:39:59 +0200128 static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
Martijn Coenen52246082013-08-30 11:14:46 -0700129 static INfcCardEmulation sService;
130
131 final Context mContext;
132
133 private CardEmulation(Context context, INfcCardEmulation service) {
134 mContext = context.getApplicationContext();
135 sService = service;
136 }
137
Martijn Coenen35bf6282013-10-14 20:39:59 +0200138 /**
139 * Helper to get an instance of this class.
140 *
141 * @param adapter A reference to an NfcAdapter object.
142 * @return
143 */
Martijn Coenen52246082013-08-30 11:14:46 -0700144 public static synchronized CardEmulation getInstance(NfcAdapter adapter) {
145 if (adapter == null) throw new NullPointerException("NfcAdapter is null");
146 Context context = adapter.getContext();
147 if (context == null) {
148 Log.e(TAG, "NfcAdapter context is null.");
149 throw new UnsupportedOperationException();
150 }
151 if (!sIsInitialized) {
152 IPackageManager pm = ActivityThread.getPackageManager();
153 if (pm == null) {
154 Log.e(TAG, "Cannot get PackageManager");
155 throw new UnsupportedOperationException();
156 }
157 try {
Jeff Sharkey115d2c12016-02-15 17:25:57 -0700158 if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)) {
Martijn Coenen52246082013-08-30 11:14:46 -0700159 Log.e(TAG, "This device does not support card emulation");
160 throw new UnsupportedOperationException();
161 }
162 } catch (RemoteException e) {
163 Log.e(TAG, "PackageManager query failed.");
164 throw new UnsupportedOperationException();
165 }
166 sIsInitialized = true;
167 }
168 CardEmulation manager = sCardEmus.get(context);
169 if (manager == null) {
170 // Get card emu service
171 INfcCardEmulation service = adapter.getCardEmulationService();
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700172 if (service == null) {
173 Log.e(TAG, "This device does not implement the INfcCardEmulation interface.");
174 throw new UnsupportedOperationException();
175 }
Martijn Coenen52246082013-08-30 11:14:46 -0700176 manager = new CardEmulation(context, service);
177 sCardEmus.put(context, manager);
178 }
179 return manager;
180 }
181
182 /**
183 * Allows an application to query whether a service is currently
184 * the default service to handle a card emulation category.
185 *
186 * <p>Note that if {@link #getSelectionModeForCategory(String)}
Martijn Coenen35bf6282013-10-14 20:39:59 +0200187 * returns {@link #SELECTION_MODE_ALWAYS_ASK} or {@link #SELECTION_MODE_ASK_IF_CONFLICT},
188 * this method will always return false. That is because in these
189 * selection modes a default can't be set at the category level. For categories where
190 * the selection mode is {@link #SELECTION_MODE_ALWAYS_ASK} or
191 * {@link #SELECTION_MODE_ASK_IF_CONFLICT}, use
192 * {@link #isDefaultServiceForAid(ComponentName, String)} to determine whether a service
193 * is the default for a specific AID.
Martijn Coenen52246082013-08-30 11:14:46 -0700194 *
195 * @param service The ComponentName of the service
196 * @param category The category
197 * @return whether service is currently the default service for the category.
Martijn Coenen35bf6282013-10-14 20:39:59 +0200198 *
199 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
Martijn Coenen52246082013-08-30 11:14:46 -0700200 */
201 public boolean isDefaultServiceForCategory(ComponentName service, String category) {
202 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700203 return sService.isDefaultServiceForCategory(mContext.getUserId(), service, category);
Martijn Coenen52246082013-08-30 11:14:46 -0700204 } catch (RemoteException e) {
205 // Try one more time
206 recoverService();
207 if (sService == null) {
208 Log.e(TAG, "Failed to recover CardEmulationService.");
209 return false;
210 }
211 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700212 return sService.isDefaultServiceForCategory(mContext.getUserId(), service,
Martijn Coenen52246082013-08-30 11:14:46 -0700213 category);
214 } catch (RemoteException ee) {
215 Log.e(TAG, "Failed to recover CardEmulationService.");
216 return false;
217 }
218 }
219 }
220
221 /**
222 *
223 * Allows an application to query whether a service is currently
224 * the default handler for a specified ISO7816-4 Application ID.
225 *
226 * @param service The ComponentName of the service
227 * @param aid The ISO7816-4 Application ID
Martijn Coenen35bf6282013-10-14 20:39:59 +0200228 * @return whether the service is the default handler for the specified AID
229 *
230 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
Martijn Coenen52246082013-08-30 11:14:46 -0700231 */
232 public boolean isDefaultServiceForAid(ComponentName service, String aid) {
233 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700234 return sService.isDefaultServiceForAid(mContext.getUserId(), service, aid);
Martijn Coenen52246082013-08-30 11:14:46 -0700235 } catch (RemoteException e) {
236 // Try one more time
237 recoverService();
238 if (sService == null) {
239 Log.e(TAG, "Failed to recover CardEmulationService.");
240 return false;
241 }
242 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700243 return sService.isDefaultServiceForAid(mContext.getUserId(), service, aid);
Martijn Coenen52246082013-08-30 11:14:46 -0700244 } catch (RemoteException ee) {
245 Log.e(TAG, "Failed to reach CardEmulationService.");
246 return false;
247 }
248 }
249 }
250
251 /**
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700252 * Returns whether the user has allowed AIDs registered in the
253 * specified category to be handled by a service that is preferred
254 * by the foreground application, instead of by a pre-configured default.
255 *
256 * Foreground applications can set such preferences using the
257 * {@link #setPreferredService(Activity, ComponentName)} method.
258 *
259 * @param category The category, e.g. {@link #CATEGORY_PAYMENT}
260 * @return whether AIDs in the category can be handled by a service
261 * specified by the foreground app.
262 */
263 public boolean categoryAllowsForegroundPreference(String category) {
264 if (CATEGORY_PAYMENT.equals(category)) {
265 boolean preferForeground = false;
266 try {
267 preferForeground = Settings.Secure.getInt(mContext.getContentResolver(),
268 Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
269 } catch (SettingNotFoundException e) {
270 }
271 return preferForeground;
272 } else {
273 // Allowed for all other categories
274 return true;
275 }
276 }
277
278 /**
Martijn Coenen35bf6282013-10-14 20:39:59 +0200279 * Returns the service selection mode for the passed in category.
Martijn Coenen52246082013-08-30 11:14:46 -0700280 * Valid return values are:
281 * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
Martijn Coenen35bf6282013-10-14 20:39:59 +0200282 * service for this category, which will be preferred.
Martijn Coenen52246082013-08-30 11:14:46 -0700283 * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
Martijn Coenen35bf6282013-10-14 20:39:59 +0200284 * every time what service he would like to use in this category.
Martijn Coenen52246082013-08-30 11:14:46 -0700285 * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
286 * to pick a service if there is a conflict.
287 * @param category The category, for example {@link #CATEGORY_PAYMENT}
Martijn Coenen35bf6282013-10-14 20:39:59 +0200288 * @return the selection mode for the passed in category
Martijn Coenen52246082013-08-30 11:14:46 -0700289 */
290 public int getSelectionModeForCategory(String category) {
291 if (CATEGORY_PAYMENT.equals(category)) {
292 String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
293 Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
294 if (defaultComponent != null) {
295 return SELECTION_MODE_PREFER_DEFAULT;
296 } else {
297 return SELECTION_MODE_ALWAYS_ASK;
298 }
299 } else {
Martijn Coenen52246082013-08-30 11:14:46 -0700300 return SELECTION_MODE_ASK_IF_CONFLICT;
301 }
302 }
303
304 /**
Martijn Coenendf48db32014-05-20 13:52:14 -0700305 * Registers a list of AIDs for a specific category for the
306 * specified service.
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700307 *
Martijn Coenendf48db32014-05-20 13:52:14 -0700308 * <p>If a list of AIDs for that category was previously
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700309 * registered for this service (either statically
310 * through the manifest, or dynamically by using this API),
Martijn Coenendf48db32014-05-20 13:52:14 -0700311 * that list of AIDs will be replaced with this one.
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700312 *
313 * <p>Note that you can only register AIDs for a service that
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700314 * is running under the same UID as the caller of this API. Typically
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700315 * this means you need to call this from the same
316 * package as the service itself, though UIDs can also
317 * be shared between packages using shared UIDs.
318 *
319 * @param service The component name of the service
Martijn Coenendf48db32014-05-20 13:52:14 -0700320 * @param category The category of AIDs to be registered
321 * @param aids A list containing the AIDs to be registered
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700322 * @return whether the registration was successful.
323 */
Martijn Coenendf48db32014-05-20 13:52:14 -0700324 public boolean registerAidsForService(ComponentName service, String category,
325 List<String> aids) {
326 AidGroup aidGroup = new AidGroup(aids, category);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700327 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700328 return sService.registerAidGroupForService(mContext.getUserId(), service, aidGroup);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700329 } catch (RemoteException e) {
330 // Try one more time
331 recoverService();
332 if (sService == null) {
333 Log.e(TAG, "Failed to recover CardEmulationService.");
334 return false;
335 }
336 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700337 return sService.registerAidGroupForService(mContext.getUserId(), service,
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700338 aidGroup);
339 } catch (RemoteException ee) {
340 Log.e(TAG, "Failed to reach CardEmulationService.");
341 return false;
342 }
343 }
344 }
345
346 /**
Ruchi Kandoi44bb5792018-10-19 13:08:34 -0700347 * Unsets the off-host Secure Element for the given service.
348 *
349 * <p>Note that this will only remove Secure Element that was dynamically
350 * set using the {@link #setOffHostForService(ComponentName, String)}
351 * and resets it to a value that was statically assigned using manifest.
352 *
353 * <p>Note that you can only unset off-host SE for a service that
354 * is running under the same UID as the caller of this API. Typically
355 * this means you need to call this from the same
356 * package as the service itself, though UIDs can also
357 * be shared between packages using shared UIDs.
358 *
359 * @param service The component name of the service
360 * @return whether the registration was successful.
361 */
362 public boolean unsetOffHostForService(ComponentName service) {
363 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
364 if (adapter == null) {
365 return false;
366 }
367
368 try {
369 return sService.unsetOffHostForService(mContext.getUserId(), service);
370 } catch (RemoteException e) {
371 // Try one more time
372 recoverService();
373 if (sService == null) {
374 Log.e(TAG, "Failed to recover CardEmulationService.");
375 return false;
376 }
377 try {
378 return sService.unsetOffHostForService(mContext.getUserId(), service);
379 } catch (RemoteException ee) {
380 Log.e(TAG, "Failed to reach CardEmulationService.");
381 return false;
382 }
383 }
384 }
385
386 /**
387 * Sets the off-host Secure Element for the given service.
388 *
389 * <p>If off-host SE was initially set (either statically
390 * through the manifest, or dynamically by using this API),
391 * it will be replaced with this one. All AIDs registered by
392 * this service will be re-routed to this Secure Element if
393 * successful.
394 *
395 * <p>Note that you can only set off-host SE for a service that
396 * is running under the same UID as the caller of this API. Typically
397 * this means you need to call this from the same
398 * package as the service itself, though UIDs can also
399 * be shared between packages using shared UIDs.
400 *
401 * <p>Registeration will be successful only if the Secure Element
402 * exists on the device.
403 *
404 * @param service The component name of the service
405 * @param offHostSecureElement Secure Element to register the AID to
406 * @return whether the registration was successful.
407 */
408 public boolean setOffHostForService(ComponentName service, String offHostSecureElement) {
409 boolean validSecureElement = false;
410
411 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
412 if (adapter == null || offHostSecureElement == null) {
413 return false;
414 }
415
416 List<String> validSE = adapter.getSupportedOffHostSecureElements();
417 if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE"))
418 || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) {
419 return false;
420 }
421
422 if (offHostSecureElement.equals("eSE")) {
423 offHostSecureElement = "eSE1";
424 } else if (offHostSecureElement.equals("SIM")) {
425 offHostSecureElement = "SIM1";
426 }
427
428 try {
429 return sService.setOffHostForService(mContext.getUserId(), service,
430 offHostSecureElement);
431 } catch (RemoteException e) {
432 // Try one more time
433 recoverService();
434 if (sService == null) {
435 Log.e(TAG, "Failed to recover CardEmulationService.");
436 return false;
437 }
438 try {
439 return sService.setOffHostForService(mContext.getUserId(), service,
440 offHostSecureElement);
441 } catch (RemoteException ee) {
442 Log.e(TAG, "Failed to reach CardEmulationService.");
443 return false;
444 }
445 }
446 }
447
448 /**
Martijn Coenendf48db32014-05-20 13:52:14 -0700449 * Retrieves the currently registered AIDs for the specified
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700450 * category for a service.
451 *
Martijn Coenendf48db32014-05-20 13:52:14 -0700452 * <p>Note that this will only return AIDs that were dynamically
453 * registered using {@link #registerAidsForService(ComponentName, String, List)}
454 * method. It will *not* return AIDs that were statically registered
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700455 * in the manifest.
456 *
457 * @param service The component name of the service
Martijn Coenendf48db32014-05-20 13:52:14 -0700458 * @param category The category for which the AIDs were registered,
459 * e.g. {@link #CATEGORY_PAYMENT}
460 * @return The list of AIDs registered for this category, or null if it couldn't be found.
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700461 */
Martijn Coenendf48db32014-05-20 13:52:14 -0700462 public List<String> getAidsForService(ComponentName service, String category) {
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700463 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700464 AidGroup group = sService.getAidGroupForService(mContext.getUserId(), service,
Martijn Coenendf48db32014-05-20 13:52:14 -0700465 category);
466 return (group != null ? group.getAids() : null);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700467 } catch (RemoteException e) {
468 recoverService();
469 if (sService == null) {
470 Log.e(TAG, "Failed to recover CardEmulationService.");
471 return null;
472 }
473 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700474 AidGroup group = sService.getAidGroupForService(mContext.getUserId(), service,
Martijn Coenendf48db32014-05-20 13:52:14 -0700475 category);
476 return (group != null ? group.getAids() : null);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700477 } catch (RemoteException ee) {
478 Log.e(TAG, "Failed to recover CardEmulationService.");
479 return null;
480 }
481 }
482 }
483
484 /**
Martijn Coenendf48db32014-05-20 13:52:14 -0700485 * Removes a previously registered list of AIDs for the specified category for the
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700486 * service provided.
487 *
Martijn Coenendf48db32014-05-20 13:52:14 -0700488 * <p>Note that this will only remove AIDs that were dynamically
489 * registered using the {@link #registerAidsForService(ComponentName, String, List)}
490 * method. It will *not* remove AIDs that were statically registered in
491 * the manifest. If dynamically registered AIDs are removed using
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700492 * this method, and a statically registered AID group for the same category
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700493 * exists in the manifest, the static AID group will become active again.
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700494 *
495 * @param service The component name of the service
Martijn Coenendf48db32014-05-20 13:52:14 -0700496 * @param category The category of the AIDs to be removed, e.g. {@link #CATEGORY_PAYMENT}
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700497 * @return whether the group was successfully removed.
498 */
Martijn Coenendf48db32014-05-20 13:52:14 -0700499 public boolean removeAidsForService(ComponentName service, String category) {
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700500 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700501 return sService.removeAidGroupForService(mContext.getUserId(), service, category);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700502 } catch (RemoteException e) {
503 // Try one more time
504 recoverService();
505 if (sService == null) {
506 Log.e(TAG, "Failed to recover CardEmulationService.");
507 return false;
508 }
509 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700510 return sService.removeAidGroupForService(mContext.getUserId(), service, category);
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700511 } catch (RemoteException ee) {
512 Log.e(TAG, "Failed to reach CardEmulationService.");
513 return false;
514 }
515 }
516 }
517
518 /**
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700519 * Allows a foreground application to specify which card emulation service
520 * should be preferred while a specific Activity is in the foreground.
521 *
522 * <p>The specified Activity must currently be in resumed state. A good
523 * paradigm is to call this method in your {@link Activity#onResume}, and to call
524 * {@link #unsetPreferredService(Activity)} in your {@link Activity#onPause}.
525 *
526 * <p>This method call will fail in two specific scenarios:
527 * <ul>
528 * <li> If the service registers one or more AIDs in the {@link #CATEGORY_PAYMENT}
529 * category, but the user has indicated that foreground apps are not allowed
530 * to override the default payment service.
531 * <li> If the service registers one or more AIDs in the {@link #CATEGORY_OTHER}
532 * category that are also handled by the default payment service, and the
533 * user has indicated that foreground apps are not allowed to override the
534 * default payment service.
535 * </ul>
536 *
537 * <p> Use {@link #categoryAllowsForegroundPreference(String)} to determine
538 * whether foreground apps can override the default payment service.
539 *
540 * <p>Note that this preference is not persisted by the OS, and hence must be
541 * called every time the Activity is resumed.
542 *
543 * @param activity The activity which prefers this service to be invoked
544 * @param service The service to be preferred while this activity is in the foreground
545 * @return whether the registration was successful
546 */
547 public boolean setPreferredService(Activity activity, ComponentName service) {
548 // Verify the activity is in the foreground before calling into NfcService
549 if (activity == null || service == null) {
550 throw new NullPointerException("activity or service or category is null");
551 }
552 if (!activity.isResumed()) {
553 throw new IllegalArgumentException("Activity must be resumed.");
554 }
555 try {
556 return sService.setPreferredService(service);
557 } catch (RemoteException e) {
558 // Try one more time
559 recoverService();
560 if (sService == null) {
561 Log.e(TAG, "Failed to recover CardEmulationService.");
562 return false;
563 }
564 try {
565 return sService.setPreferredService(service);
566 } catch (RemoteException ee) {
567 Log.e(TAG, "Failed to reach CardEmulationService.");
568 return false;
569 }
570 }
571 }
572
573 /**
574 * Unsets the preferred service for the specified Activity.
575 *
576 * <p>Note that the specified Activity must still be in resumed
577 * state at the time of this call. A good place to call this method
578 * is in your {@link Activity#onPause} implementation.
579 *
580 * @param activity The activity which the service was registered for
581 * @return true when successful
582 */
583 public boolean unsetPreferredService(Activity activity) {
584 if (activity == null) {
585 throw new NullPointerException("activity is null");
586 }
587 if (!activity.isResumed()) {
588 throw new IllegalArgumentException("Activity must be resumed.");
589 }
590 try {
591 return sService.unsetPreferredService();
592 } catch (RemoteException e) {
593 // Try one more time
594 recoverService();
595 if (sService == null) {
596 Log.e(TAG, "Failed to recover CardEmulationService.");
597 return false;
598 }
599 try {
600 return sService.unsetPreferredService();
601 } catch (RemoteException ee) {
602 Log.e(TAG, "Failed to reach CardEmulationService.");
603 return false;
604 }
605 }
606 }
607
608 /**
Martijn Coenend92c1682014-07-02 16:30:06 -0700609 * Some devices may allow an application to register all
610 * AIDs that starts with a certain prefix, e.g.
611 * "A000000004*" to register all MasterCard AIDs.
612 *
613 * Use this method to determine whether this device
614 * supports registering AID prefixes.
615 *
616 * @return whether AID prefix registering is supported on this device.
617 */
618 public boolean supportsAidPrefixRegistration() {
Martijn Coenen826a73b2014-08-05 07:51:58 -0700619 try {
620 return sService.supportsAidPrefixRegistration();
621 } catch (RemoteException e) {
622 recoverService();
623 if (sService == null) {
624 Log.e(TAG, "Failed to recover CardEmulationService.");
625 return false;
626 }
627 try {
628 return sService.supportsAidPrefixRegistration();
629 } catch (RemoteException ee) {
630 Log.e(TAG, "Failed to reach CardEmulationService.");
631 return false;
632 }
633 }
Martijn Coenend92c1682014-07-02 16:30:06 -0700634 }
635
636 /**
Martijn Coenen52246082013-08-30 11:14:46 -0700637 * @hide
638 */
639 public boolean setDefaultServiceForCategory(ComponentName service, String category) {
640 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700641 return sService.setDefaultServiceForCategory(mContext.getUserId(), service, category);
Martijn Coenen52246082013-08-30 11:14:46 -0700642 } catch (RemoteException e) {
643 // Try one more time
644 recoverService();
645 if (sService == null) {
646 Log.e(TAG, "Failed to recover CardEmulationService.");
647 return false;
648 }
649 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700650 return sService.setDefaultServiceForCategory(mContext.getUserId(), service,
Martijn Coenen52246082013-08-30 11:14:46 -0700651 category);
652 } catch (RemoteException ee) {
653 Log.e(TAG, "Failed to reach CardEmulationService.");
654 return false;
655 }
656 }
657 }
658
659 /**
660 * @hide
661 */
662 public boolean setDefaultForNextTap(ComponentName service) {
663 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700664 return sService.setDefaultForNextTap(mContext.getUserId(), service);
Martijn Coenen52246082013-08-30 11:14:46 -0700665 } catch (RemoteException e) {
666 // Try one more time
667 recoverService();
668 if (sService == null) {
669 Log.e(TAG, "Failed to recover CardEmulationService.");
670 return false;
671 }
672 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700673 return sService.setDefaultForNextTap(mContext.getUserId(), service);
Martijn Coenen52246082013-08-30 11:14:46 -0700674 } catch (RemoteException ee) {
675 Log.e(TAG, "Failed to reach CardEmulationService.");
676 return false;
677 }
678 }
679 }
Martijn Coenen35bf6282013-10-14 20:39:59 +0200680
Martijn Coenen52246082013-08-30 11:14:46 -0700681 /**
682 * @hide
683 */
684 public List<ApduServiceInfo> getServices(String category) {
685 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700686 return sService.getServices(mContext.getUserId(), category);
Martijn Coenen52246082013-08-30 11:14:46 -0700687 } catch (RemoteException e) {
688 // Try one more time
689 recoverService();
690 if (sService == null) {
691 Log.e(TAG, "Failed to recover CardEmulationService.");
692 return null;
693 }
694 try {
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700695 return sService.getServices(mContext.getUserId(), category);
Martijn Coenen52246082013-08-30 11:14:46 -0700696 } catch (RemoteException ee) {
697 Log.e(TAG, "Failed to reach CardEmulationService.");
698 return null;
699 }
700 }
701 }
702
Martijn Coenenb5144112014-07-02 12:44:33 -0700703 /**
704 * A valid AID according to ISO/IEC 7816-4:
705 * <ul>
706 * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
707 * <li>Consist of only hex characters
708 * <li>Additionally, we allow an asterisk at the end, to indicate
709 * a prefix
Love Khannae910e8b2017-05-12 13:53:42 +0530710 * <li>Additinally we allow an (#) at symbol at the end, to indicate
711 * a subset
Martijn Coenenb5144112014-07-02 12:44:33 -0700712 * </ul>
713 *
714 * @hide
715 */
716 public static boolean isValidAid(String aid) {
717 if (aid == null)
718 return false;
719
Love Khannae910e8b2017-05-12 13:53:42 +0530720 // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*')
721 if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) {
Martijn Coenenb5144112014-07-02 12:44:33 -0700722 Log.e(TAG, "AID " + aid + " is not a valid AID.");
723 return false;
724 }
725
Love Khannae910e8b2017-05-12 13:53:42 +0530726 // If not a prefix/subset AID, the total length must be even (even # of AID chars)
727 if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) {
Martijn Coenenb5144112014-07-02 12:44:33 -0700728 Log.e(TAG, "AID " + aid + " is not a valid AID.");
729 return false;
730 }
731
732 // Verify hex characters
Love Khannae910e8b2017-05-12 13:53:42 +0530733 if (!aid.matches("[0-9A-Fa-f]{10,32}\\*?\\#?")) {
Martijn Coenenb5144112014-07-02 12:44:33 -0700734 Log.e(TAG, "AID " + aid + " is not a valid AID.");
735 return false;
736 }
737
738 return true;
739 }
740
Martijn Coenen52246082013-08-30 11:14:46 -0700741 void recoverService() {
742 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
743 sService = adapter.getCardEmulationService();
744 }
Martijn Coenenb5144112014-07-02 12:44:33 -0700745
Martijn Coenen52246082013-08-30 11:14:46 -0700746}