Add option to display test + provisioning profiles #30

Closed
opened 2024-05-23 21:25:27 +02:00 by laf0rge · 5 comments

Right now it seems that EasyEUICC has a filter to filter the list of profiles to "operational" profiles. This means that users (mostly developers, system integrators, technicians) who work with test profiles are not able to use it.

If you do want to keep the default filter to "operational", I would suggest to introduce a setting by which the users can disable the filter to really see all profiles on the eUICC.

Thanks again for your work and for considering feature requests.

Right now it seems that EasyEUICC has a filter to filter the list of profiles to "operational" profiles. This means that users (mostly developers, system integrators, technicians) who work with test profiles are not able to use it. If you do want to keep the default filter to "operational", I would suggest to introduce a setting by which the users can disable the filter to really see all profiles on the eUICC. Thanks again for your work and for considering feature requests.

I agree that the test profiles need to (optionally) be visible to users. I just had to buy a SIM reader so that I could disable the test profile and enable my "operational" profile.

I agree that the test profiles need to (optionally) be visible to users. I just had to buy a SIM reader so that I could disable the test profile and enable my "operational" profile.
Author

Note: I really have absolutely no existing clue about Android development or the Kotlin language, but still decided to have a look if I can add the related functionality. Sadly, it's not as easy as I would have hoped.

Adding the preference itself is of course relatively trivial, see 8a17e5c058

But the more interesting question is how to actually make use of it.

The actual filtering seems to be done in the following piece of util/LPAUtils.kt

val List.operational: List
    get() = filter {
        it.profileClass == LocalProfileInfo.Clazz.Operational
    }

The actual code using that to retrieving the list is

            val profiles = withContext(Dispatchers.IO) {
                euiccChannelManager.notifyEuiccProfilesChanged(channel.logicalSlotId)
                channel.lpa.profiles.operational
            }

as well as

        val profiles = channel.lpa.profiles.operational.map {
            EuiccProfileInfo.Builder(it.iccid).apply {
                setProfileName(it.name)
                setNickname(it.displayName)
                setServiceProviderName(it.providerName)
                setState(
                    when (it.state) {
                        LocalProfileInfo.State.Enabled -> EuiccProfileInfo.PROFILE_STATE_ENABLED
                        LocalProfileInfo.State.Disabled -> EuiccProfileInfo.PROFILE_STATE_DISABLED
                    }
                )
                setProfileClass(
                    when (it.profileClass) {
                        LocalProfileInfo.Clazz.Testing -> EuiccProfileInfo.PROFILE_CLASS_TESTING
                        LocalProfileInfo.Clazz.Provisioning -> EuiccProfileInfo.PROFILE_CLASS_PROVISIONING
                        LocalProfileInfo.Clazz.Operational -> EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL
                    }
                )
            }.build()
        }

So my approach would be to

  • create a new visible property, then use that at the two user sites instead of the operational property
  • implement the getter get() in a way that it referencse the preference. Not sure how to do that ...
Note: I really have absolutely no existing clue about Android development or the Kotlin language, but still decided to have a look if I can add the related functionality. Sadly, it's not as easy as I would have hoped. Adding the preference itself is of course relatively trivial, see https://gitea.angry.im/laf0rge/OpenEUICC-SGP.26/commit/8a17e5c0581b5ae9b330831b613176cad2a0df23 But the more interesting question is how to actually make use of it. The actual filtering seems to be done in the following piece of `util/LPAUtils.kt` <pre> val List<LocalProfileInfo>.operational: List<LocalProfileInfo> get() = filter { it.profileClass == LocalProfileInfo.Clazz.Operational } </pre> The actual code using that to retrieving the list is <pre> val profiles = withContext(Dispatchers.IO) { euiccChannelManager.notifyEuiccProfilesChanged(channel.logicalSlotId) channel.lpa.profiles.operational } </pre> as well as <pre> val profiles = channel.lpa.profiles.operational.map { EuiccProfileInfo.Builder(it.iccid).apply { setProfileName(it.name) setNickname(it.displayName) setServiceProviderName(it.providerName) setState( when (it.state) { LocalProfileInfo.State.Enabled -> EuiccProfileInfo.PROFILE_STATE_ENABLED LocalProfileInfo.State.Disabled -> EuiccProfileInfo.PROFILE_STATE_DISABLED } ) setProfileClass( when (it.profileClass) { LocalProfileInfo.Clazz.Testing -> EuiccProfileInfo.PROFILE_CLASS_TESTING LocalProfileInfo.Clazz.Provisioning -> EuiccProfileInfo.PROFILE_CLASS_PROVISIONING LocalProfileInfo.Clazz.Operational -> EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL } ) }.build() } </pre> So my approach would be to * create a new `visible` [property](https://kotlinlang.org/docs/properties.html), then use that at the two user sites instead of the `operational` property * implement the getter get() in a way that it referencse the preference. Not sure how to do that ...

