As far as I understand, Open Mobile API is bundled with the Android ROMs created by manufacturers. We are using an SDK that is heavily using Open Mobile API, and found out, that some vendors create ROMs, where the version of the Open Mobile API is incompatible with the version of Android. This results in a disaster, that when we try to use the mentioned SDK, the application crashes. Because the SDK starts a new thread, and crashes on it. We cannot even put the whole logic in a try-catch block because of all this is running in a separate thread.
We decided to check the version of Android and the Open Mobile API, and see if they are incompatible, and if they are, completely disable the functionality that requires it.
Is there a way to determine the version of the preinstalled Open Mobile API? If there is, how can I do it?
That depends on what version you actually want to find out: the version of the SmartcardService system component or the version of the Open Mobile API framework.
The most obvious way would be to check the version information of the SmartcardService application package:
final String SMARTCARD_SERVICE_PACKAGE = "org.simalliance.openmobileapi.service";
PackageInfo pi = getPackageManager().getPackageInfo(SMARTCARD_SERVICE_PACKAGE, 0);
String versionName = pi.versionName;
String versionCode = pi.versionCode;
Typical values for versionName
are "2.3.0" (versionCode = 1), "2.4.0" (versionCode = 3), "3.0.0" (versionCode = 4), "3.1.0" (versionCode = 5), and "4.0.0" (versionCode = 8). Thus, you can determine the exact version of SEEK that the SmartcardService was forked from.
Unfortunately, several OEMs (e.g. Samsung) decided to remove the version information from the application package. Consequently, this is not as reliable as one might expect.
Another method that allows you to distinguish between implementations based on SEEK versions < 4.0.0 and SEEK versions >= 4.0.0 is to check the intent filter of the SmartcardService component:
final String SMARTCARD_SERVICE_PACKAGE = "org.simalliance.openmobileapi.service";
final String SMARTCARD_SERVICE_CLASS = "org.simalliance.openmobileapi.service.SmartcardService";
final String SMARTCARD_SERVICE_ACTION_V4 = "org.simalliance.openmobileapi.BIND_SERVICE";
final String SMARTCARD_SERVICE_ACTION_PRE4 = "org.simalliance.openmobileapi.service.ISmartcardService";
Intent intent = new Intent();
intent.setClassName(SMARTCARD_SERVICE_PACKAGE, SMARTCARD_SERVICE_CLASS);
intent.setAction(SMARTCARD_SERVICE_ACTION_V4);
ResolveInfo ri = getPackageManager().resolveService(intent, 0);
if (ri != null) {
// is version >= 4.0.0
} else {
intent.setAction(SMARTCARD_SERVICE_ACTION_PRE4);
ResolveInfo ri = getPackageManager().resolveService(intent, 0);
if (ri != null) {
// is version < 4.0.0
} else {
// is unknown version
}
}
Yet another method that allows you to distinguish between SEEK < 4.0.0 and SEEK >= 4.0.0 is to check if the SmartcardService holds the permission BIND_TERMINAL, a permission introduced in SEEK 4.0.0:
final String SMARTCARD_SERVICE_PACKAGE = "org.simalliance.openmobileapi.service";
final String PERMISSION_BIND_TERMINAL = "org.simalliance.openmobileapi.BIND_TERMINAL";
if (PackageManager.PERMISSION_GRANTED == getPackageManager().checkPermission(PERMISSION_BIND_TERMINAL, SMARTCARD_SERVICE_PACKAGE)) {
// is version >= 4.0.0
} else {
// is version < 4.0.0
}
Starting with SEEK version 4.0.0 the SEService
class of the Open Mobile API framework exposes a method getVersion()
that returns the version string of the implemented Open Mobile API specification ("3.0" for SEEK 4.0.0). Thus, you could query that method to find the implemented Open Mobile API version:
Class cls = org.simalliance.openmobileapi.SEService.class;
Method getVersion = null;
try {
getVersion = cls.getDeclaredMethod("getVersion");
} catch (NoSuchMethodException e) {}
if (getVersion != null) {
// probably SEEK >= 4.0.0
} else {
// probably SEEK < 4.0.0
}
Further, if you have an instance of the SEService
object, you could invoke the getVersion()
method to find the implemented Open Mobile API specification version:
If your application was compiled against SEEK < 4.0.0:
if (getVersion != null) {
String version = (String)getVersion.invoke(seService);
}
If your application was compiled against SEEK >= 4.0.0:
if (getVersion != null) {
String version = seService.getVersion();
}
Note that trying to obtain an instance of the SEService
class may result in exactly the undesired behavior that you found in the first place since the constructor of the SEService
class will automatically initiate the connection to the SmartcardService.
Similar to discovering the getVersion()
method, you could also try to discover methods in the API that are specific to a certain version of the Open Mobile API specification. For instance, you could test for the existence of the method
public Channel openBasicChannel(byte[] aid, byte p2);
in the Session
class (org.simalliance.openmobileapi.Session
). This method was introduced in version 3.0 of the specification.
However, you should be aware that detection based on the framework classes will only work if your app uses the Open Mobile API framework classes that are shipped with the target device and does not package its own version of the relevant framework classes. Else you would only detect what you packed into your app and not what's available on the system.
The Open Mobile API framework that is preinstalled on a device would usually be compatible with its backend (SMartcardService) on the same device. Since you seem to have version conflicts it is likely the case that your app packages its own version of the Open Mobile API framework that is incomatible with the target Android version and the Smartcard system service installed on the target device. This is something that you simply should not do.