238 lines
6.6 KiB
Markdown
238 lines
6.6 KiB
Markdown
# openeuicc-bridge
|
|
|
|
An Android ContentProvider that exposes [OpenEUICC/EasyEUICC](https://gitea.angry.im/PeterCxy/OpenEUICC) LPA functionality via ADB, enabling programmatic eSIM profile management.
|
|
|
|
## Build & Install
|
|
|
|
```bash
|
|
./build+install.sh
|
|
```
|
|
|
|
The script:
|
|
1. Compiles [`LpaProvider.java`](https://github.com/Laiteux/openeuicc-bridge/blob/main/src/im/angry/openeuicc/bridge/LpaProvider.java) to `.class` files
|
|
2. Converts to `.dex` using d8, then disassembles to smali
|
|
3. Injects smali into the decompiled EasyEUICC APK
|
|
4. Rebuilds and signs the APK with [9eSIM Community Key](https://github.com/9esim/9eSIMCommunityKey)
|
|
5. Installs via `adb install`
|
|
|
|
**Requirements:** apktool, adb, java, javac
|
|
|
|
**Included:** Build tools in `tools/`, Java dependencies in `deps/`
|
|
|
|
## Content Authority
|
|
|
|
```
|
|
content://lpa/<endpoint>
|
|
```
|
|
|
|
## Endpoints
|
|
|
|
| Endpoint | Description | Input | Output |
|
|
|----------|-------------|-------|--------|
|
|
| `cards` | List eSIM cards | — | `slot`, `port`, `eid` |
|
|
| `profiles` | Get profiles on a card | `slot`, `port` | `iccid`, `enabled`, `provider`, `nickname` |
|
|
| `downloadProfile` | Download a profile | `slot`, `port`, `activationCode`¹, `address`¹, `matchingId`?, `confirmationCode`?, `imei`?, `callbackUrl`? | `iccid`, `enabled`, `provider`, `nickname` |
|
|
| `deleteProfile` | Delete a profile | `slot`, `port`, `iccid` | `success` |
|
|
| `enableProfile` | Enable a profile | `slot`, `port`, `iccid`, `refresh`?=true | `success` |
|
|
| `setProfileNickname` | Set/clear profile nickname | `slot`, `port`, `iccid`, `nickname`?="" | `success` |
|
|
| `preferences` | Get all preferences | — | `name`, `enabled` |
|
|
| `setPreference` | Set a preference | `name`, `enabled` | `success` |
|
|
|
|
¹ Provide either `activationCode` OR `address`
|
|
? = optional
|
|
|
|
## Usage Examples
|
|
|
|
#### List cards
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/cards'
|
|
```
|
|
```
|
|
Row: 0 slot=0, port=0, eid=89049032123456789012345678901234
|
|
Row: 1 slot=1, port=0, eid=89044012345678901234567890123456
|
|
```
|
|
|
|
#### List profiles
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/profiles?slot=0&port=0'
|
|
```
|
|
```
|
|
Row: 0 iccid=8901234567890123456, enabled=true, provider=Example Carrier, nickname=Work
|
|
Row: 1 iccid=8909876543210987654, enabled=false, provider=Another Carrier, nickname=NULL
|
|
```
|
|
|
|
#### Download profile
|
|
|
|
With activation code:
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/downloadProfile?slot=0&port=0&activationCode=LPA:1$smdp.example.com$ABC123'
|
|
```
|
|
|
|
With address and matching ID:
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/downloadProfile?slot=0&port=0&address=smdp.example.com&matchingId=ABC123'
|
|
```
|
|
|
|
With confirmation code:
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/downloadProfile?slot=0&port=0&activationCode=LPA:1$smdp.example.com$ABC123&confirmationCode=1234'
|
|
```
|
|
|
|
With callback URL:
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/downloadProfile?slot=0&port=0&activationCode=LPA:1$smdp.example.com$ABC123&callbackUrl=https://example.com/callback'
|
|
```
|
|
```
|
|
Row: 0 iccid=8901234567890123456, enabled=true, provider=Example Carrier, nickname=NULL
|
|
```
|
|
|
|
#### Delete profile
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/deleteProfile?slot=0&port=0&iccid=8901234567890123456'
|
|
```
|
|
```
|
|
Row: 0 success=true
|
|
```
|
|
|
|
#### Enable profile
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/enableProfile?slot=0&port=0&iccid=8901234567890123456'
|
|
```
|
|
```
|
|
Row: 0 success=true
|
|
```
|
|
|
|
#### Set profile nickname
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/setProfileNickname?slot=0&port=0&iccid=8901234567890123456&nickname=Work'
|
|
```
|
|
|
|
Clear profile nickname:
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/setProfileNickname?slot=0&port=0&iccid=8901234567890123456'
|
|
```
|
|
```
|
|
Row: 0 success=true
|
|
```
|
|
|
|
#### Get preferences
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/preferences'
|
|
```
|
|
```
|
|
Row: 0 name=verboseLogging, enabled=false
|
|
Row: 1 name=safeguardActiveProfile, enabled=true
|
|
Row: 2 name=filterProfileList, enabled=true
|
|
Row: 3 name=ignoreTlsCertificate, enabled=true
|
|
Row: 4 name=notificationsDownload, enabled=false
|
|
Row: 5 name=notificationsDelete, enabled=false
|
|
Row: 6 name=notificationsEnableDisable, enabled=false
|
|
```
|
|
|
|
#### Set preference
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/setPreference?name=ignoreTlsCertificate&enabled=true'
|
|
```
|
|
```
|
|
Row: 0 success=true
|
|
```
|
|
|
|
#### Error example
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/profiles'
|
|
```
|
|
```
|
|
Row: 0 error=missing_arg_slot
|
|
```
|
|
|
|
### JSON Output
|
|
|
|
Add the `json` parameter to any endpoint to receive results as JSON in a single `rows` column.
|
|
|
|
```bash
|
|
adb shell content query --uri 'content://lpa/profiles?slot=0&port=0&json'
|
|
```
|
|
|
|
Example outputs:
|
|
|
|
```json
|
|
// cards
|
|
[
|
|
{"slot":0,"port":0,"eid":"89049032123456789012345678901234"},
|
|
{"slot":1,"port":0,"eid":"89044012345678901234567890123456"}
|
|
]
|
|
|
|
// profiles
|
|
[
|
|
{"iccid":"8901234567890123456","enabled":true,"provider":"Example Carrier","nickname":"Work"},
|
|
{"iccid":"8909876543210987654","enabled":false,"provider":"Another Carrier","nickname":null}
|
|
]
|
|
|
|
// preferences
|
|
[
|
|
{"name":"verboseLogging","enabled":false},
|
|
{"name":"safeguardActiveProfile","enabled":true},
|
|
{"name":"filterProfileList","enabled":true},
|
|
{"name":"ignoreTlsCertificate","enabled":true},
|
|
{"name":"notificationsDownload","enabled":false},
|
|
{"name":"notificationsDelete","enabled":false},
|
|
{"name":"notificationsEnableDisable","enabled":false}
|
|
]
|
|
|
|
// success response (enableProfile, deleteProfile, setProfileNickname, setPreference)
|
|
[
|
|
{"success":true}
|
|
]
|
|
|
|
// error response
|
|
[
|
|
{"error":"missing_arg_slot"}
|
|
]
|
|
```
|
|
|
|
## Errors
|
|
|
|
Errors are returned in an `error` column:
|
|
- `no_endpoint` - No endpoint specified
|
|
- `unknown_endpoint` - Endpoint not found
|
|
- `missing_arg_<name>` - Required argument missing
|
|
- `unknown_preference_name` - Invalid preference name
|
|
- `safeguard_active_profile` - Operation blocked by safeguard
|
|
|
|
## Preferences
|
|
|
|
| Name | Description |
|
|
|------|-------------|
|
|
| `verboseLogging` | Enable verbose logging |
|
|
| `forceUseTelephonyManager` | Force use TelephonyManager API (privileged only) |
|
|
| `safeguardActiveProfile` | Prevent operations on active profile |
|
|
| `filterProfileList` | Filter to show only operational profiles |
|
|
| `ignoreTlsCertificate` | Ignore TLS certificate errors |
|
|
| `notificationsDownload` | Process download notifications |
|
|
| `notificationsDelete` | Process delete notifications |
|
|
| `notificationsEnableDisable` | Process enable/disable notifications |
|
|
|
|
## Download profile callback URL
|
|
|
|
When `callbackUrl` is provided for `downloadProfile`, progress updates are POSTed as JSON:
|
|
|
|
```json
|
|
{
|
|
"timestamp": 1770108790,
|
|
"state": "Authenticating",
|
|
"progress": 40,
|
|
"address": "smdp.example.com",
|
|
"matchingId": "ABC123",
|
|
"confirmationCode": null,
|
|
"imei": null
|
|
}
|
|
```
|