First of all, LocalProfileInfo is an imported class, coming from another project, so extending it directly is difficult, so I guess that's the reason why LPAUtilsexists in the first place, since it makes use of Kotlins' "Extensions" mechanism, i.e. extension functions:
https://kotlinlang.org/docs/extensions.html
Using it, you can "attach" additional functions to existing classes, even if they are imported. For example, you can even extend "library" functions like the List class, etc.

What I am not sure about is where the information will be provided, when which profile should be visible.
In case you will provide it, something like this should work:

val visibleList = listOf ("LocalProfileName1" , "LocalProfileName2" , "LocalProfileName3") 

and

val List<LocalProfileInfo>.visible: List<LocalProfileInfo>
  get() = filter {
    visibleList.contains(it.profileClass)
}

The list the must contain the elements which one would expect in the profileClass properties.

After that, you can replace the calls to channel.lpa.profiles.operational by channel.lpa.profiles.visible.

First of all, `LocalProfileInfo` is an imported class, coming from another project, so extending it directly is difficult, so I guess that's the reason why `LPAUtils`exists in the first place, since it makes use of Kotlins' "Extensions" mechanism, i.e. extension functions: https://kotlinlang.org/docs/extensions.html Using it, you can "attach" additional functions to existing classes, even if they are imported. For example, you can even extend "library" functions like the List class, etc. What I am not sure about is where the information will be provided, when which profile should be visible. In case you will provide it, something like this should work: ```kotlin val visibleList = listOf ("LocalProfileName1" , "LocalProfileName2" , "LocalProfileName3") ``` and ```kotlin val List<LocalProfileInfo>.visible: List<LocalProfileInfo> get() = filter { visibleList.contains(it.profileClass) } ``` The list the must contain the elements which one would expect in the `profileClass` properties. After that, you can replace the calls to `channel.lpa.profiles.operational` by `channel.lpa.profiles.visible`.
Author

Hi @retrofreak83 - thanks for taking some time to help me out here.

First of all, LocalProfileInfo is an imported class, coming from another project, so extending it directly is difficult, so I guess that's the reason why LPAUtilsexists in the first place, since it makes use of Kotlins' "Extensions" mechanism, i.e. extension functions: [...]

While I was lacking any Kotlin background knowledge, I did manage to figure that much just by looking at the code. Thanks for confirming.

What I am not sure about is where the information will be provided, when which profile should be visible.

The list of profiles is actually read from the eUICC chip inside the device (usually a smartphone). In terms of EasyEUICC, this is done by utilizing the lpac program (an external C language program) with JNI bindings. The lpac basically has three interfaces:

  • A https client (somehow mapped to the Android OS libraries for that) to talk to the eSIM backend servers (SM-DP+)
  • The APDU driver, which is how the lpac can talk to the eUICC. In the EasyEUICC integration, his happens via android OMAPI.
  • JSON-formatted output of the command lpac was instructed to perform by EasyEUICC

