132 lines
3.6 KiB
Markdown
132 lines
3.6 KiB
Markdown
# Device Profile Generation Design
|
|
|
|
## Goal
|
|
|
|
Build a self-contained device identity generation pipeline for producing many stable,
|
|
self-consistent Android device profiles without depending on live APP runtime state.
|
|
|
|
The first deliverable is local identity generation and persistence. Online DFP
|
|
bootstrap is intentionally separated into a later step because `egid` and cloud
|
|
`did` are server-issued values and require a coherent `deviceInfo` payload.
|
|
|
|
## Current Findings
|
|
|
|
Known runtime relationships:
|
|
|
|
- `oDid = "ANDROID_" + android_id`
|
|
- `rdid = "ANDROID_" + md5(gRdi2)[16:32]`
|
|
- local fallback `did` can be generated independently
|
|
- cloud `did` and `cdid_tag` override local fallback values after unifiedId refresh
|
|
- `egid` is returned by DFP report, not derived by a local hash
|
|
|
|
Observed sample:
|
|
|
|
```text
|
|
android_id=46a032e0a2af8184
|
|
oDid=ANDROID_46a032e0a2af8184
|
|
gRdi2=799999139::8641|899999556::8641|999999345::4741|899999995::8641|999999515::4741
|
|
md5(gRdi2)=be49e5841412c571741de4351c44850d
|
|
rdid=ANDROID_741de4351c44850d
|
|
did=ANDROID_e8dfd2f16b618053
|
|
cdid_tag=2
|
|
```
|
|
|
|
## Scope
|
|
|
|
### Included in phase 1
|
|
|
|
- Generate local Android identity fields.
|
|
- Generate a stable `gRdi2` string and matching `rdid`.
|
|
- Generate a local fallback `did`.
|
|
- Persist and reload profiles without changing identities.
|
|
- Apply server identity values later through an explicit update method.
|
|
- Export profiles as JSON and `.env` snippets for other scripts.
|
|
|
|
### Excluded from phase 1
|
|
|
|
- Calling DFP/unifiedId services.
|
|
- Producing a guaranteed valid `egid`.
|
|
- Replacing `main.py` task runner behavior.
|
|
- Reusing HAR files as runtime templates.
|
|
|
|
## Architecture
|
|
|
|
### `core/device_profile.py`
|
|
|
|
Owns device identity data and local generation rules.
|
|
|
|
Main objects:
|
|
|
|
- `DeviceProfile`: serializable profile model.
|
|
- `DeviceProfileGenerator`: creates new profiles from a random source.
|
|
- `load_device_profile(path)`: loads a persisted profile.
|
|
- `save_device_profile(profile, path)`: writes profile JSON.
|
|
|
|
### `tools/new_device.py`
|
|
|
|
Small CLI wrapper around the core generator.
|
|
|
|
Responsibilities:
|
|
|
|
- create one or more profiles
|
|
- save JSON files
|
|
- optionally print `.env` format
|
|
- avoid depending on APP, Frida, HAR, or `out/`
|
|
|
|
### Tests
|
|
|
|
`tests/test_device_profile.py` verifies:
|
|
|
|
- android_id is 16 lowercase hex characters
|
|
- `oDid` matches android_id
|
|
- `rdid` matches `md5(gRdi2)[16:32]`
|
|
- persisted profile reloads identically
|
|
- cloud identity update changes `did`, `cdid_tag`, and `egid` only when explicit
|
|
|
|
## Data Flow
|
|
|
|
```text
|
|
new_device.py
|
|
-> DeviceProfileGenerator.new_profile()
|
|
-> DeviceProfile.to_dict()
|
|
-> save JSON / print env
|
|
|
|
existing JSON
|
|
-> load_device_profile()
|
|
-> use stable identity in request builders
|
|
|
|
server response
|
|
-> profile.apply_cloud_identity(did, cdid_tag, egid)
|
|
-> save JSON
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
- Invalid `android_id`, `did`, `oDid`, `rdid`, or `egid` values raise `ValueError`.
|
|
- Loading malformed JSON raises `ValueError` with the file path.
|
|
- Existing output files are not overwritten unless the CLI receives `--force`.
|
|
- Batch generation creates separate files and fails fast on duplicate filenames.
|
|
|
|
## Testing Strategy
|
|
|
|
Use TDD:
|
|
|
|
1. Write failing tests for local generation and persistence.
|
|
2. Implement minimal core code to pass tests.
|
|
3. Add CLI tests or smoke checks.
|
|
4. Run focused tests and compile checks before claiming completion.
|
|
|
|
## Future Phase
|
|
|
|
After phase 1, add an online bootstrap layer:
|
|
|
|
```text
|
|
DeviceProfile
|
|
-> build DFP fetch/check/repair/report forms
|
|
-> call unifiedId / gdfp report
|
|
-> apply cloud did / cdid_tag / egid
|
|
```
|
|
|
|
That layer should migrate useful code out of `out/build_dfp_*.py` into `core/`
|
|
without making `main.py` depend on HAR templates.
|