# Device Profile Generation Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build a local device profile generator that can create, persist, reload, and export stable synthetic Android device identities. **Architecture:** Add a focused `core/device_profile.py` module for pure identity generation and validation. Add a small `tools/new_device.py` CLI that uses the core module without depending on APP runtime, Frida, HAR, or `out/`. **Tech Stack:** Python 3.13, standard library only, pytest for tests. --- ## File Structure - Create `tests/test_device_profile.py` - Unit tests for profile generation, validation, persistence, cloud override, and env export. - Create `core/device_profile.py` - Dataclass model, generator, validation helpers, JSON persistence, env export. - Create `tools/new_device.py` - CLI for generating one or more profiles. - Modify `core/__init__.py` - Export `DeviceProfile`, `DeviceProfileGenerator`, `load_device_profile`, and `save_device_profile`. --- ### Task 1: Add failing tests for device profile core **Files:** - Create: `tests/test_device_profile.py` - Create later: `core/device_profile.py` - [ ] **Step 1: Write the failing tests** ```python import hashlib import json import pytest from core.device_profile import ( DeviceProfile, DeviceProfileGenerator, load_device_profile, save_device_profile, ) def test_generator_creates_consistent_local_identity(): profile = DeviceProfileGenerator(seed=1234).new_profile() assert len(profile.android_id) == 16 assert profile.android_id == profile.android_id.lower() int(profile.android_id, 16) assert profile.o_did == f"ANDROID_{profile.android_id}" assert profile.local_did.startswith("ANDROID_") assert profile.did == profile.local_did expected_rdid = hashlib.md5(profile.g_rdi2.encode("utf-8")).hexdigest()[16:32] assert profile.rdid == f"ANDROID_{expected_rdid}" def test_profile_persistence_roundtrip(tmp_path): path = tmp_path / "device.json" profile = DeviceProfileGenerator(seed=5678).new_profile() save_device_profile(profile, path) loaded = load_device_profile(path) assert loaded == profile assert json.loads(path.read_text(encoding="utf-8"))["android_id"] == profile.android_id def test_apply_cloud_identity_updates_only_server_fields(): profile = DeviceProfileGenerator(seed=9012).new_profile() old_android_id = profile.android_id old_o_did = profile.o_did old_rdid = profile.rdid profile.apply_cloud_identity( did="ANDROID_e8dfd2f16b618053", cdid_tag=2, egid="DFP68CA12B5D3C714E4439D5E255B197DA809D63CB77139F1420A763F53FE718", ) assert profile.did == "ANDROID_e8dfd2f16b618053" assert profile.cdid_tag == 2 assert profile.egid == "DFP68CA12B5D3C714E4439D5E255B197DA809D63CB77139F1420A763F53FE718" assert profile.android_id == old_android_id assert profile.o_did == old_o_did assert profile.rdid == old_rdid def test_env_export_contains_expected_identity_keys(): profile = DeviceProfileGenerator(seed=3456).new_profile() env_text = profile.to_env() assert f"KS_ANDROID_ID={profile.android_id}" in env_text assert f"KS_DID={profile.did}" in env_text assert f"KS_ODID={profile.o_did}" in env_text assert f"KS_RDID={profile.rdid}" in env_text assert f"KS_LOCAL_DID={profile.local_did}" in env_text @pytest.mark.parametrize( "field,value", [ ("android_id", "XYZ"), ("local_did", "BAD"), ("did", "BAD"), ("o_did", "BAD"), ("rdid", "BAD"), ], ) def test_profile_validation_rejects_invalid_identity_fields(field, value): data = DeviceProfileGenerator(seed=7890).new_profile().to_dict() data[field] = value with pytest.raises(ValueError): DeviceProfile.from_dict(data) ``` - [ ] **Step 2: Run test to verify it fails** Run: ```powershell uv run pytest tests/test_device_profile.py -v ``` Expected: ```text ModuleNotFoundError: No module named 'core.device_profile' ``` --- ### Task 2: Implement core device profile module **Files:** - Create: `core/device_profile.py` - Modify: `core/__init__.py` - Test: `tests/test_device_profile.py` - [ ] **Step 1: Create `core/device_profile.py`** ```python from __future__ import annotations import hashlib import json import random import re import time import uuid from dataclasses import asdict, dataclass from pathlib import Path from typing import Any ANDROID_ID_RE = re.compile(r"^[0-9a-f]{16}$") ANDROID_PREFIX_RE = re.compile(r"^ANDROID_[0-9a-f]{16}$") EGID_RE = re.compile(r"^$|^DFP[0-9A-F]{61}$") @dataclass class DeviceProfile: android_id: str local_did: str did: str o_did: str rdid: str g_rdi2: str cdid_tag: int = 0 egid: str = "" install_time_ms: int = 0 cold_launch_time_ms: int = 0 sid: str = "" def __post_init__(self) -> None: self.validate() def validate(self) -> None: if not ANDROID_ID_RE.fullmatch(self.android_id): raise ValueError(f"invalid android_id: {self.android_id!r}") for name in ("local_did", "did", "o_did", "rdid"): value = getattr(self, name) if not ANDROID_PREFIX_RE.fullmatch(value): raise ValueError(f"invalid {name}: {value!r}") if self.o_did != f"ANDROID_{self.android_id}": raise ValueError("o_did must equal ANDROID_") expected_rdid = hashlib.md5(self.g_rdi2.encode("utf-8")).hexdigest()[16:32] if self.rdid != f"ANDROID_{expected_rdid}": raise ValueError("rdid must equal ANDROID_") if not isinstance(self.cdid_tag, int) or self.cdid_tag < 0: raise ValueError(f"invalid cdid_tag: {self.cdid_tag!r}") if not EGID_RE.fullmatch(self.egid): raise ValueError(f"invalid egid: {self.egid!r}") def apply_cloud_identity(self, did: str, cdid_tag: int, egid: str = "") -> None: self.did = did self.cdid_tag = cdid_tag if egid: self.egid = egid self.validate() def to_dict(self) -> dict[str, Any]: return asdict(self) @classmethod def from_dict(cls, data: dict[str, Any]) -> "DeviceProfile": return cls( android_id=str(data["android_id"]), local_did=str(data["local_did"]), did=str(data["did"]), o_did=str(data["o_did"]), rdid=str(data["rdid"]), g_rdi2=str(data["g_rdi2"]), cdid_tag=int(data.get("cdid_tag", 0)), egid=str(data.get("egid", "")), install_time_ms=int(data.get("install_time_ms", 0)), cold_launch_time_ms=int(data.get("cold_launch_time_ms", 0)), sid=str(data.get("sid", "")), ) def to_env(self) -> str: lines = [ f"KS_ANDROID_ID={self.android_id}", f"KS_DID={self.did}", f"KS_LOCAL_DID={self.local_did}", f"KS_ODID={self.o_did}", f"KS_RDID={self.rdid}", f"KS_GRDI2={self.g_rdi2}", f"KS_CDID_TAG={self.cdid_tag}", f"KS_EGID={self.egid}", f"KS_INSTALL_TIME_MS={self.install_time_ms}", f"KS_COLD_LAUNCH_TIME_MS={self.cold_launch_time_ms}", f"KS_SID={self.sid}", ] return "\n".join(lines) + "\n" class DeviceProfileGenerator: def __init__(self, seed: int | None = None) -> None: self._random = random.Random(seed) def new_profile(self) -> DeviceProfile: now_ms = int(time.time() * 1000) android_id = self._hex16() local_did = f"ANDROID_{self._hex16()}" g_rdi2 = self._g_rdi2() rdid_suffix = hashlib.md5(g_rdi2.encode("utf-8")).hexdigest()[16:32] return DeviceProfile( android_id=android_id, local_did=local_did, did=local_did, o_did=f"ANDROID_{android_id}", rdid=f"ANDROID_{rdid_suffix}", g_rdi2=g_rdi2, install_time_ms=now_ms - self._random.randint(10_000, 600_000), cold_launch_time_ms=now_ms, sid=str(uuid.UUID(int=self._random.getrandbits(128))), ) def _hex16(self) -> str: return f"{self._random.getrandbits(64):016x}" def _g_rdi2(self) -> str: parts = [] for _ in range(5): left = self._random.choice((7, 8, 9)) * 100_000_000 + self._random.randint(0, 999_999) right = self._random.choice((4741, 8641)) parts.append(f"{left}::{right}") return "|".join(parts) def save_device_profile(profile: DeviceProfile, path: str | Path) -> None: target = Path(path) target.parent.mkdir(parents=True, exist_ok=True) target.write_text( json.dumps(profile.to_dict(), ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8", ) def load_device_profile(path: str | Path) -> DeviceProfile: source = Path(path) try: data = json.loads(source.read_text(encoding="utf-8")) except Exception as exc: raise ValueError(f"failed to load device profile: {source}") from exc if not isinstance(data, dict): raise ValueError(f"device profile must be a JSON object: {source}") return DeviceProfile.from_dict(data) ``` - [ ] **Step 2: Export from `core/__init__.py`** ```python from .device_profile import ( DeviceProfile, DeviceProfileGenerator, load_device_profile, save_device_profile, ) __all__ = [ "DeviceProfile", "DeviceProfileGenerator", "load_device_profile", "save_device_profile", ] ``` - [ ] **Step 3: Run tests** Run: ```powershell uv run pytest tests/test_device_profile.py -v ``` Expected: ```text 8 passed ``` --- ### Task 3: Add CLI for generating profiles **Files:** - Create: `tools/new_device.py` - Test with smoke commands. - [ ] **Step 1: Create `tools/new_device.py`** ```python from __future__ import annotations import argparse from pathlib import Path from core.device_profile import DeviceProfileGenerator, save_device_profile def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Generate local Kuaishou device profiles") parser.add_argument("--count", type=int, default=1) parser.add_argument("--out-dir", default="out/devices") parser.add_argument("--prefix", default="device") parser.add_argument("--seed", type=int, default=None) parser.add_argument("--env", action="store_true", help="also write .env files") parser.add_argument("--force", action="store_true", help="overwrite existing files") return parser def main() -> int: args = build_parser().parse_args() if args.count < 1: raise SystemExit("--count must be >= 1") out_dir = Path(args.out_dir) out_dir.mkdir(parents=True, exist_ok=True) generator = DeviceProfileGenerator(seed=args.seed) for index in range(1, args.count + 1): profile = generator.new_profile() stem = f"{args.prefix}_{index:03d}" json_path = out_dir / f"{stem}.json" env_path = out_dir / f"{stem}.env" if not args.force and json_path.exists(): raise SystemExit(f"refusing to overwrite existing file: {json_path}") if args.env and not args.force and env_path.exists(): raise SystemExit(f"refusing to overwrite existing file: {env_path}") save_device_profile(profile, json_path) if args.env: env_path.write_text(profile.to_env(), encoding="utf-8") print(f"[OK] {json_path} did={profile.did} odid={profile.o_did} rdid={profile.rdid}") return 0 if __name__ == "__main__": raise SystemExit(main()) ``` - [ ] **Step 2: Run CLI smoke test** Run: ```powershell uv run python tools/new_device.py --count 2 --out-dir out/devices_test --seed 1 --env --force ``` Expected: ```text [OK] out\devices_test\device_001.json ... [OK] out\devices_test\device_002.json ... ``` - [ ] **Step 3: Compile check** Run: ```powershell uv run python -m compileall core tools tests ``` Expected: ```text Listing 'core'... Listing 'tools'... Listing 'tests'... ``` --- ### Task 4: Final verification **Files:** - Verify: `core/device_profile.py` - Verify: `tools/new_device.py` - Verify: `tests/test_device_profile.py` - [ ] **Step 1: Run focused tests** Run: ```powershell uv run pytest tests/test_device_profile.py -v ``` Expected: ```text 8 passed ``` - [ ] **Step 2: Run existing test suite** Run: ```powershell uv run pytest tests -v ``` Expected: ```text all tests passed ``` - [ ] **Step 3: Generate sample profile** Run: ```powershell uv run python tools/new_device.py --count 1 --out-dir out/devices_sample --seed 20260711 --env --force ``` Expected: ```text [OK] out\devices_sample\device_001.json did=ANDROID_... ``` Generated files: ```text out/devices_sample/device_001.json out/devices_sample/device_001.env ``` --- ## Self-Review - Spec coverage: local generation, persistence, env export, cloud override, and validation are covered. - Placeholder scan: no `TBD`, `TODO`, or unspecified code steps remain. - Type consistency: `DeviceProfile`, `DeviceProfileGenerator`, `save_device_profile`, and `load_device_profile` names match across all tasks. - Scope check: online DFP bootstrap remains out of phase 1 by design.