#!/usr/bin/env python3 """Reinstall each restored app with its original installer (pm set-installer refuses root). Same APKs, `install-multiple -r -i ` keeps data. The device comes from ANDROID_SERIAL, as for adb. Usage: fix-installers.py BACKUP_DIR WORK_DIR """ import re import subprocess import sys import time from pathlib import Path backup, work = Path(sys.argv[1]), Path(sys.argv[2]) log_file = (work / "restore.log").open("a") def log(msg: str) -> None: line = f"{time.strftime('%H:%M:%S')} {msg}" print(line, flush=True) log_file.write(line + "\n") log_file.flush() def adb(*args: str, timeout: int = 900) -> str: r = subprocess.run(["adb", *args], capture_output=True, text=True, timeout=timeout) return (r.stdout + r.stderr).strip() def installers(text: str) -> dict: # `pm list packages -i` prints "package:PKG", with -f "package:PATH=PKG". return dict(re.findall(r"[:=]([\w.]+)\s+installer=(\S+)", text)) want = installers((backup / "rooted-fresh/packages-3.txt").read_text()) now = installers(adb("shell", "pm list packages -3 -i")) disabled = set(re.findall(r"package:([\w.]+)", adb("shell", "pm list packages -d"))) log("=== installer fix-up start") for pkg, inst in sorted(want.items()): if inst == "null" or pkg not in now or now[pkg] == inst or inst not in now | {"com.android.vending": 1}: continue apks = sorted(str(p) for p in (backup / "apks" / pkg).glob("*.apk")) if not apks: continue # Reinstalling re-enables a frozen app; disable it again afterwards. out = adb("install-multiple", "-r", "-d", "-i", inst, "--bypass-low-target-sdk-block", *apks) if pkg in disabled: adb("shell", f"su -c 'pm disable-user --user 0 {pkg}'") log(f"{pkg}: installer {now[pkg]} -> {inst}: {'OK' if 'Success' in out else 'FAILED ' + out[-200:]}") log("=== installer fix-up done")