So basically EasyEUICC asks lpac to list the profiles, which will make lpac trigger some APDU exchanges with the eUICC, which come right back into EasyEUICC which feeds those into OMAPI calls. The responses go back from eUICC via OMAPI into lpac, which does ASN.1 decoding and outputs a JSON document towards EasyEUICC. that JSON document now contains the list of profiles.

And that list of profiles is so-far filtered/constrained to the list of operational profiles. I want to introduce a preference (see 8a17e5c058 already linked above), wich controls that filter.

After that, you can replace the calls to channel.lpa.profiles.operational by channel.lpa.profiles.visible.

The critical question is: How do I integrate that "Flow" preference in a way that my "visible" filter changes depending on whether or not that boolean prefreence is set or not? Let me know if you have any input on that. Thanks!

Hi @retrofreak83 - thanks for taking some time to help me out here. > First of all, `LocalProfileInfo` is an imported class, coming from another project, so extending it directly is difficult, so I guess that's the reason why `LPAUtils`exists in the first place, since it makes use of Kotlins' "Extensions" mechanism, i.e. extension functions: [...] While I was lacking any Kotlin background knowledge, I did manage to figure that much just by looking at the code. Thanks for confirming. > What I am not sure about is where the information will be provided, when which profile should be visible. The list of profiles is actually read from the eUICC chip inside the device (usually a smartphone). In terms of EasyEUICC, this is done by utilizing the `lpac` program (an external C language program) with JNI bindings. The lpac basically has three interfaces: * A https client (somehow mapped to the Android OS libraries for that) to talk to the eSIM backend servers (SM-DP+) * The APDU driver, which is how the lpac can talk to the eUICC. In the EasyEUICC integration, his happens via android OMAPI. * JSON-formatted output of the command lpac was instructed to perform by EasyEUICC So basically EasyEUICC asks lpac to list the profiles, which will make lpac trigger some APDU exchanges with the eUICC, which come right back into EasyEUICC which feeds those into OMAPI calls. The responses go back from eUICC via OMAPI into lpac, which does ASN.1 decoding and outputs a JSON document towards EasyEUICC. that JSON document now contains the list of profiles. And that list of profiles is so-far filtered/constrained to the list of *operational* profiles. I want to introduce a preference (see https://gitea.angry.im/laf0rge/OpenEUICC-SGP.26/commit/8a17e5c0581b5ae9b330831b613176cad2a0df23 already linked above), wich controls that filter. > After that, you can replace the calls to `channel.lpa.profiles.operational` by `channel.lpa.profiles.visible`. The critical question is: How do I integrate that "Flow" preference in a way that my "visible" filter changes depending on whether or not that boolean prefreence is set or not? Let me know if you have any input on that. Thanks!

Ah, I'm sorry, I guess I didn't really understand what you're trying to accomplish in the first place.
I'm a bit puzzled why the preference seems to be modeled as a Flow, since this is type for providing a series of values one after another.

So, most likely you will need to change add an extension function, and replace calls to the operational property by calling a function, like so:

fun List<LocalProfileInfo>.getProfiles(onlyOperational:Boolean): List<LocalProfileInfo>
{
  if (onlyOperational)
    {return this.operational}
  else
    {return this}
}

In the calling classes you then need to figure out how to access your new preference. But at least in one of them, preferenceRepository seems to be accessible from there.

Ah, I'm sorry, I guess I didn't really understand what you're trying to accomplish in the first place. I'm a bit puzzled why the preference seems to be modeled as a `Flow`, since this is type for providing a series of values one after another. So, most likely you will need to change add an extension function, and replace calls to the `operational` property by calling a function, like so: ```kotlin fun List<LocalProfileInfo>.getProfiles(onlyOperational:Boolean): List<LocalProfileInfo> { if (onlyOperational) {return this.operational} else {return this} } ``` In the calling classes you then need to figure out how to access your new preference. But at least in one of them, `preferenceRepository` seems to be accessible from there.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: PeterCxy/OpenEUICC#30
No description provided.