/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.nfc.cardemulation; import android.app.Activity; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.nfc.INfcFCardEmulation; import android.nfc.NfcAdapter; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import java.util.HashMap; import java.util.List; /** * This class can be used to query the state of * NFC-F card emulation services. * * For a general introduction into NFC card emulation, * please read the * NFC card emulation developer guide.
* *Use of this class requires the
* {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION_NFCF}
* to be present on the device.
*/
public final class NfcFCardEmulation {
static final String TAG = "NfcFCardEmulation";
static boolean sIsInitialized = false;
static HashMap Before calling {@link #registerSystemCodeForService(ComponentName, String)},
* the System Code contained in the Manifest file is returned. After calling
* {@link #registerSystemCodeForService(ComponentName, String)}, the System Code
* registered there is returned. After calling
* {@link #unregisterSystemCodeForService(ComponentName)}, "null" is returned.
*
* @param service The component name of the service
* @return the current System Code
*/
public String getSystemCodeForService(ComponentName service) throws RuntimeException {
if (service == null) {
throw new NullPointerException("service is null");
}
try {
return sService.getSystemCodeForService(UserHandle.myUserId(), service);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return null;
}
try {
return sService.getSystemCodeForService(UserHandle.myUserId(), service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
return null;
}
}
}
/**
* Registers a System Code for the specified service.
*
* The System Code must be in range from "4000" to "4FFF" (excluding "4*FF").
*
* If a System Code was previously registered for this service
* (either statically through the manifest, or dynamically by using this API),
* it will be replaced with this one.
*
* Even if the same System Code is already registered for another service,
* this method succeeds in registering the System Code.
*
* Note that you can only register a System Code for a service that
* is running under the same UID as the caller of this API. Typically
* this means you need to call this from the same
* package as the service itself, though UIDs can also
* be shared between packages using shared UIDs.
*
* @param service The component name of the service
* @param systemCode The System Code to be registered
* @return whether the registration was successful.
*/
public boolean registerSystemCodeForService(ComponentName service, String systemCode)
throws RuntimeException {
if (service == null || systemCode == null) {
throw new NullPointerException("service or systemCode is null");
}
try {
return sService.registerSystemCodeForService(UserHandle.myUserId(),
service, systemCode);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
try {
return sService.registerSystemCodeForService(UserHandle.myUserId(),
service, systemCode);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
return false;
}
}
}
/**
* Removes a registered System Code for the specified service.
*
* @param service The component name of the service
* @return whether the System Code was successfully removed.
*/
public boolean unregisterSystemCodeForService(ComponentName service) throws RuntimeException {
if (service == null) {
throw new NullPointerException("service is null");
}
try {
return sService.removeSystemCodeForService(UserHandle.myUserId(), service);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
try {
return sService.removeSystemCodeForService(UserHandle.myUserId(), service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
return false;
}
}
}
/**
* Retrieves the current NFCID2 for the specified service.
*
* Before calling {@link #setNfcid2ForService(ComponentName, String)},
* the NFCID2 contained in the Manifest file is returned. If "random" is specified
* in the Manifest file, a random number assigned by the system at installation time
* is returned. After setting an NFCID2
* with {@link #setNfcid2ForService(ComponentName, String)}, this NFCID2 is returned.
*
* @param service The component name of the service
* @return the current NFCID2
*/
public String getNfcid2ForService(ComponentName service) throws RuntimeException {
if (service == null) {
throw new NullPointerException("service is null");
}
try {
return sService.getNfcid2ForService(UserHandle.myUserId(), service);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return null;
}
try {
return sService.getNfcid2ForService(UserHandle.myUserId(), service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
return null;
}
}
}
/**
* Set a NFCID2 for the specified service.
*
* The NFCID2 must be in range from "02FE000000000000" to "02FEFFFFFFFFFFFF".
*
* If a NFCID2 was previously set for this service
* (either statically through the manifest, or dynamically by using this API),
* it will be replaced.
*
* Note that you can only set the NFCID2 for a service that
* is running under the same UID as the caller of this API. Typically
* this means you need to call this from the same
* package as the service itself, though UIDs can also
* be shared between packages using shared UIDs.
*
* @param service The component name of the service
* @param nfcid2 The NFCID2 to be registered
* @return whether the setting was successful.
*/
public boolean setNfcid2ForService(ComponentName service, String nfcid2)
throws RuntimeException {
if (service == null || nfcid2 == null) {
throw new NullPointerException("service or nfcid2 is null");
}
try {
return sService.setNfcid2ForService(UserHandle.myUserId(),
service, nfcid2);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
try {
return sService.setNfcid2ForService(UserHandle.myUserId(),
service, nfcid2);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
return false;
}
}
}
/**
* Allows a foreground application to specify which card emulation service
* should be enabled while a specific Activity is in the foreground.
*
* The specified HCE-F service is only enabled when the corresponding application is
* in the foreground and this method has been called. When the application is moved to
* the background, {@link #disableService(Activity)} is called, or
* NFCID2 or System Code is replaced, the HCE-F service is disabled.
*
* The specified Activity must currently be in resumed state. A good
* paradigm is to call this method in your {@link Activity#onResume}, and to call
* {@link #disableService(Activity)} in your {@link Activity#onPause}.
*
* Note that this preference is not persisted by the OS, and hence must be
* called every time the Activity is resumed.
*
* @param activity The activity which prefers this service to be invoked
* @param service The service to be preferred while this activity is in the foreground
* @return whether the registration was successful
*/
public boolean enableService(Activity activity, ComponentName service) throws RuntimeException {
if (activity == null || service == null) {
throw new NullPointerException("activity or service is null");
}
// Verify the activity is in the foreground before calling into NfcService
if (!activity.isResumed()) {
throw new IllegalArgumentException("Activity must be resumed.");
}
try {
return sService.enableNfcFForegroundService(service);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
try {
return sService.enableNfcFForegroundService(service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
return false;
}
}
}
/**
* Disables the service for the specified Activity.
*
* Note that the specified Activity must still be in resumed
* state at the time of this call. A good place to call this method
* is in your {@link Activity#onPause} implementation.
*
* @param activity The activity which the service was registered for
* @return true when successful
*/
public boolean disableService(Activity activity) throws RuntimeException {
if (activity == null) {
throw new NullPointerException("activity is null");
}
if (!activity.isResumed()) {
throw new IllegalArgumentException("Activity must be resumed.");
}
try {
return sService.disableNfcFForegroundService();
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
try {
return sService.disableNfcFForegroundService();
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
return false;
}
}
}
/**
* @hide
*/
public List