009: publish AXPR23 final source and public documentation
This commit is contained in:
1 parent
72930e0fd6
commit
2c714d8d34
35 files changed
+2498
-403
No files matched your search
@@ -18,7 +18,7 @@ unless the owner explicitly asks.
|
||||
|
||||
## Sources of truth
|
||||
|
||||
Resume point: `docs/009-handoff-20261008.md` (closure
|
||||
Resume point: `docs/009-handoff-20261016.md` (closure
|
||||
`docs/009-closure-parity-20261009.md`). Doc index: `docs/README.md`.
|
||||
Next LOS sync / first project OTA zip: `docs/009-upstream-rebase-ota.md`.
|
||||
Read the handoff before older remaining-work docs; many fortify/CVE/`007`
|
||||
|
||||
@@ -8,13 +8,12 @@ This tree is a **parallel product track** to `sunfish-a16-rom` (KernelSU /
|
||||
split-roadmap standby). Same Spec Kit / docs home pattern; **not** a
|
||||
SSYSTEMS / buildout-saar project.
|
||||
|
||||
## Paths
|
||||
## Project locations
|
||||
|
||||
- Laptop workspace: `/home/aventura/Workspaces/AXP_Resurection`
|
||||
- Sibling standby (KSU track): `/home/aventura/Workspaces/sunfish-a16-rom`
|
||||
- Gitea (rom-dev01): `https://a16.ssystems.de/` — repo
|
||||
`sunfish-admin/AXP-Resurection` (private)
|
||||
- Builds stay on `rom-dev01`; laptop = Cursor + adb/fastboot
|
||||
- Public canonical repository: `https://gitea.vbits.me/sunfish-admin/AXP-Resurection`
|
||||
- Builds stay on the A16 `rom-dev01` worker; local checkouts are used for
|
||||
development and device tooling.
|
||||
- The sibling KernelSU/split-roadmap tree remains a separate track.
|
||||
|
||||
## Active feature
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# AXPR23 — end of life (2026-10-26)
|
||||
|
||||
Status: **EOL / archived**. No new features, no new AXPR23 builds. AXPR24
|
||||
(LOS24 / Android 17) is the active line. AXPR23 remains the reference for how
|
||||
the LOS port, the AXP packaging and the WebView integration were done.
|
||||
|
||||
## Final shipped artifact
|
||||
|
||||
- ZIP: `AXP-Resurection-Pro-23.2-20261025-sunfish.zip`
|
||||
- The signed ZIP is retained in the build archive; generated ROM artifacts are
|
||||
intentionally not stored in Git.
|
||||
- SHA-256: `9d205fd3cc945438dfff3a23b129bf88516c7654f5922bc2ee4d478f9485eef0`
|
||||
- Status at build: `FLASH_READY`
|
||||
- Signed with the project release key
|
||||
(`C=DE, O=AXP-Resurection, OU=sunfish-a16, CN=release`), cert SHA-256
|
||||
`694a8941b64850a2acd0739c3757286c918eb16cc9788a2d7201222f351f1876`.
|
||||
- Installed on the designated reference sunfish via A/B `adb sideload` without
|
||||
a wipe; it booted and was verified.
|
||||
|
||||
## Verified state on the flashed device
|
||||
|
||||
- `ro.lineage.version`: `23.2-20261025-PRO-sunfish`.
|
||||
- WebView: `org.axpos.aosmium_wv` only, native provider through Lineage's static
|
||||
RRO (`framework-res__lineage_sunfish__auto_generated_rro_product.apk`), order:
|
||||
Google entries (all `availableByDefault=false`) → AOSmium
|
||||
(`availableByDefault=true`) → AOSP. LOS `webview` prebuilt removed from the
|
||||
image by `apply_009_single_webview.py`.
|
||||
- Runtime WebView smoke (harness `com.axpr.wvtest`): HTTPS load, reduced Vanadium
|
||||
UA, JS, WASM, Canvas2D, WebGL 2.0, WebCrypto, Blob fetch, localStorage,
|
||||
AudioContext, WebRTC — all pass; sandboxed renderer with seccomp.
|
||||
- `hardened_malloc`: linked statically into 64-bit libc
|
||||
(`h_malloc_info`, `h_malloc_trim`, … present in the runtime APEX libc);
|
||||
32-bit stays Scudo.
|
||||
- `configstore` HAL crash loop: gone (0 occurrences; `disable_configstore` stub
|
||||
present, HAL binary and rc absent).
|
||||
- No `FATAL EXCEPTION`, ANR, watchdog, tombstone or lowmemorykiller events in the
|
||||
audit window. Remaining errors are AOSP telephony/RIL/SIM messages and
|
||||
expected avc denials from apps probing `/proc` or the sensors HAL touching
|
||||
`diag_device`.
|
||||
|
||||
## Known device-side caveat (not part of the ROM)
|
||||
|
||||
Vector 2.2 (bootloader-spoof host) ships a `sepolicy.rule` that Magisk 30.7 does
|
||||
not load, and it lacks the Android 16 `artd` path, so dexopt fails while Vector
|
||||
is installed. Policy-only Magisk module
|
||||
`axpr-vector-dex2oat` (v3) fixes it:
|
||||
|
||||
- `/sdcard/Download/axpr-vector-dex2oat-v3.zip`
|
||||
- SHA-256 `e30194c26070031debad0ad0eabff1065b386de7b103b714220a5c6ae51a9881`
|
||||
- Delivered via `post-fs-data.sh` + `magiskpolicy --live` (no binaries, no hooks).
|
||||
- With it: `VectorDex2Oat` spoofs its cmdline, `dex2oat returned code 0`,
|
||||
`cmd package compile` succeeds.
|
||||
- v3 installed on GOLDEN on 2026-10-26 and verified live: policy rules present
|
||||
(`artd xposed_file`), `cmd package compile -m verify -f com.aurora.store` =
|
||||
Success, 0 `art_exec` aborts. Persistence across reboot is the only remaining
|
||||
confirmation (v3 ships the complete rule set in `policy.rules`).
|
||||
|
||||
Details and the full rule list: `docs/010-axpr-webview-los23-port.md`.
|
||||
|
||||
## Upstream context
|
||||
|
||||
LineageOS 23.2 for sunfish is frozen: last nightly `20261008` (SDK 36, patch
|
||||
level 2026-09-01); `24.0` nightlies start `20261015` (SDK 37, patch level
|
||||
2026-10-01). LineageOS does not keep both lines per device. Therefore AXPR23
|
||||
cannot receive further upstream security patches and is intentionally closed.
|
||||
|
||||
## What stays authoritative for AXPR24
|
||||
|
||||
- The LOS→AXP port method (`scripts/009-*.sh`, `apply_009_*.py`).
|
||||
- AXP packaging/signing shape (release keys, AVB flags, Magisk preroot).
|
||||
- WebView adaptation chain Vanadium → Mulch → AOSmium, the prebuilt module
|
||||
pattern and the provider overlay/RRO lesson.
|
||||
- The device-side IME/kernel notes in `docs/009-*`.
|
||||
@@ -245,6 +245,39 @@ against Kconfig `depends on` before flipping.
|
||||
IDs); Lineage rarely matches AXP here.
|
||||
- **Flash**: owner on return; agent does not need to flash for this phase.
|
||||
|
||||
## 2026-10-16 cgroup and dirty-source audit
|
||||
|
||||
The 20261016 GOLDEN build reports a cgroup v2 mount, but the kernel exposes no
|
||||
v2 controllers at the root. `/proc/cgroups` also lacks `memory` and `pids`, and
|
||||
the log contains:
|
||||
|
||||
```text
|
||||
Activation of cgroup controller +memory failed ... Invalid argument
|
||||
```
|
||||
|
||||
The source Kconfig has independent `MEMCG` and `CGROUP_PIDS` options, but the
|
||||
ship defconfig does not enable either. This is a real kernel configuration gap,
|
||||
not an Android app issue. `lmkd` still works through the legacy low-memory path,
|
||||
so it is not an immediate boot blocker, but Android 16 UID memory isolation is
|
||||
incomplete.
|
||||
|
||||
Do not flip these options directly in the current ship tree. A candidate must
|
||||
be built in a disposable clean worktree, retain `SLAB_FREELIST_RANDOM=y`, Yama
|
||||
unset, `USERFAULTFD` off and all existing ship patches, then be checked for:
|
||||
|
||||
1. boot and USB/ADB recovery;
|
||||
2. cgroup v2 `memory`/`pids` controller availability and UID activation;
|
||||
3. lmkd behavior, memory pressure, camera, media, radio, Wi-Fi and Bluetooth;
|
||||
4. kernel module ABI and vendor SPL compatibility.
|
||||
|
||||
The current host kernel is also intentionally patched in place: 503 modified
|
||||
paths contain the Stage1/CVE/hardening result, and 13 untracked `*.orig` patch
|
||||
backups remain. That is why `uname` ends in `-dirty`. Do not delete or reset
|
||||
those files in the canonical tree. A future final bacon should use a disposable
|
||||
worktree with patch provenance recorded and no `.orig` residue, or explicitly
|
||||
document why the generated kernel is accepted as dirty. The current 20261016
|
||||
artifact remains the rollback/reference build until that decision is made.
|
||||
|
||||
## 2026 catalog refresh (started 2026-09-19)
|
||||
|
||||
CIP `kernel-sec` cloned at `/srv/rom/references/cip-kernel-sec` @ `025be706`
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
Owner cadence: refresh bundled **system / privileged** packages about every
|
||||
**2–3 months** without a full ROM reflash. **No** AXP OTA / external repos.
|
||||
|
||||
Status: **stub** — fill before claiming FR-019 done. Packaging of the
|
||||
shipping image MUST already preserve update eligibility.
|
||||
Status: **documented** — commands and signing gates below define the intended
|
||||
refresh path. The owner has also verified full-ROM upgrades over existing
|
||||
userdata without a wipe. A real individual-APK refresh drill can wait for the
|
||||
final ROM candidate; packaging of the shipping image MUST preserve update
|
||||
eligibility.
|
||||
|
||||
**Full ROM updates** (new LOS base + rebacon) are a different path: recovery
|
||||
sideload of the project zip. See [`009-upstream-rebase-ota.md`](009-upstream-rebase-ota.md).
|
||||
@@ -31,3 +34,97 @@ sideload of the project zip. See [`009-upstream-rebase-ota.md`](009-upstream-reb
|
||||
- [ ] Privileged permissions still grant after update (or re-flash privapp XML noted)
|
||||
- [ ] Magisk version after update matches kernel/boot expectation
|
||||
- [ ] Record new versions in `docs/009-version-pins.md`
|
||||
|
||||
## Preparation and safety
|
||||
|
||||
1. Keep the last known-good full ROM zip and its SHA-256. APK refreshes do not
|
||||
replace a boot/kernel rollback path.
|
||||
2. Download the new APK from the upstream/project source into a host-only
|
||||
staging directory. Never put it in Git or paste its bytes into evidence.
|
||||
3. Verify the APK before connecting the phone:
|
||||
|
||||
```bash
|
||||
apksigner verify --verbose --print-certs NEW.apk
|
||||
sha256sum NEW.apk
|
||||
```
|
||||
|
||||
4. Compare the signer with the installed package. For a system app, first
|
||||
record `adb shell pm path PACKAGE` and the installed version. Do not use
|
||||
`-d` for routine refreshes and do not accept a signer mismatch.
|
||||
|
||||
## microG / GSF Proxy
|
||||
|
||||
For a compatible same-signer update on a test device:
|
||||
|
||||
```bash
|
||||
adb install -r GmsCore.apk
|
||||
adb install -r GsfProxy.apk
|
||||
adb shell pm path com.google.android.gms
|
||||
adb shell dumpsys package com.google.android.gms | grep -E 'versionName=|versionCode='
|
||||
adb shell dumpsys package com.google.android.gsf | grep -E 'versionName=|versionCode='
|
||||
```
|
||||
|
||||
Then run microG Self-Check, open Phonesky, and verify one ordinary app update.
|
||||
If a package is privileged or its signer/permissions change, stop and rebuild
|
||||
the image or provide a recovery package with the matching privapp XML; do not
|
||||
paper over the rejection with a weaker permission policy.
|
||||
|
||||
## Project Phonesky
|
||||
|
||||
Phonesky must remain the project-signed artifact and must retain the same
|
||||
package identity and signer as the image:
|
||||
|
||||
```bash
|
||||
adb install -r Phonesky.apk
|
||||
adb shell pm path com.android.vending
|
||||
adb shell dumpsys package com.android.vending | grep -E 'versionName=|versionCode='
|
||||
adb shell monkey -p com.android.vending 1
|
||||
```
|
||||
|
||||
Verify an ordinary install/update through Phonesky. A signature mismatch,
|
||||
privileged-permission loss, or broken microG path is a failed refresh; restore
|
||||
the previous APK or the known-good ROM rather than accepting the update.
|
||||
|
||||
## Other bundled system apps
|
||||
|
||||
Aurora, Droidify, Fossify Gallery, Jelly and OpenEUICC may be refreshed only
|
||||
with an APK matching the shipped package identity and signing policy:
|
||||
|
||||
```bash
|
||||
adb install -r APP.apk
|
||||
adb shell pm path PACKAGE
|
||||
adb shell dumpsys package PACKAGE | grep -E 'versionName=|versionCode='
|
||||
```
|
||||
|
||||
For an app whose update must retain system/privileged permissions, verify its
|
||||
normal function and the relevant permissions after install. If PackageManager
|
||||
places the update under `/data/app/`, that is expected only when the signer and
|
||||
permission model remain valid; record the path change and any required XML
|
||||
handling in the version pin record.
|
||||
|
||||
## Magisk
|
||||
|
||||
Magisk manager APK updates and boot-preroot updates are separate:
|
||||
|
||||
- Manager-only update: use the manager's normal update path, then verify
|
||||
`magisk -v`, Zygisk/DenyList behavior, and the test package before banking
|
||||
experiments.
|
||||
- Boot/runtime update: patch and test a new boot image with the matching
|
||||
Magisk version. This is not an APK-only refresh and must use the full build
|
||||
and LAB-only flash procedure; never mix a new Magisk manager with an older
|
||||
preroot boot image casually.
|
||||
|
||||
## Post-update evidence
|
||||
|
||||
Record the following under the relevant host artifact or release note:
|
||||
|
||||
```bash
|
||||
adb shell getprop ro.lineage.version
|
||||
adb shell pm path PACKAGE
|
||||
adb shell dumpsys package PACKAGE
|
||||
adb shell logcat -d -t 500 | grep -E 'PackageManager|avc: denied|FATAL EXCEPTION'
|
||||
```
|
||||
|
||||
Reboot once after privileged/system-package changes and repeat the package
|
||||
path, permissions, microG Self-Check, Phonesky install, and WebView checks.
|
||||
Record versions and SHA-256 values in `docs/009-version-pins.md`.
|
||||
@@ -1,19 +1,22 @@
|
||||
# 009 version pins
|
||||
|
||||
Policy: newest stable (or newest tagged pre-release upstream ships) that works
|
||||
on sunfish / LOS 23.2 — except Phonesky = project artifact, WebView = LOS
|
||||
default. Refresh with `git fetch --tags` / release APIs before long builds.
|
||||
Never ship from stale laptop caches.
|
||||
on sunfish / LOS 23.2 — except Phonesky = project artifact and WebView = AXPR
|
||||
AOSmium (default, only engine shipped; LOS/Google remain allowed). Refresh with
|
||||
`git fetch --tags` / release APIs before long builds. Never ship from stale
|
||||
laptop caches.
|
||||
|
||||
| component | version_name | version_code | upstream_ref | policy | verified_on | notes |
|
||||
|-----------|--------------|--------------|--------------|--------|-------------|-------|
|
||||
| VanillaBaseline | lineage-23.2 sunfish | | `/srv/rom/artifacts/003-vanilla/20260914-lineage-23.2-sunfish` | los_default | 2026-09-14 | `repo sync` finished 2026-09-13. **Not** the 2026-10-08 nightly |
|
||||
| Next OTA upstream pin | lineage-23.2-20261008-nightly-sunfish | | https://download.lineageos.org/devices/sunfish/builds | owner pin | pending | Rebuild via `scripts/009-rebuild-from-los.sh` after `LOS_SYNC=1` + manifest diff |
|
||||
| VanillaBaseline | lineage-23.2-20261008 sunfish | 36 | `/srv/rom/artifacts/003-vanilla/20260914-lineage-23.2-sunfish` | los_default | 2026-10-25 | Revisions verified equal to the official `lineageos-23.2-sunfish-20261008-build-manifest.xml` (1130 vs 1129 projects, 0 differing). This is the **last** 23.2 nightly for sunfish |
|
||||
| AXPR24 upstream pin | lineage-24.0-20261022-nightly-sunfish | 37 | https://download.lineageos.org/devices/sunfish/builds | owner pin (AXPR24) | 2026-10-25 | SHA-256 `faddd497fe34fb8b57a46f42aa27b38f3276b8dc8a10fea5e91a93ac51e08f03`; 24.0 replaced 23.2 on sunfish from `20261015` (no parallel builds per device) |
|
||||
| Current 20261016 source snapshot | lineage-23.2 host tree | | `repo manifest -r` on `rom-dev01` | observed snapshot, not official pin | 2026-10-16 | Manifest SHA256 `44467aa549a9ce50b6c636d60b4bba3d01c398c659b7dff5d61fced2d365c4b1`; verified 2026-10-25: equals the official 20261008 revisions |
|
||||
| microG GmsCore | v0.3.17.252432 | 252432034 | https://github.com/microg/GmsCore/releases/tag/v0.3.17.252432 | newest_stable | 2026-10-06 staged | default flavor, not `-hw`. PRESIGNED on `system_ext`. GOLDEN live overlay was 0.3.16.1 |
|
||||
| GSF Proxy | v0.1.0 | | microG official | newest_stable | 2026-09-16 staged | |
|
||||
| Phonesky | 52.9.22-24 | | project patched host artifact | project_phonesky | 2026-09-14 | `/srv/rom/prebuilts/phonesky/`; never Git |
|
||||
| Magisk | v30.7 | | https://github.com/topjohnwu/Magisk/releases/tag/v30.7 | newest_stable | 2026-09-17 | Staged APK; GOLDEN oracle **29.0** layout only |
|
||||
| Kernel sunfish | 4.14.357-openela | | `kernel/google/msm-4.14` @ `28f9290ae067` | los_default + FR-017 stage1 | 2026-09-18 | AXP `kernel_patches` @ `df3cab2155b3`; see `docs/009-kernel-hardening-map.md` |
|
||||
| Current 20261016 kernel image | `4.14.357-openela-g28f9290ae067-dirty` | `#82` | host patched tree | observed artifact | 2026-10-16 | `-dirty` reflects the uncommitted applied hardening/CVE tree; clean-source policy is open |
|
||||
| Aurora Store | present on GOLDEN | | `com.aurora.store` | newest_stable | 2026-09-17 seen | Stage APK later |
|
||||
| F-Droid | present on GOLDEN | | `org.fdroid.fdroid` | newest_stable | 2026-09-17 seen | |
|
||||
| Fossify Gallery | present on GOLDEN | | `org.fossify.gallery` | newest_stable | 2026-09-17 seen | |
|
||||
|
||||
@@ -19,6 +19,7 @@ Do **not** delete these. Owner may retry on the current `009` kernel/userspace.
|
||||
| Pre-closure acceptance | `../009-lab-acceptance-20261002-*`, `../009-user-zip-audit-20261007.md` | Older `UNOFFICIAL` / `dev-keys` zips |
|
||||
| Vanilla LOS | `../003-*` | First `repo sync` / `mka bacon` (done) |
|
||||
| Speckit dumps | `../sunfish-a16-rom-speckit-dump*.txt`, `../001-*-review-pack.txt` | Import noise |
|
||||
| USB migration / polish | `009-usb-polish-session-20261016.md` | Historical USB-to-polish session, FLASH_READY AXPR23 pointers; not an AXPR24 build input |
|
||||
|
||||
Fortify/CVE files keep `Status: historical` banners. Prefer linking from
|
||||
[`../README.md`](../README.md).
|
||||
@@ -0,0 +1,299 @@
|
||||
# AXPR23: AXP Resurrection Pro para el Pixel 4a
|
||||
|
||||
**LineageOS 23.2 · Android 16 · `sunfish` · adaptación comunitaria · EOL**
|
||||
|
||||
AXP Resurrection Pro (AXPR) es una adaptación de **AXP.OS Pro** para el
|
||||
Google Pixel 4a. La idea no era crear otra ROM genérica basada en LineageOS,
|
||||
sino recuperar la experiencia de AXP sobre una base Android más reciente,
|
||||
manteniendo el soporte de hardware de LineageOS y añadiendo hardening propio.
|
||||
|
||||
AXPR23 fue la adaptación para **LineageOS 23.2 / Android 16**. Su última build
|
||||
es `23.2-20261025-PRO-sunfish`. La línea está cerrada porque LineageOS dejó de
|
||||
publicar Android 16 para `sunfish`; la siguiente generación del proyecto es
|
||||
**AXPR24**, sobre LineageOS 24 / Android 17.
|
||||
|
||||
La forma más clara de entender la ROM es separar sus cuatro procedencias:
|
||||
|
||||
1. la base y el soporte de dispositivo de **LineageOS**;
|
||||
2. la experiencia de producto recuperada de **AXP.OS / EXTENDROM**;
|
||||
3. las ideas y parches de hardening procedentes de **GrapheneOS / Android
|
||||
Hardening**;
|
||||
4. el trabajo propio de **AXPR** para integrar y adaptar todo lo anterior a
|
||||
Android 16 y al Pixel 4a.
|
||||
|
||||
---
|
||||
|
||||
## 1. La base: qué aporta LineageOS
|
||||
|
||||
AXPR23 conserva LineageOS como base real del sistema, no como una colección de
|
||||
drivers reutilizados:
|
||||
|
||||
- LineageOS 23.2 y Android 16;
|
||||
- árbol de dispositivo y soporte de hardware del Pixel 4a;
|
||||
- vendor y firmware de `sunfish`;
|
||||
- kernel Qualcomm `msm-4.14`;
|
||||
- Launcher3, Dialer y componentes centrales del sistema;
|
||||
- integración de radio, IMS, Wi‑Fi, cámara, eSIM y recovery;
|
||||
- las protecciones de Android y LineageOS ya presentes en la base, como CFI,
|
||||
Shadow Call Stack, KASLR y PAN.
|
||||
|
||||
Por eso AXPR sigue comportándose como una ROM LineageOS en lo esencial. Lo que
|
||||
cambia es la capa de producto, aplicaciones, ajustes y hardening que se coloca
|
||||
encima.
|
||||
|
||||
---
|
||||
|
||||
## 2. Lo que recuperamos de AXP.OS y EXTENDROM
|
||||
|
||||
AXP/EXTENDROM aporta la referencia funcional de la ROM: qué aplicaciones deben
|
||||
estar integradas, cómo debe comportarse el preroot, qué controles de privacidad
|
||||
son útiles y qué partes de la experiencia merecía la pena conservar.
|
||||
|
||||
### Servicios y aplicaciones
|
||||
|
||||
AXPR23 integra el stack que en AXP hacía posible utilizar el teléfono sin
|
||||
depender de una instalación completa de Google:
|
||||
|
||||
- **microG Services / GmsCore**;
|
||||
- **GSF Proxy** para aplicaciones que esperan Google Services Framework;
|
||||
- **Phonesky**, cliente Play parcheado y firmado para el proyecto;
|
||||
- las bibliotecas nativas de GmsCore necesarias para aplicaciones como Maps;
|
||||
- **Aurora Store**;
|
||||
- **Droidify**;
|
||||
- **Fossify Gallery** en lugar de Gallery2/Glimpse;
|
||||
- **Jelly**, el navegador ligero de LineageOS;
|
||||
- **OpenEUICC** como gestor privilegiado de eSIM.
|
||||
|
||||
La ROM mantiene Launcher3 y las aplicaciones centrales de LineageOS. No intenta
|
||||
convertir el teléfono en una interfaz completamente distinta: recupera la
|
||||
selección de AXP donde aporta una diferencia práctica.
|
||||
|
||||
### Preroot y producto
|
||||
|
||||
El modelo de root sigue el de AXP/EXTENDROM:
|
||||
|
||||
- Magisk se integra durante la construcción del `boot.img`;
|
||||
- el manager se activa después de instalar la ROM;
|
||||
- el usuario decide qué módulos y configuración quiere añadir;
|
||||
- la ROM no fuerza una colección de modificaciones adicionales.
|
||||
|
||||
También se recuperan elementos de producto de AXP: identidad **AXP Resurrection
|
||||
Pro**, pantalla de bienvenida, animación de arranque, nombre de paquete de
|
||||
release y firma del sistema con las claves `release-keys` del proyecto.
|
||||
|
||||
### Ajustes y controles de privacidad
|
||||
|
||||
Estos cambios son funciones del sistema, no simples APKs añadidos:
|
||||
|
||||
| Función | Qué recupera de AXP |
|
||||
|---|---|
|
||||
| **Private DNS** | Lista amplia de proveedores conocidos, sin servidores propios de AXP |
|
||||
| **Comprobación de conectividad** | Proveedor seleccionable o comprobación desactivada |
|
||||
| **SUPL y asistencia de ubicación** | Controles para limitar asistencia adicional de localización |
|
||||
| **Signature spoofing** | Opción restringida para microG y aplicaciones compatibles, desactivada por defecto |
|
||||
| **Grabación de llamadas** | Desbloqueo de la ruta de Dialer donde el dispositivo y la región lo permiten |
|
||||
| **Desactivar aplicaciones** | Acción clásica para aplicaciones instaladas por el usuario |
|
||||
| **Reinicio automático** | Reinicio por inactividad desde los ajustes de seguridad |
|
||||
| **Temporizadores Wi‑Fi/Bluetooth** | Desconexión automática tras el tiempo configurado |
|
||||
| **Quick Settings sensibles** | Algunos controles requieren desbloquear el teléfono |
|
||||
| **Capturas de pantalla** | Opción global para ventanas que usan `FLAG_SECURE`, desactivada por defecto |
|
||||
|
||||
La meta de estos ajustes es devolver control al usuario sin imponer una política
|
||||
única. La mayoría se mantienen desactivados o conservadores por defecto.
|
||||
|
||||
### Debloat selectivo
|
||||
|
||||
AXPR no utiliza una eliminación ciega de todo lo que parezca de Google. La
|
||||
limpieza se adaptó a Android 16 y se hizo por grupos:
|
||||
|
||||
1. se retiraron las aplicaciones Google de eUICC/LPA y se integró OpenEUICC;
|
||||
2. se redujeron componentes de carrier, RCS y entitlement que no son necesarios
|
||||
para el modelo microG;
|
||||
3. se retiraron herramientas de mantenimiento que no forman parte del producto
|
||||
final, como `bash`, `htop`, `nano`, `unrar` y `addon.d/50-lineage.sh`;
|
||||
4. se redujo la superficie de diagnóstico sin eliminar los servicios necesarios
|
||||
para radio, IMS, ubicación y conectividad;
|
||||
5. no se distribuyen `ssh`, `sshd`, `scp`, `sftp`, `rsync` ni `procmem` como
|
||||
herramientas de administración del teléfono.
|
||||
|
||||
Se conservaron deliberadamente los componentes Pixel, Qualcomm, IMS y eSIM que
|
||||
el funcionamiento diario necesita.
|
||||
|
||||
---
|
||||
|
||||
## 3. Lo que viene de GrapheneOS y Android Hardening
|
||||
|
||||
GrapheneOS y Android Hardening son referencias técnicas para determinadas
|
||||
protecciones. AXPR no es GrapheneOS, no usa su plataforma completa y no copia
|
||||
un kernel moderno de forma literal. Los parches se adaptaron al kernel no-GKI
|
||||
4.14 de `sunfish`.
|
||||
|
||||
### Kernel
|
||||
|
||||
Sobre el kernel de LineageOS se aplicó un conjunto Stage 1 de aproximadamente
|
||||
30 parches, con medidas como:
|
||||
|
||||
- FORTIFY intra-object y comprobaciones de tamaño;
|
||||
- inicialización de memoria al reservar y liberar;
|
||||
- endurecimiento y aleatorización de las listas libres de SLAB;
|
||||
- datos `ro_after_init`;
|
||||
- sanitización de rutas sensibles;
|
||||
- restricciones de `dmesg`;
|
||||
- protección y control de determinadas rutas USB;
|
||||
- ajustes de integridad y reducción de superficie de depuración.
|
||||
|
||||
La configuración final conserva CFI y Shadow Call Stack junto con el hardening
|
||||
que ya aportaba Android/LineageOS.
|
||||
|
||||
### Backports CVE
|
||||
|
||||
El inventario de la build final contiene **449 identificadores CVE** y parches
|
||||
acompañantes. Son backports seleccionados de distintas generaciones del kernel,
|
||||
con problemas de memoria, drivers, USB, Bluetooth, red, filesystem y
|
||||
subsystems Qualcomm.
|
||||
|
||||
Algunos ejemplos del inventario son:
|
||||
|
||||
`CVE-2015-7837`, `CVE-2017-18232`, `CVE-2020-0255`, `CVE-2021-28039`,
|
||||
`CVE-2022-21546` y `CVE-2022-48674`, además de una serie amplia de correcciones
|
||||
de 2023–2025.
|
||||
|
||||
No se aplicó un megapatch opaco: las series se comprobaron contra el offset
|
||||
real del árbol `4.14.357-openela` y se conservaron listas de atribución. El
|
||||
inventario completo está en `kernel-cve-backport-list.txt` dentro de la
|
||||
documentación del proyecto.
|
||||
|
||||
### Allocator y userspace
|
||||
|
||||
La integración de **hardened_malloc** sigue la línea de trabajo de GrapheneOS y
|
||||
DivestOS, adaptada a la memoria virtual del Pixel 4a:
|
||||
|
||||
- `hardened_malloc` en la libc de 64 bits;
|
||||
- Scudo para procesos de 32 bits;
|
||||
- excepciones específicas para cámara y SurfaceFlinger, para no romper funciones
|
||||
básicas del dispositivo;
|
||||
- SELinux en enforcing;
|
||||
- cifrado de datos estándar de Android.
|
||||
|
||||
La protección se aplicó de forma compatible con el hardware. No se importaron
|
||||
dependencias de MTE ni requisitos propios de dispositivos más modernos.
|
||||
|
||||
---
|
||||
|
||||
## 4. Trabajo propio de AXPR
|
||||
|
||||
La parte más importante del proyecto fue hacer que esas piezas funcionaran
|
||||
juntas en una base distinta. Ese trabajo es el que convierte el conjunto en una
|
||||
adaptación de AXP y no en una lista de parches:
|
||||
|
||||
### Portar AXP a Android 16
|
||||
|
||||
- localizar los equivalentes de Android 16 para ajustes y servicios que AXP
|
||||
implementaba sobre una generación anterior;
|
||||
- adaptar los cambios de Settings, SystemUI, SystemServer, Dialer y framework;
|
||||
- conservar la compatibilidad con el vendor y el kernel `sunfish`;
|
||||
- revalidar microG, Phonesky, eSIM, radio y las aplicaciones integradas;
|
||||
- combinar el debloat con los servicios que Android 16 aún necesita.
|
||||
|
||||
### Construir el WebView del proyecto
|
||||
|
||||
AXPR integra el AOSmium del proyecto mediante la cadena **Vanadium → Mulch →
|
||||
AOSmium**. Hubo que adaptar el build de Chromium/WebView, generar una versión
|
||||
compatible con las dos ABIs necesarias y conectar el proveedor mediante el
|
||||
overlay estático de LineageOS. El resultado final selecciona AOSmium, pero
|
||||
mantiene el mecanismo Android para instalar y elegir otros proveedores.
|
||||
|
||||
### Integración y compatibilidad
|
||||
|
||||
También son trabajo propio de AXPR:
|
||||
|
||||
- seleccionar qué partes de AXP seguían teniendo sentido en Android 16;
|
||||
- adaptar los offsets y excepciones de los backports al kernel real;
|
||||
- integrar allocator, Magisk, aplicaciones, permisos y overlays en un único
|
||||
producto;
|
||||
- mantener la firma, el empaquetado y el flujo de recovery de AXP;
|
||||
- probar el resultado en el Pixel 4a y corregir incompatibilidades concretas,
|
||||
en lugar de importar configuraciones completas de otra ROM.
|
||||
|
||||
---
|
||||
|
||||
## 5. Matriz de procedencia y diferencias
|
||||
|
||||
La siguiente tabla resume qué aporta cada proyecto y qué trabajo se hizo en
|
||||
AXPR. “GrapheneOS / AH” indica una referencia técnica o una familia de parches;
|
||||
no significa que AXPR incorpore GrapheneOS completo.
|
||||
|
||||
| Componente | LineageOS aporta | AXP / EXTENDROM aporta | GrapheneOS / AH aporta | Trabajo AXPR en la build final |
|
||||
|---|---|---|---|---|
|
||||
| **Base y hardware** | Android 16, device tree, vendor, radio y recovery | — | — | Portar la experiencia AXP a `sunfish` sin cambiar la base de dispositivo |
|
||||
| **Servicios Google** | Base sin microG ni Play | Modelo microG + GSF Proxy + Phonesky | — | Adaptar permisos, JNI, privilegios y empaquetado a Android 16 |
|
||||
| **Aplicaciones** | Launcher3, Dialer y apps centrales | Aurora, Droidify, Fossify y OpenEUICC | — | Seleccionar versiones, ubicaciones y permisos compatibles |
|
||||
| **WebView** | Framework y selección de proveedores | Necesidad de conservar el WebView del producto | Chromium/Vanadium como referencia técnica | Construir e integrar AOSmium mediante Vanadium → Mulch → AOSmium |
|
||||
| **Root** | No incluido | Preroot de EXTENDROM | — | Integrar Magisk en `boot.img` sin convertirlo en una app de sistema obligatoria |
|
||||
| **Privacidad y UX** | Ajustes estándar | DNS, conectividad, llamadas, spoofing, timeouts y controles AXP | Ideas puntuales de SystemUI | Reubicar y adaptar los controles a Settings/SystemUI Android 16 |
|
||||
| **Debloat** | Paquetes de la base | Objetivo de reducir superficie | — | Cuatro oleadas selectivas sin romper radio, IMS, eSIM ni Qualcomm |
|
||||
| **Kernel** | `msm-4.14` y hardening Android existente | Orden y selección de parches del track AXP | Stage 1, FORTIFY, slab, sanitización y alloc hardening | Adaptar aproximadamente 30 parches al árbol real de `sunfish` |
|
||||
| **CVE** | Parches upstream disponibles | Política de backports | Fuentes y referencias de hardening | Integrar y verificar 449 identificadores en series aplicables |
|
||||
| **Allocator** | Bionic y Scudo de Android | Objetivo de allocator reforzado | `hardened_malloc` y modelo de excepciones | Adaptar `hardened_malloc` a 64 bits, Scudo a 32 bits y VA39 |
|
||||
| **SELinux y cifrado** | Base Android/LineageOS | Requisitos de producto | Referencia de endurecimiento | Mantener SELinux enforcing y cifrado de datos |
|
||||
| **Identidad y release** | Formato de build LineageOS | Forma de empaquetado AXP Pro | — | Branding, `release-keys`, ZIP completo y flujo recovery/sideload |
|
||||
|
||||
En forma de comparación rápida:
|
||||
|
||||
| Área | LineageOS 23.2 limpio | AXPR23 |
|
||||
|---|---|---|
|
||||
| Servicios Google | No incluidos | microG, GSF Proxy y Phonesky |
|
||||
| Aplicaciones | Conjunto estándar de LineageOS | Aurora, Droidify, Fossify, Jelly y OpenEUICC |
|
||||
| WebView | Proveedor de LineageOS | AOSmium del proyecto como proveedor seleccionado |
|
||||
| Root | No incluido | Magisk preroot en `boot.img` |
|
||||
| Ajustes | Opciones estándar | Controles de DNS, conectividad, ubicación, llamadas y privacidad de AXP |
|
||||
| Debloat | Paquetes estándar de la base | Limpieza selectiva adaptada a microG y `sunfish` |
|
||||
| Kernel | `msm-4.14` de LineageOS | Stage 1, FORTIFY, backports CVE y configuración reforzada |
|
||||
| Allocator | Bionic estándar | `hardened_malloc` para 64 bits y Scudo para 32 bits |
|
||||
| Identidad | Build LineageOS habitual | Producto PRO firmado con `release-keys` |
|
||||
| Actualizaciones | Nightlies de LineageOS | ZIP completo por recovery/sideload |
|
||||
|
||||
AXPR mantiene la base de dispositivo de LineageOS y mejora sus capas de
|
||||
producto, privacidad y hardening. No es una copia literal de GrapheneOS ni una
|
||||
continuación oficial de AXP.OS: es una adaptación bastante directa de AXP.OS
|
||||
Pro, con las mejoras y correcciones necesarias para Android 16.
|
||||
|
||||
---
|
||||
|
||||
## 6. Estado de AXPR23 y última build
|
||||
|
||||
AXPR23 está **EOL / archivada**. LineageOS dejó de publicar nuevas builds de
|
||||
Android 16 para `sunfish` al comenzar la línea Android 17. No habrá nuevas
|
||||
features ni nuevas builds AXPR23; se conserva como referencia y rollback.
|
||||
|
||||
- **Build:** `AXP-Resurection-Pro-23.2-20261025-sunfish.zip`
|
||||
- **Base:** LineageOS 23.2 / Android 16
|
||||
- **SHA-256:** `9d205fd3cc945438dfff3a23b129bf88516c7654f5922bc2ee4d478f9485eef0`
|
||||
- **Siguiente línea:** AXPR24 sobre LineageOS 24 / Android 17
|
||||
|
||||
AXPR24 no es una actualización automática de AXPR23. Es una nueva adaptación
|
||||
que debe volver a validar aplicaciones, WebView, allocator, kernel y packaging
|
||||
contra Android 17.
|
||||
|
||||
## Descarga
|
||||
|
||||
La última build publicada es un ZIP completo para el Google Pixel 4a
|
||||
(`sunfish`), firmado con las claves de release del proyecto:
|
||||
|
||||
[**Descargar AXPR23 — AXP.OS sobre LineageOS 23 para Pixel 4a**](https://vbits.me/download/284/)
|
||||
|
||||
El botón usa Download Monitor y conserva el contador dinámico de descargas.
|
||||
|
||||
- **Build:** `AXP-Resurection-Pro-23.2-20261025-sunfish.zip`
|
||||
- **Tamaño:** 1.397.472.560 bytes (1,30 GiB)
|
||||
- **MD5:** `23378f132ac8b1f53606183451715a9c`
|
||||
- **SHA-256:** `9d205fd3cc945438dfff3a23b129bf88516c7654f5922bc2ee4d478f9485eef0`
|
||||
- **Instalación:** ZIP completo mediante recovery/sideload. Haz una copia de
|
||||
seguridad antes de instalarlo.
|
||||
|
||||
## Créditos
|
||||
|
||||
AXPR23 se apoya en el trabajo de **AXP.OS / EXTENDROM**, **LineageOS**,
|
||||
**microG**, **GrapheneOS / Android Hardening**, **Vanadium / Mulch / AOSmium**,
|
||||
Magisk, OpenEUICC y los proyectos de las aplicaciones integradas. Es una
|
||||
adaptación comunitaria independiente para `sunfish`.
|
||||
@@ -1,436 +1,299 @@
|
||||
# AXP Resurrection Pro — Pixel 4a (sunfish)
|
||||
# AXPR23: AXP Resurrection Pro para el Pixel 4a
|
||||
|
||||
**LineageOS 23.2 · Android 16 · Privacy-oriented custom ROM**
|
||||
**LineageOS 23.2 · Android 16 · `sunfish` · adaptación comunitaria · EOL**
|
||||
|
||||
AXP Resurrection Pro (*AXPR*) brings the **AXP.OS Pro** experience forward onto
|
||||
**LineageOS 23.2** for the **Google Pixel 4a**. It is aimed at users who want a
|
||||
maintained Android 16 base, **microG** instead of proprietary Google Play
|
||||
services, optional **root via Magisk**, and **extra kernel and system
|
||||
hardening** drawn from established community sources (including
|
||||
**GrapheneOS-class** kernel work), without tying you to deprecated AXP update
|
||||
servers.
|
||||
AXP Resurrection Pro (AXPR) es una adaptación de **AXP.OS Pro** para el
|
||||
Google Pixel 4a. La idea no era crear otra ROM genérica basada en LineageOS,
|
||||
sino recuperar la experiencia de AXP sobre una base Android más reciente,
|
||||
manteniendo el soporte de hardware de LineageOS y añadiendo hardening propio.
|
||||
|
||||
This document describes **what the ROM contains**. It is written for end users
|
||||
and reviewers, not for build infrastructure.
|
||||
AXPR23 fue la adaptación para **LineageOS 23.2 / Android 16**. Su última build
|
||||
es `23.2-20261025-PRO-sunfish`. La línea está cerrada porque LineageOS dejó de
|
||||
publicar Android 16 para `sunfish`; la siguiente generación del proyecto es
|
||||
**AXPR24**, sobre LineageOS 24 / Android 17.
|
||||
|
||||
**Current generation (example build id):** `23.2-20261008` · `BP4A.251205.006`
|
||||
· `release-keys`
|
||||
La forma más clara de entender la ROM es separar sus cuatro procedencias:
|
||||
|
||||
1. la base y el soporte de dispositivo de **LineageOS**;
|
||||
2. la experiencia de producto recuperada de **AXP.OS / EXTENDROM**;
|
||||
3. las ideas y parches de hardening procedentes de **GrapheneOS / Android
|
||||
Hardening**;
|
||||
4. el trabajo propio de **AXPR** para integrar y adaptar todo lo anterior a
|
||||
Android 16 y al Pixel 4a.
|
||||
|
||||
---
|
||||
|
||||
## 1. At a glance
|
||||
## 1. La base: qué aporta LineageOS
|
||||
|
||||
| Topic | What you get |
|
||||
|--------|----------------|
|
||||
| **Base** | LineageOS **23.2**, Android **16**, Pixel 4a only |
|
||||
| **Google services** | **microG** + **GSF proxy**; no built-in Google account requirement |
|
||||
| **Play Store** | **Play Store (Phonesky)** build patched for this project (exact-alarm and related fixes) |
|
||||
| **Root** | **Magisk** embedded at build time; you activate from the Magisk app after flash |
|
||||
| **Privacy apps** | **Aurora Store**, **Droidify**, **Fossify Gallery**, **Jelly** browser, **OpenEUICC** |
|
||||
| **Hardening** | Kernel patches (Graphene-derived + CVE backports), **hardened_malloc**, trimmed system surface |
|
||||
| **Updates** | Full ROM via **recovery sideload**; individual system apps can be updated in place when signed compatibly |
|
||||
| **Bootloader** | **Unlocked** workflow (orange state); not designed for relocked retail boot |
|
||||
AXPR23 conserva LineageOS como base real del sistema, no como una colección de
|
||||
drivers reutilizados:
|
||||
|
||||
- LineageOS 23.2 y Android 16;
|
||||
- árbol de dispositivo y soporte de hardware del Pixel 4a;
|
||||
- vendor y firmware de `sunfish`;
|
||||
- kernel Qualcomm `msm-4.14`;
|
||||
- Launcher3, Dialer y componentes centrales del sistema;
|
||||
- integración de radio, IMS, Wi‑Fi, cámara, eSIM y recovery;
|
||||
- las protecciones de Android y LineageOS ya presentes en la base, como CFI,
|
||||
Shadow Call Stack, KASLR y PAN.
|
||||
|
||||
Por eso AXPR sigue comportándose como una ROM LineageOS en lo esencial. Lo que
|
||||
cambia es la capa de producto, aplicaciones, ajustes y hardening que se coloca
|
||||
encima.
|
||||
|
||||
---
|
||||
|
||||
## 2. Everything added on top of LineageOS 23.2
|
||||
## 2. Lo que recuperamos de AXP.OS y EXTENDROM
|
||||
|
||||
Stock **LineageOS 23.2 for sunfish** is a clean AOSP-derived system: no microG,
|
||||
no Play Store, no Magisk, **test-keys** or UNOFFICIAL builds in typical user
|
||||
trees, default Private DNS list, default connectivity check (Google), Gallery2
|
||||
or Glimpse, full Lineage tool bundle on system, Google carrier/eUICC apps from
|
||||
vendor makefiles, and the upstream **4.14** kernel without the AXP/Graphene
|
||||
CVE and hardening layers below.
|
||||
AXP/EXTENDROM aporta la referencia funcional de la ROM: qué aplicaciones deben
|
||||
estar integradas, cómo debe comportarse el preroot, qué controles de privacidad
|
||||
son útiles y qué partes de la experiencia merecía la pena conservar.
|
||||
|
||||
**AXP Resurrection Pro** keeps the Lineage base (Launcher3, Dialer, updaters,
|
||||
device support) and layers the following.
|
||||
### Servicios y aplicaciones
|
||||
|
||||
### 2.1 Product identity and visuals
|
||||
AXPR23 integra el stack que en AXP hacía posible utilizar el teléfono sin
|
||||
depender de una instalación completa de Google:
|
||||
|
||||
| Change | LineageOS default | AXPR |
|
||||
|--------|-------------------|------|
|
||||
| Build channel | Often UNOFFICIAL / nightly naming | **PRO** product (`…-PRO-sunfish`) |
|
||||
| Signing | Test-keys on many community builds | **release-keys** (project keys) |
|
||||
| Manufacturer / brand props | Lineage | **AXP Resurrection** system branding |
|
||||
| Setup welcome | Lineage wording | **“Welcome to AXP Resurrection”** |
|
||||
| Boot animation | Narrow band layout | **Full-screen** 1080×2340 animation |
|
||||
| OTA zip name | `lineage-*.zip` | Matching **`AXP-Resurrection-Pro-*.zip`** (same payload) |
|
||||
- **microG Services / GmsCore**;
|
||||
- **GSF Proxy** para aplicaciones que esperan Google Services Framework;
|
||||
- **Phonesky**, cliente Play parcheado y firmado para el proyecto;
|
||||
- las bibliotecas nativas de GmsCore necesarias para aplicaciones como Maps;
|
||||
- **Aurora Store**;
|
||||
- **Droidify**;
|
||||
- **Fossify Gallery** en lugar de Gallery2/Glimpse;
|
||||
- **Jelly**, el navegador ligero de LineageOS;
|
||||
- **OpenEUICC** como gestor privilegiado de eSIM.
|
||||
|
||||
### 2.2 Google replacement stack (not in vanilla LOS)
|
||||
La ROM mantiene Launcher3 y las aplicaciones centrales de LineageOS. No intenta
|
||||
convertir el teléfono en una interfaz completamente distinta: recupera la
|
||||
selección de AXP donde aporta una diferencia práctica.
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| **microG Services (GmsCore)** | Open-source reimplementation of Play Services |
|
||||
| **GSF Proxy** | Framework stub for apps expecting Google Services Framework |
|
||||
| **Play Store (Phonesky)** | Play client build maintained for this ROM (exact-alarm and related fixes) |
|
||||
| **GmsCore native libraries** | ARM64 JNI libs shipped **inside the image** (required for Maps / TLS stacks) |
|
||||
### Preroot y producto
|
||||
|
||||
Vanilla Lineage does **not** ship these; you would install microG yourself on a
|
||||
separate track.
|
||||
El modelo de root sigue el de AXP/EXTENDROM:
|
||||
|
||||
### 2.3 Extra applications (vs typical LOS sunfish)
|
||||
- Magisk se integra durante la construcción del `boot.img`;
|
||||
- el manager se activa después de instalar la ROM;
|
||||
- el usuario decide qué módulos y configuración quiere añadir;
|
||||
- la ROM no fuerza una colección de modificaciones adicionales.
|
||||
|
||||
| App | Notes |
|
||||
|-----|--------|
|
||||
| **Aurora Store** | Play store front-end |
|
||||
| **Droidify** | F-Droid client (instead of shipping classic F-Droid) |
|
||||
| **Fossify Gallery** | Replaces **Gallery2 / Glimpse** on this product |
|
||||
| **Jelly** | Lightweight browser added on **product** (Lineage 23 does not include Jelly in the default sunfish product set) |
|
||||
| **OpenEUICC** | Privileged eSIM manager where Google EUICC is removed |
|
||||
| **Magisk** | Preroot boot integration + manager (see §5 Root and Magisk) |
|
||||
También se recuperan elementos de producto de AXP: identidad **AXP Resurrection
|
||||
Pro**, pantalla de bienvenida, animación de arranque, nombre de paquete de
|
||||
release y firma del sistema con las claves `release-keys` del proyecto.
|
||||
|
||||
**Unchanged vs LOS intent:** **Launcher3** (no NeoLauncher), standard Lineage
|
||||
core apps, **Lineage WebView** as default provider.
|
||||
### Ajustes y controles de privacidad
|
||||
|
||||
### 2.4 Settings and framework behaviour (AXP / GrapheneOS / Calyx class)
|
||||
Estos cambios son funciones del sistema, no simples APKs añadidos:
|
||||
|
||||
These are **code changes in the platform**, not separate APKs.
|
||||
| Función | Qué recupera de AXP |
|
||||
|---|---|
|
||||
| **Private DNS** | Lista amplia de proveedores conocidos, sin servidores propios de AXP |
|
||||
| **Comprobación de conectividad** | Proveedor seleccionable o comprobación desactivada |
|
||||
| **SUPL y asistencia de ubicación** | Controles para limitar asistencia adicional de localización |
|
||||
| **Signature spoofing** | Opción restringida para microG y aplicaciones compatibles, desactivada por defecto |
|
||||
| **Grabación de llamadas** | Desbloqueo de la ruta de Dialer donde el dispositivo y la región lo permiten |
|
||||
| **Desactivar aplicaciones** | Acción clásica para aplicaciones instaladas por el usuario |
|
||||
| **Reinicio automático** | Reinicio por inactividad desde los ajustes de seguridad |
|
||||
| **Temporizadores Wi‑Fi/Bluetooth** | Desconexión automática tras el tiempo configurado |
|
||||
| **Quick Settings sensibles** | Algunos controles requieren desbloquear el teléfono |
|
||||
| **Capturas de pantalla** | Opción global para ventanas que usan `FLAG_SECURE`, desactivada por defecto |
|
||||
|
||||
#### Privacy and network
|
||||
La meta de estos ajustes es devolver control al usuario sin imponer una política
|
||||
única. La mayoría se mantienen desactivados o conservadores por defecto.
|
||||
|
||||
| Feature | What Lineage does | What AXPR adds |
|
||||
|---------|-------------------|----------------|
|
||||
| **Private DNS presets** | Small default set | **15+ named providers** (AdGuard, Mullvad, Quad9, Cloudflare, regional DoT hosts, etc.) |
|
||||
| **Internet connectivity check** | Fixed Google-style check | **User-selectable** provider (Disabled, Amazon, Cloudflare, Google, Huawei, Kuketz, Microsoft, openSUSE, Ubuntu, …) — no AXP-owned servers |
|
||||
| **Force disable SUPL** | Not exposed | Settings toggle; forces SUPL off for stricter GNSS |
|
||||
| **Stop extra location assistance** | Not exposed | Toggle to stop extra Qualcomm / XTRA-style assistance when disabled |
|
||||
| **Kernel command line privacy** | `/proc/cmdline` often world-readable | **Mode 0440** on `/proc/cmdline` for unprivileged apps (GOLDEN/AXP class) |
|
||||
### Debloat selectivo
|
||||
|
||||
#### Security, biometrics, and media metadata
|
||||
AXPR no utiliza una eliminación ciega de todo lo que parezca de Google. La
|
||||
limpieza se adaptó a Android 16 y se hizo por grupos:
|
||||
|
||||
| Feature | What Lineage does | What AXPR adds |
|
||||
|---------|-------------------|----------------|
|
||||
| **Fingerprint permanent lockout** | Higher failed-attempt threshold | **Stricter lockout** (AXP constant: fewer attempts before permanent lockout) |
|
||||
| **Build serial exposure** | Legacy paths may expose serial via deprecated APIs | **`Build.SERIAL` stays unknown**; real serial only via `Build.getSerial()` + permission |
|
||||
| **Screenshot EXIF** | OS name, time, unique id in metadata | **Minimal EXIF** (width/height only — Calyx/AXP class) |
|
||||
| **Recovery OTA metadata** | Can accept packages restricted to device serial | **Rejects** update packages that carry a `serialno=` constraint |
|
||||
| **Signature spoofing** | microG base only | Developer option **`allow_signature_fake`** (default **off**) for microG / compatible apps |
|
||||
| **Call recording** | Region/carrier gated | Developer option **`extendrom_call_recording`** unlocks recording path in Dialer where supported |
|
||||
| **FLAG_SECURE / screen capture** | Apps can block capture | Optional **`disable_secure_windows`** (default **off**), works on **user** builds without `ro.debuggable=1` |
|
||||
| **Disable app** | Android 16 emphasises **Archive**; disable hidden for many user apps | **Disable** restored for **user-installed** apps in Settings |
|
||||
| **Native debug** | Standard ptrace policy | **`persist.native_debug`** — when enabled, relaxes ptrace scope for debugging (default off) |
|
||||
1. se retiraron las aplicaciones Google de eUICC/LPA y se integró OpenEUICC;
|
||||
2. se redujeron componentes de carrier, RCS y entitlement que no son necesarios
|
||||
para el modelo microG;
|
||||
3. se retiraron herramientas de mantenimiento que no forman parte del producto
|
||||
final, como `bash`, `htop`, `nano`, `unrar` y `addon.d/50-lineage.sh`;
|
||||
4. se redujo la superficie de diagnóstico sin eliminar los servicios necesarios
|
||||
para radio, IMS, ubicación y conectividad;
|
||||
5. no se distribuyen `ssh`, `sshd`, `scp`, `sftp`, `rsync` ni `procmem` como
|
||||
herramientas de administración del teléfono.
|
||||
|
||||
#### Timeouts and System UI
|
||||
|
||||
| Feature | What Lineage does | What AXPR adds |
|
||||
|---------|-------------------|----------------|
|
||||
| **Automatic reboot** | Not in stock LOS Settings | Idle **auto-reboot** (Security settings) |
|
||||
| **Bluetooth timeout** | Manual only | Turns **Bluetooth off** after configurable inactivity |
|
||||
| **Wi‑Fi timeout** | Manual only | Turns **Wi‑Fi off** after configurable inactivity |
|
||||
| **Quick Settings tiles** | Usable from lock screen per tile | **Sensitive tiles require unlock** first (GrapheneOS SystemUI patch) |
|
||||
| **Keyguard / reboot** | Standard | SystemUI / SystemServer hooks for timeout and reboot integration (AXP server extensions) |
|
||||
|
||||
### 2.5 Packages and binaries removed (vs Lineage sunfish)
|
||||
|
||||
Selective **deblob**, not a generic “remove all Google” script.
|
||||
|
||||
#### Wave 1 — Google eUICC LPA
|
||||
|
||||
| Removed | Replaced by |
|
||||
|---------|-------------|
|
||||
| **EuiccGoogle**, **EuiccGoogleOverlay** | **OpenEUICC** (EuiccSupportPixel support packages **kept**) |
|
||||
|
||||
#### Wave 2 — Carrier / RCS Google & AOSP entitlement
|
||||
|
||||
| Removed (examples) | Kept for radio |
|
||||
|--------------------|----------------|
|
||||
| **CarrierServices**, **CarrierSettings**, **CarrierWifi**, **CarrierSetup**, **RilConfigService**, **grilservice** | **ims**, **QtiTelephonyService**, Qualcomm daemons, **Dialer**, **GoogleParts** |
|
||||
| **PresencePolling**, **RcsService** (device makefile) | |
|
||||
| **ImsServiceEntitlement** (telephony product) | |
|
||||
|
||||
#### Wave 3 — Lineage maintenance tools on system
|
||||
|
||||
| Removed from system / system_ext | Still on AXPR (AXP parity) |
|
||||
|----------------------------------|----------------------------|
|
||||
| **bash**, **htop**, **nano**, **unrar**, **addon.d/50-lineage.sh** | **curl**, **vim**, **zstd**, recovery **nano** |
|
||||
|
||||
#### Wave 4 — Debug / diag surface
|
||||
|
||||
| Removed | Explicitly kept |
|
||||
|---------|-----------------|
|
||||
| Qualcomm **diag_*** tools, **test_diag**, **imsrcsd** | **loc_launcher**, **lowi-server**, **xtra-daemon**, IMS RTP/data daemons, **cnss-daemon**; keep Lineage **disable_configstore** (overrides HIDL configstore HAL — required on A16 VINTF) |
|
||||
| Some **AOSP debug** host tools on device where safe | **bpftool**, **alloctop**, **apexd-lsof** remain in build where the OS requires them |
|
||||
|
||||
#### Product OpenSSH (US-S4-1)
|
||||
|
||||
| Removed | Kept |
|
||||
|---------|------|
|
||||
| **ssh**, **sshd**, **scp**, **sftp**, **rsync**, **procmem** on product | **`init.openssh.rc`** stub only (no `sshd` binary) — remote admin via **adb** / Magisk |
|
||||
|
||||
### 2.6 Kernel, allocator, and CVEs (summary)
|
||||
|
||||
On top of the **same msm-4.14** tree Lineage ships:
|
||||
|
||||
- **30 GrapheneOS-derived** kernel patches (Deny USB, allocsize, fortify, slab, sanitize, ro_after_init, randomisation, …) — see §8 below.
|
||||
- Extra **Kconfig**: FORTIFY, init-on-alloc/free, dmesg restrict, slab hardened, **SLAB_FREELIST_RANDOM**; **no Yama** (app compatibility).
|
||||
- **449** documented **CVE** backport identifiers + companion fixes (e.g. USB lock-scope instead of raw **CVE-2022-49980** one-liner).
|
||||
- **hardened_malloc** in **bionic** for 64-bit processes (with graphics/camera exclusions).
|
||||
|
||||
Lineage’s existing hardening (CFI, shadow stack, KASLR, PAN, …) is **retained**.
|
||||
|
||||
### 2.7 Intentionally **not** changed vs Lineage
|
||||
|
||||
- **No** GrapheneOS-style **location lockdown** kernel (GPS behaviour stays LOS-like).
|
||||
- **No** Aosmium / proprietary AXP WebView baked in.
|
||||
- **No** AXP online OTA or external market repositories.
|
||||
- **No** KernelSU in the standard product.
|
||||
- **No** bundled Play Integrity / banking Magisk modules.
|
||||
- **Device tree / modem firmware** line unchanged (Pixel 4a vendor blobs).
|
||||
Se conservaron deliberadamente los componentes Pixel, Qualcomm, IMS y eSIM que
|
||||
el funcionamiento diario necesita.
|
||||
|
||||
---
|
||||
|
||||
## 3. Who this ROM is for
|
||||
## 3. Lo que viene de GrapheneOS y Android Hardening
|
||||
|
||||
- You use **microG** and install apps via **Aurora** / **Droidify** / Play as you choose.
|
||||
- You want **AXP-style settings** (private DNS presets, connectivity controls, classic **Disable app**, timeouts, optional signature spoofing and call-recording unlock) on a **current Lineage** tree.
|
||||
- You accept **custom ROM risks**: unlocked bootloader, community maintenance, no warranty.
|
||||
GrapheneOS y Android Hardening son referencias técnicas para determinadas
|
||||
protecciones. AXPR no es GrapheneOS, no usa su plataforma completa y no copia
|
||||
un kernel moderno de forma literal. Los parches se adaptaron al kernel no-GKI
|
||||
4.14 de `sunfish`.
|
||||
|
||||
**Not goals of this ROM:** banking attestation guarantees, locked bootloader,
|
||||
proprietary AXP OTA infrastructure, or shipping every Google / carrier app from
|
||||
stock Pixel firmware.
|
||||
### Kernel
|
||||
|
||||
Sobre el kernel de LineageOS se aplicó un conjunto Stage 1 de aproximadamente
|
||||
30 parches, con medidas como:
|
||||
|
||||
- FORTIFY intra-object y comprobaciones de tamaño;
|
||||
- inicialización de memoria al reservar y liberar;
|
||||
- endurecimiento y aleatorización de las listas libres de SLAB;
|
||||
- datos `ro_after_init`;
|
||||
- sanitización de rutas sensibles;
|
||||
- restricciones de `dmesg`;
|
||||
- protección y control de determinadas rutas USB;
|
||||
- ajustes de integridad y reducción de superficie de depuración.
|
||||
|
||||
La configuración final conserva CFI y Shadow Call Stack junto con el hardening
|
||||
que ya aportaba Android/LineageOS.
|
||||
|
||||
### Backports CVE
|
||||
|
||||
El inventario de la build final contiene **449 identificadores CVE** y parches
|
||||
acompañantes. Son backports seleccionados de distintas generaciones del kernel,
|
||||
con problemas de memoria, drivers, USB, Bluetooth, red, filesystem y
|
||||
subsystems Qualcomm.
|
||||
|
||||
Algunos ejemplos del inventario son:
|
||||
|
||||
`CVE-2015-7837`, `CVE-2017-18232`, `CVE-2020-0255`, `CVE-2021-28039`,
|
||||
`CVE-2022-21546` y `CVE-2022-48674`, además de una serie amplia de correcciones
|
||||
de 2023–2025.
|
||||
|
||||
No se aplicó un megapatch opaco: las series se comprobaron contra el offset
|
||||
real del árbol `4.14.357-openela` y se conservaron listas de atribución. El
|
||||
inventario completo está en `kernel-cve-backport-list.txt` dentro de la
|
||||
documentación del proyecto.
|
||||
|
||||
### Allocator y userspace
|
||||
|
||||
La integración de **hardened_malloc** sigue la línea de trabajo de GrapheneOS y
|
||||
DivestOS, adaptada a la memoria virtual del Pixel 4a:
|
||||
|
||||
- `hardened_malloc` en la libc de 64 bits;
|
||||
- Scudo para procesos de 32 bits;
|
||||
- excepciones específicas para cámara y SurfaceFlinger, para no romper funciones
|
||||
básicas del dispositivo;
|
||||
- SELinux en enforcing;
|
||||
- cifrado de datos estándar de Android.
|
||||
|
||||
La protección se aplicó de forma compatible con el hardware. No se importaron
|
||||
dependencias de MTE ni requisitos propios de dispositivos más modernos.
|
||||
|
||||
---
|
||||
|
||||
## 4. Preinstalled applications
|
||||
## 4. Trabajo propio de AXPR
|
||||
|
||||
| App | Role |
|
||||
|-----|------|
|
||||
| **microG Services (GmsCore)** | Replaces Google Play Services for many apps |
|
||||
| **GSF Proxy** | Google Services Framework compatibility layer |
|
||||
| **Play Store (Phonesky)** | Google Play client (project-maintained APK) |
|
||||
| **Aurora Store** | Alternative store front-end for Play |
|
||||
| **Droidify** | Lightweight F-Droid client |
|
||||
| **Fossify Gallery** | Gallery (replaces Lineage Gallery2 / Glimpse on this product) |
|
||||
| **Jelly** | Small browser on the product partition |
|
||||
| **OpenEUICC** | eSIM / EUICC management (replaces Google EUICC where removed) |
|
||||
| **Magisk** | Root manager (stub until you complete first-time setup) |
|
||||
| **LineageOS core** | Launcher3, Dialer, Messaging, etc. |
|
||||
La parte más importante del proyecto fue hacer que esas piezas funcionaran
|
||||
juntas en una base distinta. Ese trabajo es el que convierte el conjunto en una
|
||||
adaptación de AXP y no en una lista de parches:
|
||||
|
||||
**WebView:** default is **Lineage / AOSP WebView**. You may install another
|
||||
WebView provider and select it under **Developer options → WebView
|
||||
implementation** (Aosmium and other legacy AXP WebViews are **not** shipped as
|
||||
default).
|
||||
### Portar AXP a Android 16
|
||||
|
||||
- localizar los equivalentes de Android 16 para ajustes y servicios que AXP
|
||||
implementaba sobre una generación anterior;
|
||||
- adaptar los cambios de Settings, SystemUI, SystemServer, Dialer y framework;
|
||||
- conservar la compatibilidad con el vendor y el kernel `sunfish`;
|
||||
- revalidar microG, Phonesky, eSIM, radio y las aplicaciones integradas;
|
||||
- combinar el debloat con los servicios que Android 16 aún necesita.
|
||||
|
||||
### Construir el WebView del proyecto
|
||||
|
||||
AXPR integra el AOSmium del proyecto mediante la cadena **Vanadium → Mulch →
|
||||
AOSmium**. Hubo que adaptar el build de Chromium/WebView, generar una versión
|
||||
compatible con las dos ABIs necesarias y conectar el proveedor mediante el
|
||||
overlay estático de LineageOS. El resultado final selecciona AOSmium, pero
|
||||
mantiene el mecanismo Android para instalar y elegir otros proveedores.
|
||||
|
||||
### Integración y compatibilidad
|
||||
|
||||
También son trabajo propio de AXPR:
|
||||
|
||||
- seleccionar qué partes de AXP seguían teniendo sentido en Android 16;
|
||||
- adaptar los offsets y excepciones de los backports al kernel real;
|
||||
- integrar allocator, Magisk, aplicaciones, permisos y overlays en un único
|
||||
producto;
|
||||
- mantener la firma, el empaquetado y el flujo de recovery de AXP;
|
||||
- probar el resultado en el Pixel 4a y corregir incompatibilidades concretas,
|
||||
en lugar de importar configuraciones completas de otra ROM.
|
||||
|
||||
---
|
||||
|
||||
## 5. Root and Magisk
|
||||
## 5. Matriz de procedencia y diferencias
|
||||
|
||||
- Magisk is integrated into the **boot image at ROM build time** (AXP / EXTENDROM
|
||||
style **preroot**), not as a normal preinstalled Play-style system app only.
|
||||
- After flashing, open **Magisk** once and allow it to **download and install**
|
||||
the current manager APK if prompted.
|
||||
- **Do not** use “Install to inactive slot” or repeated boot repatching unless
|
||||
you know you need it: the boot image is already prepared. Extra patching often
|
||||
confuses the app and can break A/B slots.
|
||||
- The home screen line **“Ramdisk: Yes/No”** describes how Magisk would install
|
||||
on a *stock* device; on preroot builds it may read **No** even when root works.
|
||||
Check **Magisk version** and whether apps can request superuser.
|
||||
- **Zygisk**, **DenyList**, and modules are **your choice** after activation;
|
||||
the ROM does not ship banking or Play Integrity fix modules.
|
||||
La siguiente tabla resume qué aporta cada proyecto y qué trabajo se hizo en
|
||||
AXPR. “GrapheneOS / AH” indica una referencia técnica o una familia de parches;
|
||||
no significa que AXPR incorpore GrapheneOS completo.
|
||||
|
||||
**KernelSU** is **not** part of this product line.
|
||||
| Componente | LineageOS aporta | AXP / EXTENDROM aporta | GrapheneOS / AH aporta | Trabajo AXPR en la build final |
|
||||
|---|---|---|---|---|
|
||||
| **Base y hardware** | Android 16, device tree, vendor, radio y recovery | — | — | Portar la experiencia AXP a `sunfish` sin cambiar la base de dispositivo |
|
||||
| **Servicios Google** | Base sin microG ni Play | Modelo microG + GSF Proxy + Phonesky | — | Adaptar permisos, JNI, privilegios y empaquetado a Android 16 |
|
||||
| **Aplicaciones** | Launcher3, Dialer y apps centrales | Aurora, Droidify, Fossify y OpenEUICC | — | Seleccionar versiones, ubicaciones y permisos compatibles |
|
||||
| **WebView** | Framework y selección de proveedores | Necesidad de conservar el WebView del producto | Chromium/Vanadium como referencia técnica | Construir e integrar AOSmium mediante Vanadium → Mulch → AOSmium |
|
||||
| **Root** | No incluido | Preroot de EXTENDROM | — | Integrar Magisk en `boot.img` sin convertirlo en una app de sistema obligatoria |
|
||||
| **Privacidad y UX** | Ajustes estándar | DNS, conectividad, llamadas, spoofing, timeouts y controles AXP | Ideas puntuales de SystemUI | Reubicar y adaptar los controles a Settings/SystemUI Android 16 |
|
||||
| **Debloat** | Paquetes de la base | Objetivo de reducir superficie | — | Cuatro oleadas selectivas sin romper radio, IMS, eSIM ni Qualcomm |
|
||||
| **Kernel** | `msm-4.14` y hardening Android existente | Orden y selección de parches del track AXP | Stage 1, FORTIFY, slab, sanitización y alloc hardening | Adaptar aproximadamente 30 parches al árbol real de `sunfish` |
|
||||
| **CVE** | Parches upstream disponibles | Política de backports | Fuentes y referencias de hardening | Integrar y verificar 449 identificadores en series aplicables |
|
||||
| **Allocator** | Bionic y Scudo de Android | Objetivo de allocator reforzado | `hardened_malloc` y modelo de excepciones | Adaptar `hardened_malloc` a 64 bits, Scudo a 32 bits y VA39 |
|
||||
| **SELinux y cifrado** | Base Android/LineageOS | Requisitos de producto | Referencia de endurecimiento | Mantener SELinux enforcing y cifrado de datos |
|
||||
| **Identidad y release** | Formato de build LineageOS | Forma de empaquetado AXP Pro | — | Branding, `release-keys`, ZIP completo y flujo recovery/sideload |
|
||||
|
||||
En forma de comparación rápida:
|
||||
|
||||
| Área | LineageOS 23.2 limpio | AXPR23 |
|
||||
|---|---|---|
|
||||
| Servicios Google | No incluidos | microG, GSF Proxy y Phonesky |
|
||||
| Aplicaciones | Conjunto estándar de LineageOS | Aurora, Droidify, Fossify, Jelly y OpenEUICC |
|
||||
| WebView | Proveedor de LineageOS | AOSmium del proyecto como proveedor seleccionado |
|
||||
| Root | No incluido | Magisk preroot en `boot.img` |
|
||||
| Ajustes | Opciones estándar | Controles de DNS, conectividad, ubicación, llamadas y privacidad de AXP |
|
||||
| Debloat | Paquetes estándar de la base | Limpieza selectiva adaptada a microG y `sunfish` |
|
||||
| Kernel | `msm-4.14` de LineageOS | Stage 1, FORTIFY, backports CVE y configuración reforzada |
|
||||
| Allocator | Bionic estándar | `hardened_malloc` para 64 bits y Scudo para 32 bits |
|
||||
| Identidad | Build LineageOS habitual | Producto PRO firmado con `release-keys` |
|
||||
| Actualizaciones | Nightlies de LineageOS | ZIP completo por recovery/sideload |
|
||||
|
||||
AXPR mantiene la base de dispositivo de LineageOS y mejora sus capas de
|
||||
producto, privacidad y hardening. No es una copia literal de GrapheneOS ni una
|
||||
continuación oficial de AXP.OS: es una adaptación bastante directa de AXP.OS
|
||||
Pro, con las mejoras y correcciones necesarias para Android 16.
|
||||
|
||||
---
|
||||
|
||||
## 6. Settings and toggles (reference)
|
||||
## 6. Estado de AXPR23 y última build
|
||||
|
||||
Same features as **§2.4**; quick lookup by menu area.
|
||||
AXPR23 está **EOL / archivada**. LineageOS dejó de publicar nuevas builds de
|
||||
Android 16 para `sunfish` al comenzar la línea Android 17. No habrá nuevas
|
||||
features ni nuevas builds AXPR23; se conserva como referencia y rollback.
|
||||
|
||||
These appear in **Settings** / **Developer options** (exact menus follow AOSP
|
||||
Lineage layout). Names may be localized.
|
||||
- **Build:** `AXP-Resurection-Pro-23.2-20261025-sunfish.zip`
|
||||
- **Base:** LineageOS 23.2 / Android 16
|
||||
- **SHA-256:** `9d205fd3cc945438dfff3a23b129bf88516c7654f5922bc2ee4d478f9485eef0`
|
||||
- **Siguiente línea:** AXPR24 sobre LineageOS 24 / Android 17
|
||||
|
||||
### 6.1 Privacy and network
|
||||
AXPR24 no es una actualización automática de AXPR23. Es una nueva adaptación
|
||||
que debe volver a validar aplicaciones, WebView, allocator, kernel y packaging
|
||||
contra Android 17.
|
||||
|
||||
| Feature | What it does |
|
||||
|---------|----------------|
|
||||
| **Private DNS providers** | Extended preset list (AdGuard, Mullvad, Quad9, Cloudflare, Google DoT, regional providers, etc.) in addition to Lineage defaults |
|
||||
| **Internet connectivity check** | Configurable connectivity validation (AXP-style behaviour; no dependency on AXP-owned servers) |
|
||||
| **Force disable SUPL** | When enabled, disables SUPL-assisted GNSS (stricter location privacy) |
|
||||
| **Stop extra location assistance** | When enabled, reduces extra Qualcomm / XTRA-style assistance daemons |
|
||||
## Descarga
|
||||
|
||||
### 6.2 Security and developer
|
||||
La última build publicada es un ZIP completo para el Google Pixel 4a
|
||||
(`sunfish`), firmado con las claves de release del proyecto:
|
||||
|
||||
| Feature | What it does |
|
||||
|---------|----------------|
|
||||
| **Signature spoofing** | Developer option to allow microG / selected apps to spoof signatures (restricted API) |
|
||||
| **Call recording unlock** | Toggle to allow call recording where supported (AXP / EXTENDROM class) |
|
||||
| **FLAG_SECURE override** | Optional global override for screen capture blocking (default **off**) |
|
||||
| **Native debug / ptrace** | `persist.native_debug` — when enabled, relaxes ptrace scope for debugging (default off) |
|
||||
| **Disable app** | Classic **disable** for user apps restored alongside Android’s archive behaviour |
|
||||
[**Descargar AXPR23 — AXP.OS sobre LineageOS 23 para Pixel 4a**](https://vbits.me/download/284/)
|
||||
|
||||
### 6.3 Timeouts (Security / connected devices)
|
||||
El botón usa Download Monitor y conserva el contador dinámico de descargas.
|
||||
|
||||
| Feature | What it does |
|
||||
|---------|----------------|
|
||||
| **Automatic reboot** | Reboot after configurable idle time |
|
||||
| **Bluetooth timeout** | Turn off Bluetooth after inactivity |
|
||||
| **Wi‑Fi timeout** | Turn off Wi‑Fi after inactivity |
|
||||
- **Build:** `AXP-Resurection-Pro-23.2-20261025-sunfish.zip`
|
||||
- **Tamaño:** 1.397.472.560 bytes (1,30 GiB)
|
||||
- **MD5:** `23378f132ac8b1f53606183451715a9c`
|
||||
- **SHA-256:** `9d205fd3cc945438dfff3a23b129bf88516c7654f5922bc2ee4d478f9485eef0`
|
||||
- **Instalación:** ZIP completo mediante recovery/sideload. Haz una copia de
|
||||
seguridad antes de instalarlo.
|
||||
|
||||
### 6.4 Branding
|
||||
## Créditos
|
||||
|
||||
- **Build type:** Lineage **PRO** channel naming (`AXP Resurrection Pro` product)
|
||||
- **Release keys** (not `test-keys`) on shipped builds
|
||||
|
||||
---
|
||||
|
||||
## 7. System surface changes (deblob summary)
|
||||
|
||||
Full package-level detail is in **§2.5**. Short overview:
|
||||
|
||||
AXP Resurrection Pro **selectively removes** components that AXP Pro omitted or
|
||||
that are unnecessary on a microG device. This is **not** a blind “remove
|
||||
everything Google” script.
|
||||
|
||||
**Typical removals (by category):**
|
||||
|
||||
| Category | Examples of what is removed or not shipped |
|
||||
|----------|---------------------------------------------|
|
||||
| **Google EUICC / LPA** | Google’s eUICC app (OpenEUICC replaces user-facing LPA) |
|
||||
| **Carrier / RCS Google** | Selected Google carrier and RCS packages not needed for microG |
|
||||
| **Lineage maintenance tools on device** | Interactive shells and convenience packages on **system** that AXP Pro did not ship (e.g. `bash`, `htop`, `nano`, `unrar`, Lineage `addon.d` helper) |
|
||||
| **Lab / debug commands** | Further debug-only binaries trimmed in the full “wave 4” product |
|
||||
|
||||
**Retained (examples):** IMS, telephony services, Wi‑Fi / cellular stack,
|
||||
Widevine, camera, Qualcomm services required for daily use, `curl` / `vim` where
|
||||
AXP Pro kept them.
|
||||
|
||||
**Product partition:** OpenSSH **binaries** are not shipped; an **`init.openssh.rc`
|
||||
stub** may remain (AXP Pro parity) without shipping `sshd` on product.
|
||||
|
||||
---
|
||||
|
||||
## 8. Kernel and memory hardening
|
||||
|
||||
**Kernel version line:** Linux **4.14** (Qualcomm msm-4.14), as shipped by
|
||||
Lineage for Pixel 4a — **not** upgraded to a GKI 6.x kernel.
|
||||
|
||||
### 7.1 Already strong on Lineage (kept)
|
||||
|
||||
Examples: Clang CFI, shadow call stack, stack protector, KASLR, PAN, hardened
|
||||
usercopy, restricted perf events, and related options in the upstream sunfish
|
||||
defconfig.
|
||||
|
||||
### 7.2 GrapheneOS-derived Stage 1 pack (30 patches)
|
||||
|
||||
Backported **GrapheneOS / Android Hardening** series via the AXP kernel patch
|
||||
collection, including:
|
||||
|
||||
| Series | Purpose (summary) |
|
||||
|--------|-------------------|
|
||||
| **Deny USB** | Optional hardening around USB attack surface |
|
||||
| **allocsize** | Allocation size hardening helpers |
|
||||
| **bugon** | `ksize` / BUG_ON integrity checks |
|
||||
| **fortify** | Intra-object FORTIFY helpers in kernel |
|
||||
| **misc** | Miscellaneous hardening commits |
|
||||
| **random** | Brk / argument randomization |
|
||||
| **ro** | `__ro_after_init` and related read-only data hardening |
|
||||
| **sanitize** | Page / slab sanitization helpers |
|
||||
| **slab** | SLAB hardening companions |
|
||||
| **unused** | Removal of unused kernel API noise |
|
||||
|
||||
**Kernel config enabled with this stack (examples):**
|
||||
|
||||
- `CONFIG_FORTIFY_SOURCE`
|
||||
- `CONFIG_INIT_ON_ALLOC_DEFAULT_ON` / `CONFIG_INIT_ON_FREE_DEFAULT_ON`
|
||||
- `CONFIG_SECURITY_DMESG_RESTRICT`
|
||||
- `CONFIG_SLAB_HARDENED`
|
||||
- `CONFIG_SLAB_FREELIST_RANDOM` (US-S5-1 class)
|
||||
- **Yama LSM not enabled** (compat with apps using self-ptrace; matches classic AXP kernel posture)
|
||||
|
||||
**Not ported:** GrapheneOS **location lockdown** kernel policy (this ROM keeps
|
||||
Lineage-style location behaviour and precision).
|
||||
|
||||
### 7.3 Userspace allocator
|
||||
|
||||
- **hardened_malloc** (64-bit) integrated into **bionic** for eligible processes,
|
||||
with exclusions for graphics / camera stacks where required for stability.
|
||||
|
||||
### 7.4 CVE backports
|
||||
|
||||
The ROM applies a large set of **Linux kernel CVE backports** on top of the
|
||||
4.14 sunfish kernel, from the same curated lists used in AXP / Divest-style
|
||||
trees:
|
||||
|
||||
| Set | Count | Notes |
|
||||
|-----|-------|--------|
|
||||
| **Primary backport set** | **429** patch entries | Stable “clean offset” apply list |
|
||||
| **High-offset batches 1–3** | **27** extra patch entries | Larger backports with relaxed offset matching |
|
||||
| **Unique CVE identifiers (combined)** | **449** | Deduplicated ids in the list file below |
|
||||
|
||||
**Full CVE identifier list:** [`kernel-cve-backport-list.txt`](kernel-cve-backport-list.txt)
|
||||
(one CVE id per line, sorted).
|
||||
|
||||
**Special cases (not in the 449 list as-is):**
|
||||
|
||||
- **CVE-2022-49980** — raw one-liner backport is **denied**; a **lock-scope /
|
||||
UAF companion patch** is applied instead (USB subsystem stability).
|
||||
- Additional CVE paths may appear on a **denylist** when they conflict with
|
||||
sunfish drivers (Wi‑Fi, audio, etc.); those are skipped intentionally.
|
||||
|
||||
Platform **Android security patch** level follows **LineageOS merges** (e.g.
|
||||
`2026-09-01` on recent builds). **Vendor** (bootloader/modem) patch level
|
||||
remains on the **2023-09-05** line appropriate for Pixel 4a vendor blobs.
|
||||
|
||||
---
|
||||
|
||||
## 9. Verified Boot and signing
|
||||
|
||||
- Builds are signed with **project release keys** (`release-keys`).
|
||||
- **AVB** metadata uses the project key; **verification flags** suit an
|
||||
**unlocked** bootloader with Magisk (not a locked retail verified-boot product).
|
||||
- **Do not** downgrade vendor security patch below the ROM’s minimum without
|
||||
expecting install blocks or encryption init failures.
|
||||
|
||||
---
|
||||
|
||||
## 10. Installation (summary)
|
||||
|
||||
1. **Unlock bootloader** (erases data).
|
||||
2. Flash **boot** + **dtbo** from the **same** ROM package if you are not already
|
||||
on this ROM’s recovery (Pixel 4a uses recovery-in-boot).
|
||||
3. Boot **recovery** → **Factory reset / format data**.
|
||||
4. **Apply update → ADB sideload** the full `AXP-Resurrection-Pro-*.zip`.
|
||||
5. Reboot, complete setup, then **Magisk** first-time APK install as above.
|
||||
|
||||
Use the **full OTA zip**; flashing only `boot.img` does not update system,
|
||||
microG, or apps.
|
||||
|
||||
---
|
||||
|
||||
## 11. What this ROM does **not** promise
|
||||
|
||||
- No official Google certification or Play Protect “device integrity” guarantee.
|
||||
- No bundled **Play Integrity Fix** or banking modules.
|
||||
- No **AXP.OS** online update servers or external market repositories.
|
||||
- No **NeoLauncher** (Lineage Launcher3 remains).
|
||||
- No **KernelSU** in the standard build.
|
||||
- **Banking apps** may work in work profiles or Island; that is **app-specific**
|
||||
and not a ship guarantee.
|
||||
|
||||
---
|
||||
|
||||
## 12. Credits and lineage
|
||||
|
||||
- **LineageOS** — base operating system and device support.
|
||||
- **microG project** — free Google services reimplementation.
|
||||
- **Magisk** (topjohnwu) — root framework.
|
||||
- **AXP.OS / EXTENDROM** — product philosophy, preroot Magisk packaging, and
|
||||
many userspace behaviours cloned on this port.
|
||||
- **GrapheneOS / Android Hardening** — kernel patch series (via AXP kernel
|
||||
patch collection).
|
||||
- **Community apps:** Aurora, Droidify, Fossify, OpenEUICC, Jelly, etc.
|
||||
|
||||
AXP Resurrection Pro is an **independent community port**. It is not an official
|
||||
LineageOS, Google, or AXP.OS GmbH product.
|
||||
|
||||
---
|
||||
|
||||
## 13. Document history
|
||||
|
||||
| Version | Date | Notes |
|
||||
|---------|------|--------|
|
||||
| 1.1 | 2026-10-11 | Added **§2** delta vs LineageOS (features, removals, framework) |
|
||||
| 1.0 | 2026-10-11 | Initial public feature list for `23.2-20261008` generation |
|
||||
|
||||
For maintainers: internal engineering docs remain separate under `docs/` in the
|
||||
source repository.
|
||||
AXPR23 se apoya en el trabajo de **AXP.OS / EXTENDROM**, **LineageOS**,
|
||||
**microG**, **GrapheneOS / Android Hardening**, **Vanadium / Mulch / AOSmium**,
|
||||
Magisk, OpenEUICC y los proyectos de las aplicaciones integradas. Es una
|
||||
adaptación comunitaria independiente para `sunfish`.
|
||||
@@ -0,0 +1,62 @@
|
||||
# IntelliRAT v0.2
|
||||
|
||||
Systemless Magisk controller for automatic, reversible Android 16
|
||||
network-profile changes. It does not modify `/system`, `/system_ext`, `/vendor`,
|
||||
RIL, modem firmware or framework.
|
||||
|
||||
## Scope
|
||||
|
||||
- Backend: Android 16 `cmd phone set-allowed-network-types-for-users`.
|
||||
- Target: one slot at a time, default slot `0`.
|
||||
- HIGH: LTE + UMTS/HSPA + GSM families.
|
||||
- LOW: UMTS/HSPA + GSM families.
|
||||
- Safety: capture/restore, active-call refusal, result verification and event log.
|
||||
- No Xposed or Vector. The companion APK source is included under `app/`; the
|
||||
module zip includes it only after the AOSP app build produces
|
||||
`system/app/IntelliRAT/IntelliRAT.apk`.
|
||||
|
||||
The binary masks are derived from the LOS23/A16 `TelephonyManager` constants:
|
||||
network type bits are `1 << (NETWORK_TYPE - 1)`, with LTE CA normalized to LTE.
|
||||
They must be treated as target-build data, not a universal Qualcomm constant.
|
||||
|
||||
## Install on GOLDEN only
|
||||
|
||||
Do not install this in the production ROM or on LAB yet. Build a Magisk module
|
||||
zip with `scripts/build-intellirat-module.sh`, install it on GOLDEN with a test SIM, and keep the
|
||||
known-good ROM available. GOLDEN now detects the inserted test SIM as
|
||||
`LOADED` on `SIMon mobile`.
|
||||
|
||||
After Magisk loads the module:
|
||||
|
||||
```text
|
||||
su -g 2000 -G 2000 -c /system/bin/intellirat status 0
|
||||
su -g 2000 -G 2000 -c /system/bin/intellirat capture 0
|
||||
su -g 2000 -G 2000 -c /system/bin/intellirat --dry-run high 0
|
||||
su -g 2000 -G 2000 -c /system/bin/intellirat high 0
|
||||
su -g 2000 -G 2000 -c /system/bin/intellirat status 0
|
||||
su -g 2000 -G 2000 -c /system/bin/intellirat low 0
|
||||
su -g 2000 -G 2000 -c /system/bin/intellirat restore 0
|
||||
```
|
||||
|
||||
`capture` must be run before a transition. `restore` uses the captured user
|
||||
mask and does not guess a stock mode. `events.log` is stored under
|
||||
`/data/local/tmp/intellirat/`. The prototype uses the root-owned `shell` group
|
||||
for this small network-mask state because MagiskSU drops DAC capabilities for
|
||||
`su` commands; no credentials or private data are stored.
|
||||
|
||||
When the companion APK is present, launch **IntelliRAT** from the app drawer.
|
||||
It shows the current backend result, slot selector, automatic toggle, delay,
|
||||
foreground-package exceptions, capture/HIGH/LOW/restore buttons and a
|
||||
persistent status notification with quick actions. The automatic policy uses
|
||||
lock delay, unlock restoration and a safe call guard.
|
||||
|
||||
## Not implemented yet
|
||||
|
||||
- traffic protection
|
||||
- multi-SIM/eSIM UI and subscription discovery
|
||||
- Quick Settings tile
|
||||
|
||||
The current automatic policy is deliberately limited to Keyguard lock state, a
|
||||
configurable delay and explicit foreground-package exceptions. Wi-Fi, battery,
|
||||
traffic and multi-SIM rules remain disabled until their real device behavior is
|
||||
specified and tested with calls, IMS, SMS, data and signal loss.
|
||||
@@ -0,0 +1,10 @@
|
||||
android_app {
|
||||
name: "IntelliRAT",
|
||||
srcs: ["src/**/*.java"],
|
||||
manifest: "AndroidManifest.xml",
|
||||
certificate: "platform",
|
||||
platform_apis: true,
|
||||
optimize: {
|
||||
proguard_flags_files: ["proguard.flags"],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.axp.intellirat">
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:label="IntelliRAT"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Material.NoActionBar">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".IntelliRatService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="specialUse">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="telephony network profile controller" />
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".ActionReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".BootReceiver"
|
||||
android:directBootAware="false"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.USER_UNLOCKED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".IntelliRatTileService"
|
||||
android:label="IntelliRAT HIGH/LOW"
|
||||
android:icon="@drawable/ic_intellirat"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1 @@
|
||||
-keep class com.axp.intellirat.** { *; }
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M3,5h18v14H3zM5,7v10h14V7zM7,9h2v2H7zM11,9h6v2h-6zM7,13h2v2H7zM11,13h6v2h-6z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.axp.intellirat;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public final class ActionReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String command = intent.getStringExtra(IntelliRatService.EXTRA_COMMAND);
|
||||
if (command == null) return;
|
||||
Intent service = new Intent(context, IntelliRatService.class);
|
||||
service.putExtra(IntelliRatService.EXTRA_COMMAND, command);
|
||||
context.startService(service);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.axp.intellirat;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
final class Backend {
|
||||
static final class Result {
|
||||
final int exitCode;
|
||||
final String output;
|
||||
|
||||
Result(int exitCode, String output) {
|
||||
this.exitCode = exitCode;
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
boolean ok() {
|
||||
return exitCode == 0;
|
||||
}
|
||||
}
|
||||
|
||||
private Backend() {}
|
||||
|
||||
static Result run(String command, String slot) {
|
||||
if (!slot.matches("[0-9]+")) {
|
||||
return new Result(2, "Invalid slot: " + slot);
|
||||
}
|
||||
String shellCommand = "/system/bin/intellirat " + command + " " + slot;
|
||||
Process process = null;
|
||||
try {
|
||||
process = new ProcessBuilder("su", "-g", "2000", "-G", "2000", "-c", shellCommand)
|
||||
.redirectErrorStream(true)
|
||||
.start();
|
||||
StringBuilder output = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (output.length() > 0) output.append('\n');
|
||||
output.append(line);
|
||||
}
|
||||
}
|
||||
return new Result(process.waitFor(), output.toString());
|
||||
} catch (IOException e) {
|
||||
return new Result(127, "Root backend unavailable: " + e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return new Result(130, "Interrupted");
|
||||
} finally {
|
||||
if (process != null) process.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.axp.intellirat;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
public final class BootReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!context.getSharedPreferences(IntelliRatService.PREFS, Context.MODE_PRIVATE)
|
||||
.getBoolean(IntelliRatService.AUTO_ENABLED, false)) {
|
||||
return;
|
||||
}
|
||||
Intent service = new Intent(context, IntelliRatService.class);
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
context.startForegroundService(service);
|
||||
} else {
|
||||
context.startService(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
package com.axp.intellirat;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.app.KeyguardManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Build;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.BatteryManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkCapabilities;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public final class IntelliRatService extends Service {
|
||||
static final String ACTION_COMMAND = "com.axp.intellirat.COMMAND";
|
||||
static final String EXTRA_COMMAND = "command";
|
||||
static final String ACTION_AUTO_CONFIG = "com.axp.intellirat.AUTO_CONFIG";
|
||||
static final String EXTRA_AUTO_ENABLED = "automatic_enabled";
|
||||
static final String EXTRA_AUTO_DELAY_SECONDS = "automatic_delay_seconds";
|
||||
static final String EXTRA_AUTO_UNLOCK_DELAY_SECONDS = "automatic_unlock_delay_seconds";
|
||||
static final String EXTRA_AUTO_EXCEPTIONS = "automatic_exceptions";
|
||||
static final String PREFS = "intellirat";
|
||||
static final String AUTO_ENABLED = "automatic_enabled";
|
||||
static final String AUTO_DELAY_SECONDS = "automatic_delay_seconds";
|
||||
static final String AUTO_UNLOCK_DELAY_SECONDS = "automatic_unlock_delay_seconds";
|
||||
static final String AUTO_EXCEPTIONS = "automatic_exceptions";
|
||||
static final String CHANNEL = "intellirat_status";
|
||||
private static final int NOTIFICATION_ID = 4103;
|
||||
private static final int RETRY_DELAY_SECONDS = 30;
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
private Runnable lowTask;
|
||||
private Runnable unlockTask;
|
||||
private Runnable foregroundPoll;
|
||||
private Runnable lockPoll;
|
||||
private Runnable dataRecovery;
|
||||
private int dataRecoveryAttempt;
|
||||
private boolean charging;
|
||||
private final BroadcastReceiver chargingReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
boolean nowCharging = isCharging();
|
||||
if (nowCharging != charging) {
|
||||
charging = nowCharging;
|
||||
onChargingChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
private boolean keyguardLocked;
|
||||
private boolean foregroundExceptionActive;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
createChannel();
|
||||
startForeground(NOTIFICATION_ID, notification("Starting root backend..."));
|
||||
keyguardLocked = isDeviceKeyguardLocked();
|
||||
charging = isCharging();
|
||||
IntentFilter powerFilter = new IntentFilter();
|
||||
powerFilter.addAction(Intent.ACTION_POWER_CONNECTED);
|
||||
powerFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
|
||||
registerReceiver(chargingReceiver, powerFilter);
|
||||
refresh("status");
|
||||
if (isAutomaticEnabled()) {
|
||||
configureAutomatic(true,
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getInt(AUTO_DELAY_SECONDS, 30),
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getInt(AUTO_UNLOCK_DELAY_SECONDS, 0),
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getString(AUTO_EXCEPTIONS, ""));
|
||||
}
|
||||
scheduleForegroundPoll();
|
||||
scheduleLockPoll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null && ACTION_AUTO_CONFIG.equals(intent.getAction())) {
|
||||
configureAutomatic(
|
||||
intent.getBooleanExtra(EXTRA_AUTO_ENABLED, false),
|
||||
intent.getIntExtra(EXTRA_AUTO_DELAY_SECONDS, 30),
|
||||
intent.getIntExtra(EXTRA_AUTO_UNLOCK_DELAY_SECONDS, 0),
|
||||
intent.getStringExtra(EXTRA_AUTO_EXCEPTIONS));
|
||||
} else if (intent != null && intent.hasExtra(EXTRA_COMMAND)) {
|
||||
enqueueTransition(intent.getStringExtra(EXTRA_COMMAND), "Manual transition");
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
cancelLow();
|
||||
cancelUnlock();
|
||||
cancelForegroundPoll();
|
||||
cancelLockPoll();
|
||||
cancelDataRecovery();
|
||||
unregisterReceiver(chargingReceiver);
|
||||
executor.shutdownNow();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void configureAutomatic(boolean enabled, int delaySeconds, int unlockDelaySeconds,
|
||||
String exceptions) {
|
||||
if (delaySeconds < 5 || delaySeconds > 3600) {
|
||||
updateNotification("Automatic policy rejected: delay must be 5-3600 seconds");
|
||||
return;
|
||||
}
|
||||
if (unlockDelaySeconds < 0 || unlockDelaySeconds > 3600) {
|
||||
updateNotification("Automatic policy rejected: unlock delay must be 0-3600 seconds");
|
||||
return;
|
||||
}
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE).edit()
|
||||
.putBoolean(AUTO_ENABLED, enabled)
|
||||
.putInt(AUTO_DELAY_SECONDS, delaySeconds)
|
||||
.putInt(AUTO_UNLOCK_DELAY_SECONDS, unlockDelaySeconds)
|
||||
.putString(AUTO_EXCEPTIONS, exceptions == null ? "" : exceptions)
|
||||
.apply();
|
||||
cancelLow();
|
||||
cancelUnlock();
|
||||
if (!enabled) {
|
||||
cancelForegroundPoll();
|
||||
cancelLockPoll();
|
||||
cancelDataRecovery();
|
||||
enqueueTransition("high", "Automatic policy disabled");
|
||||
return;
|
||||
}
|
||||
scheduleLockPoll();
|
||||
scheduleForegroundPoll();
|
||||
executor.execute(() -> {
|
||||
if (charging) {
|
||||
updateNotification("Charging: HIGH/LTE forced");
|
||||
runTransition("high", "Charging forced HIGH");
|
||||
return;
|
||||
}
|
||||
updateNotification("Automatic policy enabled: LOW after lock "
|
||||
+ delaySeconds + "s, HIGH after unlock " + unlockDelaySeconds + "s");
|
||||
keyguardLocked = isDeviceKeyguardLocked();
|
||||
if (!keyguardLocked) {
|
||||
scheduleUnlockHigh();
|
||||
} else if (isForegroundException()) {
|
||||
runTransition("high", "Streaming exception kept HIGH");
|
||||
} else {
|
||||
scheduleLow();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void scheduleLow() {
|
||||
cancelLow();
|
||||
if (!isAutomaticEnabled() || !keyguardLocked || charging) return;
|
||||
int delay = getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getInt(AUTO_DELAY_SECONDS, 30);
|
||||
lowTask = () -> {
|
||||
if (isAutomaticEnabled() && keyguardLocked) {
|
||||
enqueueTransition("low", "Automatic locked LOW");
|
||||
}
|
||||
};
|
||||
handler.postDelayed(lowTask, delay * 1000L);
|
||||
updateNotification("Locked: LOW in " + delay + "s");
|
||||
}
|
||||
|
||||
private void cancelLow() {
|
||||
if (lowTask != null) {
|
||||
handler.removeCallbacks(lowTask);
|
||||
lowTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleUnlockHigh() {
|
||||
cancelUnlock();
|
||||
if (!isAutomaticEnabled() || keyguardLocked || charging) return;
|
||||
int delay = getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getInt(AUTO_UNLOCK_DELAY_SECONDS, 0);
|
||||
unlockTask = () -> {
|
||||
if (isAutomaticEnabled() && !keyguardLocked) {
|
||||
enqueueTransition("high", "Automatic unlocked HIGH");
|
||||
}
|
||||
};
|
||||
if (delay == 0) {
|
||||
handler.post(unlockTask);
|
||||
} else {
|
||||
handler.postDelayed(unlockTask, delay * 1000L);
|
||||
updateNotification("Unlocked: HIGH in " + delay + "s");
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelUnlock() {
|
||||
if (unlockTask != null) {
|
||||
handler.removeCallbacks(unlockTask);
|
||||
unlockTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleLockPoll() {
|
||||
cancelLockPoll();
|
||||
if (!isAutomaticEnabled()) return;
|
||||
lockPoll = () -> {
|
||||
if (!isAutomaticEnabled()) return;
|
||||
boolean locked = isDeviceKeyguardLocked();
|
||||
if (locked != keyguardLocked) {
|
||||
keyguardLocked = locked;
|
||||
cancelLow();
|
||||
if (locked) {
|
||||
if (foregroundExceptionActive) {
|
||||
enqueueTransition("high", "Streaming exception kept HIGH");
|
||||
} else {
|
||||
scheduleLow();
|
||||
}
|
||||
} else {
|
||||
scheduleUnlockHigh();
|
||||
}
|
||||
}
|
||||
handler.postDelayed(lockPoll, 1000L);
|
||||
};
|
||||
handler.post(lockPoll);
|
||||
}
|
||||
|
||||
private void cancelLockPoll() {
|
||||
if (lockPoll != null) {
|
||||
handler.removeCallbacks(lockPoll);
|
||||
lockPoll = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleDataRecovery() {
|
||||
cancelDataRecovery();
|
||||
dataRecoveryAttempt = 0;
|
||||
dataRecovery = () -> {
|
||||
if (!isAutomaticEnabled() || !keyguardLocked || hasValidatedData()
|
||||
|| dataRecoveryAttempt >= 3) {
|
||||
return;
|
||||
}
|
||||
int attempt = ++dataRecoveryAttempt;
|
||||
executor.execute(() -> {
|
||||
Backend.Result result = Backend.run("low", "0");
|
||||
updateNotification(result.ok()
|
||||
? "LOW data recovery attempt " + attempt + ": " + result.output
|
||||
: "LOW data recovery failed: " + result.output);
|
||||
handler.postDelayed(dataRecovery, attempt * 15000L);
|
||||
});
|
||||
};
|
||||
handler.postDelayed(dataRecovery, 10000L);
|
||||
}
|
||||
|
||||
private void cancelDataRecovery() {
|
||||
if (dataRecovery != null) {
|
||||
handler.removeCallbacks(dataRecovery);
|
||||
dataRecovery = null;
|
||||
}
|
||||
dataRecoveryAttempt = 0;
|
||||
}
|
||||
|
||||
private boolean hasValidatedData() {
|
||||
ConnectivityManager connectivity =
|
||||
(ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
|
||||
NetworkCapabilities capabilities = connectivity.getNetworkCapabilities(
|
||||
connectivity.getActiveNetwork());
|
||||
return capabilities != null
|
||||
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
|
||||
}
|
||||
|
||||
private boolean isDeviceKeyguardLocked() {
|
||||
KeyguardManager keyguard = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
|
||||
if (keyguard.isKeyguardLocked()) return true;
|
||||
return Build.VERSION.SDK_INT >= 23 && keyguard.isDeviceLocked();
|
||||
}
|
||||
|
||||
private boolean isCharging() {
|
||||
Intent battery = registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
if (battery == null) return false;
|
||||
int status = battery.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
||||
return status == BatteryManager.BATTERY_STATUS_CHARGING
|
||||
|| status == BatteryManager.BATTERY_STATUS_FULL;
|
||||
}
|
||||
|
||||
private void onChargingChanged() {
|
||||
if (!isAutomaticEnabled()) return;
|
||||
cancelLow();
|
||||
cancelUnlock();
|
||||
cancelDataRecovery();
|
||||
if (charging) {
|
||||
enqueueTransition("high", "Charging forced HIGH");
|
||||
updateNotification("Charging: HIGH/LTE forced");
|
||||
} else if (keyguardLocked) {
|
||||
scheduleLow();
|
||||
} else {
|
||||
scheduleUnlockHigh();
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleForegroundPoll() {
|
||||
cancelForegroundPoll();
|
||||
if (!isAutomaticEnabled()) return;
|
||||
foregroundPoll = () -> {
|
||||
if (!isAutomaticEnabled()) return;
|
||||
if (!keyguardLocked) {
|
||||
boolean exception = isForegroundException();
|
||||
if (exception != foregroundExceptionActive) {
|
||||
foregroundExceptionActive = exception;
|
||||
if (exception) {
|
||||
enqueueTransition("high", "Foreground exception kept HIGH");
|
||||
}
|
||||
}
|
||||
}
|
||||
handler.postDelayed(foregroundPoll, 5000L);
|
||||
};
|
||||
handler.post(foregroundPoll);
|
||||
}
|
||||
|
||||
private void cancelForegroundPoll() {
|
||||
if (foregroundPoll != null) {
|
||||
handler.removeCallbacks(foregroundPoll);
|
||||
foregroundPoll = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isForegroundException() {
|
||||
String configured = getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getString(AUTO_EXCEPTIONS, "");
|
||||
if (configured.trim().isEmpty()) return false;
|
||||
String packageName = null;
|
||||
try {
|
||||
UsageStatsManager usage = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
|
||||
UsageEvents events = usage.queryEvents(
|
||||
System.currentTimeMillis() - 30000L, System.currentTimeMillis());
|
||||
UsageEvents.Event event = new UsageEvents.Event();
|
||||
while (events != null && events.hasNextEvent()) {
|
||||
events.getNextEvent(event);
|
||||
if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
|
||||
packageName = event.getPackageName();
|
||||
}
|
||||
}
|
||||
} catch (SecurityException ignored) {
|
||||
return false;
|
||||
}
|
||||
if (packageName == null) return foregroundExceptionActive;
|
||||
for (String candidate : configured.split("[,\\n\\r ]+")) {
|
||||
if (candidate.trim().equals(packageName)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void enqueueTransition(String command, String label) {
|
||||
executor.execute(() -> runTransition(command, label));
|
||||
}
|
||||
|
||||
private void runTransition(String command, String label) {
|
||||
Backend.Result result = Backend.run(command, "0");
|
||||
String text = result.ok() ? label + ": " + result.output
|
||||
: label + " failed: " + result.output;
|
||||
updateNotification(text);
|
||||
if (result.ok() && "low".equals(command) && isAutomaticEnabled() && keyguardLocked) {
|
||||
scheduleDataRecovery();
|
||||
} else if ("high".equals(command)) {
|
||||
cancelDataRecovery();
|
||||
}
|
||||
if (!result.ok() && "low".equals(command) && isAutomaticEnabled() && keyguardLocked) {
|
||||
handler.postDelayed(() -> {
|
||||
if (isAutomaticEnabled() && keyguardLocked) {
|
||||
enqueueTransition("low", "Automatic locked LOW retry");
|
||||
}
|
||||
}, RETRY_DELAY_SECONDS * 1000L);
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh(String command) {
|
||||
executor.execute(() -> {
|
||||
Backend.Result result = Backend.run(command, "0");
|
||||
String text = result.ok() ? result.output : "Backend error: " + result.output;
|
||||
updateNotification(text);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateNotification(String text) {
|
||||
((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
|
||||
.notify(NOTIFICATION_ID, notification(text));
|
||||
}
|
||||
|
||||
private boolean isAutomaticEnabled() {
|
||||
return getSharedPreferences(PREFS, MODE_PRIVATE).getBoolean(AUTO_ENABLED, false);
|
||||
}
|
||||
|
||||
private Notification notification(String text) {
|
||||
Intent open = new Intent(this, MainActivity.class);
|
||||
PendingIntent content = PendingIntent.getActivity(
|
||||
this, 1, open, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
return new Notification.Builder(this, CHANNEL)
|
||||
.setSmallIcon(android.R.drawable.ic_menu_manage)
|
||||
.setContentTitle("IntelliRAT")
|
||||
.setContentText(oneLine(text))
|
||||
.setStyle(new Notification.BigTextStyle().bigText(text))
|
||||
.setContentIntent(content)
|
||||
.setOngoing(true)
|
||||
.addAction(action("HIGH", "high", 2))
|
||||
.addAction(action("LOW", "low", 3))
|
||||
.build();
|
||||
}
|
||||
|
||||
private Notification.Action action(String title, String command, int requestCode) {
|
||||
Intent intent = new Intent(this, ActionReceiver.class);
|
||||
intent.setAction(ACTION_COMMAND);
|
||||
intent.putExtra(EXTRA_COMMAND, command);
|
||||
PendingIntent pending = PendingIntent.getBroadcast(
|
||||
this, requestCode, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
return new Notification.Action.Builder(null, title, pending).build();
|
||||
}
|
||||
|
||||
private String oneLine(String text) {
|
||||
return text.replace('\n', ' ');
|
||||
}
|
||||
|
||||
private void createChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL, "IntelliRAT status", NotificationManager.IMPORTANCE_LOW);
|
||||
channel.setDescription("Current IntelliRAT network profile");
|
||||
((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
|
||||
.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.axp.intellirat;
|
||||
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public final class IntelliRatTileService extends TileService {
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
super.onStartListening();
|
||||
refreshTile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
super.onClick();
|
||||
updateTile(Tile.STATE_INACTIVE, "IntelliRAT");
|
||||
executor.execute(() -> {
|
||||
Backend.Result status = Backend.run("status", "0");
|
||||
if (!status.ok()) {
|
||||
updateTile(Tile.STATE_INACTIVE, "IntelliRAT");
|
||||
return;
|
||||
}
|
||||
String command = status.output.contains("state=HIGH") ? "low" : "high";
|
||||
Backend.Result result = Backend.run(command, "0");
|
||||
updateTile(result.ok() && "high".equals(command)
|
||||
? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE, "IntelliRAT");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
executor.shutdownNow();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void refreshTile() {
|
||||
executor.execute(() -> {
|
||||
Backend.Result result = Backend.run("status", "0");
|
||||
if (!result.ok()) {
|
||||
updateTile(Tile.STATE_INACTIVE, "LOW");
|
||||
} else if (result.output.contains("state=HIGH")) {
|
||||
updateTile(Tile.STATE_ACTIVE, "HIGH");
|
||||
} else if (result.output.contains("state=LOW")) {
|
||||
updateTile(Tile.STATE_INACTIVE, "LOW");
|
||||
} else {
|
||||
updateTile(Tile.STATE_UNAVAILABLE, "IntelliRAT");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateTile(int state, String label) {
|
||||
getMainExecutor().execute(() -> {
|
||||
Tile tile = getQsTile();
|
||||
if (tile == null) return;
|
||||
tile.setState(state);
|
||||
tile.setLabel("IntelliRAT");
|
||||
tile.setContentDescription("IntelliRAT " + label);
|
||||
tile.updateTile();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
package com.axp.intellirat;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.provider.Settings;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class MainActivity extends Activity {
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
private TextView status;
|
||||
private EditText slot;
|
||||
private EditText delay;
|
||||
private EditText unlockDelay;
|
||||
private EditText exceptions;
|
||||
private Switch automatic;
|
||||
private final Handler applyHandler = new Handler(Looper.getMainLooper());
|
||||
private final Runnable applyTask = this::configureAutomaticPolicy;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
setTitle("IntelliRAT");
|
||||
buildUi();
|
||||
requestNotifications();
|
||||
requestUsageAccessIfNeeded();
|
||||
startService(new Intent(this, IntelliRatService.class));
|
||||
runCommand("status");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
executor.shutdownNow();
|
||||
applyHandler.removeCallbacks(applyTask);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void buildUi() {
|
||||
LinearLayout root = new LinearLayout(this);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setPadding(32, 28, 32, 28);
|
||||
|
||||
TextView title = new TextView(this);
|
||||
title.setText("IntelliRAT");
|
||||
title.setTextSize(28);
|
||||
title.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
|
||||
root.addView(title, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
TextView subtitle = new TextView(this);
|
||||
subtitle.setText("Android 16 reversible network profile controller");
|
||||
subtitle.setPadding(0, 4, 0, 24);
|
||||
root.addView(subtitle, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
LinearLayout slotRow = new LinearLayout(this);
|
||||
slotRow.setGravity(Gravity.CENTER_VERTICAL);
|
||||
TextView slotLabel = new TextView(this);
|
||||
slotLabel.setText("Slot: ");
|
||||
slotRow.addView(slotLabel, new LinearLayout.LayoutParams(-2, -2));
|
||||
slot = new EditText(this);
|
||||
slot.setText("0");
|
||||
slot.setSingleLine(true);
|
||||
slot.setInputType(2);
|
||||
slotRow.addView(slot, new LinearLayout.LayoutParams(120, -2));
|
||||
root.addView(slotRow, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
TextView mode = new TextView(this);
|
||||
mode.setText("Automatic lock policy: LOW after lock delay, HIGH after unlock.");
|
||||
mode.setPadding(0, 20, 0, 12);
|
||||
root.addView(mode, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
TextView lockLabel = new TextView(this);
|
||||
lockLabel.setText("Lock LOW delay (seconds)");
|
||||
root.addView(lockLabel, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
LinearLayout policy = new LinearLayout(this);
|
||||
policy.setGravity(Gravity.CENTER_VERTICAL);
|
||||
automatic = new Switch(this);
|
||||
automatic.setText("Automatic lock policy");
|
||||
automatic.setChecked(getSharedPreferences(IntelliRatService.PREFS, MODE_PRIVATE)
|
||||
.getBoolean(IntelliRatService.AUTO_ENABLED, false));
|
||||
automatic.setOnCheckedChangeListener((button, checked) -> scheduleAutomaticApply());
|
||||
policy.addView(automatic, new LinearLayout.LayoutParams(0, -2, 1));
|
||||
delay = new EditText(this);
|
||||
delay.setText(String.valueOf(getSharedPreferences(IntelliRatService.PREFS, MODE_PRIVATE)
|
||||
.getInt(IntelliRatService.AUTO_DELAY_SECONDS, 30)));
|
||||
delay.setHint("seconds");
|
||||
delay.setSingleLine(true);
|
||||
delay.setInputType(2);
|
||||
policy.addView(delay, new LinearLayout.LayoutParams(220, -2));
|
||||
addApplyWatcher(delay);
|
||||
root.addView(policy, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
TextView unlockLabel = new TextView(this);
|
||||
unlockLabel.setText("Unlock HIGH delay (seconds; 0 = immediate)");
|
||||
root.addView(unlockLabel, new LinearLayout.LayoutParams(-1, -2));
|
||||
unlockDelay = new EditText(this);
|
||||
unlockDelay.setText(String.valueOf(getSharedPreferences(IntelliRatService.PREFS, MODE_PRIVATE)
|
||||
.getInt(IntelliRatService.AUTO_UNLOCK_DELAY_SECONDS, 0)));
|
||||
unlockDelay.setHint("unlock HIGH seconds (0 = immediate)");
|
||||
unlockDelay.setSingleLine(true);
|
||||
unlockDelay.setInputType(2);
|
||||
addApplyWatcher(unlockDelay);
|
||||
root.addView(unlockDelay, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
Button selectExceptions = new Button(this);
|
||||
selectExceptions.setText("Select exception apps");
|
||||
selectExceptions.setOnClickListener(view -> showExceptionPicker());
|
||||
root.addView(selectExceptions, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
exceptions = new EditText(this);
|
||||
exceptions.setHint("Exception packages, one per line (for example com.spotify.music)");
|
||||
exceptions.setInputType(android.text.InputType.TYPE_CLASS_TEXT
|
||||
| android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE);
|
||||
exceptions.setMinLines(2);
|
||||
exceptions.setText(getSharedPreferences(IntelliRatService.PREFS, MODE_PRIVATE)
|
||||
.getString(IntelliRatService.AUTO_EXCEPTIONS, ""));
|
||||
addApplyWatcher(exceptions);
|
||||
root.addView(exceptions, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
LinearLayout actions = new LinearLayout(this);
|
||||
actions.setOrientation(LinearLayout.VERTICAL);
|
||||
addButton(actions, "Refresh status", "status");
|
||||
addButton(actions, "HIGH LTE / 3G / 2G", "high");
|
||||
addButton(actions, "LOW 3G / 2G", "low");
|
||||
root.addView(actions, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
status = new TextView(this);
|
||||
status.setTextIsSelectable(true);
|
||||
status.setTypeface(Typeface.MONOSPACE);
|
||||
status.setPadding(0, 24, 0, 0);
|
||||
status.setText("Waiting for root backend...");
|
||||
ScrollView scroll = new ScrollView(this);
|
||||
scroll.addView(status);
|
||||
root.addView(scroll, new LinearLayout.LayoutParams(-1, 0, 1));
|
||||
|
||||
setContentView(root);
|
||||
}
|
||||
|
||||
private void addButton(LinearLayout parent, String label, String command) {
|
||||
Button button = new Button(this);
|
||||
button.setText(label);
|
||||
button.setOnClickListener(view -> runCommand(command));
|
||||
parent.addView(button, new LinearLayout.LayoutParams(-1, -2));
|
||||
}
|
||||
|
||||
private void runCommand(String command) {
|
||||
String selectedSlot = slot == null ? "0" : slot.getText().toString().trim();
|
||||
status.setText("Running: " + command + " slot " + selectedSlot + "...");
|
||||
executor.execute(() -> {
|
||||
Backend.Result result = Backend.run(command, selectedSlot);
|
||||
runOnUiThread(() -> status.setText(
|
||||
(result.ok() ? "OK\n" : "FAILED (" + result.exitCode + ")\n")
|
||||
+ result.output));
|
||||
});
|
||||
}
|
||||
|
||||
private void configureAutomaticPolicy() {
|
||||
String value = delay == null ? "30" : delay.getText().toString().trim();
|
||||
int seconds;
|
||||
try {
|
||||
seconds = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
status.setText("Invalid delay: use 5-3600 seconds");
|
||||
return;
|
||||
}
|
||||
if (seconds < 5 || seconds > 3600) {
|
||||
status.setText("Invalid delay: use 5-3600 seconds");
|
||||
return;
|
||||
}
|
||||
Intent service = new Intent(this, IntelliRatService.class)
|
||||
.setAction(IntelliRatService.ACTION_AUTO_CONFIG)
|
||||
.putExtra(IntelliRatService.EXTRA_AUTO_ENABLED,
|
||||
automatic != null && automatic.isChecked())
|
||||
.putExtra(IntelliRatService.EXTRA_AUTO_DELAY_SECONDS, seconds)
|
||||
.putExtra(IntelliRatService.EXTRA_AUTO_UNLOCK_DELAY_SECONDS,
|
||||
parseUnlockDelay())
|
||||
.putExtra(IntelliRatService.EXTRA_AUTO_EXCEPTIONS,
|
||||
exceptions == null ? "" : exceptions.getText().toString());
|
||||
startService(service);
|
||||
status.setText("Applying automatic policy...");
|
||||
}
|
||||
|
||||
private void addApplyWatcher(EditText field) {
|
||||
field.addTextChangedListener(new TextWatcher() {
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
||||
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
scheduleAutomaticApply();
|
||||
}
|
||||
@Override public void afterTextChanged(Editable s) { }
|
||||
});
|
||||
}
|
||||
|
||||
private void scheduleAutomaticApply() {
|
||||
applyHandler.removeCallbacks(applyTask);
|
||||
applyHandler.postDelayed(applyTask, 600L);
|
||||
}
|
||||
|
||||
private void requestUsageAccessIfNeeded() {
|
||||
AppOpsManager appOps = (AppOpsManager) getSystemService(APP_OPS_SERVICE);
|
||||
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
|
||||
Process.myUid(), getPackageName());
|
||||
if (mode != AppOpsManager.MODE_ALLOWED) {
|
||||
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
|
||||
}
|
||||
}
|
||||
|
||||
private int parseUnlockDelay() {
|
||||
String value = unlockDelay == null ? "0" : unlockDelay.getText().toString().trim();
|
||||
int seconds;
|
||||
try {
|
||||
seconds = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(0, Math.min(3600, seconds));
|
||||
}
|
||||
|
||||
private void showExceptionPicker() {
|
||||
PackageManager packageManager = getPackageManager();
|
||||
List<ApplicationInfo> applications = new ArrayList<>();
|
||||
for (ApplicationInfo application : packageManager.getInstalledApplications(
|
||||
PackageManager.GET_META_DATA)) {
|
||||
if ((application.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
|| application.packageName.equals("android")
|
||||
|| application.packageName.equals(getPackageName())) {
|
||||
continue;
|
||||
}
|
||||
applications.add(application);
|
||||
}
|
||||
Collections.sort(applications, Comparator.comparing(application ->
|
||||
packageManager.getApplicationLabel(application).toString().toLowerCase()));
|
||||
String[] labels = new String[applications.size()];
|
||||
boolean[] checked = new boolean[applications.size()];
|
||||
Set<String> selected = new HashSet<>();
|
||||
for (String value : exceptions.getText().toString().split("[,\\n\\r ]+")) {
|
||||
if (!value.trim().isEmpty()) selected.add(value.trim());
|
||||
}
|
||||
for (int i = 0; i < applications.size(); i++) {
|
||||
ApplicationInfo application = applications.get(i);
|
||||
labels[i] = packageManager.getApplicationLabel(application).toString();
|
||||
checked[i] = selected.contains(application.packageName);
|
||||
}
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Keep HIGH for selected apps")
|
||||
.setMultiChoiceItems(labels, checked, (dialog, which, isChecked) -> {
|
||||
checked[which] = isChecked;
|
||||
})
|
||||
.setPositiveButton("Use selected", (dialog, which) -> {
|
||||
StringBuilder value = new StringBuilder();
|
||||
for (int i = 0; i < applications.size(); i++) {
|
||||
if (checked[i]) {
|
||||
if (value.length() > 0) value.append('\n');
|
||||
value.append(applications.get(i).packageName);
|
||||
}
|
||||
}
|
||||
exceptions.setText(value.toString());
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void requestNotifications() {
|
||||
if (Build.VERSION.SDK_INT >= 33
|
||||
&& checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# IntelliRAT v0.1 manual prototype.
|
||||
# Masks are Android TelephonyManager bitmasks encoded as 20-bit binary strings.
|
||||
# HIGH = LTE + UMTS/HSPA + GSM families. LOW = UMTS/HSPA + GSM families.
|
||||
TARGET_SLOT=0
|
||||
HIGH_MASK=00011101001110000111
|
||||
LOW_MASK=00011100001110000111
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
INSTALL_LOG=/data/local/tmp/intellirat-install.log
|
||||
INSTALL_APK=/data/local/tmp/intellirat-install.apk
|
||||
|
||||
printf '%s phase=customize start\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> "$INSTALL_LOG"
|
||||
set_perm "$MODPATH/service.sh" 0 0 0755
|
||||
set_perm "$MODPATH/uninstall.sh" 0 0 0755
|
||||
set_perm "$MODPATH/system/bin/intellirat" 0 0 0755
|
||||
set_perm "$MODPATH/payload/IntelliRAT.apk" 0 0 0644
|
||||
|
||||
package_ready=0
|
||||
attempt=0
|
||||
while [ "$attempt" -lt 30 ]; do
|
||||
if cmd package path android >/dev/null 2>&1; then
|
||||
package_ready=1
|
||||
break
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ "$package_ready" != 1 ]; then
|
||||
printf '%s phase=customize error=package_service_timeout\n' \
|
||||
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> "$INSTALL_LOG"
|
||||
abort "IntelliRAT: Android package service did not become available"
|
||||
fi
|
||||
|
||||
if ! cp "$MODPATH/payload/IntelliRAT.apk" "$INSTALL_APK"; then
|
||||
printf '%s phase=customize error=copy_failed\n' \
|
||||
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> "$INSTALL_LOG"
|
||||
abort "IntelliRAT: could not stage APK for PackageManager"
|
||||
fi
|
||||
chmod 0644 "$INSTALL_APK"
|
||||
install_result=$(pm install -r --user 0 "$INSTALL_APK" 2>&1 || true)
|
||||
printf '%s phase=customize result=%s\n' \
|
||||
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$install_result" >> "$INSTALL_LOG"
|
||||
rm -f "$INSTALL_APK"
|
||||
case "$install_result" in
|
||||
*Success*) ;;
|
||||
*) abort "IntelliRAT APK installation failed: $install_result" ;;
|
||||
esac
|
||||
|
||||
for user in $(cmd user list 2>/dev/null | awk -F '[{}:]' '/UserInfo/ {print $2}'); do
|
||||
if [ "$user" != 0 ]; then
|
||||
pm uninstall --user "$user" com.axp.intellirat >/dev/null 2>&1 || true
|
||||
fi
|
||||
done
|
||||
|
||||
TILE='custom(com.axp.intellirat/.IntelliRatTileService)'
|
||||
tiles=$(settings get secure sysui_qs_tiles 2>/dev/null || true)
|
||||
case ",$tiles," in
|
||||
*",$TILE,"*) ;;
|
||||
*) settings put secure sysui_qs_tiles "$tiles,$TILE" ;;
|
||||
esac
|
||||
printf '%s phase=customize integration_complete\n' \
|
||||
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> "$INSTALL_LOG"
|
||||
@@ -0,0 +1,6 @@
|
||||
id=intellirat
|
||||
name=IntelliRAT
|
||||
version=0.2.0
|
||||
versionCode=2
|
||||
author=AXP Resurrection
|
||||
description=Automatic reversible Android 16 RAT profile controller
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
# Initialize IntelliRAT state and register its Quick Settings tile.
|
||||
MODDIR=${0%/*}
|
||||
STATE_DIR=/data/local/tmp/intellirat
|
||||
APK="$MODDIR/payload/IntelliRAT.apk"
|
||||
INSTALL_APK=/data/local/tmp/intellirat-install.apk
|
||||
package_ready=1
|
||||
|
||||
if ! pm path com.axp.intellirat >/dev/null 2>&1 && [ -f "$APK" ]; then
|
||||
if cp "$APK" "$INSTALL_APK" 2>/dev/null; then
|
||||
chmod 0644 "$INSTALL_APK"
|
||||
install_result=$(pm install -r --user 0 "$INSTALL_APK" 2>&1 || true)
|
||||
printf '%s install_result=%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$install_result" \
|
||||
>> /data/local/tmp/intellirat-install.log
|
||||
rm -f "$INSTALL_APK"
|
||||
else
|
||||
printf '%s install_error=copy_failed source=%s\n' \
|
||||
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$APK" >> /data/local/tmp/intellirat-install.log
|
||||
fi
|
||||
elif [ "$package_ready" = 1 ] && ! pm path com.axp.intellirat >/dev/null 2>&1; then
|
||||
printf '%s install_error=payload_missing path=%s\n' \
|
||||
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$APK" >> /data/local/tmp/intellirat-install.log
|
||||
fi
|
||||
for user in $(cmd user list 2>/dev/null | awk -F '[{}:]' '/UserInfo/ {print $2}'); do
|
||||
if [ "$user" != 0 ]; then
|
||||
pm uninstall --user "$user" com.axp.intellirat >/dev/null 2>&1 || true
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$STATE_DIR"
|
||||
chown 0 2000 "$STATE_DIR"
|
||||
chmod 0770 "$STATE_DIR"
|
||||
|
||||
if [ ! -f "$STATE_DIR/config.conf" ]; then
|
||||
cp "$MODDIR/config/default.conf" "$STATE_DIR/config.conf"
|
||||
chown 0 2000 "$STATE_DIR/config.conf"
|
||||
chmod 0600 "$STATE_DIR/config.conf"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ') module_loaded version=0.1.0" \
|
||||
>> "$STATE_DIR/events.log"
|
||||
chown 0 2000 "$STATE_DIR/events.log"
|
||||
chmod 0600 "$STATE_DIR/events.log"
|
||||
|
||||
TILE='custom(com.axp.intellirat/.IntelliRatTileService)'
|
||||
tiles=$(settings get secure sysui_qs_tiles 2>/dev/null || true)
|
||||
case ",$tiles," in
|
||||
*",$TILE,"*) ;;
|
||||
*) settings put secure sysui_qs_tiles "$tiles,$TILE" ;;
|
||||
esac
|
||||
Executable
+296
@@ -0,0 +1,296 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
set -u
|
||||
|
||||
STATE_DIR=${INTELLIRAT_STATE_DIR:-/data/local/tmp}
|
||||
CONFIG_FILE=${INTELLIRAT_CONFIG:-$STATE_DIR/config.conf}
|
||||
LOG_FILE=${INTELLIRAT_LOG:-$STATE_DIR/events.log}
|
||||
CMD_BIN=${INTELLIRAT_CMD:-cmd}
|
||||
DUMPSYS_BIN=${INTELLIRAT_DUMPSYS:-dumpsys}
|
||||
|
||||
DEFAULT_SLOT=0
|
||||
DEFAULT_HIGH_MASK=00011101001110000111
|
||||
DEFAULT_LOW_MASK=00011100001110000111
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
IntelliRAT v0.1 manual prototype
|
||||
|
||||
Usage:
|
||||
intellirat status [slot]
|
||||
intellirat capture [slot]
|
||||
intellirat high [slot]
|
||||
intellirat low [slot]
|
||||
intellirat restore [slot]
|
||||
intellirat --dry-run high|low|restore [slot]
|
||||
|
||||
The prototype never changes RAT automatically. Capture the current state before
|
||||
the first transition. Restore returns the captured user network mask.
|
||||
EOF
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf 'IntelliRAT: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
load_config() {
|
||||
TARGET_SLOT=$DEFAULT_SLOT
|
||||
HIGH_MASK=$DEFAULT_HIGH_MASK
|
||||
LOW_MASK=$DEFAULT_LOW_MASK
|
||||
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
# The file is created root-only by service.sh; reject unsafe ownership
|
||||
# only when the platform exposes stat in the available shell.
|
||||
# shellcheck disable=SC1090
|
||||
. "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
case "$TARGET_SLOT" in
|
||||
''|*[!0-9]*) fail "invalid TARGET_SLOT" ;;
|
||||
esac
|
||||
case "$HIGH_MASK:$LOW_MASK" in
|
||||
*[!01:]*|:*) fail "masks must be non-empty binary strings" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ensure_state_dir() {
|
||||
mkdir -p "$STATE_DIR" || fail "cannot create $STATE_DIR"
|
||||
if [ "$STATE_DIR" = /data/local/tmp ]; then
|
||||
return
|
||||
fi
|
||||
chown 0 2000 "$STATE_DIR" 2>/dev/null || true
|
||||
chmod 0770 "$STATE_DIR" 2>/dev/null || true
|
||||
}
|
||||
|
||||
log_event() {
|
||||
ensure_state_dir
|
||||
printf '%s %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$*" >> "$LOG_FILE"
|
||||
chmod 0600 "$LOG_FILE" 2>/dev/null || true
|
||||
}
|
||||
|
||||
slot_or_default() {
|
||||
if [ "${1:-}" = '' ]; then
|
||||
printf '%s' "$TARGET_SLOT"
|
||||
else
|
||||
case "$1" in
|
||||
''|*[!0-9]*) fail "invalid slot: $1" ;;
|
||||
esac
|
||||
printf '%s' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
state_file() {
|
||||
printf '%s/intellirat-sub-%s.state' "$STATE_DIR" "$1"
|
||||
}
|
||||
|
||||
read_user_names() {
|
||||
slot=$1
|
||||
result=$(
|
||||
"$CMD_BIN" phone get-allowed-network-types-for-users -s "$slot" 2>&1
|
||||
) || fail "cannot read network types for slot $slot: $result"
|
||||
case "$result" in
|
||||
*'No valid subscription found'*|*'No valid NETWORK_TYPES_BITMASK'*)
|
||||
fail "no valid subscription for slot $slot: $result"
|
||||
;;
|
||||
esac
|
||||
result=$(printf '%s' "$result" | tr -d '[:space:]\r\n')
|
||||
[ -n "$result" ] || fail "empty network type response for slot $slot"
|
||||
printf '%s' "$result"
|
||||
}
|
||||
|
||||
network_names_to_decimal() {
|
||||
names=$1
|
||||
mask=0
|
||||
old_ifs=$IFS
|
||||
IFS='|'
|
||||
# shellcheck disable=SC2086
|
||||
for name in $names; do
|
||||
case "$name" in
|
||||
GPRS) mask=$((mask | 1)) ;;
|
||||
EDGE) mask=$((mask | 2)) ;;
|
||||
UMTS) mask=$((mask | 4)) ;;
|
||||
CDMA) mask=$((mask | 8)) ;;
|
||||
EVDO_0) mask=$((mask | 16)) ;;
|
||||
EVDO_A) mask=$((mask | 32)) ;;
|
||||
1xRTT) mask=$((mask | 64)) ;;
|
||||
HSDPA) mask=$((mask | 128)) ;;
|
||||
HSUPA) mask=$((mask | 256)) ;;
|
||||
HSPA) mask=$((mask | 512)) ;;
|
||||
HSPA+) mask=$((mask | 16384)) ;;
|
||||
IDEN) mask=$((mask | 1024)) ;;
|
||||
EVDO_B) mask=$((mask | 2048)) ;;
|
||||
LTE) mask=$((mask | 4096)) ;;
|
||||
EHRPD) mask=$((mask | 8192)) ;;
|
||||
HSPAP) mask=$((mask | 16384)) ;;
|
||||
GSM) mask=$((mask | 32768)) ;;
|
||||
TD_SCDMA) mask=$((mask | 65536)) ;;
|
||||
IWLAN) mask=$((mask | 131072)) ;;
|
||||
LTE_CA) mask=$((mask | 4096)) ;;
|
||||
NR) mask=$((mask | 524288)) ;;
|
||||
UNKNOWN|NONE) ;;
|
||||
*) IFS=$old_ifs; fail "unknown network type from telephony: $name" ;;
|
||||
esac
|
||||
done
|
||||
IFS=$old_ifs
|
||||
printf '%s' "$mask"
|
||||
}
|
||||
|
||||
decimal_to_binary() {
|
||||
mask=$1
|
||||
output=
|
||||
bit=19
|
||||
while [ "$bit" -ge 0 ]; do
|
||||
value=$((1 << bit))
|
||||
if [ $((mask & value)) -ne 0 ]; then
|
||||
output="${output}1"
|
||||
else
|
||||
output="${output}0"
|
||||
fi
|
||||
bit=$((bit - 1))
|
||||
done
|
||||
printf '%s' "$output"
|
||||
}
|
||||
|
||||
read_user_binary() {
|
||||
names=$(read_user_names "$1")
|
||||
decimal=$(network_names_to_decimal "$names")
|
||||
decimal_to_binary "$decimal"
|
||||
}
|
||||
|
||||
call_in_progress() {
|
||||
dump=$(
|
||||
"$DUMPSYS_BIN" telecom 2>/dev/null || true
|
||||
)
|
||||
current_calls=$(printf '%s\n' "$dump" | awk '
|
||||
/^ All calls:/ { in_calls=1; next }
|
||||
/^ Active dialing, or connecting calls:/ { in_calls=1; next }
|
||||
/^ Ringing calls:/ { in_calls=1; next }
|
||||
/^ Holding calls:/ { in_calls=1; next }
|
||||
/^[[:space:]]*[A-Za-z][A-Za-z _-]*:$/ { in_calls=0 }
|
||||
in_calls { print }
|
||||
')
|
||||
printf '%s\n' "$current_calls" | grep -Eq \
|
||||
'(^|[^A-Za-z])(ACTIVE|RINGING|DIALING|CONNECTING)([^A-Za-z]|$)|mCallState=(ACTIVE|RINGING|DIALING|CONNECTING)'
|
||||
}
|
||||
|
||||
write_state() {
|
||||
slot=$1
|
||||
original_names=$2
|
||||
original_binary=$3
|
||||
file=$(state_file "$slot")
|
||||
tmp="${file}.tmp.$$"
|
||||
{
|
||||
printf 'original_names=%s\n' "$original_names"
|
||||
printf 'original_mask=%s\n' "$original_binary"
|
||||
printf 'captured_at=%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
} > "$tmp" || fail "cannot write $file"
|
||||
chmod 0600 "$tmp"
|
||||
mv "$tmp" "$file" || fail "cannot commit $file"
|
||||
}
|
||||
|
||||
capture() {
|
||||
slot=$1
|
||||
ensure_state_dir
|
||||
names=$(read_user_names "$slot")
|
||||
decimal=$(network_names_to_decimal "$names")
|
||||
binary=$(decimal_to_binary "$decimal")
|
||||
file=$(state_file "$slot")
|
||||
if [ -f "$file" ] && [ "${INTELLIRAT_FORCE_CAPTURE:-0}" != 1 ]; then
|
||||
fail "state already captured for slot $slot; set INTELLIRAT_FORCE_CAPTURE=1 to replace it"
|
||||
fi
|
||||
write_state "$slot" "$names" "$binary"
|
||||
log_event "capture slot=$slot mask=$binary names=$names"
|
||||
printf 'captured slot=%s mask=%s names=%s\n' "$slot" "$binary" "$names"
|
||||
}
|
||||
|
||||
get_original_mask() {
|
||||
file=$(state_file "$1")
|
||||
[ -f "$file" ] || {
|
||||
printf '%s' "$HIGH_MASK"
|
||||
return
|
||||
}
|
||||
value=$(grep '^original_mask=' "$file" | cut -d= -f2-)
|
||||
case "$value" in
|
||||
''|*[!01]*) fail "invalid saved state for slot $1" ;;
|
||||
esac
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
apply_mask() {
|
||||
slot=$1
|
||||
target=$2
|
||||
reason=$3
|
||||
current=$(read_user_binary "$slot")
|
||||
if [ "$current" = "$target" ]; then
|
||||
log_event "noop slot=$slot state=$reason mask=$target"
|
||||
printf 'already state=%s slot=%s mask=%s\n' "$reason" "$slot" "$target"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if call_in_progress; then
|
||||
fail "telephony call is active; transition refused"
|
||||
fi
|
||||
|
||||
if [ "${INTELLIRAT_DRY_RUN:-0}" = 1 ]; then
|
||||
printf 'dry-run cmd phone set-allowed-network-types-for-users -s %s %s\n' \
|
||||
"$slot" "$target"
|
||||
return 0
|
||||
fi
|
||||
|
||||
result=$(
|
||||
"$CMD_BIN" phone set-allowed-network-types-for-users -s "$slot" "$target" 2>&1
|
||||
) || fail "network transition failed: $result"
|
||||
case "$result" in
|
||||
*completed*) ;;
|
||||
*) fail "telephony did not confirm transition: $result" ;;
|
||||
esac
|
||||
|
||||
actual=$(read_user_binary "$slot")
|
||||
[ "$actual" = "$target" ] || fail "verification mismatch: requested=$target actual=$actual"
|
||||
log_event "transition slot=$slot previous=$current state=$reason requested=$target actual=$actual"
|
||||
printf 'transition slot=%s state=%s previous=%s actual=%s\n' \
|
||||
"$slot" "$reason" "$current" "$actual"
|
||||
}
|
||||
|
||||
status() {
|
||||
slot=$1
|
||||
names=$(read_user_names "$slot")
|
||||
decimal=$(network_names_to_decimal "$names")
|
||||
current=$(decimal_to_binary "$decimal")
|
||||
state=UNKNOWN
|
||||
[ "$current" = "$HIGH_MASK" ] && state=HIGH
|
||||
[ "$current" = "$LOW_MASK" ] && state=LOW
|
||||
saved=NO
|
||||
[ -f "$(state_file "$slot")" ] && saved=YES
|
||||
printf 'slot=%s state=%s mask=%s names=%s saved_original=%s\n' \
|
||||
"$slot" "$state" "$current" "$names" "$saved"
|
||||
}
|
||||
|
||||
main() {
|
||||
dry_run=0
|
||||
if [ "${1:-}" = '--dry-run' ]; then
|
||||
dry_run=1
|
||||
INTELLIRAT_DRY_RUN=1
|
||||
export INTELLIRAT_DRY_RUN
|
||||
shift
|
||||
fi
|
||||
load_config
|
||||
ensure_state_dir
|
||||
|
||||
command=${1:-help}
|
||||
shift || true
|
||||
slot=$(slot_or_default "${1:-}")
|
||||
|
||||
case "$command" in
|
||||
help|-h|--help) usage ;;
|
||||
status) status "$slot" ;;
|
||||
capture) capture "$slot" ;;
|
||||
high) apply_mask "$slot" "$HIGH_MASK" HIGH ;;
|
||||
low) apply_mask "$slot" "$LOW_MASK" LOW ;;
|
||||
restore) apply_mask "$slot" "$(get_original_mask "$slot")" RESTORE ;;
|
||||
*) usage >&2; exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
for user in $(cmd user list 2>/dev/null | awk -F '[{}:]' '/UserInfo/ {print $2}'); do
|
||||
pm uninstall --user "$user" com.axp.intellirat >/dev/null 2>&1 || true
|
||||
done
|
||||
rm -f /data/local/tmp/intellirat-sub-*.state
|
||||
rm -f /data/local/tmp/intellirat-events.log
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Package the systemless IntelliRAT prototype without touching a ROM build.
|
||||
set -eu
|
||||
|
||||
ROOT=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||
MODULE="$ROOT/modules/intellirat"
|
||||
OUTPUT=${1:-"$ROOT/artifacts/IntelliRAT-v0.2.0.zip"}
|
||||
|
||||
test -f "$MODULE/module.prop"
|
||||
test -x "$MODULE/system/bin/intellirat"
|
||||
test ! -e "$MODULE/action.sh"
|
||||
if [ ! -f "$MODULE/payload/IntelliRAT.apk" ]; then
|
||||
echo "WARNING: companion APK is not built; packaging backend-only module" >&2
|
||||
fi
|
||||
mkdir -p "$(dirname "$OUTPUT")"
|
||||
rm -f "$OUTPUT"
|
||||
|
||||
(
|
||||
cd "$MODULE"
|
||||
zip -qr "$OUTPUT" . -x README.md 'app/*' 'app/**'
|
||||
)
|
||||
|
||||
printf 'OK: %s\n' "$OUTPUT"
|
||||
sha256sum "$OUTPUT"
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
**Created**: 2026-09-16
|
||||
|
||||
**Status**: Spec clarified 2026-09-17 (banking deferred; kernel hardening in;
|
||||
localization compare later) — tasks updated; ready for `/speckit-implement`
|
||||
**Status**: Implementation closure baseline built 2026-10-16. LAB acceptance,
|
||||
official upstream manifest equivalence, FR-019, cgroup decision and clean
|
||||
kernel-source policy remain open; banking is deferred and localization compare
|
||||
is later.
|
||||
|
||||
**Input**: User description: "009-axp based LOS22 feature port to LOS23 —
|
||||
take vanilla LineageOS 23 for sunfish and integrate the full AXP.OS (LOS22 /
|
||||
|
||||
@@ -78,6 +78,17 @@ class NextBaconGuardsTest(unittest.TestCase):
|
||||
self.assertIn("SUNFISH_A16_008_CALL_REC_FLAG_SECURE", ship)
|
||||
self.assertIn("extendrom_call_recording", ship)
|
||||
|
||||
def test_defconfig_enables_android_cgroup_controllers_and_hides_dirty_suffix(self) -> None:
|
||||
apply = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/009-kernel-hardening/apply_uss5_1_defconfig.sh"
|
||||
).read_text()
|
||||
self.assertIn("CONFIG_MEMCG=y", apply)
|
||||
self.assertIn("CONFIG_CGROUP_PIDS=y", apply)
|
||||
self.assertIn("# CONFIG_LOCALVERSION_AUTO is not set", apply)
|
||||
self.assertIn("CONFIG_SECURITY_YAMA", apply)
|
||||
self.assertIn("CONFIG_USERFAULTFD", apply)
|
||||
|
||||
def test_artifact_metadata_names_49980_and_vendor_spl(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
self.assertIn(
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Host-side guards for the IntelliRAT v0.1 manual prototype."""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
MODULE = ROOT / "modules/intellirat"
|
||||
CONTROLLER = MODULE / "system/bin/intellirat"
|
||||
|
||||
|
||||
class IntelliRatTest(unittest.TestCase):
|
||||
def test_prototype_is_manual_and_uses_modern_command(self) -> None:
|
||||
script = CONTROLLER.read_text()
|
||||
service = (MODULE / "service.sh").read_text()
|
||||
self.assertIn("get-allowed-network-types-for-users", script)
|
||||
self.assertIn("set-allowed-network-types-for-users", script)
|
||||
self.assertIn("capture", script)
|
||||
self.assertIn("original_mask", script)
|
||||
self.assertIn("call_in_progress", script)
|
||||
self.assertNotIn("setPreferredNetworkType", script)
|
||||
self.assertNotIn("preferred_network_mode", script)
|
||||
self.assertNotIn("Xposed", script)
|
||||
self.assertNotIn("intellirat high", service)
|
||||
self.assertNotIn("intellirat low", service)
|
||||
|
||||
def test_companion_app_has_menu_and_persistent_notification_surface(self) -> None:
|
||||
manifest = (MODULE / "app/AndroidManifest.xml").read_text()
|
||||
activity = (MODULE / "app/src/com/axp/intellirat/MainActivity.java").read_text()
|
||||
service = (MODULE / "app/src/com/axp/intellirat/IntelliRatService.java").read_text()
|
||||
boot = (MODULE / "app/src/com/axp/intellirat/BootReceiver.java").read_text()
|
||||
tile = (MODULE / "app/src/com/axp/intellirat/IntelliRatTileService.java").read_text()
|
||||
self.assertIn("POST_NOTIFICATIONS", manifest)
|
||||
self.assertIn("RECEIVE_BOOT_COMPLETED", manifest)
|
||||
self.assertIn("PACKAGE_USAGE_STATS", manifest)
|
||||
self.assertIn("foregroundServiceType=\"specialUse\"", manifest)
|
||||
for command in ("status", "capture", "high", "low", "restore"):
|
||||
self.assertIn(f'addButton(actions, "', activity)
|
||||
self.assertIn(f'"{command}")', activity)
|
||||
self.assertIn("startForeground", service)
|
||||
self.assertIn("KeyguardManager", service)
|
||||
self.assertIn("isKeyguardLocked", service)
|
||||
self.assertIn("Automatic unlocked HIGH", service)
|
||||
self.assertIn("AUTO_UNLOCK_DELAY_SECONDS", service)
|
||||
self.assertIn("AUTO_DELAY_SECONDS", service)
|
||||
self.assertIn("AUTO_EXCEPTIONS", service)
|
||||
self.assertIn("UsageStatsManager", service)
|
||||
self.assertIn("START_STICKY", service)
|
||||
self.assertIn("BOOT_COMPLETED", manifest)
|
||||
self.assertIn("startForegroundService", boot)
|
||||
self.assertIn("Automatic lock policy", activity)
|
||||
self.assertIn("Apply automatic policy", activity)
|
||||
self.assertIn("Exception packages", activity)
|
||||
self.assertIn("IntelliRatTileService", manifest)
|
||||
self.assertIn("onClick", tile)
|
||||
self.assertIn('"low"', tile)
|
||||
self.assertIn('"high"', tile)
|
||||
self.assertIn('action("HIGH", "high"', service)
|
||||
self.assertIn('action("LOW", "low"', service)
|
||||
self.assertIn('action("RESTORE", "restore"', service)
|
||||
|
||||
def test_shell_files_parse(self) -> None:
|
||||
for path in (CONTROLLER, MODULE / "service.sh"):
|
||||
result = subprocess.run(["sh", "-n", str(path)], capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
|
||||
def test_capture_low_restore_and_call_guard(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
bin_dir = root / "bin"
|
||||
bin_dir.mkdir()
|
||||
fake_state = root / "network"
|
||||
fake_state.write_text(
|
||||
"GSM|GPRS|EDGE|UMTS|HSDPA|HSUPA|HSPA|LTE|HSPAP|TD_SCDMA\n"
|
||||
)
|
||||
fake_call = root / "call"
|
||||
fake_call.write_text("IDLE\n")
|
||||
|
||||
(bin_dir / "cmd").write_text(
|
||||
"""#!/bin/sh
|
||||
state=${FAKE_NETWORK_STATE:?}
|
||||
if [ "$2" = get-allowed-network-types-for-users ]; then
|
||||
tr -d '\\n' < "$state"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$2" = set-allowed-network-types-for-users ]; then
|
||||
case "$5" in
|
||||
00011100001110000111)
|
||||
printf '%s\\n' 'GSM|GPRS|EDGE|UMTS|HSDPA|HSUPA|HSPA|HSPAP|TD_SCDMA' > "$state" ;;
|
||||
00011101001110000111)
|
||||
printf '%s\\n' 'GSM|GPRS|EDGE|UMTS|HSDPA|HSUPA|HSPA|LTE|HSPAP|TD_SCDMA' > "$state" ;;
|
||||
*) printf 'unexpected mask\\n' >&2; exit 1 ;;
|
||||
esac
|
||||
printf '%s\\n' 'set-allowed-network-types-for-users completed'
|
||||
exit 0
|
||||
fi
|
||||
printf 'unexpected command\\n' >&2
|
||||
exit 1
|
||||
"""
|
||||
)
|
||||
(bin_dir / "dumpsys").write_text(
|
||||
"""#!/bin/sh
|
||||
cat "$FAKE_CALL"
|
||||
"""
|
||||
)
|
||||
for path in (bin_dir / "cmd", bin_dir / "dumpsys"):
|
||||
path.chmod(0o755)
|
||||
|
||||
env = os.environ.copy()
|
||||
env.update(
|
||||
{
|
||||
"INTELLIRAT_STATE_DIR": str(root / "state"),
|
||||
"INTELLIRAT_CMD": str(bin_dir / "cmd"),
|
||||
"INTELLIRAT_DUMPSYS": str(bin_dir / "dumpsys"),
|
||||
"FAKE_NETWORK_STATE": str(fake_state),
|
||||
"FAKE_CALL": str(fake_call),
|
||||
}
|
||||
)
|
||||
|
||||
def run(*args: str) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
["sh", str(CONTROLLER), *args],
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
self.assertEqual(run("capture", "0").returncode, 0)
|
||||
low = run("low", "0")
|
||||
self.assertEqual(low.returncode, 0, low.stderr + low.stdout)
|
||||
self.assertIn("state=LOW", run("status", "0").stdout)
|
||||
self.assertEqual(run("restore", "0").returncode, 0)
|
||||
self.assertIn("state=HIGH", run("status", "0").stdout)
|
||||
|
||||
fake_call.write_text("mCallState=ACTIVE\n")
|
||||
blocked = run("low", "0")
|
||||
self.assertNotEqual(blocked.returncode, 0)
|
||||
self.assertIn("call is active", blocked.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Vendored
+22
@@ -96,3 +96,25 @@ include $(BUILD_PREBUILT)
|
||||
|
||||
# EuiccGoogle is removed by apply_009_drop_euicc_google.py from
|
||||
# vendor/google/sunfish/sunfish-vendor.mk.
|
||||
|
||||
# AXPR AOSmium WebView (org.axpos.aosmium_wv), presigned with the AXPR release
|
||||
# key. Installed on /product/app (system/system_ext have no room for ~275 MB).
|
||||
# It is the default WebView provider via config_webview_packages.xml; AOSP/LOS
|
||||
# and Google providers stay in the allowlist.
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := AosmiumWebView
|
||||
LOCAL_MODULE_CLASS := APPS
|
||||
LOCAL_PRODUCT_MODULE := true
|
||||
LOCAL_CERTIFICATE := PRESIGNED
|
||||
LOCAL_SRC_FILES := prebuilt/AosmiumWebView.apk
|
||||
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/AosmiumWebView.apk
|
||||
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
|
||||
# Chromium-built WebView declares these as optional uses-library entries; the
|
||||
# ROM does not provide android.ext.adservices or android.test.base, so keep
|
||||
# them optional to satisfy enforce_uses_libraries.
|
||||
LOCAL_OPTIONAL_USES_LIBRARIES := \
|
||||
android.test.base \
|
||||
androidx.window.extensions \
|
||||
android.ext.adservices
|
||||
LOCAL_DEX_PREOPT := false
|
||||
include $(BUILD_PREBUILT)
|
||||
Vendored
+9
@@ -9,6 +9,15 @@ PRODUCT_PACKAGES += \
|
||||
FossifyGallery \
|
||||
OpenEUICC
|
||||
|
||||
# AXPR AOSmium WebView: default provider, /product/app, presigned release key.
|
||||
PRODUCT_PACKAGES += AosmiumWebView
|
||||
|
||||
# The LOS/AOSP `webview` prebuilt is removed by
|
||||
# patches/009-axp-los23-port/apply_009_single_webview.py (it is added later by
|
||||
# build/make/target/product/media_product.mk, so filtering here has no effect).
|
||||
# Its provider allowlist entry remains, so it can be installed and selected
|
||||
# later.
|
||||
|
||||
# EuiccGoogle is removed by apply_009_drop_euicc_google.py. inherit-product
|
||||
# merges PRODUCT_PACKAGES per makefile, so a filter-out here does not see
|
||||
# the sunfish-vendor.mk entries.
|
||||
+13
-5
@@ -17,20 +17,28 @@
|
||||
|
||||
<webviewproviders>
|
||||
|
||||
<webviewprovider description="Google WebView" packageName="com.google.android.webview" availableByDefault="true">
|
||||
<webviewprovider description="Google WebView" packageName="com.google.android.webview" availableByDefault="false">
|
||||
<signature>MIIDuzCCAqOgAwIBAgIJANi6DgBQG4ZTMA0GCSqGSIb3DQEBBQUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKDAtHb29nbGUgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHd2VidmlldzAeFw0xNDA4MDgyMzIwMjBaFw00MTEyMjQyMzIwMjBaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKDAtHb29nbGUgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHd2VidmlldzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbtaFX0r5aZJMAbPVMAgK1ZZ29dTn91VsGxXv2hqrQo7IpqEy2JmPvPnoMsSiuTAe+UcQy8oKDQ2aYVSAd1DGIy+nSRyFTt3LSIAdwSBkB1qT4a+OqkpsR6bSNXQXQ18lCQu9gREY3h3QlYBQAyzRxw4hRGlrXAzuSz1Ec4W+6x4nLG5DG61MAMR8ClF9XSqbmGB3kyZ70A0X9OPYYxiMWP1ExaYvpaVqjyZZcrPwr+vtW8oCuGBUtHpBUH3OoG+9s2YMcgLG7vCK9awKDqlPcJSpIAAj6uGs4gORmkqxZRMskLSTWbhP4p+3Ap8jYzTVB6Y1/DMVmYTWRMcPW0macCAwEAAaNQME4wHQYDVR0OBBYEFJ6bAR6/QVm4w9LRSGQiaR5Rhp3TMB8GA1UdIwQYMBaAFJ6bAR6/QVm4w9LRSGQiaR5Rhp3TMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEQu8QiVxax7/diEiJrgKE1LwdXsIygJK/KnaKdnYEkAQpeu/QmrLiycm+OFbL1qHJIB7OuI/PQBUtcaNSiJSCVgtwtEbZWWIdsynqG/Nf4aGOndXegSQNRH54M05sRHLoeRycPrY7xQlEwGikNFR76+5UdwFBQI3Gn22g6puJnVukQm/wXQ+ajoiS4QclrNlixoDQsZ4STLH4+Wju2wIWKFFArIhVEIlbamq+p6BghuzH3aIz/Fy0YTQKi7SA+0fuNeCaqlSm5pYSt6p5CH89y1Fr+wFc5r3iLRnUwRcy08ESC7bZJnxV3d/YQ5valTxBbzku/dQbXVj/xg69H8l8M</signature>
|
||||
</webviewprovider>
|
||||
<webviewprovider description="Google WebView Beta" packageName="com.google.android.webview.beta" availableByDefault="true">
|
||||
<webviewprovider description="Google WebView Beta" packageName="com.google.android.webview.beta" availableByDefault="false">
|
||||
<signature>MIIFxzCCA6+gAwIBAgIVAO+zzx6JOZx3HWDG+fzlC1m53BsqMA0GCSqGSIb3DQEBCwUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0xOTA0MDUxNzIxNTRaFw00OTA0MDUxNzIxNTRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIbFePU+vsq6CEYIREsFFhwzXDoCwO6aK9ic9WJubui5VV7OqPCZmoZ/LY3852JK1A1OrWU643Ra7Z/wcvBXHHy5g08bF35gfelz9l53rIiG/1ubAApmxMNqASmQ5bMseetUIrBVqFnWspOWyrNuD0UsF4YkLgm+T1X5Hz1RNZwIV7WcVlL5qC2tm6kEy50bcAhbo5dDDud+ft8oc9g7vAiUpV0yqtNjZ+tsdJI4DrW5Nf2UhB8E+91se2IzQyWT6Vs/wB67HwroGr8qwPQYLQvzcyxZo2yA+qU2+k1IgJGTG5/0K/LheNxjqOKj6Zuhceff0JlbmrGKKqbXlz1F/E582MfqjeETB1gjw9Y0tHWEofEGdL4+ub8ZBJmveH9iz4BVKplzmYACLYWCGICiHBvmkkvx9dhmf5SsUsL9o4axPtAOKtjcKIDXBVqOtYCZssoVe9FFlZDHxRhQlbGY6ip0CK/lYlcx8iLfvI8Hf2AlwZa4j7HpFityaAWgYvo9x7bMJbBHiN/HW/NuhCF1B54KnQUmzQoyEnr5Qb4NjiDiXNzR8gYWYa9a/6Zg9iggr4jIbbEVanvCw9FAAZKRR8rk0ToJuFk2fRJdi2NhKo1GbVWOjj1Cd/Xbahd/uZhZGf1Uc61bG4rn9NU4GqliO2Wl2L78EkxuLDKXdovdHFjzAgMBAAGjUDBOMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFCAIx3WLD1UQ3YUE9ZBt36vfdQ6eMB8GA1UdIwQYMBaAFCAIx3WLD1UQ3YUE9ZBt36vfdQ6eMA0GCSqGSIb3DQEBCwUAA4ICAQBdHaxW69Hx4WPMXAVC5+S419L7/+AgxTyCn7YsD77aiKlIU20BMhvGln1d/Og6y1mf/z/x+GAm1ZfQniOih21Gxq3h4C3FvJ7gHgrjtZ6r0ymz7a1YOYc6LG1DNrTK7gKs5syifUd68PxUwNm5U5R9ixj0iglZDAC0apehGH/nfyJ4btS39N+oSQkUz3FpLoD5wYRlfdzhL2rMVDcv4WcEVDO8X8Md5Bk3hYtkh5MHzbVlu2yQ1qcNeXLUxUVeTjGebMu7B54Fgf+tHSBFBiyDdocpgF0B7RjF5579MXLpxdf9hoQImgzmp6xe0oHwqS1nQZR0pYfwP1Y8vxMSUWTPbY0YB9jQaElXznE/eQBuXe4kRkjEO6QvhziiQFxBlbfGSEFVySeqgWR91tJ0OiEWkMraQaI2bz941Qbt12PhS7r0KIkAsC5LpVRDOEgmo2e6+evst7DXpIAUvzNNSHrnmwmMGZ4QP4AUi7uIsclDJUT9sXDhwutx2Edic0X8+ZZ8D4e+HEupXI30z72En0U6ZyPqb6Ll9SOgK6pN6dbhdakucvRHCpmSfIlO8XXmPN5x3RsSteZl20Mc+ZSQuninApdirEs/9CfRoSSYXlNabxklBZd5jeb0py09FjjE+Nqf4EdZFYvzpYFSGJz1RUrsaDWOmx4gF3YeXo8iUbGtPA==</signature>
|
||||
</webviewprovider>
|
||||
<webviewprovider description="Google WebView Dev" packageName="com.google.android.webview.dev" availableByDefault="true">
|
||||
<webviewprovider description="Google WebView Dev" packageName="com.google.android.webview.dev" availableByDefault="false">
|
||||
<signature>MIIFxzCCA6+gAwIBAgIVAMQCAZgONsTyki5gq7E1h9s6KFvaMA0GCSqGSIb3DQEBCwUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0xOTA0MDUxNzE3MDJaFw00OTA0MDUxNzE3MDJaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJjFQ4Jm1Tf5BESiSnU1w5H5//IbLr6wNIfVbws8mbPb3fq9vs3LAHbtfzgWJ3eG2eNx7GH6ge+XjaNiZzQ8223Fqu9AxBinve4KSm6Mrkq7gk7lMCbUUIap6m0ftDa+7lrxbhjWHrFzFq8Muq43CM7i8CxLdKTGSx098dcvY6giOQstAKQyPmlbH6zjwnS8KNYakDKv0Fv4VdKi14T6ANXAblicfTaclA1HLWhFqv5DaKL8YobQu8FN2Egk9nlimwExbRs1twrmuyX2tNtgX7vMvt3+HHTrkYweARVvvqVoEXkNGfZPlIkExBZSHpJgqSIb6vcBzgntneo9X+iB7K0VRovkK20PDuxKvrt07dZ15qACGC364O1NKSK0+1tiRU6tPI8P1VI3JR2c8C5cnCg9zGCHinZR3iyY+ISA5G+bVEU8WWhwMuSuM2Vo5hmpDnX+p1K2XiuNdV2VblXPKZIfV3xahCZTvemVBt6WhrxeshhTBG2+Z2Qz0TSZSFkeX4ruo0WffOy7A2ybwhmCWryltgDe04Z+kkF+kmwZ6N/cQWWBqYPTILYfs/t9XWz3VyrFEJK+Uf6T+/JO3T0nGZZm4+kr5wH6YMbffrF5S8t0/DpIZfxExcyPnwxYAkI8TohMR4BPX4FPJL5j5ulZbnEbAqoVC6nphlVSulRLM5LFAgMBAAGjUDBOMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFBDgxJQIS7rzqWgJ5oSIuvg3hos6MB8GA1UdIwQYMBaAFBDgxJQIS7rzqWgJ5oSIuvg3hos6MA0GCSqGSIb3DQEBCwUAA4ICAQAiEpFVrLKCY99LxFmIqUvE/pL2ULzhBtZ0YQvWT4QgMABrC2ZKsG0Ttx2OyGXjI3Hl9sEDGdCTA2D0lN3gS44RYSxCK0RANF2wamY5vGgEA4uqQ6o/JIH9VLTEk7A7g+Lu4fl4lnIX3+kw2Thfk4CQ15cWI9tISUPfC4rO2vG9Ct9G3kkXvEj1r7cRH7wmGJcOWKoiMVsLNVIfCYPO4qZOnj+ZpVkxLxRTTjJzqXPYNsHlfywM3zoqtexDdwXFVGP8M/cW7pIp46UE0bvU9jHurF+ECwUykLn/GHG36DYyKAFeIsgpGsLZXW9jHLspSaVF3kR77qPpvlhuF29rfY4E2Bd2d24HHWR3mHMosdzKomatOZbeh6Dj0wdrq+GmRpC70knWrXxmNshYSJri41jvAUEnXVAkbQznxeCct6ST2JhYzuNP8OGVxO0xqs/Hpu9x1aSN0BUotO7ZJPqVR5qCpgEWE1BuWljutrhpI5/d0Oz/DKs14TDrPjlrYJgmR766MnqxQAzYAcM1nC6QcxAkc+N1BLGujI+WQbz9sAlUC0fOf3KuVqemt9XoIh5Z33kJ1QI/VKflCQtlvgSBnJX9zge/iAEImud+Z3MyPT6FxVtkEM9KwBWytT1TvJTyPeOK9SV+2w7xbCoLC8apPlamx13yDngxg0c3eJAd9+p2OA==</signature>
|
||||
</webviewprovider>
|
||||
<webviewprovider description="Google WebView Canary" packageName="com.google.android.webview.canary" availableByDefault="true">
|
||||
<webviewprovider description="Google WebView Canary" packageName="com.google.android.webview.canary" availableByDefault="false">
|
||||
<signature>MIIFxzCCA6+gAwIBAgIVAMtzFe17x7TJnswywhkvEJa+6EIQMA0GCSqGSIb3DQEBCwUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0xOTA0MDUxNzIzMDZaFw00OTA0MDUxNzIzMDZaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJbi91Ehi2xTxKCzeC6XO+COhaJp4dv6LqA8EfTBZEKoLQZ6FQQcDE4dirK78tBIbF4SQLkWt+Lxh3LnV6yfOt3hNmK3Rc9IhFAi0A56dGWazNo0pY8DiCftmPwnkPNT2aTZzmvWuDQHjLoMju2y7nDOliZThOybF9KMKSVsAr2Ahcmx4cRdyxCZKAutzsYOoBk77dFIO0kSaEPof9/Zlan4mAEnQ6GWgbK5wyIYN7DuaCDkJ5FYxUJxyp53/c5jKF/zZRPoGzXlt5e22yymD8MPSj716OolmKPFLUtzCVUnrm4xmmtgheWS1X43tRWit0rPl92bBHFiSECyIQriNS5ZlLuh712G6D7A3uaQqIIRmKVRE6DT3xRWI3l78cDquWIgU+x99qS9bb/txv/oaT1SIlM/rJij11iOISJCJ++vcyuR4j3pydkfl5ePH8YKAmUOCFlDJM3vOt7aqYpx3Ql5GFLeHOoSw4vOzwvMSROV/i6mXBv1d5O1o5XzDUWuIsFFroC1yz11/s547eV091BGuB60pyv7pXZY2EFOjjFcqWfsKrmcxzqCwel9bDd1WvsUY2Dt6Xl13hFMZ9ab2XiZCqA+OF+PrZKJYhTcpcdEjTYbVYEofst5Zg6WWL28WfQlDq0QwVDWFAsBdnegp40dNKwdkjp0LTXYdykzu8otAgMBAAGjUDBOMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFI0WYMtA13YCvaXP3y7SD7+BUoGGMB8GA1UdIwQYMBaAFI0WYMtA13YCvaXP3y7SD7+BUoGGMA0GCSqGSIb3DQEBCwUAA4ICAQAmJmDx+9Bfshs9PtNPb+2PEckvwheOurS15JAV10OTvmcpu0AON56RpItaMm29Uea5wYxaPcPwUQDum0vahHt7FIYxnPIxQ0FxsBDSVDn5veWMVXf5oSVDzsR61+x7i67Qk3dsMgRrY0PS/HZv+cFl0fb87b54mrtFIl8P9KjV5g5PUNJI2BIwLOKgnlU2kDrE1emm7lIKvoJqwb9JjlWl0lB+xXmntNQ+ZNvCzULnr1o0QblVK1Iowe++17GzOtrUlkATTmGx2exaeEEsZcQ23J6u8XycAk9+80aspTjZHC9aneQVcIdbCEBdWkF3xQ0TSnytT/0jLnLBnk2/kIV2ynw2zu/nMczgW2eUnfiIjqzBuP0uRMJ8NFPv/4Agmdqex+A233K9wYc92iflm/aVT3qw7wQntsmSesSBfZgLVVQ++dVD0bd5qYtyELCA2DNCaORU62bfNnPNTkQQo/FeyFDBLNzfWxiDINk6SxNxrZLgQFj9gil+CBgltrJ90Qv8Ats1ES66A0o+2T/j0GA9nOkfznID0VU1SlmOub/0SMwZL98WpkYqYTu/9AOqwzZBO/uT4ADANnMrMd4dtzZ1n0AdSvr1QyqO9XJRmW0k1PlmBM6iPU9p3JMVEqeQ7/zYnYq0tCK+k1mvA5BQ20NVSfea27X7/9EgGanchrFdCQ==</signature>
|
||||
</webviewprovider>
|
||||
|
||||
<!-- The default WebView implementation -->
|
||||
<!-- AXPR default WebView: AOSmium, matched by the AXPR release certificate.
|
||||
AOSP/LOS WebView stays allowed and selectable as fallback. -->
|
||||
<webviewprovider description="AXPR AOSmium WebView" packageName="org.axpos.aosmium_wv" availableByDefault="true">
|
||||
<signature>
|
||||
MIIDtTCCAp2gAwIBAgIUTJblq5nAW98vX9yhEc0Bby+gWy0wDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCREUxCzAJBgNVBAgMAk5BMQswCQYDVQQHDAJOQTEYMBYGA1UECgwPQVhQLVJlc3VyZWN0aW9uMRQwEgYDVQQLDAtzdW5maXNoLWExNjEQMA4GA1UEAwwHcmVsZWFzZTAgFw0yNjA5MjkxNjM2NTBaGA8yMDU0MDIxNDE2MzY1MFowaTELMAkGA1UEBhMCREUxCzAJBgNVBAgMAk5BMQswCQYDVQQHDAJOQTEYMBYGA1UECgwPQVhQLVJlc3VyZWN0aW9uMRQwEgYDVQQLDAtzdW5maXNoLWExNjEQMA4GA1UEAwwHcmVsZWFzZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALcLm1u2161EvRarM9Es8g+0jqkAFqaF92IzRLQUZSipI3/+8qIC3WBWTx6znMwNOaLH4quWkzWU7IEg0HXCf1mO26mMGTpilP/ZboB9QUntGVfJ9Eapt0PDI89hgXTxYYiX/FWnGX0VWSY4wimbhK2D3P4u7aTt1A6ZrGhzXTb99V9uPgo+S5Cvrmrue2H0oM8ps5Cb3gQ1bdDUCh1MOgHocpJP/i80CsGfJ/hPvcSEWt29zS5fsbr5B9lNXTghKqn2Bq/ze6PXl66zB6gjoAbnz0/BTOg1cB1fq7yRjUyAw9adeQKsuCBl0/LFA25ORDfDWwJ28q+wfpzwAcDdG+ECAwEAAaNTMFEwHQYDVR0OBBYEFDfChe+j21EqxGzjIDbiSHh3IloXMB8GA1UdIwQYMBaAFDfChe+j21EqxGzjIDbiSHh3IloXMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKmlveoSMQRgEZbGGt/MsuNowK8nQsJmYyFgEftZFxdXkpOVRs9W+LYHGyq9SfMaLBP+0g6+VR1riw8nVAkZoGoR/8tODRx2GhLCdHlEpcUx2+iXrfVbE1fyyIzNWUd18RvQ8dh50Fi95dsW4sk/mAVuQnvcVm4hLDbuFcXMLK2RMIECYKhfWIO+dsijOP9EgQebWCYTBEcaj9l5g1CmLfsuEe0L6dlKNBXUS3vgpQoXixN4l+Lzx4nIzpc2NDmpsbOB6bAND/qK/0PnQdBCXiZ5Cz5GJPHRJJ/F7cWUS7kLWN6RnzSaXhf4rlJVlmTvlvpJbkZU/fR9Ji3kaWHYjNE=
|
||||
</signature>
|
||||
</webviewprovider>
|
||||
|
||||
<!-- AOSP/LOS WebView stays available in the implementation list. -->
|
||||
<webviewprovider description="AOSP WebView" packageName="com.android.webview" availableByDefault="true" />
|
||||
|
||||
</webviewproviders>
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""009: ship a single WebView engine (AXPR AosmiumWebView only).
|
||||
|
||||
The AOSP/Lineage `webview` prebuilt (~255 MB) is added by
|
||||
build/make/target/product/media_product.mk, which is inherited later than
|
||||
vendor/sunfish-a16/apps.mk, so a PRODUCT_PACKAGES filter there has no effect.
|
||||
Remove the package from the media_product list instead.
|
||||
|
||||
The provider allowlist (config_webview_packages.xml) still declares
|
||||
com.android.webview and the Google providers, so they can be installed and
|
||||
selected later; they are simply not preinstalled.
|
||||
|
||||
Idempotent: guarded by a marker comment.
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
TREE = Path(__file__).resolve().parents[4]
|
||||
MEDIA = TREE / "build/make/target/product/media_product.mk"
|
||||
MARKER = "# sunfish-a16-009-single-webview"
|
||||
LINE = " webview \\\n"
|
||||
|
||||
if not MEDIA.is_file():
|
||||
raise SystemExit(f"FAIL: missing {MEDIA}")
|
||||
|
||||
text = MEDIA.read_text()
|
||||
|
||||
if MARKER in text:
|
||||
print(f"already applied: {MEDIA} (single-webview)")
|
||||
raise SystemExit(0)
|
||||
|
||||
if LINE not in text:
|
||||
raise SystemExit(f"FAIL: expected 'webview' entry not found in {MEDIA}")
|
||||
|
||||
text = text.replace(LINE, "", 1)
|
||||
text = text.rstrip("\n") + f"\n\n{MARKER}: AosmiumWebView is the only shipped WebView.\n"
|
||||
MEDIA.write_text(text)
|
||||
print(f"patched: {MEDIA} (webview package removed)")
|
||||
@@ -16,6 +16,14 @@
|
||||
# sunfish_defconfig turns it on. This kernel has no UFFDIO_MOVE (that ioctl
|
||||
# is 6.1+), so ART only opens a fd and times out. Match GOLDEN.
|
||||
#
|
||||
# MEMCG / CGROUP_PIDS: Android 16 mounts cgroup v2 and attempts per-UID memory
|
||||
# and PID controller activation. The old sunfish defconfig leaves both off,
|
||||
# which produces `+memory ... Invalid argument` at runtime. Enable the native
|
||||
# 4.14 controllers for the next candidate.
|
||||
#
|
||||
# LOCALVERSION_AUTO: the kernel source is intentionally patched in the build;
|
||||
# do not expose the source worktree's incidental `-dirty` suffix in uname.
|
||||
#
|
||||
# Usage: apply_uss5_1_defconfig.sh <kernel-tree>
|
||||
set -euo pipefail
|
||||
|
||||
@@ -23,7 +31,8 @@ K=${1:?kernel tree}
|
||||
DEF="$K/arch/arm64/configs/sunfish_defconfig"
|
||||
test -f "$DEF"
|
||||
|
||||
for k in SECURITY_YAMA SLAB_FREELIST_RANDOM USERFAULTFD HAVE_ARCH_USERFAULTFD_MINOR; do
|
||||
for k in SECURITY_YAMA SLAB_FREELIST_RANDOM USERFAULTFD HAVE_ARCH_USERFAULTFD_MINOR \
|
||||
MEMCG CGROUP_PIDS LOCALVERSION_AUTO; do
|
||||
sed -i "/CONFIG_${k}/d" "$DEF"
|
||||
done
|
||||
|
||||
@@ -31,6 +40,9 @@ done
|
||||
echo "# CONFIG_SECURITY_YAMA is not set"
|
||||
echo "CONFIG_SLAB_FREELIST_RANDOM=y"
|
||||
echo "# CONFIG_USERFAULTFD is not set"
|
||||
echo "CONFIG_MEMCG=y"
|
||||
echo "CONFIG_CGROUP_PIDS=y"
|
||||
echo "# CONFIG_LOCALVERSION_AUTO is not set"
|
||||
} >> "$DEF"
|
||||
|
||||
echo "OK: US-S5-1 defconfig (SLAB_FREELIST_RANDOM; YAMA, USERFAULTFD off) on $K"
|
||||
echo "OK: US-S5-1 defconfig (SLAB_FREELIST_RANDOM; MEMCG, CGROUP_PIDS on; YAMA, USERFAULTFD, LOCALVERSION_AUTO off) on $K"
|
||||
Reference in new issue
Block a user