009: Closure wave 3 deblob, handoff, and optional wave4/malloc bacons.
Ship script gates wave 4 behind AXP_DEBLOB_WAVE4, documents secrets cert path, and adds deblob appliers plus tests; docs capture Swissquote fix and parity. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
deaaf42caa
commit
3f941e8451
64 files changed
+6270
-86
No files matched your search
@@ -7,6 +7,8 @@
|
||||
.idea/
|
||||
.vscode/
|
||||
.cursor/
|
||||
# Codex (ChatGPT) local project config — review agent; never commit
|
||||
.codex/
|
||||
# Spec Kit skills live in .agents/skills/ (tracked); Cursor may mirror under .cursor/
|
||||
|
||||
# Spec Kit local state (also ignored under .specify/)
|
||||
@@ -51,4 +53,5 @@ vendor/sunfish-a16/prebuilt/
|
||||
|
||||
# Local AXP OTA extracts (payload.bin ~1GB+)
|
||||
AXP.OS-*/
|
||||
AXP.OS-*.zip
|
||||
*.bin
|
||||
@@ -11,7 +11,7 @@ This constitution is specific to `AXP_Resurection`. Sibling `sunfish-a16-rom`
|
||||
is the KernelSU standby track. It does **not** inherit SSYSTEMS, company
|
||||
GitLab, Redmine, or buildout-saar governance.
|
||||
|
||||
**Version**: 0.3.0 | **Ratified**: 2026-08-09 | **Last Amended**: 2026-09-16
|
||||
**Version**: 0.3.1 | **Ratified**: 2026-08-09 | **Last Amended**: 2026-10-07
|
||||
|
||||
## Core Principles
|
||||
|
||||
@@ -49,6 +49,13 @@ functionality, except features that are obsolete, incompatible with LineageOS
|
||||
23.2 / Android 16 / sunfish, or deliberately substituted (for example Magisk →
|
||||
KernelSU-Next; AXP backup tooling → NeoBackup as an external user tool).
|
||||
|
||||
Degoogle as far as a free replacement keeps the function. microG plus the
|
||||
project-patched Play Store stay. Location uses the LineageOS 23.2 stack so a
|
||||
fix stays accurate and fast; do not apply AXP's location lockdown or delete
|
||||
the IZat/XTRA/xtwifi blobs. A settings switch, default off, may stop LPP
|
||||
(and the LPPE session it carries) plus the `loc_launcher` service that
|
||||
starts XTRA and IZat.
|
||||
|
||||
`docs/axp-pro-parity.md` and `docs/hardening-backlog.md` are **non-normative**
|
||||
product/backlog trackers. A row becomes a requirement only when promoted into a
|
||||
feature `spec.md`. For an active feature, this constitution plus that feature's
|
||||
@@ -578,3 +585,10 @@ Upstream freshness for root stack and similar deps: integrate from newest
|
||||
compatible stable/tagged upstream (full git history), verify version codes
|
||||
against the matching manager **before** long builds; pin only after LAB works.
|
||||
Triggered by stale shallow ReSukiSU mirror yielding `KSU_VERSION=30701`.
|
||||
|
||||
### Amendment 0.3.1 — 2026-10-07
|
||||
|
||||
Owner: degoogle where a free alternative keeps the function; microG and the
|
||||
patched Play Store stay. Location stays on the LineageOS 23.2 code (accurate
|
||||
and fast), not AXP's location cuts. Moving apps such as OpenEUICC track the
|
||||
latest upstream, not the revision frozen in an AXP zip.
|
||||
@@ -18,6 +18,10 @@ unless the owner explicitly asks.
|
||||
|
||||
## Sources of truth
|
||||
|
||||
Resume point for the unfinished ROM (2026-10-08): `docs/009-handoff-20261008.md`.
|
||||
Read that before the older remaining-work docs; several of those still describe
|
||||
pre-boot state.
|
||||
|
||||
1. **Constitution** (`.specify/memory/constitution.md`) — global project rules
|
||||
for this Magisk-resurrection track.
|
||||
2. **Feature `spec.md`** — especially `specs/009-axp-los23-port/spec.md`.
|
||||
|
||||
@@ -36,7 +36,7 @@ Host packaging: `vendor/sunfish-a16/{Android.mk,gms.mk,BoardConfig.mk}`.
|
||||
1. Download latest **default** `com.google.android.gms-NNNNNNN.apk` (no `-hw`) +
|
||||
`GsfProxy.apk` from microG GitHub releases. Match GOLDEN: `versionName`
|
||||
must **not** end in `-hw` (Huawei flavor drops NLP Location services /
|
||||
UI). Example: `v0.3.16.252432` → `com.google.android.gms-252432032.apk`.
|
||||
UI). Newest stable staged 2026-10-06: `v0.3.17.252432` → `com.google.android.gms-252432034.apk` (versionCode 252432034, default flavor, PRESIGNED).
|
||||
2. Keep **official signature** (`LOCAL_CERTIFICATE := PRESIGNED`). **Never**
|
||||
re-sign / ABI-strip GmsCore for size — that breaks F-Droid updates.
|
||||
3. Do **not** install microG’s `com.android.vending-*.apk` (FakeStore).
|
||||
|
||||
@@ -38,7 +38,8 @@ Script (idempotent, marker `SUNFISH_A16_005_SIGSPOOF`):
|
||||
Changes on the LOS tree:
|
||||
|
||||
1. Extend `isMicrogSigned()` to accept **project Phonesky** signer for
|
||||
`com.android.vending`.
|
||||
`com.android.vending`. Do **not** keep LOS `isDebuggable()` — AXP/GOLDEN
|
||||
is `user` (`ro.debuggable=0`) and still spoofs via the toggle + grant.
|
||||
2. Gate spoofing on `Settings.Secure.allow_signature_fake` (default **0**).
|
||||
3. Require grant of `android.permission.FAKE_PACKAGE_SIGNATURE`.
|
||||
4. Port EXTENDROM permission + Settings Developer Options toggle + strings.
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# 009 — Remaining path to AXP / Graphene-class ship (post clean-429)
|
||||
|
||||
**Stale as an execution order.** Boot, vendor SPL, lock-scope, release-keys,
|
||||
and `hardened_malloc` moved on 2026-10-08. Start from
|
||||
`docs/009-handoff-20261008.md`.
|
||||
|
||||
|
||||
|
||||
Non-normative roadmap after LAB **BOOT_OK** on Stage1+combo4+FORTIFY+Magisk+
|
||||
**CVE clean-offset 429** (denylist `CVE-2022-49980`), kernel `#47`
|
||||
(`20261001T180955Z-axp-stage1-combo4-cve-clean`). Authoritative requirements
|
||||
@@ -8,6 +14,12 @@ remain `specs/009-axp-los23-port/spec.md` + constitution.
|
||||
Goal (owner): **almost all** applicable AXP.OS / Graphene-class behaviour on
|
||||
LOS 23.2 sunfish Magisk track — not a blind dump, not bootloader lock.
|
||||
|
||||
**2026-10-07 next-bacon correction:** `misc/0021` is already present in the
|
||||
LOS kernel base. Stage1 must omit it and use `patch --forward`; otherwise GNU
|
||||
patch auto-reverses it and shrinks `KSYM_NAME_LEN` 192→128 under Clang CFI.
|
||||
The first corrected candidate retains 192 and applies only the
|
||||
`CVE-2022-49980` lock-scope companion as its new kernel delta.
|
||||
|
||||
## Already landed (kernel / Magisk core)
|
||||
|
||||
| Layer | Status |
|
||||
@@ -16,7 +28,7 @@ LOS 23.2 sunfish Magisk track — not a blind dump, not bootloader lock.
|
||||
| Combo4 + Clang FORTIFY | Ship |
|
||||
| Magisk preroot EXTENDROM-class | Ship |
|
||||
| CVE clean-offset 429 | Ship (LAB OK) |
|
||||
| `CVE-2022-49980` | Denylist until lock-scope port |
|
||||
| `CVE-2022-49980` | Original one-liner denylisted; corrected lock-scope candidate next |
|
||||
| High-offset CVE (~73) | Quarantine — triage started |
|
||||
|
||||
Recover/bisect baseline: `20260929T123401Z-axp-stage1-combo4` (`#32`).
|
||||
@@ -27,7 +39,8 @@ Recover/bisect baseline: `20260929T123401Z-axp-stage1-combo4` (`#32`).
|
||||
nf_tables leftovers still skipped. Next kernel depth: **US-S5-1** defconfig
|
||||
(Yama + `SLAB_FREELIST_RANDOM`), not more bulk CVE.
|
||||
2. **`CVE-2022-49980` proper port** — keep UAF fix without recursive
|
||||
`udc_lock` (mainline lock-scope series). Optional; denylist is safe.
|
||||
`udc_lock` using the reviewed lock-scope companion. Boot and USB gadget
|
||||
smoke are still required; the original one-liner remains denylisted.
|
||||
3. Failed Stage1 probes (allocsize/ro/slab) — low priority vs high-offset.
|
||||
4. Graphene modern techniques / `hardened_malloc` — backlog (`docs/hardening-backlog.md`);
|
||||
evaluate after CVE depth is stable.
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# Closure parity — AXP (GOLDEN) vs AXPR (LAB) — 2026-10-09
|
||||
|
||||
Status after **wave 3** boot on LAB slot `_a`. Swissquote 9.11.0 in Island
|
||||
**PASS** (owner confirmed). Root cause for the prior `-69` / `onPrivilegedAccess`
|
||||
was **Lineage tools on system** (`bash`, `htop`, `nano`, `unrar`,
|
||||
`system/addon.d/50-lineage.sh`), removed in wave 3 — not Magisk PIF/cmdline.
|
||||
|
||||
GOLDEN was not on USB for this pass; oracle uses
|
||||
`/srv/rom/references/axp/golden/2026-09-13/` plus prior live diffs.
|
||||
|
||||
## Ship candidate (closure baseline)
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Dir | `20261009T145241Z-axp-stage1-combo4-cve-hi-releasekeys` |
|
||||
| Zip | `AXP-Resurection-Pro-23.2-20261009-sunfish.zip` |
|
||||
| SHA256 | `7cd20e4d218bc04f0b4a31838a5bb9040db36148a2b38d90342b3bc9f224a5fd` |
|
||||
| `boot.img` | Same dir (Magisk preroot, AVB flags 0) |
|
||||
| deblob | wave1 euicc + wave2 carrier/rcs + wave3 lineage tools |
|
||||
| Do not re-sideload | Owner booted this zip once |
|
||||
|
||||
## Identity / Gate G (LAB measured)
|
||||
|
||||
| Check | LAB | GOLDEN ref | Notes |
|
||||
|-------|-----|------------|-------|
|
||||
| `ro.build.type` | `user` | `user` | OK |
|
||||
| `ro.build.tags` | `release-keys` | `release-keys` | OK |
|
||||
| `ro.debuggable` | `0` | `0` | OK |
|
||||
| SPL system | `2026-09-01` | `2026-03-03` | LOS23 newer |
|
||||
| SPL vendor | `2023-09-05` | `2023-08-05` | deliberate AXPR guard |
|
||||
| SELinux | Enforcing | Enforcing | OK |
|
||||
| Boot state | orange / unlocked | same | OK |
|
||||
| Phonesky | `/system/priv-app/Phonesky` | AXP path variant | function OK |
|
||||
| microG | `/system_ext/app/GmsCore` | often `/data` update on GOLDEN | version may differ |
|
||||
|
||||
## System surface (commands)
|
||||
|
||||
**Removed in wave 3 (was LAB-only, absent on GOLDEN):** `bash`, `htop`,
|
||||
`nano`, `unrar`, `addon.d/50-lineage.sh`.
|
||||
|
||||
**Still LAB-only vs GOLDEN (deferred wave 4):** Qualcomm `diag_*`, `test_diag`,
|
||||
`imsrcsd`, `disable_configstore`, AOSP debug bins (`bpftool`, `alloctop`,
|
||||
`pbtombstone`, `apexd-lsof.sh`). Not required for Swissquote PASS.
|
||||
|
||||
**Kept like GOLDEN:** `curl` (`/system/bin`), `vim` (`/system_ext`).
|
||||
|
||||
## Recovery / boot
|
||||
|
||||
Sunfish: **recovery-as-boot** only (`boot_a` / `boot_b`). No `fastboot flash
|
||||
recovery`. Scratch install: flash `boot.img` to both slots → recovery → factory
|
||||
reset → sideload full zip → reboot system.
|
||||
|
||||
## Repo vs next bacon
|
||||
|
||||
- `apply_009_drop_lab_only_commands.py` exists but is **commented out** in the
|
||||
ship script (wave 4 deferred).
|
||||
- Optional later: `scripts/009-bacon-hardened-malloc.sh` (012) — separate bacon;
|
||||
not in the booted wave 3 image.
|
||||
- Host keys: `/srv/rom/secrets/android-certs` (2026-10-09); `~/.android-certs`
|
||||
symlinks there. Backup `~/backups/android-certs-secrets-20261009T1517Z.tar.gz`.
|
||||
|
||||
## Physical-SIM call gate
|
||||
|
||||
Before any wave beyond carrier/rcs deblob: owner places a **mobile-network**
|
||||
voice call on LAB (SIM in slot, not Wi‑Fi calling only). Note result and time
|
||||
in `docs/009-handoff-20261008.md`.
|
||||
|
||||
## GOLDEN parity capture
|
||||
|
||||
When GOLDEN `11071JEC203839` is on USB (read-only oracle):
|
||||
|
||||
```bash
|
||||
adb -s 11071JEC203839 shell pm list packages -f | sort > /tmp/golden-packages.txt
|
||||
adb -s 11231JEC207477 shell pm list packages -f | sort > /tmp/lab-packages.txt
|
||||
diff -u /tmp/golden-packages.txt /tmp/lab-packages.txt
|
||||
adb -s 11071JEC203839 shell cat /proc/cmdline > /tmp/golden-cmdline.txt
|
||||
adb -s 11231JEC207477 shell cat /proc/cmdline > /tmp/lab-cmdline.txt
|
||||
```
|
||||
|
||||
Offline fallback: `/srv/rom/references/axp/golden/2026-09-13/` (not a live
|
||||
cmdline). Refresh this doc when a live GOLDEN dump exists.
|
||||
|
||||
## Follow-on bacons (not the booted closure zip)
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `scripts/009-bacon-wave4-deblob.sh` | wave 4 lab-only command drops |
|
||||
| `scripts/009-bacon-hardened-malloc.sh` | 012 hardened_malloc (2 GiB regions) |
|
||||
|
||||
Launch on `rom-dev01` via `rom-detach`; never two ninjas on one `out/`.
|
||||
|
||||
## Scratch reinstall
|
||||
|
||||
Owner-driven virgin `/data` with the **same** closure zip: flash `boot.img` to
|
||||
both slots → recovery factory reset → sideload → reboot system from menu.
|
||||
|
||||
## Open (non-blocking)
|
||||
|
||||
- LOS 23 nightly rebase; regen plan/tasks.
|
||||
@@ -6,6 +6,19 @@ AVB/signing gaps (or “impossible on this stack”). This file is that list for
|
||||
**kernel** half. Userspace AVB is tracked separately below as still open.
|
||||
|
||||
**LAB device:** `11231JEC207477` only.
|
||||
|
||||
> **2026-10-07 correction / next bacon:** LAB currently runs the
|
||||
> `20261007T133154Z` PRO/user/release-keys artifact. Audit found that Stage1
|
||||
> treated `misc/0021` as reversed and silently changed the LOS base's
|
||||
> `KSYM_NAME_LEN` from 192 to 128 while `CONFIG_CFI_CLANG=y`. The next recipe
|
||||
> removes that already-present patch, invokes Stage1 with `patch --forward`,
|
||||
> and gates `KSYM_NAME_LEN=192`. Artifact `20261007T161115Z` is quarantined
|
||||
> and must not be flashed. Its corrected replacement,
|
||||
> `20261007T171530Z`, completed all artifact gates and keeps the original CVE
|
||||
> one-liner denylisted while testing only the lock-scope companion as the new
|
||||
> kernel delta. It remains **unflashed** pending LAB USB-gadget acceptance.
|
||||
> The historical acceptance record below remains unchanged.
|
||||
|
||||
**Accepted kernel ship pack (LAB 2026-10-01):** Stage1 Graphene/AH + combo4 +
|
||||
Clang FORTIFY + Magisk preroot + **CVE clean-offset 429** + **hi-batch1+2 (15)**
|
||||
with `CVE-2022-49980` denylisted. Clean-only `#47` / hi-batch1 `#48` remain bisect GOODs.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# GOLDEN reference vs LAB packages — 2026-10-09
|
||||
|
||||
GOLDEN was **not** on USB. Comparison uses the frozen oracle dump
|
||||
`/srv/rom/references/axp/golden/2026-09-13/packages.txt` (owner-installed /
|
||||
spot-check format, not `pm list packages -f` of the full system image).
|
||||
|
||||
A live LAB `pm list packages -f` was **not** captured in this pass: scratch
|
||||
started immediately after `adb reboot bootloader` (broken pipe during reboot).
|
||||
|
||||
## Reference excerpt (GOLDEN dump)
|
||||
|
||||
Curated hits from the oracle file include Island, Swissquote, microG/Phonesky,
|
||||
F-Droid/Aurora, Lineage recorder, Yuh, etc. See the full file on `rom-dev01`.
|
||||
|
||||
## When GOLDEN is on USB
|
||||
|
||||
```bash
|
||||
adb -s 11071JEC203839 shell pm list packages -f | sort > /tmp/golden-live.txt
|
||||
adb -s 11231JEC207477 shell pm list packages -f | sort > /tmp/lab-live.txt
|
||||
diff -u /tmp/golden-live.txt /tmp/lab-live.txt
|
||||
```
|
||||
|
||||
Merge results into `docs/009-closure-parity-20261009.md` (system surface table
|
||||
stays authoritative for deblob; package list diff is for user-installed drift).
|
||||
@@ -0,0 +1,644 @@
|
||||
# 009 handoff — 2026-10-08 evening
|
||||
|
||||
Start here. Older remaining-work docs still describe the tree from before
|
||||
LAB booted the 2026-10-08 zip. This file is the execution map for the
|
||||
final ROM. Do not commit unless the owner asks. Do not flash GOLDEN.
|
||||
|
||||
## Hard rules already paid for
|
||||
|
||||
- LAB only: `11231JEC207477`. GOLDEN `11071JEC203839` is the live oracle.
|
||||
It may be plugged in at the same time. Never flash, wipe, or change Magisk
|
||||
on it. Always `adb -s 11231JEC207477`.
|
||||
- Do not factory-reset LAB except an **owner-driven scratch** (recovery wipe +
|
||||
sideload closure zip). The recovery screen "Can't load Android system /
|
||||
data may be corrupt / reason=init_user0_failed" is **not** corrupt data.
|
||||
- Never lower `VENDOR_SECURITY_PATCH` below `2023-09-05`.
|
||||
- KernelSU stays out of Magisk ship boots.
|
||||
- Do not run AXP `Deblob.sh` defaults.
|
||||
- Do not start a second ninja on the same `out/`.
|
||||
- Sideload the full zip. A boot-only flash does not carry userspace.
|
||||
- User builds reject an undeclared downgrade (error 28).
|
||||
- `fastboot flash vendor` of a sparse image: `fastboot reboot fastboot`
|
||||
(`is-userspace=yes`) and `fastboot -S 128M`. A 256 MB chunk drops USB.
|
||||
Do not `fastboot -w`. Do not flash another build's vbmeta in the same test.
|
||||
- Host: `ssh -F docs/ssh/config a16-build`. Long builds via
|
||||
`/srv/rom/artifacts/bin/rom-detach` (tmux). Do not leave `mka` on a laptop SSH.
|
||||
- Keep the last booting zip plus two test builds. Laptop disk was tight.
|
||||
Host artifacts are canonical.
|
||||
|
||||
## Running now
|
||||
|
||||
**Closure baseline (booted, Swissquote Island PASS):** wave 3 zip
|
||||
`20261009T145241Z-axp-stage1-combo4-cve-hi-releasekeys`
|
||||
SHA256 `7cd20e4d218bc04f0b4a31838a5bb9040db36148a2b38d90342b3bc9f224a5fd`.
|
||||
LAB slot `_a`, `boot_completed=1`. Owner confirmed Swissquote no longer
|
||||
exits `-69` in Island after wave 3 (Lineage tools removal). Parity notes:
|
||||
`docs/009-closure-parity-20261009.md`. Do not sideload this zip again.
|
||||
|
||||
**Closure checklist (2026-10-09 evening):**
|
||||
|
||||
| Item | Status |
|
||||
|------|--------|
|
||||
| Keys → `/srv/rom/secrets/android-certs` | done |
|
||||
| Scratch reinstall (virgin `/data`, same closure zip) | `boot_a`/`boot_b` flashed 2026-10-09; recovery **unauthorized** — owner: USB auth → Factory reset → sideload zip in `/tmp/axp-closure-scratch/` (laptop) or host artifact dir |
|
||||
| `~/.android-certs.pre-secrets-20261009` removed on host | done |
|
||||
| GOLDEN package/cmdline parity | needs GOLDEN USB or reference dump |
|
||||
| `bacon009wave4` (diag drop) | **running** `2026-10-09T15:29:17Z` — log `detach-bacon009wave4-20261009T152917Z.log` |
|
||||
| `bacon012malloc` (hardened_malloc) | **queue after wave4** (one `out/`): `rom-detach bacon012malloc -- /srv/rom/artifacts/bin/009-bacon-hardened-malloc.sh` |
|
||||
| Physical-SIM call gate | owner — before more radio deblob |
|
||||
| Git commit of repo closure deltas | owner asked 2026-10-09 |
|
||||
|
||||
Check detach logs under `/srv/rom/artifacts/detach-bacon009*.log`. Do not run
|
||||
two ninjas on the same `out/`.
|
||||
|
||||
Last closure build `bacon009tools` finished `exit=0` `2026-10-09T14:54:03Z`.
|
||||
|
||||
`bacon009deblob4` finished `exit=0` at `2026-10-09T12:01:31Z`. Zip
|
||||
`/srv/rom/artifacts/009-axp-los23-port/20261009T120025Z-axp-stage1-combo4-cve-hi-releasekeys/AXP-Resurection-Pro-23.2-20261009-sunfish.zip`
|
||||
SHA256 `bd1ce97896b5da31d4c22d4ffc3f2050ad204eea45768bbf86f7527f44b70c51`.
|
||||
Sideloaded to LAB at 14:58 local (`Total xfer: 2.00x`). Owner
|
||||
confirmed boot. Slot `_b`. `ro.build.date` `Fri Oct 9 11:04:11 UTC 2026`.
|
||||
The nine wave-2 paths are absent from the image. `/data` leftovers
|
||||
`com.google.android.ims` and `com.google.android.apps.carrier.carrierwifi`
|
||||
were uninstalled for user 0 and user 10. `ims` and `qcrilmsgtunnel` remain.
|
||||
Previous slot `_a` is the wave-1 zip. If this slot dies later,
|
||||
`fastboot -s 11231JEC207477 --set-active=a`. Do not wipe. Do not sideload
|
||||
this zip again.
|
||||
|
||||
Wave 2 drops
|
||||
CarrierServices, CarrierSettings, CarrierWifi, CarrierSetup, RilConfigService,
|
||||
grilservice, ImsServiceEntitlement, PresencePolling, and RcsService.
|
||||
Qualcomm radio packages stay. AVB flags 0. Boot of this zip confirmed on LAB slot `_b`.
|
||||
Log `/srv/rom/artifacts/detach-bacon009deblob4-20261009T110253Z.log`.
|
||||
|
||||
`bacon009deblob3` finished `exit=0` at `2026-10-09T10:18:22Z`,
|
||||
was sideloaded to LAB at 12:33 local (`Total xfer: 2.00x`), and the owner
|
||||
confirmed boot. Slot `_a`. `ro.build.date` is `Fri Oct 9 09:18:53 UTC 2026`.
|
||||
`/product/priv-app/EuiccGoogle` and
|
||||
`/system_ext/priv-app/EuiccGoogleOverlay` are absent. The `/data` leftover
|
||||
`com.google.android.euicc` was uninstalled for user 0 and Island user 10
|
||||
(`pm uninstall` Success, `pm path` empty). EuiccSupportPixel,
|
||||
EuiccSupportPixelPermissions, and OpenEUICC remain.
|
||||
`im.angry.openeuicc/.ui.PrivilegedMainActivity` stayed up and opened
|
||||
`DownloadWizardActivity` (empty card). That is the wave-1 pass. Do not
|
||||
sideload this zip again. Next gate is a physical-SIM call, then VoLTE and
|
||||
VoWiFi, before any wave-2 bacon. If this slot dies later,
|
||||
`fastboot -s 11231JEC207477 --set-active=b` (flags-0 zip). Do not wipe.
|
||||
|
||||
`bacon009deblob2` finished `exit=0` at `2026-10-09T09:09:32Z`. Do not
|
||||
sideload it. Zip
|
||||
`/srv/rom/artifacts/009-axp-los23-port/20261009T090831Z-axp-stage1-combo4-cve-hi-releasekeys/AXP-Resurection-Pro-23.2-20261009-sunfish.zip`
|
||||
SHA256 `722e6d2e5cd639c3dfcea8af1585d5a741ea3ffd13ff5e0fad62d2c76722b70e`.
|
||||
Flags 0. `EuiccGoogle.apk` and `EuiccGoogleOverlay` are still in the image.
|
||||
|
||||
`bacon009deblob` finished `exit=0` at `2026-10-09T07:38:53Z`. Do not
|
||||
sideload it: flags are 0, but both Google LPA packages are still in the
|
||||
images. Zip
|
||||
`/srv/rom/artifacts/009-axp-los23-port/20261009T073753Z-axp-stage1-combo4-cve-hi-releasekeys/AXP-Resurection-Pro-23.2-20261009-sunfish.zip`
|
||||
SHA256 `63851d964a259aefa8247883401481296dd7c6bb51dbcc13ee901361c7d8ab50`.
|
||||
|
||||
`bacon009timeouts` finished `exit=0` at `2026-10-08T22:02:51Z`. Zip, not
|
||||
flashed:
|
||||
|
||||
`/srv/rom/artifacts/009-axp-los23-port/20261008T220151Z-axp-stage1-combo4-cve-hi-releasekeys/AXP-Resurection-Pro-23.2-20261008-sunfish.zip`
|
||||
|
||||
SHA256 `1717d23db2024f524dfe48364b13ee66c998364adfc10c3f9bde676a3d926a58`.
|
||||
Same hash on the `lineage-23.2-20261008-PRO-sunfish.zip` twin. vbmeta flags
|
||||
still 3. It carries the timeout settings and Droidify 0.7.8.
|
||||
|
||||
`bacon009avb0` finished `exit=0` at `2026-10-09T00:26:40Z`. Log line
|
||||
`avb_infra=... flags=0`. Sideloaded to LAB `11231JEC207477` on 2026-10-09
|
||||
morning. `Total xfer: 2.00x`. Before the sideload the running slot was
|
||||
`_a` (cmdline zip). Recovery adb came back unauthorized, so the owner
|
||||
reboots from the recovery menu. If it sticks on the Google logo,
|
||||
`fastboot -s 11231JEC207477 --set-active=a`. Do not wipe.
|
||||
|
||||
Zip:
|
||||
`/srv/rom/artifacts/009-axp-los23-port/20261009T002537Z-axp-stage1-combo4-cve-hi-releasekeys/AXP-Resurection-Pro-23.2-20261008-sunfish.zip`
|
||||
|
||||
SHA256 `1aabda75c672e1e7d7a7df046a3428ef50fa47aaa3501808ff3d12bbb075e96b`.
|
||||
Local copy `/tmp/axp-flash/AXP-Resurection-Pro-23.2-20261009-avb0-sunfish.zip`.
|
||||
No ninja is running. The deblob Soong files are still only on the laptop.
|
||||
|
||||
## What is on LAB right now
|
||||
|
||||
**Closure baseline (wave 3)** unless a scratch reinstall is in progress.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Artifact dir | `20261009T145241Z-axp-stage1-combo4-cve-hi-releasekeys` |
|
||||
| Zip | `AXP-Resurection-Pro-23.2-20261009-sunfish.zip` |
|
||||
| SHA256 | `7cd20e4d218bc04f0b4a31838a5bb9040db36148a2b38d90342b3bc9f224a5fd` |
|
||||
| Host path | `/srv/rom/artifacts/009-axp-los23-port/20261009T145241Z-axp-stage1-combo4-cve-hi-releasekeys/` |
|
||||
| Slot (last known) | `_a` after wave 3 sideload |
|
||||
| deblob | wave1 euicc + wave2 carrier/rcs + wave3 lineage tools |
|
||||
| AVB | flags `0` on shipped `boot.img` |
|
||||
| Swissquote Island | **PASS** after wave 3 (owner confirmed); prior `-69` was Lineage tools on system |
|
||||
|
||||
Wave 3 removes `bash`, `htop`, `nano`, `unrar`, `system/addon.d/50-lineage.sh`.
|
||||
`curl` and `vim` stay. Wave 4 diag/`bpftool`/… are **still present** on this
|
||||
image; use `scripts/009-bacon-wave4-deblob.sh` for a candidate without them.
|
||||
|
||||
**Rollback slots (do not wipe casually):** other slot may hold an older zip
|
||||
(wave 2 on `_b`, wave 1, or flags-only builds). `fastboot -s 11231JEC207477
|
||||
--set-active=a|b` only if the active slot boot-loops.
|
||||
|
||||
**Scratch reinstall (owner 2026-10-09):** bootloader → `fastboot flash boot_a`
|
||||
and `boot_b` with closure `boot.img` → recovery → **Factory reset** → sideload
|
||||
the closure zip above → **Reboot system** from recovery (not `adb reboot` if adb
|
||||
is unauthorized). Re-verify Magisk, Island, Swissquote after setup.
|
||||
|
||||
**Physical-SIM call gate:** place a normal mobile call (not only Wi‑Fi) before
|
||||
any further Qualcomm radio deblob. Record pass/fail in this file.
|
||||
|
||||
Parity matrix: `docs/009-closure-parity-20261009.md`.
|
||||
|
||||
## 1. hardened_malloc — 2 GiB rebacon finished, not flashed
|
||||
|
||||
`bacon012malloc3` finished `exit=0` at `2026-10-08T18:43:47Z` (58 min, ccache).
|
||||
Zip: `/srv/rom/artifacts/009-axp-los23-port/20261008T184238Z-axp-stage1-combo4-cve-hi-releasekeys/AXP-Resurection-Pro-23.2-20261008-sunfish.zip`
|
||||
SHA256 `f85a0233be1ca83d6a866c081e7cf314e9b5e547a4d6324addb7139ed90030e9`.
|
||||
Vendor SPL `2023-09-05`. Not sideloaded. The phone is still on the morning
|
||||
zip in slot `a`.
|
||||
|
||||
The first malloc zip boot-looped. `bacon012malloc2`
|
||||
(`20261008T170027Z`, SHA256
|
||||
`28c7f0bfa11c60f24e86d35c080cd1e52c244c3640168e34673e4ef0252d690c`)
|
||||
was sideloaded to LAB slot `b` at 19:17 local and rebooted at the Google logo.
|
||||
Recovered without a wipe: `fastboot --set-active=a`, morning zip booted
|
||||
(`boot_completed=1`, vendor SPL `2023-09-05`). Slot `b` still holds the bad
|
||||
build and is not marked successful. Do not set it active.
|
||||
|
||||
Cause: sunfish msm-4.14 is `CONFIG_ARM64_VA_BITS=39` (3-level page tables),
|
||||
on LAB and on GOLDEN. Upstream `Android.bp` uses
|
||||
`CONFIG_CLASS_REGION_SIZE=34359738368` (32 GiB), which needs 48-bit VA;
|
||||
the README says the defaults do not work on 39-bit. DivestOS Patch.sh
|
||||
(LineageOS-17.1/20.0/21.0) does
|
||||
`sed -i 's/34359738368/2147483648/' Android.bp` ("revert 48-bit address
|
||||
space requirement"). GOLDEN ships hardened_malloc on the same 39-bit kernel
|
||||
and its `system_server` holds about 210 GiB of `---p` reservations, which
|
||||
matches 2 GiB regions. `config/light.mk` is still 32 GiB; it is not a fix.
|
||||
|
||||
Fixed in `apply_012_hardened_malloc.py` and applied on the host tree:
|
||||
|
||||
- region size `2147483648`; the script exits if it is not 2 GiB;
|
||||
- The `// 32GiB` comment next to `2147483648` was replaced after
|
||||
`bacon012malloc3` exited. The line now says
|
||||
`// 2GiB, 39-bit VA (DivestOS/AXP)`.
|
||||
- the exclusion helper had been inserted between `__attribute__((noinline))`
|
||||
and `__libc_preinit_impl`, which moved `noinline` off the function bionic
|
||||
needs it on. The applier now places the helper before the attribute,
|
||||
repairs the misplaced form, and exits if `noinline` is lost.
|
||||
|
||||
Next: sideload the `20261008T184238Z` zip to LAB only (it lands on slot `b`),
|
||||
then smoke boot, USB, Magisk, camera, and that surfaceflinger stays on Scudo.
|
||||
If it loops at the Google logo again, `fastboot --set-active=a`. Do not
|
||||
factory-reset.
|
||||
|
||||
Original notes for the first attempt follow.
|
||||
|
||||
Session `bacon012malloc2`, started `2026-10-08T10:25:49Z`.
|
||||
|
||||
- Status: `/srv/rom/artifacts/detach-bacon012malloc2.status`
|
||||
- Log: `/srv/rom/artifacts/detach-bacon012malloc2-20261008T102549Z.log`
|
||||
- At 13:55 local: still `running`, ninja ~10 % (6453/63504), no error.
|
||||
`libc` had already linked at `2026-10-08T10:30:05Z`.
|
||||
- A 30-minute watcher lived in the 2026-10-08 chat. If that chat is gone,
|
||||
read the status file. Do not start another ninja.
|
||||
|
||||
Wrapper: `scripts/009-bacon-hardened-malloc.sh`. It applies
|
||||
`vendor/sunfish-a16/patches/012-hardened-malloc/apply_012_hardened_malloc.py`,
|
||||
builds `libc`, then execs the main Stage1 bacon. The host tree is already
|
||||
patched, so a plain re-run of the main bacon script also ships malloc.
|
||||
Do not revert `bionic` unless this experiment is abandoned.
|
||||
|
||||
Pin: GrapheneOS `hardened_malloc` `1d7fc7ffe0f8068a32ab2ba33df7515e7bad3f5f`
|
||||
(branch 14 tip, "support GCC15"). 64-bit libc uses it; 32-bit stays Scudo.
|
||||
`frameworks/av` `RLIMIT_AS` hunk does not exist on this LOS 23 tree and was
|
||||
not ported. Sunfish has no MTE; the `device_has_arm_mte` product variable was
|
||||
stripped from `Android.bp`.
|
||||
|
||||
Exclusions, required: `/system/bin/surfaceflinger` exact path, and any exe
|
||||
whose path contains `camera`. Real LAB paths:
|
||||
`/vendor/bin/hw/android.hardware.camera.provider@2.7-service-google` and
|
||||
`/system/bin/cameraserver`. Env `DISABLE_HARDENED_MALLOC` also disables it.
|
||||
`init.rc` sets `vm.max_map_count` to `1048576`.
|
||||
|
||||
Previous failure, already fixed: `h_malloc_info` was undeclared
|
||||
(`h_malloc.h` omits it on Android). Declaration is in `malloc_common.h`.
|
||||
The dead log is `detach-bacon012malloc-20261008T101328Z.log`. Do not treat
|
||||
that log as the current job.
|
||||
|
||||
This bacon does **not** contain the Swissquote cmdline fix, the Settings
|
||||
timeout patches, deblob, or AVB flags 0.
|
||||
|
||||
When it finishes with `exit=0`: verify the zip hash, sideload only LAB,
|
||||
then smoke boot, USB, Magisk, camera, and that surfaceflinger is still on
|
||||
Scudo. Keep the 20261008 zip until that smoke passes. If the bacon fails,
|
||||
diagnose before relaunching. Do not factory-reset.
|
||||
|
||||
## 2. Swissquote — next bacon only after malloc boots
|
||||
|
||||
Measured 2026-10-08 with both phones online. Same app on both:
|
||||
`com.swissquote.android` 9.10.0 (`versionCode=91000000`). Both have Island
|
||||
user 10, Zygisk, DenyList on, and the same module set except GOLDEN also
|
||||
has `flag-secure-disabler`. DenyList on both already has
|
||||
|
||||
- `com.swissquote.android|com.swissquote.android`
|
||||
- `com.swissquote.android|com.swissquote.android_zygote`
|
||||
|
||||
DenyList has no user id. It covers Island. Vector's scope is `user_id=0`
|
||||
on both, and GOLDEN does not even have Swissquote installed on user 0
|
||||
(only user 10) and it still passes. The module screen that cannot pick
|
||||
work-profile apps is a red herring. Do not chase it.
|
||||
|
||||
### Proven ROM difference
|
||||
|
||||
GOLDEN `/system/bin/init` contains `chmod("/proc/cmdline", 0440)`.
|
||||
LAB's init does not. Magisk's binary on neither phone contains that string.
|
||||
No module script was found chmod'ing it.
|
||||
|
||||
| | LAB | GOLDEN |
|
||||
|---|---|---|
|
||||
| `/proc/cmdline` mode | `0444` root:radio | `0440` root:radio |
|
||||
| shell can read it | yes, sees `verifiedbootstate=orange` | no (`Permission denied`) |
|
||||
| `init.rc` | `chown root radio` only | same `chown`, no chmod line |
|
||||
|
||||
Both `init.rc` files only chown. The mode change is a C call inside GOLDEN's
|
||||
init. Clone that call. Do not implement it as a Magisk module. The owner
|
||||
asked for that specifically.
|
||||
|
||||
Where it lives: `apply_009_cmdline_mode.py`, called by the ship bacon.
|
||||
It inserts AOSP's `CHECKCALL(chmod("/proc/cmdline", 0440))` in
|
||||
`first_stage_init.cpp` immediately after the proc mount. That CHECKCALL
|
||||
is what logs `chmod("/proc/cmdline", 0440) failed`, the string found in
|
||||
GOLDEN's init. LOS 23.2 already chmod's `/proc/bootconfig` and did not
|
||||
chmod cmdline. `bacon009cmdline` is the build for this and keeps the
|
||||
2 GiB hardened_malloc already in the tree. Do not flash it until it
|
||||
finishes and the owner asks.
|
||||
The compiling malloc tree does not have this call (`init.cpp` has no
|
||||
`cmdline` match; `init.rc` only has the chown at line 1282).
|
||||
|
||||
### 2026-10-08 22:30 — writer found, do not bake the spoof into the ROM
|
||||
|
||||
Play Integrity Fix `service.sh` (same file on both phones) runs after
|
||||
`sys.boot_completed=1` and calls `resetprop_if_diff`. That helper does
|
||||
nothing when the property is empty:
|
||||
|
||||
```
|
||||
[ -z "$CURRENT" ] || [ "$CURRENT" = "$EXPECTED" ] || resetprop -n "$NAME" "$EXPECTED"
|
||||
```
|
||||
|
||||
`spoofProps=false` does not gate this block. Both phones now use the same
|
||||
PIF print: `oriole` Pixel 6 CANARY, `ZP11.260618.005`. The earlier
|
||||
`tegu` / `stallion` note is stale.
|
||||
|
||||
GOLDEN's bootloader cmdline creates the properties, so the module rewrites
|
||||
them. LAB's cmdline never creates `vbmeta.device_state` or `veritymode`, so
|
||||
the same script skips them.
|
||||
|
||||
| `getprop` | LAB | GOLDEN |
|
||||
|---|---|---|
|
||||
| `ro.boot.verifiedbootstate` | `green` (was `orange`) | `green` (was `orange`) |
|
||||
| `ro.boot.flash.locked` | `1` | `1` |
|
||||
| `ro.boot.vbmeta.device_state` | empty | `locked` (cmdline said `unlocked`) |
|
||||
| `ro.boot.veritymode` | empty | `enforcing` (real cmdline value, not a spoof) |
|
||||
| `sys.oem_unlock_allowed` | empty on the 22:32 read | `0` (cmdline-era value was `1`) |
|
||||
|
||||
Vector's `/data/adb/lspd/log/props.txt` on GOLDEN still records the real
|
||||
values (`orange`, `unlocked`, `enforcing`, `oem_unlock_allowed=1`). They
|
||||
are not in `/data/property/persistent_properties`. Island sees the rewritten
|
||||
`getprop` because the property area is global. It cannot open
|
||||
`/proc/cmdline` (`0440`), so it never sees the raw `unlocked` / `orange`.
|
||||
|
||||
The owner said Swissquote on GOLDEN also worked with no Magisk modules.
|
||||
The PIF rewrite is therefore not the first fix. Hope is that the init
|
||||
chmod alone is enough. Do not set `sys.oem_unlock_allowed=0` in the ROM;
|
||||
it conflicts with the OEM-unlock escape hatch. Do not add the missing
|
||||
cmdline tokens in this bacon. If Island still fails after the chmod,
|
||||
those tokens belong to the separate AVB flags experiment.
|
||||
|
||||
TEESimulator 71 on LAB (`71-17be1f0`) did not change the outcome. Its
|
||||
target list is still only `com.google.android.gms`, `com.android.vending`,
|
||||
and `io.github.vvb2060.keyattestation`. GOLDEN is still TEE 67 with the
|
||||
same three targets. The app is not in that list.
|
||||
|
||||
Test only inside Island. GOLDEN does not have the app on user 0, and the
|
||||
owner said it has never worked in the main profile there. LAB currently
|
||||
has it on user 0 and user 10. `ps` shows `magiskd`, `su`, `zygiskd64`,
|
||||
`teesim`, and `vectord` on both phones, so a visible process list does not
|
||||
explain why Island passes on GOLDEN.
|
||||
|
||||
### 2026-10-08 22:53 — chmod booted, Island still closes
|
||||
|
||||
Slot `_a`, zip SHA256 `40ed3213…ccfa`. `/proc/cmdline` is `0440`; shell
|
||||
gets permission denied; root still reads `orange`. The app still exits in
|
||||
Island and in the main profile. The 22:52 log is the same sequence as
|
||||
before the chmod: DenyList hit, Play services `261200000` vs microG
|
||||
`252432034`, `libsigner.so`, SELinux and `/cache` denied, `ps`, suppressed
|
||||
toast, process dies. GOLDEN's microG is older (`252432033`) and Island
|
||||
works there, so the version warning is not the gap.
|
||||
|
||||
What Island can still see: `ro.boot.vbmeta.device_state` and
|
||||
`ro.boot.veritymode` are empty on LAB. GOLDEN's bootloader cmdline creates
|
||||
both (`unlocked` and `enforcing`) even with no modules. Do not chase that
|
||||
inside the timeouts bacon. It stays with the AVB flags 0 experiment.
|
||||
|
||||
### If the app still fails after the chmod
|
||||
|
||||
These are owner-installed modules, not ROM payload
|
||||
(`docs/009-magisk-preinstall-runbook.md`):
|
||||
|
||||
1. TEESimulator version is already ruled out. LAB is `71-17be1f0`, GOLDEN
|
||||
is `67-4f42350`, same three targets, and 71 changed nothing. Config
|
||||
holds a keybox; do not copy it into git or into a doc.
|
||||
2. PIF print is already the same on both (`oriole` Pixel 6 CANARY). Do not
|
||||
chase `stallion`. A wrong CANARY print on sunfish already broke this
|
||||
surface once (`comet`); that is not the current gap.
|
||||
|
||||
Banking flow if the app is reinstalled (constitution): Aurora, do not open,
|
||||
DenyList including processes before the first process, clone into Island
|
||||
(`com.oasisfeng.island.fdroid`, not Insular), first launch inside Island,
|
||||
Play update from the work profile. Spec 009 still marks banking deferred.
|
||||
The owner asked to make Swissquote work on LAB anyway. Yuh on GOLDEN needed
|
||||
Vector's bootloader spoofer on top of DenyList; Swissquote on GOLDEN did not.
|
||||
|
||||
## 3. After that, one bacon each
|
||||
|
||||
Do not stack these into the malloc zip or into each other.
|
||||
|
||||
1. **Settings timeouts and sensitive QS tiles.** Already inside
|
||||
`bacon009timeouts` (see "Running tonight"). Defaults stay off except
|
||||
the sensitive QS tiles, which ask for the PIN on the lock screen.
|
||||
After boot, confirm the three settings exist and Droidify is 0.7.8.
|
||||
2. **Selective deblob.** Own bacon, after the timeouts zip exists. Study
|
||||
and the wave-1 Soong override are in the section below. Do not run
|
||||
`Deblob.sh`. Do not put wave 2 in the same bacon.
|
||||
3. **AVB flags 0.** Script is ready, not launched.
|
||||
`scripts/009-bacon-avb-flags0.sh` exports `AXP_AVB_FLAGS=0` and
|
||||
`WITH_AVB=true`. That is the only Lineage switch: `BoardConfigLineage.mk`
|
||||
skips `--flags 3`. Same `avb.pem`, same `SHA256_RSA4096`. Pubkey sha1
|
||||
`ee85ac5a2dcec0846e95dd575f874a659026d790`. The default ship script still
|
||||
expects flags 3 unless that env is set. Do not rsync the ship script over
|
||||
`/srv/rom/artifacts/bin/` while `bacon009timeouts` is running. Launch only
|
||||
after that status file shows `exit=0`, and do not flash until the owner
|
||||
asks. On LAB check Magisk, a full sideload, dm-verity, and whether the
|
||||
raw cmdline gained `veritymode` and `vbmeta.device_state`. Relock stays
|
||||
**wont**. If it boot-loops, set the active slot back to the previous zip.
|
||||
Measured on the current LAB image, before that experiment:
|
||||
`getprop ro.boot.vbmeta.device_state` and `getprop ro.boot.veritymode`
|
||||
are empty. `ro.boot.verifiedbootstate=green` is the PIF spoof; the raw
|
||||
cmdline is still `verifiedbootstate=orange`. These two missing tokens
|
||||
are bootloader cmdline properties. Flags 3 is
|
||||
`HASHTREE_DISABLED|VERIFICATION_DISABLED`. GOLDEN's raw cmdline, read
|
||||
earlier the same night, contains `androidboot.vbmeta.device_state=unlocked`
|
||||
and `androidboot.veritymode=enforcing`. Do not write `locked` or
|
||||
`enforcing` into the ROM, and do not set `sys.oem_unlock_allowed=0`.
|
||||
4. **Lock-scope concurrency review.**
|
||||
`vendor/sunfish-a16/patches/009-kernel-hardening/ports/cve-2022-49980-lock-scope.patch`.
|
||||
Boot and USB already passed on LAB. Review the companion. Do not revert
|
||||
it unless a new failure shows up. The one-liner stays denylisted.
|
||||
5. **Leftover high-offset kernel CVEs.** Small reviewed batches (≤10),
|
||||
strict applicator, boot each batch. nf_tables leftovers stay skipped.
|
||||
Failed Stage1 probes (allocsize/0007, ro/0007+0034, slab) are low
|
||||
priority. Do not reflash `20260918*-axp-kh-cve` or `kh-full`.
|
||||
6. **Droidify.** `bacon009timeouts` ships `0.7.8` (`versionCode=780`,
|
||||
targetSdk 37). Host file
|
||||
`/srv/rom/prebuilts/axp-apps/Droidify.apk`, SHA256
|
||||
`3e7934ff9b811c2f77794793381241095a02bac2d163bf922cb5a5a0ed25ca82`.
|
||||
The previous `0.7.7` apk is kept beside it as `Droidify-0.7.7.apk`.
|
||||
Aurora and Fossify stay.
|
||||
7. **GPG SHA256 sums** of published zips, the way AXP does. Owner on
|
||||
2026-10-09: not needed now. Do not start it. AXP's current publish
|
||||
path is a SHA512 file plus `gpg --verify` on that file
|
||||
(`docs/knowledge/signatures`).
|
||||
8. **Release-key custody.** Signing works (the booted zip is `release-keys`).
|
||||
Keys are still `$HOME/.android-certs/` on rom-dev01 plus a gitignored
|
||||
copy under `vendor/lineage-priv/keys` and `vendor/sunfish-a16/certs`.
|
||||
Move the private material to `/srv/rom/secrets/` and confirm the
|
||||
encrypted offline copy. Never commit `.pk8`. Cert tar already at
|
||||
`/home/android-build/backups/android-certs-20260929T1637Z.tar.gz`.
|
||||
9. **SetupWizard language bounce** after Wi-Fi connect. Wi-Fi stays up.
|
||||
Non-blocking. Looked at on 2026-10-08: `lineage_wizard_script.xml` goes
|
||||
locale, then SIM, then network, and every network result advances to
|
||||
datetime. There is no result edge that returns to language. Do not invent
|
||||
a patch. `LocaleActivity` can still call `updateLocale` from the SIM MCC
|
||||
while that page is open; that is a different moment from the Wi-Fi step.
|
||||
10. **LOS 23 nightly rebase**, then re-apply this pack. Do it after the
|
||||
malloc and cmdline bacons, not during them.
|
||||
11. **Regenerate** `plan.md` / `tasks.md` after any `spec.md` edit.
|
||||
Spec Kit order stands. Do not use upstream `taskstoissues`.
|
||||
|
||||
## Selective deblob — studied 2026-10-08
|
||||
|
||||
Do not run AXP `Deblob.sh`. It is not in the public AXP-OS repos or in
|
||||
`/srv/rom/references/axp/archive/codeberg/git`, and its usual defaults drop
|
||||
CNE, IZat/XTRA, and sometimes Widevine. Shipped GOLDEN still has
|
||||
`com.qualcomm.qti.cne`. Spec FR-018 and the constitution forbid deleting
|
||||
the IZat/XTRA/xtwifi blobs. The positive list is the owner's finish list.
|
||||
|
||||
The `[ -e $f ]` loop that printed ABSENT for every vendor binary was a
|
||||
quoting bug. `ls` of the literal path is the check: "Permission denied"
|
||||
means the file exists and shell cannot stat it; "No such file" means gone.
|
||||
|
||||
### Positive list — do not override, do not delete
|
||||
|
||||
Present on the booted LAB image (`11231JEC207477`). GPS daemons and
|
||||
`imsrcsd` were also checked on GOLDEN before it was unplugged.
|
||||
|
||||
| Keep | LAB evidence | GOLDEN |
|
||||
|---|---|---|
|
||||
| `xtra-daemon`, `lowi-server`, `loc_launcher` | `/vendor/bin/…` exists (permission denied) | those three paths are "No such file" on GOLDEN. Keep them. Default is the precise Lineage stack. The two Settings switches below, both default off, are how the owner turns that assistance off and matches the AXP posture without deleting the binaries. |
|
||||
| `xtwifi.conf` | copied by `sunfish-vendor.mk` to `/vendor/etc/xtwifi.conf` | absent on GOLDEN (`No such file`). Keep the copy rule. The location switches are what turn the assistance off. |
|
||||
| Widevine | `/vendor/bin/hw/android.hardware.drm-service.widevine` exists. Soong module `android.hardware.drm-service.widevine` | same path exists |
|
||||
| IMS | `/system_ext/priv-app/ims/ims.apk` (`org.codeaurora.ims`); `/vendor/bin/ims_rtp_daemon`, `imsdatadaemon`, `imsqmidaemon`, `imsrcsd` all exist | `imsrcsd` is absent on GOLDEN. Keep it. It is the Qualcomm RCS daemon, and the positive list says IMS. |
|
||||
| IPA | `/vendor/bin/ipacm` exists | directory listing earlier the same night also showed it on GOLDEN |
|
||||
| IWlan | `/vendor/app/IWlanService/IWlanService.apk` (`vendor.qti.iwlan`); `vendor.qti.hardware.data.iwlan@1.0.so` exists on both | keep |
|
||||
| CNE | `/vendor/app/CneApp/CneApp.apk` (`com.qualcomm.qti.cne`). Soong `CneApp`, `libcne*` | package was on GOLDEN too |
|
||||
| eUICC hardware | `/system_ext/priv-app/EuiccSupportPixel/EuiccSupportPixel.apk` and `EuiccSupportPixelPermissions` (`com.google.euiccpixel`, `.permissions`) | both phones |
|
||||
| OpenEUICC | `/system_ext/priv-app/OpenEUICC/OpenEUICC.apk` (`im.angry.openeuicc`) | both phones |
|
||||
| Radio packages already on both | `com.qualcomm.qti.telephonyservice`, `com.qualcomm.timeservice`, `com.qualcomm.uimremoteclient`, `com.qualcomm.uimremoteserver`, `com.android.carrierconfig`, `com.android.carrierdefaultapp`, `com.android.providers.telephony` | keep |
|
||||
|
||||
GPS precision stays the default. `apply_009_supl_privacy.py` is already in
|
||||
the ship bacon. Both switches live in Location settings and start off, so
|
||||
SUPL, LPP, XTRA and IZat keep running:
|
||||
|
||||
- "Force disable Secure User Plane Location (SUPL)" (`force_disable_supl`).
|
||||
On sets `SUPL_MODE=0`. IMSI and the phone number are never sent to a SUPL
|
||||
server, whether this switch is on or off.
|
||||
- "Stop extra location assistance" (`force_disable_location_assist`). On
|
||||
sets `LPP_PROFILE=0` (LPPE goes with that session), skips the framework
|
||||
XTRA download, and stops `loc_launcher` (`xtra-daemon`, lowi, IZat).
|
||||
Satellite fixes keep working. Turning it off starts `loc_launcher` again.
|
||||
|
||||
That second switch is the AXP location posture. Do not delete the blobs
|
||||
and do not change either default to on.
|
||||
|
||||
`/vendor/lib64/libimsmedia_jni.so` is missing on both phones. The JNI
|
||||
libs ship inside the ims app (`system_ext_priv-app_ims_lib_arm64_libimsmedia_jni_so`).
|
||||
That is not a deletion.
|
||||
|
||||
### Wave 1 — booted 2026-10-09, pass
|
||||
|
||||
`bacon009deblob3` (`20261009T101715Z`, SHA256
|
||||
`efbec33ac2bafc75ee2f90970a9286f51fe8073b61fc3ed11c01689c9e8a28cf`) is
|
||||
the running LAB slot `_a`. `EuiccGoogle` and `EuiccGoogleOverlay` are
|
||||
absent from the image. The `/data` update survived the sideload and was
|
||||
removed for user 0 and user 10. EuiccSupportPixel and OpenEUICC stayed.
|
||||
OpenEUICC opened onto `DownloadWizardActivity`. Empty card is the pass.
|
||||
The notes below are the failed attempts; do not rebuild them.
|
||||
|
||||
Why the old override did nothing: `EuiccGoogle` is a Soong
|
||||
`android_app_import` in `vendor/google/sunfish/Android.bp` (`product_specific:
|
||||
true`). `NoEuiccGoogle` was a Make `BUILD_PHONY_PACKAGE`. Make's
|
||||
`LOCAL_OVERRIDES_PACKAGES` only filters Make modules (`build/make/core/main.mk`
|
||||
`module-overrides`). `out/soong/soong.lineage_sunfish.variables` contains
|
||||
both `NoEuiccGoogle` and `EuiccGoogle` in the product package list, and
|
||||
`PackageNameOverrides` is empty.
|
||||
|
||||
Laptop tree now, not rsynced:
|
||||
|
||||
- `vendor/sunfish-a16/deblob/Android.bp` — `NoEuiccGoogle` (`product_specific`)
|
||||
overrides `EuiccGoogle`; `NoEuiccGoogleOverlay` (`system_ext_specific`)
|
||||
overrides `EuiccGoogleOverlay`. It does not name `EuiccSupportPixel`.
|
||||
- `vendor/sunfish-a16/apps.mk` installs both phonies.
|
||||
- The Make phony was removed from `vendor/sunfish-a16/Android.mk`. Do not
|
||||
put an `Android.bp` beside that `Android.mk`; Soong would skip the
|
||||
prebuilt APKs in the same directory.
|
||||
|
||||
After the timeouts bacon exits, rsync those three files, then a deblob
|
||||
bacon. Do not flash until asked. On the installed-files list, confirm
|
||||
`EuiccGoogle.apk` and `EuiccGoogleOverlay.apk` are gone and
|
||||
`EuiccSupportPixel.apk` and `OpenEUICC.apk` remain. The `/data` update
|
||||
can survive a sideload. If `pm path com.google.android.euicc` still points
|
||||
at `/data/app`, `adb -s 11231JEC207477 shell pm uninstall --user 0
|
||||
com.google.android.euicc` after boot. The owner has no eSIM profile, so
|
||||
do not block on downloading one. Open OpenEUICC: it must start and not
|
||||
crash. An empty card or "no profiles" is a pass. `pm path` must still
|
||||
show EuiccSupportPixel and OpenEUICC.
|
||||
|
||||
### Wave 2 — not in the wave-1 bacon
|
||||
|
||||
Rechecked read-only on GOLDEN `11071JEC203839` at 2026-10-08 23:24 local,
|
||||
with LAB unplugged. `pm path` printed no path for every row below, and
|
||||
`ls` returned `No such file` for `xtra-daemon`, `lowi-server`,
|
||||
`loc_launcher`, `imsrcsd`, and `/vendor/etc/xtwifi.conf`. Present on
|
||||
GOLDEN, same as LAB: EuiccSupportPixel, EuiccSupportPixelPermissions,
|
||||
OpenEUICC (`/system_ext/priv-app/OpenEUICC_AXP-OS/OpenEUICC_AXP-OS.apk`),
|
||||
CneApp, `org.codeaurora.ims`, IWlanService, QtiTelephonyService,
|
||||
TimeService, both uimremote packages, CarrierConfig, CarrierDefaultApp,
|
||||
TelephonyProvider, `ims_rtp_daemon`, `imsdatadaemon`, `imsqmidaemon`,
|
||||
`ipacm`, and the Widevine HAL. One bacon, after wave 1 has booted and a
|
||||
physical-SIM call, VoLTE, and VoWiFi have passed. No eSIM profile is
|
||||
required. Same Soong-phony pattern, matching partition flag.
|
||||
|
||||
Google, not the Qualcomm IMS stack:
|
||||
|
||||
| Module | Partition flag | LAB path | Package |
|
||||
|---|---|---|---|
|
||||
| `CarrierServices` | `product_specific` | `/product/priv-app/CarrierServices` (active path is `/data/app/…/com.google.android.ims…`) | `com.google.android.ims` |
|
||||
| `CarrierSettings` | `product_specific` | `/product/priv-app/CarrierSettings/CarrierSettings.apk` | `com.google.android.carrier` |
|
||||
| `CarrierWifi` | `product_specific` | active path `/data/app/…/com.google.android.apps.carrier.carrierwifi…` | `com.google.android.apps.carrier.carrierwifi` |
|
||||
| `CarrierSetup` | `system_ext_specific` | `/system_ext/priv-app/CarrierSetup/CarrierSetup.apk` | `com.google.android.carriersetup` |
|
||||
| `RilConfigService` | `system_ext_specific` | `/system_ext/priv-app/RilConfigService/RilConfigService.apk` | `com.google.RilConfigService` |
|
||||
| `grilservice` | `system_ext_specific` | `/system_ext/priv-app/grilservice/grilservice.apk` | `com.google.android.grilservice` |
|
||||
|
||||
AOSP, not vendor blobs. Override only after the same call test:
|
||||
|
||||
- `ImsServiceEntitlement` in `packages/apps/ImsServiceEntitlement/Android.bp`,
|
||||
installed at `/product/priv-app/ImsServiceEntitlement/ImsServiceEntitlement.apk`
|
||||
(`com.android.imsserviceentitlement`).
|
||||
- `com.android.service.ims` is `/system_ext/app/RcsService/RcsService.apk`.
|
||||
- `com.android.service.ims.presence` is
|
||||
`/system_ext/app/PresencePolling/PresencePolling.apk`.
|
||||
Grep `name: "RcsService"` and `name: "PresencePolling"` before writing
|
||||
the phony. The names were not in `packages/services/Telephony/Android.bp`.
|
||||
|
||||
Leave these alone until a zip that removed only the Google rows above
|
||||
has made a call. They were absent on GOLDEN, and they sit next to radio:
|
||||
|
||||
- `atfwd` → `/system_ext/app/atfwd/atfwd.apk` (`com.qualcomm.atfwd`)
|
||||
- `qcrilmsgtunnel` → `/system_ext/priv-app/qcrilmsgtunnel/qcrilmsgtunnel.apk`
|
||||
- `uceShimService` → `/system_ext/app/uceShimService/uceShimService.apk`
|
||||
- `com.qualcomm.qti.services.secureui` → same directory name under
|
||||
`/system_ext/app/`
|
||||
- `CACertService` → `/vendor/app/CACertService/CACertService.apk`
|
||||
(`vendor.qti.hardware.cacert.server`, `soc_specific: true`)
|
||||
|
||||
Do not remove `ims`, `QtiTelephonyService`, `CneApp`, `xtra-daemon`,
|
||||
`lowi-server`, `loc_launcher`, `imsrcsd`, Widevine, or `EuiccSupportPixel`
|
||||
in either wave.
|
||||
|
||||
## Already on the booted zip — do not redo
|
||||
|
||||
- Magisk 30.7 preroot (`/system_ext/bin/magisk`). GOLDEN oracle is still
|
||||
Magisk 29.0. Ship stays 30.7 unless 30.7 itself breaks a bank after the
|
||||
cmdline fix. Dormant until Additional Setup.
|
||||
- microG GmsCore `0.3.17.252432` (`252432034`), not `-hw`.
|
||||
- Phonesky from `/srv/rom/prebuilts/phonesky/`. Never git.
|
||||
- OpenEUICC privileged, platform cert, deps SDK forced to 36
|
||||
(`prebuilts/openeuicc-deps` `5402167` was generated for SDK 37).
|
||||
- Sigspoof (`apply_005_sigspoof.py`) and call recording / FLAG_SECURE
|
||||
(`apply_008_call_rec_flag_secure.py`) are in the ship script.
|
||||
FLAG_SECURE override is a project extra, default off.
|
||||
- Private DNS, connectivity check, the two location switches (both default
|
||||
off, so SUPL / XTRA / IZat stay on for precision), Yama
|
||||
`persist.native_debug`, disable-app.
|
||||
- Jelly stays. Gallery is Fossify. Gallery2 APK stays absent. WebView stays
|
||||
LOS 23. No NeoLauncher. No `axp.os` OTA endpoints.
|
||||
- Vendor SPL guard: `apply_009_axp_identity.py` rewrites a `2023-08-05`
|
||||
rollback back to `2023-09-05` and exits otherwise. AXP GOLDEN really is
|
||||
`2023-08-05`. This divergence is deliberate.
|
||||
- `LINEAGE_BUILDTYPE=PRO`, `user`, project AVB key, flags 3.
|
||||
- Stage1 + combo4 + FORTIFY + CVE-clean + hi-batch1–3 + US-S5-1 Yama/slab
|
||||
+ lock-scope. `KSYM_NAME_LEN` stays 192. Stage1 must use `patch --forward`
|
||||
and must omit `misc/0021` (already in the LOS base; GNU patch would
|
||||
reverse it and shrink KSYM 192→128).
|
||||
|
||||
## init_user0 — do not rediscover
|
||||
|
||||
The 17:15 zip (`SHA256 2c16d2385094197181c07dde4e9604137883eefedc2b346cc1071e38abb57a0b`)
|
||||
lowered vendor SPL from `2023-09-05` to `2023-08-05`. Keymaster blobs on the
|
||||
existing install refuse that rollback. `vdc cryptfs init_user0` fails and
|
||||
recovery writes
|
||||
`boot-recovery --prompt_and_wipe_data --reason=init_user0_failed`.
|
||||
|
||||
Proof, same userdata: nols boot failed, full 13:31 boot on the 17:15
|
||||
userspace failed, flashing only the 13:31 vendor (`2023-09-05`) made Android
|
||||
boot. Data was fine. The 13:31 zip
|
||||
(`SHA256 2c4eb9cb831e96a57c2c068e5c044ae5d29ef140f53817e9246cac3c61dbe008`)
|
||||
is the older known-good, without lock-scope. Quarantine
|
||||
`20261007T161115Z` (`SHA256 df2ad13f6afeb2d25f5e8f7a32ee2caa0b926550000e5cb3078c8a56d8efffd7`)
|
||||
stays unflashed.
|
||||
|
||||
Emulator and an on-screen boot debugger do not explain this. The emulator
|
||||
cannot boot the sunfish 4.14 / keymaster / userdata stack. The debug console
|
||||
is UART `ttyMSM0`. pstore on this LAB was encrypted and unreadable.
|
||||
`docs/007-debug-boot-panic-screen.md`.
|
||||
|
||||
## Uncommitted work the next session must keep
|
||||
|
||||
Not committed. Do not revert while picking up a later item.
|
||||
|
||||
- `apply_009_axp_identity.py` — vendor SPL stays `2023-09-05`.
|
||||
- `scripts/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh` — lock-scope
|
||||
applied, vendor SPL grep, Jelly/Fossify gates, the userspace applies listed
|
||||
above.
|
||||
- `scripts/009-bacon-hardened-malloc.sh` and
|
||||
`vendor/sunfish-a16/patches/012-hardened-malloc/`.
|
||||
- `tests/test_009_next_bacon_guards.py`, `tests/test_012_hardened_malloc.py`,
|
||||
`tests/test_009_a15_reboot_fixups.py`, `tests/test_009_a15_small_ports.py`.
|
||||
- Host tree under `/srv/rom/src/lineage` is dirty with the malloc apply and
|
||||
the identity/kernel applies. Laptop scripts were rsynced to
|
||||
`/srv/rom/artifacts/bin/`.
|
||||
|
||||
## Docs that will lie to you
|
||||
|
||||
- `docs/009-axp-graphene-remaining.md` — pre-boot order. Pointer added.
|
||||
- `docs/009-hardening-signing-checklist.md` — still says test-keys,
|
||||
`userdebug`, and "hardened_malloc later". The booted zip is `user` +
|
||||
`release-keys`, and malloc is the bacon in flight.
|
||||
- `docs/009-user-zip-audit-20261007.md` — an older unflashed zip. Jelly is
|
||||
**kept** now; that audit's "Jelly absent" line is obsolete.
|
||||
- `docs/009-version-pins.md` — still right for microG, Magisk 30.7, Phonesky,
|
||||
OpenEUICC. Add the malloc pin when that bacon is accepted.
|
||||
@@ -211,14 +211,27 @@ attribution. One stage per bacon when risk is unclear.
|
||||
- [x] **US-S5-1** LAB OK (`162840Z`): `CONFIG_SECURITY_YAMA=y` +
|
||||
`CONFIG_SLAB_FREELIST_RANDOM=y` — see
|
||||
`docs/009-lab-acceptance-20261002-uss5.md`
|
||||
- [ ] Optional later: `CVE-2022-49980` lock-scope port; Stage1 slab FAIL
|
||||
retries; `hardened_malloc`
|
||||
- [ ] **Next cycle after the user image is accepted** (owner 2026-10-06):
|
||||
close kernel CVE + Graphene/AXP hardening + deblob on that baseline.
|
||||
Boot-check each batch (full `cve-strict` ~503 and `CVE-2022-49980`
|
||||
alone hang at the Google logo). Inventory: remaining high-offset KEEP,
|
||||
`49980` lock-scope port, Stage1 probes that failed
|
||||
(allocsize/0007, ro/0007+0034, slab/0010+0018), `hardened_malloc`,
|
||||
AXP deblob that still leaves radio, GPS and Widevine working.
|
||||
Still out: location lockdown, bootloader relock, KernelSU, IPA removal.
|
||||
- [x] **Next-bacon recipe guard (2026-10-07):** Stage1 uses
|
||||
`patch --forward`, omits already-present `misc/0021`, and fails unless
|
||||
`KSYM_NAME_LEN=192`; full feature-008 call-recording apply is restored.
|
||||
Quarantine the earlier `bacon00949980` build and do not flash it.
|
||||
|
||||
Map updates: `docs/009-kernel-hardening-map.md` + Gate G attribution.
|
||||
|
||||
### US-S6 — Optional later
|
||||
|
||||
- [ ] Switch default lunch to `lineage_sunfish-user` for ship
|
||||
- [ ] Confirm `lineage_sunfish-user` after flash
|
||||
(bacon started 2026-10-06, tmux `009-user-bacon`, log
|
||||
`/srv/rom/artifacts/detach-009-user-bacon-20261006T203157Z.log`.
|
||||
PRO stays branding. Check `ro.build.type=user` and `release-keys` on LAB)
|
||||
- [ ] GPG-sign published SHA sums (AXP does; nice-to-have)
|
||||
- [ ] LOS23 nightly rebase (vanilla + re-apply Magisk/stage1/keys)
|
||||
- [ ] Bootloader lock — **wont**
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
# Swissquote differential investigation — 2026-10-09
|
||||
|
||||
Status: **failure classified, underlying root-detection signal not yet isolated**.
|
||||
Live observations approximately 15:18–15:26 Europe/Berlin. This supersedes
|
||||
the Swissquote runtime inventory in `009-handoff-20261008.md`, not its
|
||||
build/recovery instructions. No firmware was flashed and GOLDEN was read-only.
|
||||
|
||||
## Confirmed new finding
|
||||
|
||||
Swissquote 9.11.0 stores this nonfatal exception on LAB, Island user 10:
|
||||
|
||||
```text
|
||||
com.swissquote.android.FreeRaspException
|
||||
freeRASP: onPrivilegedAccess
|
||||
com.swissquote.android.ThreatDetectedListenerKt$getThreatDetectedListener$1.onPrivilegedAccess
|
||||
ThreatDetectedListener.kt:24
|
||||
app.talsec.rasp.security.api.ThreatListener.onReceive
|
||||
```
|
||||
|
||||
This was read from the app's existing local Crashlytics event JSON. Only
|
||||
the exception classification and stack symbols were retained; no raw
|
||||
Crashlytics report, account data, keys, or session identifiers enter Git.
|
||||
`scripts/009-read-swissquote-rasp.py --device LAB` reproduces that limited,
|
||||
read-only inspection. Retained events may be uploaded/removed by the app;
|
||||
an empty result does not prove compatibility or that no callback occurred.
|
||||
|
||||
Static inspection of the installed APK established that **-69 is an
|
||||
application-defined `RASP_FAILING_EXIT_CODE`**, shared by four handlers:
|
||||
`onPrivilegedAccess`, `onDebug`, `onAppIntegrity`, and `onHooks`. Each records
|
||||
a distinct FreeRaspException, requests the same warning, then calls
|
||||
`System.exit(-69)`. Consequently the exit code alone was not enough to
|
||||
attribute the failure to root. The retained exception now identifies the
|
||||
privileged-access branch for the observed LAB failure. It does not identify
|
||||
which underlying native check fired or exclude additional failures later.
|
||||
|
||||
The APK contains Talsec `v19_2_0_fullRelease` markers and loads `libts.so`.
|
||||
`libsigner.so` belongs to the separately present Adjust signer integration;
|
||||
loading it is not evidence that it caused this exit.
|
||||
|
||||
The previously suppressed toast was recovered by temporarily granting
|
||||
POST_NOTIFICATIONS to LAB user 10, then revoking it in a finally block.
|
||||
Its text lists generic device-safety requirements (root, debugging,
|
||||
emulation, modification, malware, official store); it does **not** disclose
|
||||
the failing check. A later observation showed the permission granted with
|
||||
USER_SET, apparently from concurrent user activity; it was not overwritten.
|
||||
|
||||
## Current comparison, not the earlier handoff inventory
|
||||
|
||||
- Both devices have Swissquote **9.11.0 / 91100003**, target SDK 37,
|
||||
arm64, with Play Store as installer and initiating package.
|
||||
- All three installed APKs match byte for byte:
|
||||
- base: `734293c7b6a7ecf48117e23ffc72ad0a1e3279336025551faf23c1c3c239c8f2`
|
||||
- arm64 split: `6ce67d98a2a4dc4b1ac38a9b3c864e11167b17ba9ed62e8a9d69cf48cb3e090d`
|
||||
- density split: `4e0ab62d8eb7c2faa959014552204c8378977459109b3ba2a0b400a87bb9c3a2`
|
||||
- Both now have Swissquote installed for user 0 **and** user 10. The earlier
|
||||
statement that GOLDEN has it only for user 10 is obsolete.
|
||||
- Island matches: `6.4.2-d46911c9`, versionCode 51801.
|
||||
- GOLDEN: Android 15 / SDK 35, Magisk 29.0. LAB: Android 16 / SDK 36,
|
||||
SDK_FULL 36.1, Magisk 30.7, deblob4 slot b.
|
||||
- Both are user/release-keys with `ro.debuggable=0`, SELinux Enforcing,
|
||||
Zygisk enabled and DenyList **enforced**. Both list the main Swissquote
|
||||
process and its `_zygote` entry. LAB logs the DenyList hit for user 10.
|
||||
- Both expose `verifiedbootstate=orange`, `vbmeta.device_state=unlocked`,
|
||||
`flash.locked=0`, and `veritymode=enforcing` at this measurement.
|
||||
Both protect `/proc/cmdline` with mode 0440.
|
||||
- GOLDEN has only hosts and flag-secure-disabler modules installed.
|
||||
LAB has hosts plus PIF and TEESimulator directories; both latter modules
|
||||
have **disable markers**. Directory presence is not active-module proof.
|
||||
- Owner confirmed GOLDEN Swissquote still opens in Island in this state.
|
||||
Its existing user-10 process was alive during read-only inspection.
|
||||
- Shell developer/ADB settings are enabled on both. No enabled
|
||||
accessibility service was returned for user 10 on either device.
|
||||
|
||||
This is strong evidence against treating spoofed green/locked properties,
|
||||
PIF, or TEESimulator as prerequisites for the observed GOLDEN success.
|
||||
It does not prove that every path through either banking app is independent
|
||||
of attestation.
|
||||
|
||||
## Process-level observations and controlled experiment
|
||||
|
||||
Read `/proc/<Swissquote PID>/mountinfo` and selected maps using root, without
|
||||
attaching a debugger or modifying the banking process. Both mount tables
|
||||
lacked obvious Magisk/module mounts; neither selected maps output contained
|
||||
Magisk/Zygisk/LSPosed libraries. The injected `/system_ext/bin/magisk` was
|
||||
absent beneath each inspected process's `/proc/<PID>/root` view even though
|
||||
it exists in the root shell's view. This supports actual unmounting, beyond
|
||||
merely checking DenyList configuration. It **does not** prove absence of
|
||||
all detectable root signals, sockets, policy changes, or runtime effects.
|
||||
|
||||
Important real ROM difference: GOLDEN has no Yama ptrace_scope node; LAB
|
||||
has Yama enabled and scope 2. The local `apply_009_yama_ptrace.py` already
|
||||
documents that the AXP init rule was inert on GOLDEN's kernel.
|
||||
|
||||
At 15:22, changed **only** LAB's transient ptrace_scope from 2 to 0, cold
|
||||
started Swissquote in user 10, and observed `System.exit(-69)` again.
|
||||
Restored scope **2**, with readback. No persistent property was changed.
|
||||
Thus relaxing ptrace_scope alone does not fix the observed failure; this
|
||||
is not a blanket clearance of every kernel-hardening difference.
|
||||
|
||||
## Why GOLDEN passes: what is known and what remains open
|
||||
|
||||
GOLDEN's actual process environment satisfies Swissquote's startup checks
|
||||
despite unlocked/orange state, Magisk, and USB debugging. LAB triggers the
|
||||
privileged-access callback despite working basic DenyList unmounting.
|
||||
The APK and Island version are controlled; the operating system, kernel,
|
||||
SELinux policy and Magisk implementation are **not** controlled yet.
|
||||
|
||||
Leading branches for investigation, not established causes:
|
||||
|
||||
1. A Magisk 29 → 30.7 difference leaves a root-detection surface after
|
||||
unmounting. Upstream changed file injection, mounting and sepolicy-related
|
||||
handling; simply seeing the same DenyList checkboxes cannot exclude this.
|
||||
2. A ROM/kernel/SELinux difference is interpreted as privileged access by
|
||||
Talsec on Android 16. Yama scope alone has been tested unsuccessfully.
|
||||
3. App-visible package/file/policy differences or retained per-install state.
|
||||
Package listings from shell are not automatically app-visible listings.
|
||||
|
||||
Do not claim “Magisk 30.7 is broken” from upstream issue 9866: that report
|
||||
had **Enforce DenyList OFF**, unlike this LAB, and was closed not planned.
|
||||
Do not infer a specific Talsec false positive from unrelated hardware-
|
||||
attestation reports. Do not mask callbacks, patch Swissquote, or transfer
|
||||
GOLDEN banking data as a diagnostic shortcut.
|
||||
|
||||
## Prepared next discriminator — same build, without Magisk
|
||||
|
||||
The current artifact directory is:
|
||||
|
||||
`/srv/rom/artifacts/009-axp-los23-port/20261009T120025Z-axp-stage1-combo4-cve-hi-releasekeys/`
|
||||
|
||||
Unpacked both boot images with the host's magiskboot. Verified:
|
||||
|
||||
- `boot.img`: SHA256 `e5822d8e8cbb39fe74ca638cdfcd52cd66fc741d130bdf962f433ae4f16299d2`;
|
||||
ramdisk `cpio test` = **1**, Magisk patched.
|
||||
- `boot-make-out.img`: SHA256
|
||||
`6caceaca1e911d327e7ad86217cc6bedb8cd32fe690cadd11c9cedeed0440d1b`;
|
||||
ramdisk `cpio test` = **0**, stock/unpatched.
|
||||
- The extracted kernel SHA256 is identical in both:
|
||||
`e093a3c7edfb2560f109c0b3df68d2722a1e05f06357316d1278b8c0348e8f89`.
|
||||
|
||||
This prepares a **temporary LAB-only boot experiment**, subject to owner
|
||||
agreement and confirming the phone is still on this same build. No image
|
||||
has been booted or flashed in this investigation. Preserve data, vendor SPL,
|
||||
the active slot and the installed APK; do not reinstall/reset the app.
|
||||
Prefer booting the image temporarily if the bootloader supports that path.
|
||||
The stock image has not yet been boot-tested; a verified hash is not boot
|
||||
acceptance. Confirm actual absence of running Magisk after boot before
|
||||
interpreting the Swissquote result; the manager APK and historical `/data`
|
||||
files can remain, so this is not a clean factory/rootless baseline.
|
||||
|
||||
- If it starts, focus on active Magisk integration; consider a compatible,
|
||||
isolated comparison against GOLDEN's version, not a blind ship downgrade.
|
||||
- If it still exits, reclassify the failure before investigating ROM-visible
|
||||
surfaces. Rootless boot failure alone would not prove a Talsec bug.
|
||||
- Return to the installed boot and repeat the original failure for an A/B/A
|
||||
comparison. A process surviving briefly is not a usable-session PASS.
|
||||
|
||||
LAB became unavailable in ADB during investigation. On the next turn the
|
||||
owner confirmed it had been reconnected and authorized the remaining tests.
|
||||
USB enumeration then confirmed LAB was physically present at 3-5.3 as
|
||||
18d1:4ee1 (MTP only), while GOLDEN was 18d1:4ee2 (MTP + debug). The earlier
|
||||
user-facing assertion that LAB was disconnected was too broad: USB was
|
||||
present, but the ADB interface was unavailable. Requested that the owner
|
||||
enable USB debugging on LAB; do not mistake GOLDEN's ADB presence for LAB.
|
||||
|
||||
## Sources and audit trail
|
||||
|
||||
- Project: `009-handoff-20261008.md`, constitution, feature 009 spec,
|
||||
Magisk runbook, current tracked diff and new patch scripts. Relevant recent
|
||||
changes include user/release signing, AXP identity and vendor-SPL guard,
|
||||
user-build signature spoofing, cmdline permissions, AVB flags 0, malloc
|
||||
2-GiB regions, kernel ports/forward-only patching, Yama, USERFAULTFD off,
|
||||
Settings/privacy ports and two deblob waves. No existing work was reverted.
|
||||
- [Talsec callback API](https://docs.talsec.app/freerasp/freerasp/integration/flutter/api)
|
||||
- [Magisk 30.7 DenyList source](https://github.com/topjohnwu/Magisk/blob/v30.7/native/src/core/deny/utils.cpp)
|
||||
- [Magisk changelog](https://github.com/topjohnwu/Magisk/blob/v30.7/docs/changes.md)
|
||||
- [Magisk issue 9866, different enforcement configuration](https://github.com/topjohnwu/Magisk/issues/9866)
|
||||
- [Adjust upstream libsigner reference](https://github.com/adjust/android_sdk/issues/655)
|
||||
- Local scratch: `/tmp/axp-swissquote-20261009/`. Proprietary APKs and
|
||||
decompiled files remain outside the repository and must not be committed.
|
||||
Partial full-APK decompilation was replaced by targeted inspection.
|
||||
@@ -0,0 +1,35 @@
|
||||
# User zip audit — 20261007T022805Z (not flashed)
|
||||
|
||||
Build finished `exit=0` at 2026-10-07T02:29:08Z. Session `009-user-bacon`.
|
||||
Log: `/srv/rom/artifacts/detach-009-user-bacon-20261006T203157Z.log`.
|
||||
No LAB flash, no wipe.
|
||||
|
||||
Artifact: `/srv/rom/artifacts/009-axp-los23-port/20261007T022805Z-axp-stage1-combo4-cve-hi-releasekeys/`
|
||||
|
||||
Ship zip (same bytes as the lineage twin):
|
||||
|
||||
`AXP-Resurection-Pro-23.2-20261006-sunfish.zip`
|
||||
|
||||
SHA256 `7c70944614a1002e83bba7c38e1ef3ab8baeb56bfb3f60a474f59230575ea5a9`
|
||||
|
||||
## Checked on the host images
|
||||
|
||||
| Check | Result |
|
||||
|-------|--------|
|
||||
| Lunch | `ro.build.flavor=lineage_sunfish-user`, `ro.build.type=user`, `ro.debuggable=0` |
|
||||
| Version | `23.2-20261006-UNOFFICIAL-AXP-Resurection-Pro-sunfish`, track `pro` |
|
||||
| OTA cert | `O=AXP-Resurection, OU=sunfish-a16, CN=release` (not AOSP testkey) |
|
||||
| `ro.build.tags` | `dev-keys` (Lineage unofficial). GOLDEN official PRO is `release-keys` |
|
||||
| `ro.lineage.releasetype` | `UNOFFICIAL` |
|
||||
| AVB | project key SHA256_RSA4096, flags `3`, boot/dtbo/vendor/system chain verified |
|
||||
| Magisk | `magiskboot_cpio_test=1` on the shipped boot |
|
||||
| KernelSU | no KernelSU strings in `boot.img`; ship script refused `CONFIG_KSU=y` |
|
||||
| microG | `system_ext/app/GmsCore`, versionCode `252432034`, versionName `0.3.17.252432` |
|
||||
| Jelly / Gallery2 | **absent** on product. FossifyGallery present |
|
||||
| Kernel pack | Stage1 + combo4 + FORTIFY + CVE-clean 429 + hi-batch1–3 + Yama |
|
||||
|
||||
## Notes for the morning
|
||||
|
||||
- `dev-keys` is the unofficial Lineage tag. The zip itself is signed with the project release key.
|
||||
- Jelly and Gallery2 were already removed in the tree, and the ship script still fails the build if those APKs are present. This zip does not contain them.
|
||||
- Flash stays a separate decision. Sideload the AXP zip; a boot-only flash is not enough.
|
||||
@@ -8,7 +8,7 @@ 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 | Known-good vanilla artifact on host |
|
||||
| microG GmsCore | v0.3.16.252432 | | microG official (matches GitHub latest 2026-09-17) | newest_stable | 2026-09-16 staged | `/srv/rom/prebuilts/microg/` |
|
||||
| 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 |
|
||||
@@ -17,6 +17,7 @@ Never ship from stale laptop caches.
|
||||
| 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 | |
|
||||
| Seedvault | present on GOLDEN | | `com.stevesoltys.seedvault` | los_default / newest | 2026-09-17 seen | Often LOS-provides |
|
||||
| OpenEUICC | upstream 2026-09-28 | | `im.angry.openeuicc` `89d677e07d0e` | newest_upstream | 2026-10-07 wired | Privileged system_ext LPA, platform cert. Latest PeterCxy master, not the AXP fork (1.6.2 / GOLDEN 1.6.1). Datastore from LOS androidx. Deps `prebuilts/openeuicc-deps` @ `5402167` for zxing |
|
||||
| Island | deferred | | `com.oasisfeng.island.fdroid` | | | US3 deferred |
|
||||
|
||||
## Host artifact hashes (copy from host VERSIONS when refreshed)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Isolated AVB flags 0 candidate. Same ship pack as the release-key bacon.
|
||||
# WITH_AVB=true makes device/google/sunfish/BoardConfigLineage.mk skip
|
||||
# `--flags 3`. The project avb.pem and SHA256_RSA4096 stay.
|
||||
# Do not start this while another ninja owns /srv/rom/src/lineage/out.
|
||||
# Do not flash until the zip is checked and the owner asks. LAB only.
|
||||
set -euo pipefail
|
||||
export AXP_AVB_FLAGS=0
|
||||
export WITH_AVB=true
|
||||
exec "$(dirname "$0")/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh"
|
||||
@@ -1,9 +1,21 @@
|
||||
#!/bin/bash
|
||||
# 009 bacon — ship pack with project release keys (US-S2).
|
||||
# Stage1+combo4+FORTIFY + CVE clean + hi-batch1+2+3 + PRODUCT_DEFAULT_DEV_CERTIFICATE.
|
||||
# Requires $HOME/.android-certs/releasekey.{pk8,x509.pem}. userdebug.
|
||||
# CVE-2022-49980 ships as the lock-scope port; the AXP one-liner (uevent under
|
||||
# udc_lock) stays denylisted because it hung LAB at the Google logo.
|
||||
# Requires $HOME/.android-certs/releasekey.{pk8,x509.pem}.
|
||||
# Lunch is lineage_sunfish-user (same flavor GOLDEN reports). PRO is branding,
|
||||
# not a different lunch target.
|
||||
set -e
|
||||
cd /srv/rom/src/lineage
|
||||
# Default ship keeps vbmeta flags 3. The isolated flags-0 candidate sets
|
||||
# AXP_AVB_FLAGS=0, which exports WITH_AVB=true before lunch. That is the only
|
||||
# effect in device/google/sunfish/BoardConfigLineage.mk: skip `--flags 3`.
|
||||
AXP_AVB_FLAGS="${AXP_AVB_FLAGS:-3}"
|
||||
if [[ "$AXP_AVB_FLAGS" == "0" ]]; then
|
||||
export WITH_AVB=true
|
||||
fi
|
||||
echo "AXP_AVB_FLAGS=$AXP_AVB_FLAGS WITH_AVB=${WITH_AVB:-unset}"
|
||||
export HOME=/home/android-build
|
||||
export CCACHE_DIR=/srv/rom/ccache
|
||||
export USE_CCACHE=1
|
||||
@@ -20,7 +32,15 @@ exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
echo "=== START 009-bacon-axp-cve-hi-releasekeys $(date -Is) ==="
|
||||
|
||||
CERT_DIR="${ANDROID_CERTS_DIR:-$HOME/.android-certs}"
|
||||
if [[ -z "${ANDROID_CERTS_DIR:-}" ]]; then
|
||||
if [[ -f /srv/rom/secrets/android-certs/releasekey.pk8 ]]; then
|
||||
CERT_DIR=/srv/rom/secrets/android-certs
|
||||
else
|
||||
CERT_DIR="$HOME/.android-certs"
|
||||
fi
|
||||
else
|
||||
CERT_DIR="$ANDROID_CERTS_DIR"
|
||||
fi
|
||||
if [[ ! -f "$CERT_DIR/releasekey.pk8" || ! -f "$CERT_DIR/releasekey.x509.pem" ]]; then
|
||||
echo "FAIL: missing $CERT_DIR/releasekey.{pk8,x509.pem}" >&2
|
||||
exit 1
|
||||
@@ -50,6 +70,22 @@ chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
|
||||
|
||||
bash "$KH/apply_stage1_combo4.sh" "$LIVE"
|
||||
|
||||
# AXP hunks that no longer apply cleanly, ported onto this msm-4.14.
|
||||
# misc/0021 stays omitted: LOS already has its KSYM_NAME_LEN=192 result.
|
||||
# slab/0010 and slab/0018 are already covered by virt_to_cache() plus
|
||||
# CONFIG_SLAB_FREELIST_HARDENED.
|
||||
grep -q '^#define KSYM_NAME_LEN 192$' "$LIVE/include/linux/kallsyms.h" \
|
||||
|| { echo "FAIL: KSYM_NAME_LEN is not 192 after Stage1" >&2; exit 1; }
|
||||
for PORT in \
|
||||
"$KH/ports/allocsize-0007-vmalloc-alloc-size.patch" \
|
||||
"$KH/ports/ro-0007-0034-sysctl-read-only.patch"
|
||||
do
|
||||
test -f "$PORT"
|
||||
patch -d "$LIVE" -p1 --forward --dry-run < "$PORT"
|
||||
patch -d "$LIVE" -p1 --forward < "$PORT"
|
||||
echo "applied $(basename "$PORT")"
|
||||
done
|
||||
|
||||
# US-S5-1 — Yama + SLAB_FREELIST_RANDOM (Graphene/AXP defconfig gaps)
|
||||
chmod +x "$KH/apply_uss5_1_defconfig.sh" 2>/dev/null || true
|
||||
bash "$KH/apply_uss5_1_defconfig.sh" "$LIVE"
|
||||
@@ -67,6 +103,14 @@ for L in "$LIST_HI1" "$LIST_HI2" "$LIST_HI3"; do
|
||||
bash "$KH/apply_cve_strict.sh" "$LIVE"
|
||||
done
|
||||
|
||||
PORT49980="$KH/ports/cve-2022-49980-lock-scope.patch"
|
||||
test -f "$PORT49980"
|
||||
patch -d "$LIVE" -p1 --forward --dry-run < "$PORT49980"
|
||||
patch -d "$LIVE" -p1 --forward < "$PORT49980"
|
||||
grep -q 'device_add() emits a uevent' "$LIVE/drivers/usb/gadget/udc/core.c" \
|
||||
|| { echo "FAIL: CVE-2022-49980 lock-scope missing" >&2; exit 1; }
|
||||
echo "applied $(basename "$PORT49980")"
|
||||
|
||||
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
|
||||
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
|
||||
echo "FAIL: KernelSU in defconfig" >&2
|
||||
@@ -77,6 +121,12 @@ for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
|
||||
SECURITY_YAMA SLAB_FREELIST_RANDOM; do
|
||||
grep -q "^CONFIG_${k}=y" "$DEF" || { echo "FAIL missing $k" >&2; exit 1; }
|
||||
done
|
||||
grep -q '^# CONFIG_USERFAULTFD is not set$' "$DEF" \
|
||||
|| { echo "FAIL: USERFAULTFD still enabled (GOLDEN leaves it unset)" >&2; exit 1; }
|
||||
if grep -q '^CONFIG_USERFAULTFD=y$' "$DEF"; then
|
||||
echo "FAIL: CONFIG_USERFAULTFD=y" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
|
||||
grep -q "mmget_still_valid" "$LIVE/mm/madvise.c"
|
||||
echo "OK: Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3 markers present"
|
||||
@@ -85,13 +135,62 @@ rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
|
||||
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
|
||||
out/target/product/sunfish/kernel
|
||||
|
||||
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
|
||||
python3 vendor/sunfish-a16/patches/005-sigspoof/apply_005_sigspoof.py
|
||||
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py
|
||||
grep -q 'SUNFISH_A16_008_CALL_REC_FLAG_SECURE' \
|
||||
frameworks/base/core/java/android/provider/Settings.java
|
||||
grep -q 'extendrom_call_recording' \
|
||||
packages/apps/Dialer/java/com/android/incallui/call/CallRecorder.java
|
||||
grep -q 'extendrom_call_recording' \
|
||||
packages/apps/Settings/res/xml/development_settings.xml
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_unofficial_build_id.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_supl_privacy.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_axp_identity.py
|
||||
grep -q '^VENDOR_SECURITY_PATCH = 2023-09-05$' device/google/sunfish/device-common.mk
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_yama_ptrace.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_cmdline_mode.py
|
||||
grep -q 'CHECKCALL(chmod("/proc/cmdline", 0440));' \
|
||||
system/core/init/first_stage_init.cpp
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_a15_small.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_a15_reboot_timeouts.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_a15_settings_timeouts.py
|
||||
grep -q 'settings_reboot_after_timeout' \
|
||||
frameworks/base/core/java/android/provider/Settings.java
|
||||
grep -q 'isAllowedWhenLocked' \
|
||||
frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
|
||||
grep -q 'auto_reboot' packages/apps/Settings/res/xml/security_dashboard_settings.xml
|
||||
grep -q 'bluetooth_timeout' packages/apps/Settings/res/xml/connected_devices.xml
|
||||
grep -q 'wifi_timeout' packages/apps/Settings/res/xml/wifi_configure_settings.xml
|
||||
grep -q 'MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 5;' \
|
||||
frameworks/base/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
|
||||
grep -q 'final String buildSerial = Build.UNKNOWN;' \
|
||||
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
|
||||
grep -q 'AXP/Calyx 0042' \
|
||||
frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
|
||||
grep -q 'Serial number constraint not permitted' \
|
||||
bootable/recovery/install/install.cpp
|
||||
# openeuicc-deps 5402167 was generated against SDK 37. LOS 23.2 ships
|
||||
# prebuilts/sdk/37 without public/android.jar, so compile these prebuilt
|
||||
# libraries against 36 (Android 16).
|
||||
sed -i 's/^ sdk_version: "37",$/ sdk_version: "36",/' prebuilts/openeuicc-deps/Android.bp
|
||||
if grep -q 'sdk_version: "37"' prebuilts/openeuicc-deps/Android.bp; then
|
||||
echo "FAIL: openeuicc-deps still asks for SDK 37" >&2
|
||||
exit 1
|
||||
fi
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_openssh_packages.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_jelly_gallery2.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_euicc_google.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_carrier_google.py
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_lineage_tools.py
|
||||
# Wave 4 (diag/bpftool/imsrcsd): only when AXP_DEBLOB_WAVE4=1 (see 009-bacon-wave4-deblob.sh).
|
||||
AXP_DEBLOB_WAVE4="${AXP_DEBLOB_WAVE4:-0}"
|
||||
if [[ "$AXP_DEBLOB_WAVE4" == 1 ]]; then
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_lab_only_commands.py
|
||||
fi
|
||||
# Jelly stays (owner 2026-10-07). Gallery is FossifyGallery. Do not re-add
|
||||
# Gallery2. The product gate requires Jelly.apk and FossifyGallery, and
|
||||
# fails if Gallery2.apk is present.
|
||||
|
||||
# Host prebuilts → vendor/sunfish-a16/prebuilt (rsync --delete must not wipe these).
|
||||
mkdir -p vendor/sunfish-a16/prebuilt
|
||||
@@ -110,7 +209,7 @@ link_prebuilt FossifyGallery.apk /srv/rom/prebuilts/axp-apps/FossifyGallery.apk
|
||||
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
|
||||
"$AAPT" dump permissions vendor/sunfish-a16/prebuilt/Phonesky.apk 2>/dev/null | grep -q USE_EXACT_ALARM
|
||||
|
||||
# Early hook so LINEAGE_VERSION includes AXP-Resurection-Pro (before version.mk).
|
||||
# Early hook before version.mk (identity itself is apply_009_axp_identity.py).
|
||||
mkdir -p vendor/extra
|
||||
ln -sfn ../sunfish-a16/extra-product.mk vendor/extra/product.mk
|
||||
test -f vendor/extra/product.mk
|
||||
@@ -132,15 +231,27 @@ set +u
|
||||
# shellcheck disable=SC1091
|
||||
source build/envsetup.sh
|
||||
set -e
|
||||
# re-export after envsetup (some trees clear it)
|
||||
export PRODUCT_DEFAULT_DEV_CERTIFICATE="$CERT_DIR/releasekey"
|
||||
breakfast sunfish userdebug
|
||||
# re-export after envsetup (some trees clear it). signing.mk sets the same path.
|
||||
export PRODUCT_DEFAULT_DEV_CERTIFICATE=vendor/lineage-priv/keys/releasekey
|
||||
breakfast sunfish user
|
||||
test "${TARGET_RELEASE:-}" = "bp4a"
|
||||
BT_SEPOL=$(get_build_var MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES)
|
||||
echo "MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES=$BT_SEPOL"
|
||||
case "$BT_SEPOL" in
|
||||
*vendor/sunfish-a16/certs*) ;;
|
||||
*) echo "FAIL: BT sepolicy certs still AOSP default ($BT_SEPOL)" >&2; exit 1 ;;
|
||||
*vendor/lineage-priv/keys*) ;;
|
||||
*) echo "FAIL: BT sepolicy certs not vendor/lineage-priv/keys ($BT_SEPOL)" >&2; exit 1 ;;
|
||||
esac
|
||||
SYS_CERT=$(get_build_var DEFAULT_SYSTEM_DEV_CERTIFICATE)
|
||||
B_TYPE=$(get_build_var LINEAGE_BUILDTYPE)
|
||||
L_VER=$(get_build_var LINEAGE_VERSION)
|
||||
echo "DEFAULT_SYSTEM_DEV_CERTIFICATE=$SYS_CERT LINEAGE_BUILDTYPE=$B_TYPE LINEAGE_VERSION=$L_VER"
|
||||
case "$SYS_CERT" in
|
||||
vendor/lineage-priv/keys/releasekey) ;;
|
||||
*) echo "FAIL: system cert $SYS_CERT would tag dev-keys" >&2; exit 1 ;;
|
||||
esac
|
||||
[[ "$B_TYPE" == "PRO" ]] || { echo "FAIL: LINEAGE_BUILDTYPE=$B_TYPE (want PRO)" >&2; exit 1; }
|
||||
case "$L_VER" in
|
||||
*UNOFFICIAL*) echo "FAIL: version still UNOFFICIAL ($L_VER)" >&2; exit 1 ;;
|
||||
esac
|
||||
AVB_KEY=$(get_build_var BOARD_AVB_KEY_PATH)
|
||||
AVB_ALG=$(get_build_var BOARD_AVB_ALGORITHM)
|
||||
@@ -192,18 +303,18 @@ for f in vendor.img product.img system.img system_ext.img; do
|
||||
done
|
||||
# Keep make OUT boot for forensics only (often stock / non-Magisk).
|
||||
cp -v "$PROD/boot.img" "$DEST/boot-make-out.img" 2>/dev/null || true
|
||||
cp -v "$PROD"/lineage-*-UNOFFICIAL*.zip "$DEST/" 2>/dev/null || \
|
||||
cp -v "$PROD"/lineage-*-PRO-sunfish.zip "$DEST/" 2>/dev/null || \
|
||||
cp -v "$PROD"/lineage-*.zip "$DEST/" 2>/dev/null || true
|
||||
|
||||
# Publish AXP Resurection Pro ship name (hardlink/copy). Lineage bacon.mk still
|
||||
# emits lineage-*.zip; do not rebuild solely to rename.
|
||||
ZIP_LINEAGE=$(ls "$DEST"/lineage-*.zip 2>/dev/null | head -1 || true)
|
||||
if [[ -n "${ZIP_LINEAGE:-}" ]]; then
|
||||
# lineage-23.2-YYYYMMDD-UNOFFICIAL[-AXP-Resurection[-Pro]]-sunfish.zip
|
||||
# lineage-23.2-YYYYMMDD-PRO-sunfish.zip
|
||||
# → AXP-Resurection-Pro-23.2-YYYYMMDD-sunfish.zip
|
||||
ZIP_BASE=$(basename "$ZIP_LINEAGE")
|
||||
ZIP_AXP=$(echo "$ZIP_BASE" | sed -E \
|
||||
's/^lineage-([0-9]+\.[0-9]+)-([0-9]{8})(-[0-9]{6})?-UNOFFICIAL(-AXP-Resurection(-Pro)?)?-sunfish\.zip$/AXP-Resurection-Pro-\1-\2\3-sunfish.zip/')
|
||||
's/^lineage-([0-9]+\.[0-9]+)-([0-9]{8})(_[0-9]{6})?-PRO-sunfish\.zip$/AXP-Resurection-Pro-\1-\2\3-sunfish.zip/')
|
||||
if [[ "$ZIP_AXP" == "$ZIP_BASE" ]]; then
|
||||
ZIP_AXP="AXP-Resurection-Pro-${ZIP_BASE#lineage-}"
|
||||
fi
|
||||
@@ -243,7 +354,20 @@ rm -rf "$MB_TMP"
|
||||
[[ "$MB_EC" -ne 0 ]] || { echo "FAIL: ship boot is stock (magiskboot cpio test=$MB_EC); expected Magisk" >&2; exit 1; }
|
||||
echo "magiskboot_cpio_test=$MB_EC (non-zero => Magisk present)"
|
||||
|
||||
ZIP=$(ls "$DEST"/lineage-*-UNOFFICIAL*.zip 2>/dev/null | head -1 || true)
|
||||
for bp in "$PROD/system/build.prop" "$PROD/product/etc/build.prop"; do
|
||||
[[ -f "$bp" ]] || continue
|
||||
if grep -q '^ro\.[a-z.]*build\.tags=' "$bp" && ! grep -q '^ro\.[a-z.]*build\.tags=release-keys$' "$bp"; then
|
||||
echo "FAIL: $bp build.tags is not release-keys" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
grep -q '^ro.lineage.releasetype=PRO$' "$PROD/product/etc/build.prop" || {
|
||||
echo "FAIL: ro.lineage.releasetype is not PRO" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "identity OK (release-keys, ro.lineage.releasetype=PRO)"
|
||||
|
||||
ZIP=$(ls "$DEST"/lineage-*.zip 2>/dev/null | head -1 || true)
|
||||
if [[ -n "$ZIP" ]]; then
|
||||
unzip -p "$ZIP" META-INF/com/android/otacert 2>/dev/null | openssl x509 -noout -subject 2>/dev/null \
|
||||
| tee "$DEST/otacert-subject.txt" || true
|
||||
@@ -272,7 +396,7 @@ FLAGS=$(awk '/^Flags:/{print $2; exit}' "$DEST/vbmeta-info.txt")
|
||||
echo "avb_pubkey_sha1 artifact=$GOT project=$EXP flags=$FLAGS"
|
||||
[[ -n "$GOT" ]] || { echo "FAIL: could not parse vbmeta public key sha1" >&2; exit 1; }
|
||||
[[ "$GOT" == "$EXP" ]] || { echo "FAIL: vbmeta pubkey != project avb_pkmd.bin" >&2; exit 1; }
|
||||
[[ "$FLAGS" == "3" ]] || { echo "FAIL: expected vbmeta Flags 3 (Magisk track), got '$FLAGS'" >&2; exit 1; }
|
||||
[[ "$FLAGS" == "$AXP_AVB_FLAGS" ]] || { echo "FAIL: expected vbmeta Flags $AXP_AVB_FLAGS, got '$FLAGS'" >&2; exit 1; }
|
||||
|
||||
# Cryptographic check against TF sibling images in $DEST (hardlinked/copied).
|
||||
# Magisk boot footer is Algorithm NONE — chain trust is top-level vbmeta + project key.
|
||||
@@ -329,17 +453,78 @@ if ! debugfs -R "stat /etc/init/init.openssh.rc" "$PROD_RAW" 2>&1 | grep -q 'Ino
|
||||
fi
|
||||
echo "US-S4-1 product surface OK (no ssh/rsync bins; init.openssh.rc present)"
|
||||
|
||||
# US-S4-2 — AXP PRO: no Jelly / Gallery2 *apps* on product (Fossify stays).
|
||||
# Note: LOCAL_OVERRIDES removes Gallery2.apk but stale JNI symlink dirs under
|
||||
# product/app/Gallery2/lib can remain from older out/ — gate on the APK.
|
||||
for p in /app/Jelly/Jelly.apk /app/Gallery2/Gallery2.apk; do
|
||||
if debugfs -R "stat $p" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW"
|
||||
echo "FAIL: US-S4-2 expected absent $p on product.img" >&2
|
||||
# Lineage tools absent on GOLDEN. curl and vim stay.
|
||||
SYS_RAW=$(mktemp /tmp/axp-system-XXXX.img)
|
||||
SYSEXT_RAW=$(mktemp /tmp/axp-systemext-XXXX.img)
|
||||
"$SIMG" "$DEST/system.img" "$SYS_RAW"
|
||||
"$SIMG" "$DEST/system_ext.img" "$SYSEXT_RAW"
|
||||
if debugfs -R "stat /addon.d/50-lineage.sh" "$SYS_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
|
||||
echo "FAIL: expected system/addon.d/50-lineage.sh absent" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! debugfs -R "stat /bin/curl" "$SYS_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
|
||||
echo "FAIL: expected system/bin/curl present" >&2
|
||||
exit 1
|
||||
fi
|
||||
for p in /bin/bash /bin/htop /bin/nano /bin/unrar; do
|
||||
if debugfs -R "stat $p" "$SYSEXT_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
|
||||
echo "FAIL: expected system_ext$p absent" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
# Directory-only Gallery2 leftover (jni symlinks) is not a shipped app — warn.
|
||||
if [[ "$AXP_DEBLOB_WAVE4" == 1 ]]; then
|
||||
for p in /bin/bpftool /bin/alloctop /bin/pbtombstone /bin/apexd-lsof.sh; do
|
||||
if debugfs -R "stat $p" "$SYS_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
|
||||
echo "FAIL: expected system$p absent" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if ! debugfs -R "stat /bin/vim" "$SYSEXT_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
|
||||
echo "FAIL: expected system_ext/bin/vim present" >&2
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$SYS_RAW" "$SYSEXT_RAW"
|
||||
echo "lineage tools surface OK (no bash/htop/nano/unrar/addon.d; curl+vim kept)"
|
||||
if [[ "$AXP_DEBLOB_WAVE4" == 1 ]]; then
|
||||
VENDOR_RAW=$(mktemp /tmp/axp-vendor-XXXX.img)
|
||||
"$SIMG" "$DEST/vendor.img" "$VENDOR_RAW"
|
||||
for p in /bin/cnss_diag /bin/diag_klog /bin/diag_mdlog /bin/diag_socket_log /bin/diag_uart_log /bin/ssr_diag /bin/test_diag /bin/imsrcsd /bin/disable_configstore /etc/init/imsrcsd.rc; do
|
||||
if debugfs -R "stat $p" "$VENDOR_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW" "$VENDOR_RAW"
|
||||
echo "FAIL: expected vendor$p absent" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
for p in /bin/loc_launcher /bin/lowi-server /bin/xtra-daemon; do
|
||||
if ! debugfs -R "stat $p" "$VENDOR_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW" "$VENDOR_RAW"
|
||||
echo "FAIL: expected vendor$p present" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
rm -f "$VENDOR_RAW"
|
||||
echo "lab-only commands surface OK (diag/imsrcsd/debug tools gone; GPS daemons kept)"
|
||||
fi
|
||||
|
||||
# US-S4-2 — Jelly stays. FossifyGallery stays; Gallery2 does not.
|
||||
# Stale JNI symlink dirs under product/app/Gallery2/lib can remain from
|
||||
# older out/ — gate Gallery2 on the APK.
|
||||
if ! debugfs -R "stat /app/Jelly/Jelly.apk" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW"
|
||||
echo "FAIL: US-S4-2 expected Jelly.apk on product.img" >&2
|
||||
exit 1
|
||||
fi
|
||||
if debugfs -R "stat /app/Gallery2/Gallery2.apk" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
rm -f "$PROD_RAW"
|
||||
echo "FAIL: US-S4-2 expected Gallery2.apk absent on product.img" >&2
|
||||
exit 1
|
||||
fi
|
||||
if debugfs -R "stat /app/Gallery2" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
|
||||
echo "WARN: US-S4-2 /app/Gallery2 dir still present (likely stale jni symlinks; no APK)"
|
||||
fi
|
||||
@@ -349,20 +534,27 @@ if ! debugfs -R "stat /app/FossifyGallery" "$PROD_RAW" 2>&1 | grep -q 'Inode:';
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$PROD_RAW"
|
||||
echo "US-S4-2 product surface OK (no Jelly/Gallery2 APK; FossifyGallery present)"
|
||||
echo "US-S4-2 product surface OK (Jelly + FossifyGallery; no Gallery2 APK)"
|
||||
|
||||
{
|
||||
echo "artifact=$DEST"
|
||||
echo "list_clean=$LIST_CLEAN (429)"
|
||||
echo "list_hi=batch1+2+3"
|
||||
echo "hardening=Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3 + US-S4-1-no-openssh + US-S4-2-no-jelly-gallery2 + US-S5-1-yama-slabrand"
|
||||
echo "kernel_stage1=30 (misc/0021 omitted; base KSYM_NAME_LEN=192 retained)"
|
||||
echo "hardening=Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3 + US-S4-1-no-openssh + US-S4-2-jelly-fossify + US-S5-1-yama-slabrand + CVE-2022-49980-lock-scope"
|
||||
echo "vendor_security_patch=2023-09-05"
|
||||
echo "branding=AXP-Resurection-Pro"
|
||||
echo "signing=$PRODUCT_DEFAULT_DEV_CERTIFICATE"
|
||||
echo "avb_infra=$AVB_KEY alg=$AVB_ALG flags=3 (HASHTREE_DISABLED|VERIFICATION_DISABLED)"
|
||||
echo "avb_infra=$AVB_KEY alg=$AVB_ALG flags=$AXP_AVB_FLAGS"
|
||||
echo "avb_runtime_enforce=no (unlocked Magisk track; not AVB-hardened boot)"
|
||||
echo "avb_pubkey_sha1=$GOT"
|
||||
echo "magiskboot_cpio_test=$MB_EC"
|
||||
echo "ship_images=target_files/IMAGES (not OUT make images)"
|
||||
if [[ "$AXP_DEBLOB_WAVE4" == 1 ]]; then
|
||||
echo "deblob=wave1-euicc-google+wave2-carrier-google-and-aosp-rcs+wave3-lineage-tools+wave4-lab-only-commands"
|
||||
else
|
||||
echo "deblob=wave1-euicc-google+wave2-carrier-google-and-aosp-rcs+wave3-lineage-tools"
|
||||
fi
|
||||
echo "ship_zip_brand=AXP-Resurection-Pro-*-sunfish.zip (sibling of lineage-*.zip; not slim)"
|
||||
echo "flash=sideload AXP-Resurection-Pro zip (or lineage twin); boot-only insufficient"
|
||||
echo "recover=20261002T085915Z-axp-stage1-combo4-cve-hi-releasekeys"
|
||||
@@ -375,6 +567,20 @@ echo "US-S4-2 product surface OK (no Jelly/Gallery2 APK; FossifyGallery present)
|
||||
lineage-*.zip AXP-Resurection-Pro-*.zip 2>/dev/null || true) \
|
||||
| tee "$DEST/SHA256SUMS"
|
||||
|
||||
# AXP publishes a hash file next to the zip and a detached signature of that
|
||||
# file. Sign only when this user already has a secret key.
|
||||
shopt -s nullglob
|
||||
for pro in "$DEST"/AXP-Resurection-Pro-*.zip; do
|
||||
(cd "$DEST" && sha512sum "$(basename "$pro")") | tee "$pro.sha512sum"
|
||||
if gpg --batch --list-secret-keys 2>/dev/null | grep -q '^sec'; then
|
||||
gpg --batch --yes --detach-sign --armor -o "$pro.sha512sum.asc" "$pro.sha512sum" \
|
||||
|| echo "WARN: gpg detach-sign failed for $pro.sha512sum"
|
||||
else
|
||||
echo "SKIP: no GPG secret key; left $pro.sha512sum unsigned"
|
||||
fi
|
||||
done
|
||||
shopt -u nullglob
|
||||
|
||||
echo "FLASH_READY $DEST"
|
||||
echo "=== DONE $DEST $(date -Is) ==="
|
||||
exit 0
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# 012 bacon: current ship pack plus Graphene hardened_malloc.
|
||||
# Compiles libc first. The full bacon runs only if that link succeeds.
|
||||
# Does not change AVB flags or run the userspace deblob.
|
||||
set -e
|
||||
cd /srv/rom/src/lineage
|
||||
export HOME=/home/android-build
|
||||
export CCACHE_DIR=/srv/rom/ccache
|
||||
export USE_CCACHE=1
|
||||
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
|
||||
unset OUT_DIR || true
|
||||
|
||||
python3 vendor/sunfish-a16/patches/012-hardened-malloc/apply_012_hardened_malloc.py
|
||||
grep -q 'SUNFISH_A16_012_HARDENED_MALLOC' bionic/libc/Android.bp
|
||||
grep -q 'libhardened_malloc' bionic/libc/Android.bp
|
||||
grep -q 'CONFIG_CLASS_REGION_SIZE=2147483648' external/hardened_malloc/Android.bp
|
||||
grep -A1 '__attribute__((noinline))$' bionic/libc/bionic/libc_init_dynamic.cpp | grep -q '__libc_preinit_impl'
|
||||
test -f external/hardened_malloc/h_malloc.c
|
||||
|
||||
set +u
|
||||
# shellcheck disable=SC1091
|
||||
source build/envsetup.sh
|
||||
set -e
|
||||
export PRODUCT_DEFAULT_DEV_CERTIFICATE=vendor/lineage-priv/keys/releasekey
|
||||
breakfast sunfish user
|
||||
echo "=== m libc $(date -Is) ==="
|
||||
m libc -j8
|
||||
echo "=== libc ok, starting bacon $(date -Is) ==="
|
||||
exec /srv/rom/artifacts/bin/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
# Wave 4 deblob candidate: closure pack plus diag/bpftool/imsrcsd drops.
|
||||
# Same AVB flags-0 track as the booted closure baseline. Do not flash until
|
||||
# checked. LAB only. Do not run while another ninja owns out/.
|
||||
set -euo pipefail
|
||||
export AXP_AVB_FLAGS=0
|
||||
export WITH_AVB=true
|
||||
export AXP_DEBLOB_WAVE4=1
|
||||
exec "$(dirname "$0")/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh"
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read only Swissquote's RASP exception classification; never dump reports.
|
||||
|
||||
Requires an already authorized adb shell su. Does not launch, stop, modify,
|
||||
instrument, or grant root to Swissquote. Raw report data stays in memory.
|
||||
APK/report/session identifiers and other application data are not emitted.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
|
||||
def classifications(value):
|
||||
if isinstance(value, dict):
|
||||
exception = value.get("exception")
|
||||
if isinstance(exception, dict):
|
||||
reason = exception.get("reason", "")
|
||||
if (exception.get("type") == "com.swissquote.android.FreeRaspException"
|
||||
and isinstance(reason, str) and reason.startswith("freeRASP: on")):
|
||||
yield reason
|
||||
for child in value.values():
|
||||
yield from classifications(child)
|
||||
elif isinstance(value, list):
|
||||
for child in value:
|
||||
yield from classifications(child)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--device", required=True, choices=["LAB", "GOLDEN"])
|
||||
args = parser.parse_args()
|
||||
serial = {"LAB": "11231JEC207477", "GOLDEN": "11071JEC203839"}[args.device]
|
||||
base = "/data/user/10/com.swissquote.android/files/.crashlytics.v3/com.swissquote.android"
|
||||
|
||||
def shell(command):
|
||||
return subprocess.check_output(
|
||||
["adb", "-s", serial, "shell", "su", "-c", "'" + command + "'"],
|
||||
text=True, timeout=15,
|
||||
)
|
||||
|
||||
results = []
|
||||
for path in shell("find " + base + " -maxdepth 4 -type f").splitlines():
|
||||
# Never interpolate arbitrary output into a shell command.
|
||||
if not path.startswith(base + "/") or any(c not in
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/._:-" for c in path):
|
||||
continue
|
||||
if not any(part in path for part in ("/event", "/reports/", "/priority-reports/")):
|
||||
continue
|
||||
size = shell("stat -c %s " + path).strip()
|
||||
if not size.isdigit() or int(size) > 4 * 1024 * 1024:
|
||||
continue
|
||||
try:
|
||||
value = json.loads(shell("cat " + path))
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
results.extend(classifications(value))
|
||||
print(json.dumps({"device": args.device, "profile": 10,
|
||||
"rasp_exceptions": results,
|
||||
"note": "Retained events only; absence is not proof of PASS."}, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -410,14 +410,16 @@ work when published.
|
||||
equivalent — not an unrelated Graphene A13 / stock A13 prebuilt), with
|
||||
**attributable kernel hardening** ported from AXP / Graphene / Divest where
|
||||
still valid on non-GKI 4.14. Skipped items MUST be documented with rationale.
|
||||
- **FR-018**: *(Deferred research — look later)* Compare how AXP/GOLDEN
|
||||
hardens or constrains **localization / location** (network location,
|
||||
backends, permissions, privacy toggles) versus stock LineageOS 23 behaviour.
|
||||
Owner historically finds AXP aggressive here; do **not** freeze a
|
||||
clone-or-LOS policy in this feature’s acceptance until that comparison
|
||||
exists. Default until then: do not silently invent extra location lockdown
|
||||
beyond what the rest of the AXP clone already implies; record the open
|
||||
question in the capability matrix.
|
||||
- **FR-018**: Location uses the **LineageOS 23.2** stack so a fix stays
|
||||
accurate and fast. Do **not** port AXP's location lockdown and do **not**
|
||||
delete the IZat / XTRA / xtwifi blobs. SUPL stays available. A settings
|
||||
toggle may force it off and defaults off. A second settings toggle,
|
||||
default off, stops LPP (LPPE rides that session), the framework XTRA
|
||||
download, and `loc_launcher` (xtra-daemon, lowi, IZat) without removing
|
||||
the binaries. The GNSS stack never sends IMSI or the phone number to a
|
||||
SUPL server. GNSS NTP is `2.android.pool.ntp.org`. Degoogle elsewhere
|
||||
where a free replacement keeps the function; microG and the
|
||||
project-patched Play Store stay.
|
||||
- **FR-019**: On a **final** shipping `009` image, owner MUST be able to
|
||||
refresh bundled **system / privileged** packages by **sideload** (or the
|
||||
documented Magisk-equivalent path) roughly every **2–3 months**, without
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
"""A16 fixups keep the AXP reboot and QS-tile behavior."""
|
||||
import importlib.util
|
||||
import pathlib
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||
APPLIER = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_a15_reboot_timeouts.py"
|
||||
)
|
||||
|
||||
|
||||
def load():
|
||||
spec = importlib.util.spec_from_file_location("apply_009_a15_reboot_timeouts", APPLIER)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class RebootFixupTest(unittest.TestCase):
|
||||
def test_a16_anchors(self) -> None:
|
||||
src = load()
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tree = pathlib.Path(tmp)
|
||||
xml = tree / "systemui.xml"
|
||||
keyguard = tree / "KeyguardViewMediator.java"
|
||||
tile = tree / "QSTileImpl.java"
|
||||
xml.write_text(src.XML_OLD)
|
||||
keyguard.write_text(src.KEYGUARD_OLD)
|
||||
tile.write_text(src.IMPORT_OLD)
|
||||
src.replace_once(xml, src.XML_OLD, src.XML_NEW)
|
||||
src.replace_once(keyguard, src.KEYGUARD_OLD, src.KEYGUARD_NEW)
|
||||
src.replace_once(tile, src.IMPORT_OLD, src.IMPORT_NEW)
|
||||
src.replace_once(xml, src.XML_OLD, src.XML_NEW)
|
||||
self.assertIn("android.permission.REBOOT", xml.read_text())
|
||||
self.assertIn(
|
||||
"sendUserPresentBroadcast();\n cancelDoRebootForOwnerAfterTimeoutIfEnabled();",
|
||||
keyguard.read_text(),
|
||||
)
|
||||
self.assertIn("KeyguardStateController", tile.read_text())
|
||||
self.assertNotIn("QsInCompose", tile.read_text())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
"""The A15 small-port applier matches AXP behavior and the A16 return type."""
|
||||
import importlib.util
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||
APPLIER = ROOT / "vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_a15_small.py"
|
||||
|
||||
|
||||
def load_applier():
|
||||
spec = importlib.util.spec_from_file_location("apply_009_a15_small", APPLIER)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def write(path: pathlib.Path, text: str) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(text)
|
||||
|
||||
|
||||
class A15SmallPortTest(unittest.TestCase):
|
||||
def test_applier_on_a16_fixtures(self) -> None:
|
||||
src = load_applier()
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tree = pathlib.Path(tmp)
|
||||
write(
|
||||
tree / "frameworks/base/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java",
|
||||
src.LOCKOUT_OLD + "\n",
|
||||
)
|
||||
write(
|
||||
tree / "frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java",
|
||||
src.SERIAL_OLD,
|
||||
)
|
||||
write(
|
||||
tree / "frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java",
|
||||
"import android.os.Build;\nimport java.time.format.DateTimeFormatter;\n"
|
||||
+ src.EXIF_OLD,
|
||||
)
|
||||
write(
|
||||
tree / "bootable/recovery/install/install.cpp",
|
||||
src.RECOVERY_OLD,
|
||||
)
|
||||
env = os.environ.copy()
|
||||
env["SUNFISH_A16_TREE"] = str(tree)
|
||||
subprocess.check_call(["python3", str(APPLIER)], env=env)
|
||||
subprocess.check_call(["python3", str(APPLIER)], env=env)
|
||||
lockout = (tree / "frameworks/base/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java").read_text()
|
||||
ams = (tree / "frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java").read_text()
|
||||
exif = (tree / "frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java").read_text()
|
||||
recovery = (tree / "bootable/recovery/install/install.cpp").read_text()
|
||||
self.assertIn("MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 5", lockout)
|
||||
self.assertNotIn("= 20", lockout)
|
||||
self.assertIn("final String buildSerial = Build.UNKNOWN;", ams)
|
||||
self.assertNotIn("sTheRealBuildSerial :", ams)
|
||||
self.assertNotIn("TAG_SOFTWARE", exif)
|
||||
self.assertNotIn("TAG_DATETIME_ORIGINAL", exif)
|
||||
self.assertNotIn("import android.os.Build;", exif)
|
||||
self.assertNotIn("DateTimeFormatter", exif)
|
||||
self.assertIn("TAG_IMAGE_WIDTH", exif)
|
||||
self.assertIn("Serial number constraint not permitted", recovery)
|
||||
self.assertIn("return false;", recovery)
|
||||
self.assertNotIn("return INSTALL_ERROR", recovery)
|
||||
self.assertNotIn("serial_number_match", recovery)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,406 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regression guards for the next 009 release-key bacon."""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
KH = ROOT / "vendor/sunfish-a16/patches/009-kernel-hardening"
|
||||
SHIP = ROOT / "scripts/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh"
|
||||
|
||||
|
||||
class NextBaconGuardsTest(unittest.TestCase):
|
||||
def test_stage1_refuses_reverse_application(self) -> None:
|
||||
apply_script = (KH / "apply_stage1.sh").read_text()
|
||||
self.assertIn('args=(-p1 --batch --forward -d "$KERNEL_DIR")', apply_script)
|
||||
|
||||
def test_stage1_does_not_revert_existing_clang_cfi_kallsyms_fix(self) -> None:
|
||||
stage1 = (KH / "stage1.list").read_text().splitlines()
|
||||
self.assertNotIn(
|
||||
"0008-Graphene-Kernel_Hardening-misc/4.14/0021.patch",
|
||||
stage1,
|
||||
)
|
||||
|
||||
def test_default_avb_flags_stay_3_and_zero_is_isolated(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
wrapper = (ROOT / "scripts/009-bacon-avb-flags0.sh").read_text()
|
||||
self.assertIn('AXP_AVB_FLAGS="${AXP_AVB_FLAGS:-3}"', ship)
|
||||
self.assertIn('[[ "$FLAGS" == "$AXP_AVB_FLAGS" ]]', ship)
|
||||
self.assertNotIn('expected vbmeta Flags 3', ship)
|
||||
self.assertIn("export AXP_AVB_FLAGS=0", wrapper)
|
||||
self.assertIn("export WITH_AVB=true", wrapper)
|
||||
|
||||
def test_ship_script_applies_axp_timeouts(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
self.assertIn("apply_009_a15_reboot_timeouts.py", ship)
|
||||
self.assertIn("apply_009_a15_settings_timeouts.py", ship)
|
||||
self.assertLess(
|
||||
ship.index("apply_009_a15_reboot_timeouts.py"),
|
||||
ship.index("apply_009_a15_settings_timeouts.py"),
|
||||
)
|
||||
self.assertIn("settings_reboot_after_timeout", ship)
|
||||
self.assertIn("isAllowedWhenLocked", ship)
|
||||
self.assertIn("wifi_timeout", ship)
|
||||
|
||||
def test_ship_script_hides_proc_cmdline(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
self.assertIn("apply_009_cmdline_mode.py", ship)
|
||||
self.assertIn('CHECKCALL(chmod("/proc/cmdline", 0440));', ship)
|
||||
|
||||
def test_ship_script_reapplies_complete_feature_008(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
self.assertIn(
|
||||
"python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/"
|
||||
"apply_008_call_rec_flag_secure.py",
|
||||
ship,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/"
|
||||
"apply_wms_disable_secure_debuggable_gate.py",
|
||||
ship,
|
||||
)
|
||||
|
||||
def test_ship_script_gates_kallsyms_and_call_recording(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
self.assertIn("#define KSYM_NAME_LEN 192", ship)
|
||||
self.assertIn("SUNFISH_A16_008_CALL_REC_FLAG_SECURE", ship)
|
||||
self.assertIn("extendrom_call_recording", ship)
|
||||
|
||||
def test_artifact_metadata_names_49980_and_vendor_spl(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
self.assertIn(
|
||||
"CVE-2022-49980-lock-scope",
|
||||
ship,
|
||||
)
|
||||
self.assertIn(
|
||||
"vendor_security_patch=2023-09-05",
|
||||
ship,
|
||||
)
|
||||
|
||||
def test_euicc_google_is_stripped_from_the_vendor_makefile(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
apply = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_euicc_google.py"
|
||||
).read_text()
|
||||
apps = (ROOT / "vendor/sunfish-a16/apps.mk").read_text()
|
||||
self.assertIn("apply_009_drop_euicc_google.py", ship)
|
||||
self.assertIn('"EuiccGoogle"', apply)
|
||||
self.assertIn('"EuiccGoogleOverlay"', apply)
|
||||
self.assertIn("EuiccSupportPixel", apply)
|
||||
self.assertNotIn("filter-out EuiccGoogle", apps)
|
||||
|
||||
def test_carrier_google_is_stripped_where_it_is_added(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
apply = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_carrier_google.py"
|
||||
).read_text()
|
||||
self.assertIn("apply_009_drop_carrier_google.py", ship)
|
||||
for name in (
|
||||
"CarrierServices",
|
||||
"CarrierSettings",
|
||||
"CarrierWifi",
|
||||
"CarrierSetup",
|
||||
"RilConfigService",
|
||||
"grilservice",
|
||||
"ImsServiceEntitlement",
|
||||
"PresencePolling",
|
||||
"RcsService",
|
||||
):
|
||||
self.assertIn(f'"{name}"', apply)
|
||||
for name in ("EuiccSupportPixel", "ims", "atfwd", "qcrilmsgtunnel", "Dialer"):
|
||||
self.assertIn(f'"{name}"', apply)
|
||||
|
||||
def test_carrier_drop_removes_package_lines_and_is_idempotent(self) -> None:
|
||||
apply = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_carrier_google.py"
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
top = Path(tmp)
|
||||
vendor = top / "vendor/google/sunfish"
|
||||
device = top / "device/google/sunfish"
|
||||
product = top / "build/make/target/product"
|
||||
for directory in (vendor, device, product):
|
||||
directory.mkdir(parents=True)
|
||||
(vendor / "sunfish-vendor.mk").write_text(
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" CarrierServices \\\n"
|
||||
" CarrierSettings \\\n"
|
||||
" CarrierWifi \\\n"
|
||||
" CarrierSetup \\\n"
|
||||
" EuiccSupportPixel \\\n"
|
||||
" EuiccSupportPixelPermissions \\\n"
|
||||
" ims \\\n"
|
||||
" QtiTelephonyService \\\n"
|
||||
" atfwd \\\n"
|
||||
" qcrilmsgtunnel \\\n"
|
||||
" uceShimService \\\n"
|
||||
" RilConfigService \\\n"
|
||||
" grilservice \\\n"
|
||||
" vendor/google/sunfish/proprietary/product/etc/CarrierSettings/vodafone_de.pb:$(TARGET_COPY_OUT_PRODUCT)/etc/CarrierSettings/vodafone_de.pb \\\n"
|
||||
)
|
||||
(device / "device-lineage.mk").write_text(
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" GoogleParts\n"
|
||||
"\n"
|
||||
"# RCS\n"
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" PresencePolling \\\n"
|
||||
" RcsService\n"
|
||||
"\n"
|
||||
"# Touch\n"
|
||||
)
|
||||
(product / "telephony_product.mk").write_text(
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" Dialer \\\n"
|
||||
" ImsServiceEntitlement \\\n"
|
||||
" preinstalled-packages-platform-telephony-product.xml\n"
|
||||
)
|
||||
env = {**os.environ, "ANDROID_BUILD_TOP": str(top)}
|
||||
first = subprocess.run(
|
||||
["python3", str(apply)], cwd=top, env=env, check=False, text=True, capture_output=True
|
||||
)
|
||||
self.assertEqual(0, first.returncode, first.stderr)
|
||||
self.assertIn("dropped", first.stdout)
|
||||
vendor_text = (vendor / "sunfish-vendor.mk").read_text()
|
||||
self.assertNotIn("CarrierServices", vendor_text)
|
||||
self.assertIn("vodafone_de.pb", vendor_text)
|
||||
self.assertIn(" ims \\\n", vendor_text)
|
||||
lineage_text = (device / "device-lineage.mk").read_text()
|
||||
self.assertNotIn("RcsService", lineage_text)
|
||||
self.assertNotIn("PRODUCT_PACKAGES += \\\n\n", lineage_text)
|
||||
self.assertIn("GoogleParts", lineage_text)
|
||||
tele_text = (product / "telephony_product.mk").read_text()
|
||||
self.assertNotIn("ImsServiceEntitlement", tele_text)
|
||||
self.assertIn("Dialer", tele_text)
|
||||
second = subprocess.run(
|
||||
["python3", str(apply)], cwd=top, env=env, check=False, text=True, capture_output=True
|
||||
)
|
||||
self.assertEqual(0, second.returncode, second.stderr)
|
||||
self.assertIn("already", second.stdout)
|
||||
|
||||
def test_lineage_tools_are_stripped_where_they_are_added(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
apply = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_lineage_tools.py"
|
||||
)
|
||||
text = apply.read_text()
|
||||
self.assertIn("apply_009_drop_lineage_tools.py", ship)
|
||||
for name in ("bash", "htop", "nano", "50-lineage.sh", "unrar"):
|
||||
self.assertIn(f'"{name}"', text)
|
||||
for name in ("curl", "vim", "zstd", "nano_recovery"):
|
||||
self.assertIn(f'"{name}"', text)
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
top = Path(tmp)
|
||||
common = top / "vendor/lineage/config"
|
||||
common.mkdir(parents=True)
|
||||
(common / "common.mk").write_text(
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" 50-lineage.sh\n"
|
||||
"\n"
|
||||
"PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \\\n"
|
||||
" system/addon.d/50-lineage.sh\n"
|
||||
"\n"
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" bash \\\n"
|
||||
" curl \\\n"
|
||||
" getcap \\\n"
|
||||
" htop \\\n"
|
||||
" nano \\\n"
|
||||
" setcap \\\n"
|
||||
" vim\n"
|
||||
"\n"
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" nano_recovery\n"
|
||||
)
|
||||
(common / "common_mobile_full.mk").write_text(
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" unrar \\\n"
|
||||
" zstd\n"
|
||||
)
|
||||
env = {**os.environ, "ANDROID_BUILD_TOP": str(top)}
|
||||
first = subprocess.run(
|
||||
["python3", str(apply)],
|
||||
cwd=top,
|
||||
env=env,
|
||||
check=False,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
self.assertEqual(0, first.returncode, first.stderr)
|
||||
self.assertIn("dropped", first.stdout)
|
||||
common_text = (common / "common.mk").read_text()
|
||||
self.assertNotIn("\n bash \\\n", common_text)
|
||||
self.assertNotIn("\n htop \\\n", common_text)
|
||||
self.assertNotIn("\n nano \\\n", common_text)
|
||||
self.assertNotIn(" 50-lineage.sh\n", common_text)
|
||||
self.assertIn("system/addon.d/50-lineage.sh", common_text)
|
||||
self.assertIn(" curl \\\n", common_text)
|
||||
self.assertIn(" vim\n", common_text)
|
||||
self.assertIn(" nano_recovery\n", common_text)
|
||||
full_text = (common / "common_mobile_full.mk").read_text()
|
||||
self.assertNotIn("unrar", full_text)
|
||||
self.assertIn(" zstd\n", full_text)
|
||||
second = subprocess.run(
|
||||
["python3", str(apply)],
|
||||
cwd=top,
|
||||
env=env,
|
||||
check=False,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
self.assertEqual(0, second.returncode, second.stderr)
|
||||
self.assertIn("already", second.stdout)
|
||||
|
||||
def test_lab_only_commands_drop_keeps_gps_and_host_pbtombstone(self) -> None:
|
||||
ship = SHIP.read_text()
|
||||
apply = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_lab_only_commands.py"
|
||||
)
|
||||
text = apply.read_text()
|
||||
self.assertIn("apply_009_drop_lab_only_commands.py", ship) # may be commented (wave 4 deferred)
|
||||
for name in (
|
||||
"cnss_diag",
|
||||
"diag_mdlog",
|
||||
"imsrcsd",
|
||||
"disable_configstore",
|
||||
"bpftool",
|
||||
"alloctop",
|
||||
"apexd-lsof.sh",
|
||||
):
|
||||
self.assertIn(name, text)
|
||||
for name in ("loc_launcher", "lowi-server", "xtra-daemon"):
|
||||
self.assertIn(f'"{name}"', text)
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
top = Path(tmp)
|
||||
vendor = top / "vendor/google/sunfish"
|
||||
device = top / "device/google/sunfish"
|
||||
product = top / "build/make/target/product"
|
||||
bpf = top / "system/bpf/loader"
|
||||
dump = top / "frameworks/native/cmds/dumpstate"
|
||||
apex = top / "system/apex/apexd"
|
||||
for directory in (vendor, device, product, bpf, dump, apex):
|
||||
directory.mkdir(parents=True)
|
||||
(vendor / "sunfish-vendor.mk").write_text(
|
||||
"PRODUCT_COPY_FILES += \\\n"
|
||||
" vendor/google/sunfish/proprietary/vendor/etc/init/imsrcsd.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/imsrcsd.rc \\\n"
|
||||
" vendor/google/sunfish/proprietary/vendor/etc/init/imsdatadaemon.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/imsdatadaemon.rc \\\n"
|
||||
"\n"
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" cnss-daemon \\\n"
|
||||
" cnss_diag \\\n"
|
||||
" diag_klog \\\n"
|
||||
" diag_mdlog \\\n"
|
||||
" diag_socket_log \\\n"
|
||||
" diag_uart_log \\\n"
|
||||
" ims_rtp_daemon \\\n"
|
||||
" imsdatadaemon \\\n"
|
||||
" imsqmidaemon \\\n"
|
||||
" imsrcsd \\\n"
|
||||
" loc_launcher \\\n"
|
||||
" lowi-server \\\n"
|
||||
" ssr_diag \\\n"
|
||||
" test_diag \\\n"
|
||||
" xtra-daemon\n"
|
||||
)
|
||||
(device / "device-lineage.mk").write_text(
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" chre_daemon_msm\n"
|
||||
"\n"
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" disable_configstore\n"
|
||||
)
|
||||
(product / "base_system.mk").write_text(
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
" logcat \\\n"
|
||||
" pbtombstone \\\n"
|
||||
" ping\n"
|
||||
"\n"
|
||||
"PRODUCT_HOST_PACKAGES += \\\n"
|
||||
" fastboot \\\n"
|
||||
" pbtombstone \\\n"
|
||||
" sqlite3\n"
|
||||
)
|
||||
(bpf / "Android.bp").write_text(
|
||||
'rust_binary {\n'
|
||||
' name: "bpfloader",\n'
|
||||
' required: [\n'
|
||||
' "bpftool",\n'
|
||||
' "timeInState.bpf",\n'
|
||||
' ],\n'
|
||||
'}\n'
|
||||
)
|
||||
(dump / "Android.bp").write_text(
|
||||
'cc_binary {\n'
|
||||
' name: "dumpstate",\n'
|
||||
' required: [\n'
|
||||
' "alloctop",\n'
|
||||
' "atrace",\n'
|
||||
' ],\n'
|
||||
'}\n'
|
||||
)
|
||||
(apex / "Android.bp").write_text(
|
||||
'cc_binary {\n'
|
||||
' name: "apexd",\n'
|
||||
' required: ["apexd-lsof.sh"],\n'
|
||||
' init_rc: ["apexd.rc"],\n'
|
||||
'}\n'
|
||||
)
|
||||
env = {**os.environ, "ANDROID_BUILD_TOP": str(top)}
|
||||
first = subprocess.run(
|
||||
["python3", str(apply)],
|
||||
cwd=top,
|
||||
env=env,
|
||||
check=False,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
self.assertEqual(0, first.returncode, first.stderr)
|
||||
self.assertIn("dropped", first.stdout)
|
||||
vendor_text = (vendor / "sunfish-vendor.mk").read_text()
|
||||
self.assertNotIn("cnss_diag", vendor_text)
|
||||
self.assertNotIn("imsrcsd", vendor_text)
|
||||
self.assertIn("imsdatadaemon.rc", vendor_text)
|
||||
self.assertIn(" loc_launcher \\\n", vendor_text)
|
||||
self.assertIn(" xtra-daemon\n", vendor_text)
|
||||
self.assertIn("cnss-daemon", vendor_text)
|
||||
self.assertNotIn("disable_configstore", (device / "device-lineage.mk").read_text())
|
||||
base = (product / "base_system.mk").read_text()
|
||||
self.assertNotIn(" pbtombstone \\\n ping\n", base)
|
||||
self.assertIn("PRODUCT_HOST_PACKAGES += \\\n fastboot \\\n pbtombstone \\\n", base)
|
||||
self.assertNotIn('"bpftool",', (bpf / "Android.bp").read_text())
|
||||
self.assertIn("timeInState.bpf", (bpf / "Android.bp").read_text())
|
||||
self.assertNotIn("alloctop", (dump / "Android.bp").read_text())
|
||||
self.assertIn("atrace", (dump / "Android.bp").read_text())
|
||||
self.assertNotIn("apexd-lsof.sh", (apex / "Android.bp").read_text())
|
||||
self.assertIn("apexd.rc", (apex / "Android.bp").read_text())
|
||||
second = subprocess.run(
|
||||
["python3", str(apply)],
|
||||
cwd=top,
|
||||
env=env,
|
||||
check=False,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
self.assertEqual(0, second.returncode, second.stderr)
|
||||
self.assertIn("already", second.stdout)
|
||||
|
||||
def test_large_axp_zip_is_ignored(self) -> None:
|
||||
result = subprocess.run(
|
||||
["git", "check-ignore", "--quiet", "AXP.OS-22.2-20260324-PRO-sunfish.zip"],
|
||||
cwd=ROOT,
|
||||
check=False,
|
||||
)
|
||||
self.assertEqual(0, result.returncode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,34 @@
|
||||
"""012 hardened_malloc port keeps the AXP/Graphene exclusions."""
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import unittest
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||
APPLY = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/012-hardened-malloc/apply_012_hardened_malloc.py"
|
||||
)
|
||||
WRAPPER = (
|
||||
ROOT
|
||||
/ "vendor/sunfish-a16/patches/012-hardened-malloc/h_malloc_wrapper.cpp"
|
||||
)
|
||||
|
||||
|
||||
class HardenedMallocPort(unittest.TestCase):
|
||||
def test_pin_and_exclusions(self) -> None:
|
||||
text = APPLY.read_text()
|
||||
self.assertIn("1d7fc7ffe0f8068a32ab2ba33df7515e7bad3f5f", text)
|
||||
self.assertIn("/system/bin/surfaceflinger", text)
|
||||
self.assertIn('strstr(exe_path, "camera")', text)
|
||||
self.assertIn("max_map_count 1048576", text)
|
||||
self.assertIn("USE_H_MALLOC", text)
|
||||
self.assertIn('REGION_39BIT = "-DCONFIG_CLASS_REGION_SIZE=2147483648"', text)
|
||||
self.assertIn("text.replace(REGION_48BIT, REGION_39BIT)", text)
|
||||
self.assertIn('lost noinline', text)
|
||||
self.assertTrue(WRAPPER.is_file())
|
||||
self.assertIn("h_malloc_info", WRAPPER.read_text())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Vendored
+2
-6
@@ -89,9 +89,5 @@ LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/FossifyGallery.ap
|
||||
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
# US-S4-2 — AXP PRO has no Jelly (system_ext noDefaultBrowserWebView).
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := NoLineageJelly
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_OVERRIDES_PACKAGES := Jelly
|
||||
include $(BUILD_PHONY_PACKAGE)
|
||||
# EuiccGoogle is removed by apply_009_drop_euicc_google.py from
|
||||
# vendor/google/sunfish/sunfish-vendor.mk.
|
||||
Vendored
-5
@@ -8,11 +8,6 @@
|
||||
|
||||
BOARD_SUPER_PARTITION_ERROR_LIMIT := 9441378304
|
||||
|
||||
# Product LINEAGE_VERSION id: set in vendor/extra/product.mk (via bacon
|
||||
# symlink to extra-product.mk) — BoardConfig is too late for version.mk.
|
||||
# Keep a duplicate here only as documentation / get_build_var fallback.
|
||||
TARGET_UNOFFICIAL_BUILD_ID := AXP-Resurection-Pro
|
||||
|
||||
# US-S3 — project AVB *signing* key (Gate G infrastructure).
|
||||
# Included after device/google/sunfish BoardConfig-common.mk → override here.
|
||||
# Keep WITH_AVB unset → BoardConfigLineage adds vbmeta --flags 3
|
||||
|
||||
Vendored
+12
-1
@@ -1,8 +1,19 @@
|
||||
# Bundled user apps (009) — Aurora Store, Droidify, Fossify Gallery.
|
||||
# APKs live on host at /srv/rom/prebuilts/axp-apps/ (never Git).
|
||||
# Symlink into vendor/sunfish-a16/prebuilt/ before bacon.
|
||||
#
|
||||
# OpenEUICC is the privileged LPA, same as AXP GOLDEN: system_ext priv-app,
|
||||
# platform certificate, privapp whitelist. Source is not an APK prebuilt.
|
||||
# Host tree: packages/apps/OpenEUICC @ upstream 89d677e07d0e (2026-09-28).
|
||||
# Not the AXP fork tag. Datastore comes from LOS androidx.
|
||||
# prebuilts/openeuicc-deps @ 5402167 (zxing only).
|
||||
|
||||
PRODUCT_PACKAGES += \
|
||||
AuroraStore \
|
||||
Droidify \
|
||||
FossifyGallery
|
||||
FossifyGallery \
|
||||
OpenEUICC
|
||||
|
||||
# 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.
|
||||
Vendored
+2
-2
@@ -3,8 +3,8 @@
|
||||
# "Pro" marks the full Magisk-resurrection track (not a future slim/minimal
|
||||
# variant). GOLDEN used releasetype PRO + ro.lineage.version=…-sunfish-pro.
|
||||
#
|
||||
# TARGET_UNOFFICIAL_BUILD_ID is set in vendor/extra/product.mk (early Lineage
|
||||
# hook, before version.mk). BoardConfig + this file are not early enough alone.
|
||||
# LINEAGE_BUILDTYPE := PRO comes from apply_009_axp_identity.py (AXP Patch.sh
|
||||
# method), so ro.lineage.releasetype=PRO and the version is not UNOFFICIAL.
|
||||
# Ship scripts also publish AXP-Resurection-Pro-*.zip from lineage-*.zip.
|
||||
|
||||
# Identity props (not axp.os endpoints). Empty on GOLDEN for ro.axp.version —
|
||||
|
||||
Vendored
+2
-4
@@ -1,5 +1,3 @@
|
||||
# Early Lineage hook (vendor/lineage/config/common.mk inherits this *before*
|
||||
# version.mk). Required so LINEAGE_VERSION picks up the unofficial id.
|
||||
# BoardConfig.mk is too late for TARGET_UNOFFICIAL_BUILD_ID.
|
||||
|
||||
TARGET_UNOFFICIAL_BUILD_ID := AXP-Resurection-Pro
|
||||
# version.mk). Ship identity is LINEAGE_BUILDTYPE := PRO, set by
|
||||
# apply_009_axp_identity.py the way AXP Patch.sh does. No unofficial build id.
|
||||
Vendored
+4
-3
@@ -4,7 +4,9 @@
|
||||
#
|
||||
# Real strips:
|
||||
# US-S4-1 apply_009_drop_openssh_packages.py (common.mk)
|
||||
# US-S4-2 apply_009_drop_jelly_gallery2.py (Jelly + Gallery2)
|
||||
# US-S4-2 Jelly stays (owner 2026-10-07). LOS 23 does not put it in
|
||||
# common_mobile.mk, so it is added here. Gallery is FossifyGallery,
|
||||
# not Gallery2 or Glimpse. Do not put Gallery2 back.
|
||||
# PRODUCT_PACKAGES filter-out via inherit alone has failed before — keep as
|
||||
# belt-and-suspenders only.
|
||||
|
||||
@@ -17,12 +19,11 @@ PRODUCT_PACKAGES := $(filter-out \
|
||||
ssh-keygen \
|
||||
start-ssh \
|
||||
rsync \
|
||||
Jelly \
|
||||
Gallery2 \
|
||||
Glimpse \
|
||||
,$(PRODUCT_PACKAGES))
|
||||
|
||||
PRODUCT_PACKAGES += NoLineageJelly
|
||||
PRODUCT_PACKAGES += Jelly
|
||||
|
||||
PRODUCT_PACKAGES_DEBUG := $(filter-out procmem,$(PRODUCT_PACKAGES_DEBUG))
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@
|
||||
# US-S2 release keys (PRODUCT_DEFAULT_DEV_CERTIFICATE) — see signing.mk
|
||||
$(call inherit-product-if-exists, vendor/sunfish-a16/signing.mk)
|
||||
|
||||
# OTA / version branding (TARGET_UNOFFICIAL_BUILD_ID=AXP-Resurection-Pro)
|
||||
# OTA / version branding (releasetype PRO)
|
||||
$(call inherit-product-if-exists, vendor/sunfish-a16/branding.mk)
|
||||
|
||||
# US-S4-1 userspace hardening (drop product OpenSSH, etc.)
|
||||
|
||||
@@ -38,8 +38,23 @@ def backup(path: Path) -> None:
|
||||
def patch_computer_engine() -> None:
|
||||
path = TREE / "frameworks/base/services/core/java/com/android/server/pm/ComputerEngine.java"
|
||||
text = path.read_text()
|
||||
# AXP/EXTENDROM spoofs on user images (GOLDEN ro.debuggable=0). LOS stock
|
||||
# isDebuggable() would disable that on lineage_sunfish-user.
|
||||
deb_gate = """ public static boolean isMicrogSigned(AndroidPackage p) {
|
||||
if (!isDebuggable()) {
|
||||
return false;
|
||||
}
|
||||
"""
|
||||
user_ok = """ public static boolean isMicrogSigned(AndroidPackage p) {
|
||||
// %s: no isDebuggable() gate (AXP user / GOLDEN).
|
||||
""" % MARKER
|
||||
if MARKER in text:
|
||||
print("ComputerEngine: already patched")
|
||||
if deb_gate not in text:
|
||||
print("ComputerEngine: already patched, user gate ok")
|
||||
return
|
||||
backup(path)
|
||||
path.write_text(text.replace(deb_gate, user_ok, 1))
|
||||
print("ComputerEngine: dropped isDebuggable gate")
|
||||
return
|
||||
backup(path)
|
||||
|
||||
@@ -78,9 +93,7 @@ def patch_computer_engine() -> None:
|
||||
}"""
|
||||
|
||||
new_is = """ public static boolean isMicrogSigned(AndroidPackage p) {
|
||||
if (!isDebuggable()) {
|
||||
return false;
|
||||
}
|
||||
// SUNFISH_A16_005_SIGSPOOF: no isDebuggable() gate (AXP user / GOLDEN).
|
||||
|
||||
// Allowlist the following apps:
|
||||
// * com.android.vending - microG Companion / patched Phonesky
|
||||
|
||||
Vendored
+153
@@ -0,0 +1,153 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: anupritaisno1 <www.anuprita804@gmail.com>
|
||||
Date: Mon, 18 Oct 2021 01:35:40 +0300
|
||||
Subject: [PATCH] automatically reboot device after timeout if set
|
||||
|
||||
Signed-off-by: anupritaisno1 <www.anuprita804@gmail.com>
|
||||
Change-Id: If891bfbcc144c9336ba013260bad2b7c7a59c054
|
||||
---
|
||||
core/java/android/provider/Settings.java | 7 ++++
|
||||
data/etc/com.android.systemui.xml | 1 +
|
||||
packages/SystemUI/AndroidManifest.xml | 3 ++
|
||||
.../keyguard/KeyguardViewMediator.java | 35 +++++++++++++++++++
|
||||
4 files changed, 46 insertions(+)
|
||||
|
||||
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
|
||||
index 783483d8e491..48913891ac52 100644
|
||||
--- a/core/java/android/provider/Settings.java
|
||||
+++ b/core/java/android/provider/Settings.java
|
||||
@@ -19027,6 +19027,13 @@ public final class Settings {
|
||||
public static final String REVIEW_PERMISSIONS_NOTIFICATION_STATE =
|
||||
"review_permissions_notification_state";
|
||||
|
||||
+ /**
|
||||
+ * Whether to automatically reboot the device after a user defined timeout
|
||||
+ *
|
||||
+ * @hide
|
||||
+ */
|
||||
+ public static final String SETTINGS_REBOOT_AFTER_TIMEOUT = "settings_reboot_after_timeout";
|
||||
+
|
||||
/**
|
||||
* Whether repair mode is active on the device.
|
||||
* <p>
|
||||
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
|
||||
index ce2543a47cf5..67108d16e099 100644
|
||||
--- a/data/etc/com.android.systemui.xml
|
||||
+++ b/data/etc/com.android.systemui.xml
|
||||
@@ -54,6 +54,7 @@
|
||||
<permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
|
||||
<permission name="android.permission.READ_WALLPAPER_INTERNAL"/>
|
||||
<permission name="android.permission.REAL_GET_TASKS"/>
|
||||
+ <permission name="android.permission.REBOOT"/>
|
||||
<permission name="android.permission.REQUEST_NETWORK_SCORES"/>
|
||||
<permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
|
||||
<permission name="android.permission.SATELLITE_COMMUNICATION"/>
|
||||
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
|
||||
index 351cce0929b7..00604a316019 100644
|
||||
--- a/packages/SystemUI/AndroidManifest.xml
|
||||
+++ b/packages/SystemUI/AndroidManifest.xml
|
||||
@@ -356,6 +356,9 @@
|
||||
|
||||
<uses-permission android:name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS" />
|
||||
|
||||
+ <!-- Permission to allow rebooting the device after a user configurable amount of time -->
|
||||
+ <uses-permission android:name="android.permission.REBOOT" />
|
||||
+
|
||||
<uses-permission android:name="android.permission.MONITOR_KEYBOARD_BACKLIGHT" />
|
||||
|
||||
<uses-permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE" />
|
||||
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
|
||||
index 6034e92be873..67dd84e8ae8f 100644
|
||||
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
|
||||
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
|
||||
@@ -240,6 +240,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
|
||||
private final static String TAG = "KeyguardViewMediator";
|
||||
|
||||
+ private static final String DELAYED_REBOOT_ACTION =
|
||||
+ "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_REBOOT";
|
||||
public static final String DELAYED_KEYGUARD_ACTION =
|
||||
"com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD";
|
||||
private static final String DELAYED_LOCK_PROFILE_ACTION =
|
||||
@@ -417,6 +419,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
*/
|
||||
private int mDelayedProfileShowingSequence;
|
||||
|
||||
+ /**
|
||||
+ * Same as {@link #mDelayedProfileShowingSequence}, but used for our reboot implementation
|
||||
+ */
|
||||
+ private int mDelayedRebootSequence;
|
||||
+
|
||||
private final DismissCallbackRegistry mDismissCallbackRegistry;
|
||||
|
||||
// the properties of the keyguard
|
||||
@@ -1503,6 +1510,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
final IntentFilter delayedActionFilter = new IntentFilter();
|
||||
delayedActionFilter.addAction(DELAYED_KEYGUARD_ACTION);
|
||||
delayedActionFilter.addAction(DELAYED_LOCK_PROFILE_ACTION);
|
||||
+ delayedActionFilter.addAction(DELAYED_REBOOT_ACTION);
|
||||
delayedActionFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||
mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter,
|
||||
SYSTEMUI_PERMISSION, null /* scheduler */,
|
||||
@@ -1866,6 +1874,18 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
}
|
||||
}
|
||||
|
||||
+ private void doRebootForOwnerAfterTimeoutIfEnabled(long rebootAfterTimeout) {
|
||||
+ long when = mSystemClock.elapsedRealtime() + rebootAfterTimeout;
|
||||
+ Intent rebootIntent = new Intent(DELAYED_REBOOT_ACTION);
|
||||
+ rebootIntent.putExtra("seq", mDelayedRebootSequence);
|
||||
+ rebootIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
+ PendingIntent sender = PendingIntent.getBroadcast(mContext,
|
||||
+ 0, rebootIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
+ mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
|
||||
+ if (DEBUG) Log.d(TAG, "setting alarm to reboot device, timeout = "
|
||||
+ + String.valueOf(rebootAfterTimeout));
|
||||
+ }
|
||||
+
|
||||
private void doKeyguardForChildProfilesLocked() {
|
||||
for (UserInfo profile : mUserTracker.getUserProfiles()) {
|
||||
if (!profile.isEnabled()) continue;
|
||||
@@ -1884,6 +1904,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
mDelayedProfileShowingSequence++;
|
||||
}
|
||||
|
||||
+ private void cancelDoRebootForOwnerAfterTimeoutIfEnabled() {
|
||||
+ mDelayedRebootSequence++;
|
||||
+ }
|
||||
+
|
||||
/**
|
||||
* It will let us know when the device is waking up.
|
||||
*/
|
||||
@@ -2298,6 +2322,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
|
||||
if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
|
||||
showKeyguard(options);
|
||||
+ final long rebootAfterTimeout = Settings.Global.getLong(mContext.getContentResolver(), Settings.Global.SETTINGS_REBOOT_AFTER_TIMEOUT, 0);
|
||||
+ if (rebootAfterTimeout >= 1) {
|
||||
+ doRebootForOwnerAfterTimeoutIfEnabled(rebootAfterTimeout);
|
||||
+ }
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@@ -2478,6 +2506,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
}
|
||||
}
|
||||
}
|
||||
+ } else if (DELAYED_REBOOT_ACTION.equals(intent.getAction())) {
|
||||
+ final int sequence = intent.getIntExtra("seq", 0);
|
||||
+ if (sequence == mDelayedRebootSequence) {
|
||||
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
|
||||
+ pm.reboot(null);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3200,6 +3234,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
mHideAnimationRun = false;
|
||||
adjustStatusBarLocked();
|
||||
sendUserPresentBroadcast();
|
||||
+ cancelDoRebootForOwnerAfterTimeoutIfEnabled();
|
||||
}
|
||||
|
||||
private Configuration.Builder createInteractionJankMonitorConf(int cuj) {
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dmitry Muhomor <muhomor.dmitry@gmail.com>
|
||||
Date: Tue, 6 Sep 2022 16:48:26 +0300
|
||||
Subject: [PATCH] bugfix: Bluetooth auto turn off ignored connected BLE devices
|
||||
|
||||
Previous attempt at fixing this didn't work properly, because getConnectionStateLeAware() didn't
|
||||
actually report BLE state.
|
||||
---
|
||||
.../android/server/ext/BluetoothAutoOff.java | 20 ++++++++++++++-----
|
||||
1 file changed, 15 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/services/core/java/com/android/server/ext/BluetoothAutoOff.java b/services/core/java/com/android/server/ext/BluetoothAutoOff.java
|
||||
index 4e7dbc042f37..a091b006214f 100644
|
||||
--- a/services/core/java/com/android/server/ext/BluetoothAutoOff.java
|
||||
+++ b/services/core/java/com/android/server/ext/BluetoothAutoOff.java
|
||||
@@ -3,6 +3,7 @@ package com.android.server.ext;
|
||||
import android.annotation.Nullable;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
+import android.bluetooth.BluetoothProfile;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -12,12 +13,14 @@ import android.provider.Settings;
|
||||
import android.util.Slog;
|
||||
|
||||
class BluetoothAutoOff extends DelayedConditionalAction {
|
||||
+ private final BluetoothManager manager;
|
||||
@Nullable
|
||||
private final BluetoothAdapter adapter;
|
||||
|
||||
BluetoothAutoOff(SystemServerExt sse) {
|
||||
super(sse, sse.bgHandler);
|
||||
- adapter = sse.context.getSystemService(BluetoothManager.class).getAdapter();
|
||||
+ manager = sse.context.getSystemService(BluetoothManager.class);
|
||||
+ adapter = manager.getAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,11 +54,18 @@ class BluetoothAutoOff extends DelayedConditionalAction {
|
||||
|
||||
private boolean isAdapterOnAndDisconnected() {
|
||||
if (adapter != null) {
|
||||
- int state = adapter.getLeStateSysApi(); // getState() converts BLE states into STATE_OFF
|
||||
+ if (adapter.isLeEnabled()) {
|
||||
+ if (adapter.getConnectionState() == BluetoothAdapter.STATE_DISCONNECTED) {
|
||||
+ // Bluetooth GATT Profile (Bluetooth LE) connection state is ignored
|
||||
+ // by getConnectionState()
|
||||
+ return manager.getConnectedDevices(BluetoothProfile.GATT).size() == 0;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) {
|
||||
- // getConnectionState() converts BLE states into STATE_DISCONNECTED
|
||||
- return adapter.getConnectionStateLeAware() == BluetoothAdapter.STATE_DISCONNECTED;
|
||||
+ // isLeEnabled() currently implies isEnabled(), but check again anyway in case
|
||||
+ // this changes in the future
|
||||
+ if (adapter.isEnabled()) {
|
||||
+ return adapter.getConnectionState() == BluetoothAdapter.STATE_DISCONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+120
@@ -0,0 +1,120 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dmitry Muhomor <muhomor.dmitry@gmail.com>
|
||||
Date: Sun, 31 Jul 2022 11:19:33 +0300
|
||||
Subject: [PATCH] Bluetooth auto turn off
|
||||
|
||||
Co-authored-by: Pratyush <codelab@pratyush.dev>
|
||||
---
|
||||
core/java/android/provider/Settings.java | 6 ++
|
||||
.../android/server/ext/BluetoothAutoOff.java | 69 +++++++++++++++++++
|
||||
.../android/server/ext/SystemServerExt.java | 4 ++
|
||||
3 files changed, 79 insertions(+)
|
||||
create mode 100644 services/core/java/com/android/server/ext/BluetoothAutoOff.java
|
||||
|
||||
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
|
||||
index 49f59950b9e0..58f1077af988 100644
|
||||
--- a/core/java/android/provider/Settings.java
|
||||
+++ b/core/java/android/provider/Settings.java
|
||||
@@ -19549,6 +19549,12 @@ public final class Settings {
|
||||
*/
|
||||
public static final String WIFI_OFF_TIMEOUT = "wifi_off_timeout";
|
||||
|
||||
+ /**
|
||||
+ * The amount of time in milliseconds before a disconnected Bluetooth adapter is turned off
|
||||
+ * @hide
|
||||
+ */
|
||||
+ public static final String BLUETOOTH_OFF_TIMEOUT = "bluetooth_off_timeout";
|
||||
+
|
||||
/**
|
||||
* Whether repair mode is active on the device.
|
||||
* <p>
|
||||
diff --git a/services/core/java/com/android/server/ext/BluetoothAutoOff.java b/services/core/java/com/android/server/ext/BluetoothAutoOff.java
|
||||
new file mode 100644
|
||||
index 000000000000..4e7dbc042f37
|
||||
--- /dev/null
|
||||
+++ b/services/core/java/com/android/server/ext/BluetoothAutoOff.java
|
||||
@@ -0,0 +1,69 @@
|
||||
+package com.android.server.ext;
|
||||
+
|
||||
+import android.annotation.Nullable;
|
||||
+import android.bluetooth.BluetoothAdapter;
|
||||
+import android.bluetooth.BluetoothManager;
|
||||
+import android.content.BroadcastReceiver;
|
||||
+import android.content.Context;
|
||||
+import android.content.Intent;
|
||||
+import android.content.IntentFilter;
|
||||
+import android.os.Build;
|
||||
+import android.provider.Settings;
|
||||
+import android.util.Slog;
|
||||
+
|
||||
+class BluetoothAutoOff extends DelayedConditionalAction {
|
||||
+ @Nullable
|
||||
+ private final BluetoothAdapter adapter;
|
||||
+
|
||||
+ BluetoothAutoOff(SystemServerExt sse) {
|
||||
+ super(sse, sse.bgHandler);
|
||||
+ adapter = sse.context.getSystemService(BluetoothManager.class).getAdapter();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected boolean shouldScheduleAlarm() {
|
||||
+ return isAdapterOnAndDisconnected();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected void alarmTriggered() {
|
||||
+ if (isAdapterOnAndDisconnected()) {
|
||||
+ adapter.disable();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected void registerStateListener() {
|
||||
+ IntentFilter f = new IntentFilter();
|
||||
+ f.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
+ f.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
|
||||
+
|
||||
+ sse.registerReceiver(new BroadcastReceiver() {
|
||||
+ @Override
|
||||
+ public void onReceive(Context broadcastContext, Intent intent) {
|
||||
+ if (Build.isDebuggable()) {
|
||||
+ Slog.d("BtAutoOff", "" + intent + ", extras " + intent.getExtras().deepCopy());
|
||||
+ }
|
||||
+ update();
|
||||
+ }
|
||||
+ }, f, handler);
|
||||
+ }
|
||||
+
|
||||
+ private boolean isAdapterOnAndDisconnected() {
|
||||
+ if (adapter != null) {
|
||||
+ int state = adapter.getLeStateSysApi(); // getState() converts BLE states into STATE_OFF
|
||||
+
|
||||
+ if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) {
|
||||
+ // getConnectionState() converts BLE states into STATE_DISCONNECTED
|
||||
+ return adapter.getConnectionStateLeAware() == BluetoothAdapter.STATE_DISCONNECTED;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected String getDelayGlobalSettingsKey() {
|
||||
+ return Settings.Global.BLUETOOTH_OFF_TIMEOUT;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/services/core/java/com/android/server/ext/SystemServerExt.java b/services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
index 66350e2b7f74..3c341ed25f4e 100644
|
||||
--- a/services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
+++ b/services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
@@ -53,6 +53,10 @@ public final class SystemServerExt {
|
||||
if (packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI, 0)) {
|
||||
new WifiAutoOff(this);
|
||||
}
|
||||
+
|
||||
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH, 0)) {
|
||||
+ new BluetoothAutoOff(this);
|
||||
+ }
|
||||
}
|
||||
|
||||
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter, Handler handler) {
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dmitry Muhomor <muhomor.dmitry@gmail.com>
|
||||
Date: Sun, 31 Jul 2022 10:06:14 +0300
|
||||
Subject: [PATCH] infrastructure for system_server extensions
|
||||
|
||||
---
|
||||
.../server/ext/DelayedConditionalAction.java | 135 ++++++++++++++++++
|
||||
.../android/server/ext/SystemServerExt.java | 58 ++++++++
|
||||
.../java/com/android/server/SystemServer.java | 2 +
|
||||
3 files changed, 195 insertions(+)
|
||||
create mode 100644 services/core/java/com/android/server/ext/DelayedConditionalAction.java
|
||||
create mode 100644 services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
|
||||
diff --git a/services/core/java/com/android/server/ext/DelayedConditionalAction.java b/services/core/java/com/android/server/ext/DelayedConditionalAction.java
|
||||
new file mode 100644
|
||||
index 000000000000..d72f302e9d42
|
||||
--- /dev/null
|
||||
+++ b/services/core/java/com/android/server/ext/DelayedConditionalAction.java
|
||||
@@ -0,0 +1,135 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2022 GrapheneOS
|
||||
+ *
|
||||
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
+ * you may not use this file except in compliance with the License.
|
||||
+ * You may obtain a copy of the License at
|
||||
+ *
|
||||
+ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
+ *
|
||||
+ * Unless required by applicable law or agreed to in writing, software
|
||||
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
+ * See the License for the specific language governing permissions and
|
||||
+ * limitations under the License.
|
||||
+ */
|
||||
+
|
||||
+package com.android.server.ext;
|
||||
+
|
||||
+import android.app.AlarmManager;
|
||||
+import android.content.ContentResolver;
|
||||
+import android.content.Context;
|
||||
+import android.database.ContentObserver;
|
||||
+import android.net.Uri;
|
||||
+import android.os.Build;
|
||||
+import android.os.Handler;
|
||||
+import android.os.Looper;
|
||||
+import android.os.SystemClock;
|
||||
+import android.provider.Settings;
|
||||
+import android.util.Slog;
|
||||
+
|
||||
+/**
|
||||
+ * Infrastructure for actions that:
|
||||
+ * - happen after a user-configurable device-wide (Settings.Global) delay
|
||||
+ * - need to be taken even when the device is in deep sleep
|
||||
+ * - need to be rescheduled based on some listenable event
|
||||
+ */
|
||||
+public abstract class DelayedConditionalAction {
|
||||
+ private static final String TAG = "DelayedConditionalAction";
|
||||
+
|
||||
+ protected final SystemServerExt sse;
|
||||
+ protected final Thread thread;
|
||||
+ protected final Handler handler;
|
||||
+
|
||||
+ protected final ContentResolver contentResolver;
|
||||
+ protected final AlarmManager alarmManager;
|
||||
+ private final AlarmManager.OnAlarmListener alarmListener;
|
||||
+
|
||||
+ protected DelayedConditionalAction(SystemServerExt sse, Handler handler) {
|
||||
+ this.sse = sse;
|
||||
+
|
||||
+ Looper looper = handler.getLooper();
|
||||
+ thread = looper.getThread();
|
||||
+ this.handler = handler;
|
||||
+
|
||||
+ if (Build.isDebuggable()) {
|
||||
+ if (thread != Thread.currentThread()) {
|
||||
+ throw new IllegalStateException("all calls should happen on the same thread");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Context ctx = sse.context;
|
||||
+ contentResolver = ctx.getContentResolver();
|
||||
+ alarmManager = ctx.getSystemService(AlarmManager.class);
|
||||
+
|
||||
+ alarmListener = () -> {
|
||||
+ if (delayDurationMillis() == 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ alarmTriggered();
|
||||
+ };
|
||||
+
|
||||
+ registerStateListener();
|
||||
+
|
||||
+ Uri delaySettingUri = Settings.Global.getUriFor(getDelayGlobalSettingsKey());
|
||||
+
|
||||
+ ContentObserver delayChangeListener = new ContentObserver(handler) {
|
||||
+ @Override
|
||||
+ public void onChange(boolean selfChange) {
|
||||
+ update();
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ contentResolver.registerContentObserver(delaySettingUri, false, delayChangeListener);
|
||||
+ }
|
||||
+
|
||||
+ private boolean alarmScheduled;
|
||||
+
|
||||
+ protected final void update() {
|
||||
+ final Thread curThread = Thread.currentThread();
|
||||
+ if (curThread != thread) {
|
||||
+ String msg = "update() called on an unknown thread " + curThread;
|
||||
+ if (Build.isDebuggable()) {
|
||||
+ throw new IllegalStateException(msg);
|
||||
+ } else {
|
||||
+ Slog.e(TAG, msg, new Throwable());
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (alarmScheduled) {
|
||||
+ alarmManager.cancel(alarmListener);
|
||||
+ alarmScheduled = false;
|
||||
+ }
|
||||
+
|
||||
+ if (!shouldScheduleAlarm()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ long delayMillis = delayDurationMillis();
|
||||
+
|
||||
+ if (delayMillis == 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ final long triggerAt = SystemClock.elapsedRealtime() + delayMillis;
|
||||
+ alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAt,
|
||||
+ getClass().getName(), alarmListener, handler);
|
||||
+ alarmScheduled = true;
|
||||
+ }
|
||||
+
|
||||
+ private long delayDurationMillis() {
|
||||
+ return Settings.Global.getLong(contentResolver, getDelayGlobalSettingsKey(), 0);
|
||||
+ }
|
||||
+
|
||||
+ // Make sure to use the same Handler that is used for all other callbacks;
|
||||
+ // call update() to reschedule / cancel the alarm
|
||||
+ protected abstract void registerStateListener();
|
||||
+
|
||||
+ protected abstract boolean shouldScheduleAlarm();
|
||||
+ protected abstract void alarmTriggered();
|
||||
+
|
||||
+ // android.provider.Settings.Global key
|
||||
+ protected abstract String getDelayGlobalSettingsKey();
|
||||
+}
|
||||
diff --git a/services/core/java/com/android/server/ext/SystemServerExt.java b/services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
new file mode 100644
|
||||
index 000000000000..83d895650473
|
||||
--- /dev/null
|
||||
+++ b/services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
@@ -0,0 +1,58 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2022 GrapheneOS
|
||||
+ *
|
||||
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
+ * you may not use this file except in compliance with the License.
|
||||
+ * You may obtain a copy of the License at
|
||||
+ *
|
||||
+ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
+ *
|
||||
+ * Unless required by applicable law or agreed to in writing, software
|
||||
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
+ * See the License for the specific language governing permissions and
|
||||
+ * limitations under the License.
|
||||
+ */
|
||||
+
|
||||
+package com.android.server.ext;
|
||||
+
|
||||
+import android.content.BroadcastReceiver;
|
||||
+import android.content.Context;
|
||||
+import android.content.IntentFilter;
|
||||
+import android.os.Handler;
|
||||
+
|
||||
+import com.android.internal.os.BackgroundThread;
|
||||
+import com.android.server.pm.PackageManagerService;
|
||||
+
|
||||
+public final class SystemServerExt {
|
||||
+
|
||||
+ public final Context context;
|
||||
+ public final Handler bgHandler;
|
||||
+ public final PackageManagerService packageManager;
|
||||
+
|
||||
+ private SystemServerExt(Context systemContext, PackageManagerService pm) {
|
||||
+ context = systemContext;
|
||||
+ bgHandler = BackgroundThread.getHandler();
|
||||
+ packageManager = pm;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ Called after system server has completed its initialization,
|
||||
+ but before any of the apps are started.
|
||||
+
|
||||
+ Call from com.android.server.SystemServer#startOtherServices(), at the end of lambda
|
||||
+ that is passed into mActivityManagerService.systemReady()
|
||||
+ */
|
||||
+ public static void init(Context systemContext, PackageManagerService pm) {
|
||||
+ SystemServerExt sse = new SystemServerExt(systemContext, pm);
|
||||
+ sse.bgHandler.post(sse::initBgThread);
|
||||
+ }
|
||||
+
|
||||
+ void initBgThread() {
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter, Handler handler) {
|
||||
+ context.registerReceiver(receiver, filter, null, handler);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
|
||||
index e92ba3ed7dc3..df60c628ccdb 100644
|
||||
--- a/services/java/com/android/server/SystemServer.java
|
||||
+++ b/services/java/com/android/server/SystemServer.java
|
||||
@@ -3454,6 +3454,8 @@ public final class SystemServer implements Dumpable {
|
||||
reportWtf("Triggering OdsignStatsLogger", e);
|
||||
}
|
||||
t.traceEnd();
|
||||
+
|
||||
+ com.android.server.ext.SystemServerExt.init(mSystemContext, mPackageManagerService);
|
||||
}, t);
|
||||
|
||||
t.traceBegin("LockSettingsThirdPartyAppsStarted");
|
||||
Vendored
+128
@@ -0,0 +1,128 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dmitry Muhomor <muhomor.dmitry@gmail.com>
|
||||
Date: Sun, 31 Jul 2022 10:06:14 +0300
|
||||
Subject: [PATCH] Wi-Fi auto turn off
|
||||
|
||||
Co-authored-by: Pratyush <codelab@pratyush.dev>
|
||||
---
|
||||
core/java/android/provider/Settings.java | 6 ++
|
||||
.../android/server/ext/SystemServerExt.java | 5 +-
|
||||
.../com/android/server/ext/WifiAutoOff.java | 69 +++++++++++++++++++
|
||||
3 files changed, 79 insertions(+), 1 deletion(-)
|
||||
create mode 100644 services/core/java/com/android/server/ext/WifiAutoOff.java
|
||||
|
||||
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
|
||||
index 10efaa78e435..49f59950b9e0 100644
|
||||
--- a/core/java/android/provider/Settings.java
|
||||
+++ b/core/java/android/provider/Settings.java
|
||||
@@ -19543,6 +19543,12 @@ public final class Settings {
|
||||
*/
|
||||
public static final String SETTINGS_REBOOT_AFTER_TIMEOUT = "settings_reboot_after_timeout";
|
||||
|
||||
+ /**
|
||||
+ * The amount of time in milliseconds before a disconnected Wi-Fi adapter is turned off
|
||||
+ * @hide
|
||||
+ */
|
||||
+ public static final String WIFI_OFF_TIMEOUT = "wifi_off_timeout";
|
||||
+
|
||||
/**
|
||||
* Whether repair mode is active on the device.
|
||||
* <p>
|
||||
diff --git a/services/core/java/com/android/server/ext/SystemServerExt.java b/services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
index 83d895650473..66350e2b7f74 100644
|
||||
--- a/services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
+++ b/services/core/java/com/android/server/ext/SystemServerExt.java
|
||||
@@ -19,6 +19,7 @@ package com.android.server.ext;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
+import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
@@ -49,7 +50,9 @@ public final class SystemServerExt {
|
||||
}
|
||||
|
||||
void initBgThread() {
|
||||
-
|
||||
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI, 0)) {
|
||||
+ new WifiAutoOff(this);
|
||||
+ }
|
||||
}
|
||||
|
||||
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter, Handler handler) {
|
||||
diff --git a/services/core/java/com/android/server/ext/WifiAutoOff.java b/services/core/java/com/android/server/ext/WifiAutoOff.java
|
||||
new file mode 100644
|
||||
index 000000000000..c7a3c05fe766
|
||||
--- /dev/null
|
||||
+++ b/services/core/java/com/android/server/ext/WifiAutoOff.java
|
||||
@@ -0,0 +1,69 @@
|
||||
+package com.android.server.ext;
|
||||
+
|
||||
+import android.content.BroadcastReceiver;
|
||||
+import android.content.Context;
|
||||
+import android.content.Intent;
|
||||
+import android.content.IntentFilter;
|
||||
+import android.net.wifi.WifiInfo;
|
||||
+import android.net.wifi.WifiManager;
|
||||
+import android.os.Build;
|
||||
+import android.provider.Settings;
|
||||
+import android.util.Slog;
|
||||
+
|
||||
+class WifiAutoOff extends DelayedConditionalAction {
|
||||
+ private final WifiManager wifiManager;
|
||||
+
|
||||
+ WifiAutoOff(SystemServerExt sse) {
|
||||
+ super(sse, sse.bgHandler);
|
||||
+ wifiManager = sse.context.getSystemService(WifiManager.class);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected boolean shouldScheduleAlarm() {
|
||||
+ return isWifiEnabledAndNotConnected();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected void alarmTriggered() {
|
||||
+ if (isWifiEnabledAndNotConnected()) {
|
||||
+ wifiManager.setWifiEnabled(false);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private boolean isWifiEnabledAndNotConnected() {
|
||||
+ if (wifiManager.isWifiEnabled()) {
|
||||
+ WifiInfo i = wifiManager.getConnectionInfo();
|
||||
+ if (i == null) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ return i.getBSSID() == null;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected void registerStateListener() {
|
||||
+ IntentFilter f = new IntentFilter();
|
||||
+ f.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
|
||||
+ f.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
|
||||
+ // ConnectivityManager APIs seem unfit for listening to Wi-Fi state specifically, they look
|
||||
+ // to be higher level than that, eg VPN over Wi-Fi isn't considered to be a Wi-Fi connection
|
||||
+ // by ConnectivityManager
|
||||
+
|
||||
+ sse.registerReceiver(new BroadcastReceiver() {
|
||||
+ @Override
|
||||
+ public void onReceive(Context context, Intent intent) {
|
||||
+ if (Build.isDebuggable()) {
|
||||
+ Slog.d("WifiAutoOff", "" + intent + ", extras " + intent.getExtras().deepCopy());
|
||||
+ }
|
||||
+ update();
|
||||
+ }
|
||||
+ }, f, handler);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected String getDelayGlobalSettingsKey() {
|
||||
+ return Settings.Global.WIFI_OFF_TIMEOUT;
|
||||
+ }
|
||||
+}
|
||||
Vendored
+141
@@ -0,0 +1,141 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dmitry Muhomor <muhomor.dmitry@gmail.com>
|
||||
Date: Mon, 11 Mar 2024 19:40:18 +0200
|
||||
Subject: [PATCH] SystemUI: require unlocking to use QS tiles by default
|
||||
|
||||
---
|
||||
.../src/com/android/systemui/qs/QSHost.java | 1 +
|
||||
.../com/android/systemui/qs/QSHostAdapter.kt | 4 +++
|
||||
.../systemui/qs/tileimpl/QSTileImpl.java | 36 +++++++++++++++++--
|
||||
.../systemui/qs/QSHostAdapterKosmos.kt | 2 ++
|
||||
4 files changed, 40 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
|
||||
index 7c7f48e0d37b..87e87348d0f2 100644
|
||||
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
|
||||
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
|
||||
@@ -51,6 +51,7 @@ public interface QSHost {
|
||||
Context getUserContext();
|
||||
int getUserId();
|
||||
Collection<QSTile> getTiles();
|
||||
+ com.android.systemui.statusbar.policy.KeyguardStateController getKeyguardStateController();
|
||||
void addCallback(Callback callback);
|
||||
void removeCallback(Callback callback);
|
||||
void removeTile(String tileSpec);
|
||||
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
|
||||
index dc3b58247152..08f3fc9d7bdb 100644
|
||||
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
|
||||
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
|
||||
@@ -31,6 +31,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
|
||||
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
|
||||
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||
import com.android.systemui.shade.ShadeDisplayAware
|
||||
+import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -53,6 +54,7 @@ constructor(
|
||||
@ShadeDisplayAware private val context: Context,
|
||||
private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder,
|
||||
@Application private val scope: CoroutineScope,
|
||||
+ private val keyguardStateController_: KeyguardStateController,
|
||||
dumpManager: DumpManager,
|
||||
) : QSHost {
|
||||
|
||||
@@ -72,6 +74,8 @@ constructor(
|
||||
return interactor.currentQSTiles
|
||||
}
|
||||
|
||||
+ override fun getKeyguardStateController() = keyguardStateController_
|
||||
+
|
||||
override fun getSpecs(): List<String> {
|
||||
return interactor.currentTilesSpecs.map { it.spec }
|
||||
}
|
||||
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
|
||||
index 1d1e9911884c..e9dc57e4c47d 100644
|
||||
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
|
||||
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
|
||||
@@ -71,6 +71,7 @@ import com.android.systemui.qs.QsEventLogger;
|
||||
import com.android.systemui.qs.SideLabelTileLayout;
|
||||
import com.android.systemui.qs.flags.QsInCompose;
|
||||
import com.android.systemui.qs.logging.QSLogger;
|
||||
+import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Objects;
|
||||
@@ -272,6 +273,32 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
return true;
|
||||
}
|
||||
|
||||
+ public enum Action {
|
||||
+ CLICK,
|
||||
+ SECONDARY_CLICK,
|
||||
+ LONG_CLICK,
|
||||
+ }
|
||||
+
|
||||
+ public boolean isAllowedWhenLocked(Action action) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ void handleAction(Action action, Runnable handler) {
|
||||
+ if (isAllowedWhenLocked(action)) {
|
||||
+ handler.run();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ KeyguardStateController ksc = mHost.getKeyguardStateController();
|
||||
+ boolean hasSecureKeyguard = ksc.isMethodSecure() && ksc.isShowing();
|
||||
+
|
||||
+ if (hasSecureKeyguard) {
|
||||
+ mActivityStarter.postQSRunnableDismissingKeyguard(handler);
|
||||
+ } else {
|
||||
+ handler.run();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// safe to call from any thread
|
||||
|
||||
public void addCallback(Callback callback) {
|
||||
@@ -616,16 +643,19 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
|
||||
} else {
|
||||
mQSLogger.logHandleClick(mTileSpec, msg.arg1);
|
||||
- handleClick((Expandable) msg.obj);
|
||||
+ Expandable view = (Expandable) msg.obj;
|
||||
+ handleAction(Action.CLICK, () -> handleClick(view));
|
||||
}
|
||||
} else if (msg.what == SECONDARY_CLICK) {
|
||||
name = "handleSecondaryClick";
|
||||
mQSLogger.logHandleSecondaryClick(mTileSpec, msg.arg1);
|
||||
- handleSecondaryClick((Expandable) msg.obj);
|
||||
+ Expandable view = (Expandable) msg.obj;
|
||||
+ handleAction(Action.SECONDARY_CLICK, () -> handleSecondaryClick(view));
|
||||
} else if (msg.what == LONG_CLICK) {
|
||||
name = "handleLongClick";
|
||||
mQSLogger.logHandleLongClick(mTileSpec, msg.arg1);
|
||||
- handleLongClick((Expandable) msg.obj);
|
||||
+ Expandable view = (Expandable) msg.obj;
|
||||
+ handleAction(Action.LONG_CLICK, () -> handleLongClick(view));
|
||||
} else if (msg.what == REFRESH_STATE) {
|
||||
name = "handleRefreshState";
|
||||
handleRefreshState(msg.obj);
|
||||
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QSHostAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QSHostAdapterKosmos.kt
|
||||
index 0bf801b35ad1..7cb93459077c 100644
|
||||
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QSHostAdapterKosmos.kt
|
||||
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QSHostAdapterKosmos.kt
|
||||
@@ -22,6 +22,7 @@ import com.android.systemui.kosmos.Kosmos
|
||||
import com.android.systemui.kosmos.applicationCoroutineScope
|
||||
import com.android.systemui.qs.external.tileServiceRequestControllerBuilder
|
||||
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
|
||||
+import com.android.systemui.statusbar.policy.keyguardStateController
|
||||
|
||||
val Kosmos.qsHostAdapter by
|
||||
Kosmos.Fixture {
|
||||
@@ -30,6 +31,7 @@ val Kosmos.qsHostAdapter by
|
||||
applicationContext,
|
||||
tileServiceRequestControllerBuilder,
|
||||
applicationCoroutineScope,
|
||||
+ keyguardStateController,
|
||||
dumpManager,
|
||||
)
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: pratyush <codelab@pratyush.dev>
|
||||
Date: Mon, 7 Jun 2021 22:15:59 +0100
|
||||
Subject: [PATCH] add auto-reboot setting
|
||||
|
||||
---
|
||||
res/values/arrays.xml | 31 +++++++
|
||||
res/values/strings.xml | 3 +
|
||||
res/xml/security_dashboard_settings.xml | 10 ++-
|
||||
.../AutoRebootPreferenceController.java | 82 +++++++++++++++++++
|
||||
.../settings/security/SecuritySettings.java | 1 +
|
||||
5 files changed, 126 insertions(+), 1 deletion(-)
|
||||
create mode 100644 src/com/android/settings/security/AutoRebootPreferenceController.java
|
||||
|
||||
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
|
||||
index febdb0412a4..c5df677975d 100644
|
||||
--- a/res/values/arrays.xml
|
||||
+++ b/res/values/arrays.xml
|
||||
@@ -105,6 +105,37 @@
|
||||
<item>1800000</item>
|
||||
</string-array>
|
||||
|
||||
+ <!-- Auto reboot settings -->
|
||||
+ <string-array name="auto_reboot_entries">
|
||||
+ <item>Off</item>
|
||||
+ <item>10 minutes</item>
|
||||
+ <item>30 minutes</item>
|
||||
+ <item>1 hour</item>
|
||||
+ <item>2 hours</item>
|
||||
+ <item>4 hours</item>
|
||||
+ <item>8 hours</item>
|
||||
+ <item>12 hours</item>
|
||||
+ <item>24 hours</item>
|
||||
+ <item>36 hours</item>
|
||||
+ <item>48 hours</item>
|
||||
+ <item>72 hours</item>
|
||||
+ </string-array>
|
||||
+
|
||||
+ <string-array name="auto_reboot_values" translatable="false">
|
||||
+ <item>0</item> <!-- Disabled -->
|
||||
+ <item>600000</item>
|
||||
+ <item>1800000</item>
|
||||
+ <item>3600000</item>
|
||||
+ <item>7200000</item>
|
||||
+ <item>14400000</item>
|
||||
+ <item>28800000</item>
|
||||
+ <item>43200000</item>
|
||||
+ <item>86400000</item>
|
||||
+ <item>129600000</item>
|
||||
+ <item>172800000</item>
|
||||
+ <item>259200000</item>
|
||||
+ </string-array>
|
||||
+
|
||||
<!-- Wi-Fi settings -->
|
||||
|
||||
<!-- Match this with the order of NetworkInfo.DetailedState. --> <skip />
|
||||
diff --git a/res/values/strings.xml b/res/values/strings.xml
|
||||
index 2e44c013a96..b7105938345 100644
|
||||
--- a/res/values/strings.xml
|
||||
+++ b/res/values/strings.xml
|
||||
@@ -675,6 +675,9 @@
|
||||
<!-- Text shown for title of owner info setting [CHAR LIMIT=30]-->
|
||||
<string name="owner_info_settings_title">Add text on lock screen</string>
|
||||
|
||||
+ <string name="auto_reboot_title">Auto reboot</string>
|
||||
+ <string name="auto_reboot_summary">Automatically reboot the device, if the phone hasn\'t been unlocked within the selected number of hours.</string>
|
||||
+
|
||||
<!-- Text shown for summary of owner info setting (if none set) [CHAR LIMIT=40]-->
|
||||
<string name="owner_info_settings_summary">None</string>
|
||||
<!-- Hint text shown in owner info edit text [CHAR LIMIT=50] -->
|
||||
diff --git a/res/xml/security_dashboard_settings.xml b/res/xml/security_dashboard_settings.xml
|
||||
index 0550441d3f2..d0aeb27fe53 100644
|
||||
--- a/res/xml/security_dashboard_settings.xml
|
||||
+++ b/res/xml/security_dashboard_settings.xml
|
||||
@@ -60,6 +60,14 @@
|
||||
android:title="@string/security_settings_biometric_preference_title"
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:keywords="@string/keywords_biometric_settings" />
|
||||
+
|
||||
+ <ListPreference
|
||||
+ android:key="auto_reboot"
|
||||
+ android:title="@string/auto_reboot_title"
|
||||
+ android:summary="@string/auto_reboot_summary"
|
||||
+ android:persistent="false"
|
||||
+ android:entries="@array/auto_reboot_entries"
|
||||
+ android:entryValues="@array/auto_reboot_values" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<Preference
|
||||
@@ -71,4 +79,4 @@
|
||||
settings:controller="com.android.settings.security.SecurityAdvancedSettingsController"
|
||||
settings:keywords="@string/security_advanced_settings_keywords" />
|
||||
|
||||
-</PreferenceScreen>
|
||||
\ No newline at end of file
|
||||
+</PreferenceScreen>
|
||||
diff --git a/src/com/android/settings/security/AutoRebootPreferenceController.java b/src/com/android/settings/security/AutoRebootPreferenceController.java
|
||||
new file mode 100644
|
||||
index 00000000000..c7a75219a5e
|
||||
--- /dev/null
|
||||
+++ b/src/com/android/settings/security/AutoRebootPreferenceController.java
|
||||
@@ -0,0 +1,82 @@
|
||||
+package com.android.settings.security;
|
||||
+
|
||||
+import android.content.Context;
|
||||
+import android.os.UserManager;
|
||||
+import android.provider.Settings;
|
||||
+import android.util.Log;
|
||||
+
|
||||
+import androidx.preference.ListPreference;
|
||||
+import androidx.preference.Preference;
|
||||
+import androidx.preference.PreferenceCategory;
|
||||
+import androidx.preference.PreferenceScreen;
|
||||
+
|
||||
+import com.android.settings.core.PreferenceControllerMixin;
|
||||
+import com.android.settingslib.core.AbstractPreferenceController;
|
||||
+import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||
+
|
||||
+public class AutoRebootPreferenceController extends AbstractPreferenceController
|
||||
+ implements PreferenceControllerMixin, OnResume,
|
||||
+ Preference.OnPreferenceChangeListener {
|
||||
+
|
||||
+ private static final String KEY_AUTO_REBOOT = "auto_reboot";
|
||||
+ private static final String PREF_KEY_SECURITY_CATEGORY = "security_category";
|
||||
+
|
||||
+ private PreferenceCategory mSecurityCategory;
|
||||
+ private boolean mIsAdmin;
|
||||
+ private final UserManager mUm;
|
||||
+
|
||||
+ public AutoRebootPreferenceController(Context context) {
|
||||
+ super(context);
|
||||
+ mUm = UserManager.get(context);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void displayPreference(PreferenceScreen screen) {
|
||||
+ super.displayPreference(screen);
|
||||
+ mSecurityCategory = screen.findPreference(PREF_KEY_SECURITY_CATEGORY);
|
||||
+ updatePreferenceState();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isAvailable() {
|
||||
+ mIsAdmin = mUm.isAdminUser();
|
||||
+ return mIsAdmin;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getPreferenceKey() {
|
||||
+ return KEY_AUTO_REBOOT;
|
||||
+ }
|
||||
+
|
||||
+ // TODO: should we use onCreatePreferences() instead?
|
||||
+ private void updatePreferenceState() {
|
||||
+ if (mSecurityCategory == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (mIsAdmin) {
|
||||
+ ListPreference autoReboot =
|
||||
+ (ListPreference) mSecurityCategory.findPreference(KEY_AUTO_REBOOT);
|
||||
+ autoReboot.setValue(Long.toString(Settings.Global.getLong(
|
||||
+ mContext.getContentResolver(), Settings.Global.SETTINGS_REBOOT_AFTER_TIMEOUT, 0)));
|
||||
+ } else {
|
||||
+ mSecurityCategory.removePreference(
|
||||
+ mSecurityCategory.findPreference(KEY_AUTO_REBOOT));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onResume() {
|
||||
+ updatePreferenceState();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean onPreferenceChange(Preference preference, Object value) {
|
||||
+ final String key = preference.getKey();
|
||||
+ if (KEY_AUTO_REBOOT.equals(key) && mIsAdmin) {
|
||||
+ long timeout = Long.parseLong((String) value);
|
||||
+ Settings.Global.putLong(mContext.getContentResolver(), Settings.Global.SETTINGS_REBOOT_AFTER_TIMEOUT, timeout);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/com/android/settings/security/SecuritySettings.java b/src/com/android/settings/security/SecuritySettings.java
|
||||
index 797d3ec621e..3f0645f7dd8 100644
|
||||
--- a/src/com/android/settings/security/SecuritySettings.java
|
||||
+++ b/src/com/android/settings/security/SecuritySettings.java
|
||||
@@ -114,6 +114,7 @@ public class SecuritySettings extends DashboardFragment {
|
||||
securityPreferenceControllers.add(new CombinedBiometricStatusPreferenceController(
|
||||
context, lifecycle));
|
||||
securityPreferenceControllers.add(new ChangeScreenLockPreferenceController(context, host));
|
||||
+ securityPreferenceControllers.add(new AutoRebootPreferenceController(context));
|
||||
controllers.add(new PreferenceCategoryController(context, SECURITY_CATEGORY)
|
||||
.setChildren(securityPreferenceControllers));
|
||||
controllers.addAll(securityPreferenceControllers);
|
||||
Vendored
+238
@@ -0,0 +1,238 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Oliver Scott <olivercscott@gmail.com>
|
||||
Date: Fri, 2 Oct 2020 12:37:30 -0400
|
||||
Subject: [PATCH] add bluetooth auto-turn-off setting
|
||||
|
||||
---
|
||||
res/values/arrays.xml | 44 +++++++
|
||||
res/values/strings.xml | 19 +++
|
||||
res/xml/connected_devices.xml | 8 ++
|
||||
.../BluetoothTimeoutPreferenceController.java | 115 ++++++++++++++++++
|
||||
4 files changed, 186 insertions(+)
|
||||
create mode 100644 src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java
|
||||
|
||||
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
|
||||
index c5df677975d..1a2796ad276 100644
|
||||
--- a/res/values/arrays.xml
|
||||
+++ b/res/values/arrays.xml
|
||||
@@ -18,6 +18,50 @@
|
||||
*/
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
+ <!-- Bluetooth settings. The delay in inactivity before bluetooth is turned off. These are shown in a list dialog. -->
|
||||
+ <string-array name="bluetooth_timeout_entries">
|
||||
+ <item>@string/bluetooth_timeout_summary_never</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_15secs</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_30secs</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_1min</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_2mins</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_5mins</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_10mins</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_30mins</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_1hour</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_2hours</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_4hours</item>
|
||||
+ <item>@string/bluetooth_timeout_summary_8hours</item>
|
||||
+ </string-array>
|
||||
+
|
||||
+ <!-- Do not translate. -->
|
||||
+ <string-array name="bluetooth_timeout_values" translatable="false">
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>0</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>15000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>30000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>60000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>120000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>300000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>600000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>1800000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>3600000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>7200000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>14400000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>28800000</item>
|
||||
+ </string-array>
|
||||
+
|
||||
<!-- Display settings. The delay in inactivity before the screen is turned off. These are shown in a list dialog. -->
|
||||
<string-array name="screen_timeout_entries">
|
||||
<item>15 seconds</item>
|
||||
diff --git a/res/values/strings.xml b/res/values/strings.xml
|
||||
index b7105938345..cf41793fd42 100644
|
||||
--- a/res/values/strings.xml
|
||||
+++ b/res/values/strings.xml
|
||||
@@ -27,6 +27,25 @@
|
||||
<!-- Used in confirmation dialogs as the action that the user will tap to turn on the feature. [CHAR LIMIT=40]-->
|
||||
<string name="confirmation_turn_on">Turn on</string>
|
||||
|
||||
+ <!-- Connected devices screen, setting option name to change bluetooth timeout -->
|
||||
+ <string name="bluetooth_timeout">Bluetooth timeout</string>
|
||||
+
|
||||
+ <!-- Connected devices screen, setting option summary to change bluetooth timeout -->
|
||||
+ <string name="bluetooth_timeout_summary">Bluetooth will turn off after <xliff:g id="timeout_description">%1$s</xliff:g> if no devices connected</string>
|
||||
+ <string name="bluetooth_timeout_summary2">Do not automatically turn off Bluetooth</string>
|
||||
+ <string name="bluetooth_timeout_summary_never">Never</string>
|
||||
+ <string name="bluetooth_timeout_summary_15secs">15 seconds</string>
|
||||
+ <string name="bluetooth_timeout_summary_30secs">30 seconds</string>
|
||||
+ <string name="bluetooth_timeout_summary_1min">1 minute</string>
|
||||
+ <string name="bluetooth_timeout_summary_2mins">2 minutes</string>
|
||||
+ <string name="bluetooth_timeout_summary_5mins">5 minutes</string>
|
||||
+ <string name="bluetooth_timeout_summary_10mins">10 minutes</string>
|
||||
+ <string name="bluetooth_timeout_summary_30mins">30 minutes</string>
|
||||
+ <string name="bluetooth_timeout_summary_1hour">1 hour</string>
|
||||
+ <string name="bluetooth_timeout_summary_2hours">2 hours</string>
|
||||
+ <string name="bluetooth_timeout_summary_4hours">4 hours</string>
|
||||
+ <string name="bluetooth_timeout_summary_8hours">8 hours</string>
|
||||
+
|
||||
<!-- Device Info screen. Used for a status item's value when the proper value is not known -->
|
||||
<string name="device_info_default">Unknown</string>
|
||||
<!-- String for removal of sensitive info on about, depending on tap -->
|
||||
diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml
|
||||
index 95aa8773c68..d66915369e4 100644
|
||||
--- a/res/xml/connected_devices.xml
|
||||
+++ b/res/xml/connected_devices.xml
|
||||
@@ -60,6 +60,14 @@
|
||||
settings:useAdminDisabledSummary="true"
|
||||
settings:userRestriction="no_config_bluetooth" />
|
||||
|
||||
+ <androidx.preference.ListPreference
|
||||
+ android:key="bluetooth_timeout"
|
||||
+ android:title="@string/bluetooth_timeout"
|
||||
+ android:summary="@string/summary_placeholder"
|
||||
+ android:entries="@array/bluetooth_timeout_entries"
|
||||
+ android:entryValues="@array/bluetooth_timeout_values"
|
||||
+ settings:controller="com.android.settings.bluetooth.BluetoothTimeoutPreferenceController"/>
|
||||
+
|
||||
<PreferenceCategory
|
||||
android:key="previously_connected_devices"
|
||||
android:title="@string/connected_device_saved_title"
|
||||
diff --git a/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java
|
||||
new file mode 100644
|
||||
index 00000000000..244147948aa
|
||||
--- /dev/null
|
||||
+++ b/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java
|
||||
@@ -0,0 +1,115 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2020 The Calyx Institute
|
||||
+ *
|
||||
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
+ * you may not use this file except in compliance with the License.
|
||||
+ * You may obtain a copy of the License at
|
||||
+ *
|
||||
+ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
+ *
|
||||
+ * Unless required by applicable law or agreed to in writing, software
|
||||
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
+ * See the License for the specific language governing permissions and
|
||||
+ * limitations under the License.
|
||||
+ */
|
||||
+
|
||||
+package com.android.settings.bluetooth;
|
||||
+
|
||||
+import android.bluetooth.BluetoothAdapter;
|
||||
+import android.content.Context;
|
||||
+import android.provider.Settings;
|
||||
+import android.util.Log;
|
||||
+
|
||||
+import androidx.preference.ListPreference;
|
||||
+import androidx.preference.Preference;
|
||||
+
|
||||
+import com.android.settings.R;
|
||||
+import com.android.settings.core.BasePreferenceController;
|
||||
+import com.android.settings.core.PreferenceControllerMixin;
|
||||
+
|
||||
+public class BluetoothTimeoutPreferenceController extends BasePreferenceController implements
|
||||
+ PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
|
||||
+ private static final String TAG = "BluetoothTimeoutPrefCtrl";
|
||||
+
|
||||
+ public static final int FALLBACK_BLUETOOTH_TIMEOUT_VALUE = 0;
|
||||
+
|
||||
+ private final String mBluetoothTimeoutKey;
|
||||
+
|
||||
+ protected BluetoothAdapter mBluetoothAdapter;
|
||||
+
|
||||
+ public BluetoothTimeoutPreferenceController(Context context, String key) {
|
||||
+ super(context, key);
|
||||
+ mBluetoothTimeoutKey = key;
|
||||
+
|
||||
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
+ if (mBluetoothAdapter == null) {
|
||||
+ Log.e(TAG, "Bluetooth is not supported on this device");
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int getAvailabilityStatus() {
|
||||
+ return mBluetoothAdapter != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getPreferenceKey() {
|
||||
+ return mBluetoothTimeoutKey;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void updateState(Preference preference) {
|
||||
+ final ListPreference timeoutListPreference = (ListPreference) preference;
|
||||
+ final long currentTimeout = Settings.Global.getLong(mContext.getContentResolver(),
|
||||
+ Settings.Global.BLUETOOTH_OFF_TIMEOUT, FALLBACK_BLUETOOTH_TIMEOUT_VALUE);
|
||||
+ timeoutListPreference.setValue(String.valueOf(currentTimeout));
|
||||
+ updateTimeoutPreferenceDescription(timeoutListPreference,
|
||||
+ Long.parseLong(timeoutListPreference.getValue()));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
+ try {
|
||||
+ long value = Long.parseLong((String) newValue);
|
||||
+ Settings.Global.putLong(mContext.getContentResolver(), Settings.Global.BLUETOOTH_OFF_TIMEOUT, value);
|
||||
+ updateTimeoutPreferenceDescription((ListPreference) preference, value);
|
||||
+ } catch (NumberFormatException e) {
|
||||
+ Log.e(TAG, "could not persist bluetooth timeout setting", e);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ public static CharSequence getTimeoutDescription(
|
||||
+ long currentTimeout, CharSequence[] entries, CharSequence[] values) {
|
||||
+ if (currentTimeout < 0 || entries == null || values == null
|
||||
+ || values.length != entries.length) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ for (int i = 0; i < values.length; i++) {
|
||||
+ long timeout = Long.parseLong(values[i].toString());
|
||||
+ if (currentTimeout == timeout) {
|
||||
+ return entries[i];
|
||||
+ }
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ private void updateTimeoutPreferenceDescription(ListPreference preference,
|
||||
+ long currentTimeout) {
|
||||
+ final CharSequence[] entries = preference.getEntries();
|
||||
+ final CharSequence[] values = preference.getEntryValues();
|
||||
+ final CharSequence timeoutDescription = getTimeoutDescription(
|
||||
+ currentTimeout, entries, values);
|
||||
+ String summary = "";
|
||||
+ if (timeoutDescription != null) {
|
||||
+ if (currentTimeout != 0)
|
||||
+ summary = mContext.getString(R.string.bluetooth_timeout_summary, timeoutDescription);
|
||||
+ else
|
||||
+ summary = mContext.getString(R.string.bluetooth_timeout_summary2);
|
||||
+ }
|
||||
+ preference.setSummary(summary);
|
||||
+ }
|
||||
+}
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Oliver Scott <olivercscott@gmail.com>
|
||||
Date: Thu, 25 Feb 2021 11:31:14 -0500
|
||||
Subject: [PATCH] add Wi-Fi timeout feature
|
||||
|
||||
---
|
||||
res/values/arrays.xml | 44 +++++++
|
||||
res/values/strings.xml | 19 +++
|
||||
res/xml/wifi_configure_settings.xml | 8 ++
|
||||
.../wifi/WifiTimeoutPreferenceController.java | 115 ++++++++++++++++++
|
||||
4 files changed, 186 insertions(+)
|
||||
create mode 100644 src/com/android/settings/wifi/WifiTimeoutPreferenceController.java
|
||||
|
||||
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
|
||||
index 1a2796ad276..9344280a905 100644
|
||||
--- a/res/values/arrays.xml
|
||||
+++ b/res/values/arrays.xml
|
||||
@@ -62,6 +62,50 @@
|
||||
<item>28800000</item>
|
||||
</string-array>
|
||||
|
||||
+ <!-- Wifi settings. The delay in inactivity before wifi is turned off. These are shown in a list dialog. -->
|
||||
+ <string-array name="wifi_timeout_entries">
|
||||
+ <item>@string/wifi_timeout_summary_never</item>
|
||||
+ <item>@string/wifi_timeout_summary_15secs</item>
|
||||
+ <item>@string/wifi_timeout_summary_30secs</item>
|
||||
+ <item>@string/wifi_timeout_summary_1min</item>
|
||||
+ <item>@string/wifi_timeout_summary_2mins</item>
|
||||
+ <item>@string/wifi_timeout_summary_5mins</item>
|
||||
+ <item>@string/wifi_timeout_summary_10mins</item>
|
||||
+ <item>@string/wifi_timeout_summary_30mins</item>
|
||||
+ <item>@string/wifi_timeout_summary_1hour</item>
|
||||
+ <item>@string/wifi_timeout_summary_2hours</item>
|
||||
+ <item>@string/wifi_timeout_summary_4hours</item>
|
||||
+ <item>@string/wifi_timeout_summary_8hours</item>
|
||||
+ </string-array>
|
||||
+
|
||||
+ <!-- Do not translate. -->
|
||||
+ <string-array name="wifi_timeout_values" translatable="false">
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>0</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>15000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>30000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>60000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>120000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>300000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>600000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>1800000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>3600000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>7200000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>14400000</item>
|
||||
+ <!-- Do not translate. -->
|
||||
+ <item>28800000</item>
|
||||
+ </string-array>
|
||||
+
|
||||
<!-- Display settings. The delay in inactivity before the screen is turned off. These are shown in a list dialog. -->
|
||||
<string-array name="screen_timeout_entries">
|
||||
<item>15 seconds</item>
|
||||
diff --git a/res/values/strings.xml b/res/values/strings.xml
|
||||
index cf41793fd42..1c3f864c956 100644
|
||||
--- a/res/values/strings.xml
|
||||
+++ b/res/values/strings.xml
|
||||
@@ -46,6 +46,25 @@
|
||||
<string name="bluetooth_timeout_summary_4hours">4 hours</string>
|
||||
<string name="bluetooth_timeout_summary_8hours">8 hours</string>
|
||||
|
||||
+ <!-- screen, setting option name to change wifi timeout -->
|
||||
+ <string name="wifi_timeout">Turn off Wi-Fi automatically</string>
|
||||
+
|
||||
+ <!-- screen, setting option summary to change wifi timeout -->
|
||||
+ <string name="wifi_timeout_summary">Wi-Fi will turn off after <xliff:g id="timeout_description">%1$s</xliff:g> if no network connected</string>
|
||||
+ <string name="wifi_timeout_summary2">Disabled</string>
|
||||
+ <string name="wifi_timeout_summary_never">Never</string>
|
||||
+ <string name="wifi_timeout_summary_15secs">15 seconds</string>
|
||||
+ <string name="wifi_timeout_summary_30secs">30 seconds</string>
|
||||
+ <string name="wifi_timeout_summary_1min">1 minute</string>
|
||||
+ <string name="wifi_timeout_summary_2mins">2 minutes</string>
|
||||
+ <string name="wifi_timeout_summary_5mins">5 minutes</string>
|
||||
+ <string name="wifi_timeout_summary_10mins">10 minutes</string>
|
||||
+ <string name="wifi_timeout_summary_30mins">30 minutes</string>
|
||||
+ <string name="wifi_timeout_summary_1hour">1 hour</string>
|
||||
+ <string name="wifi_timeout_summary_2hours">2 hours</string>
|
||||
+ <string name="wifi_timeout_summary_4hours">4 hours</string>
|
||||
+ <string name="wifi_timeout_summary_8hours">8 hours</string>
|
||||
+
|
||||
<!-- Device Info screen. Used for a status item's value when the proper value is not known -->
|
||||
<string name="device_info_default">Unknown</string>
|
||||
<!-- String for removal of sensitive info on about, depending on tap -->
|
||||
diff --git a/res/xml/wifi_configure_settings.xml b/res/xml/wifi_configure_settings.xml
|
||||
index 5866c5898d9..663e3109d76 100644
|
||||
--- a/res/xml/wifi_configure_settings.xml
|
||||
+++ b/res/xml/wifi_configure_settings.xml
|
||||
@@ -26,6 +26,14 @@
|
||||
android:summary="@string/wifi_wakeup_summary"
|
||||
settings:controller="com.android.settings.wifi.WifiWakeupPreferenceController"/>
|
||||
|
||||
+ <ListPreference
|
||||
+ android:key="wifi_timeout"
|
||||
+ android:title="@string/wifi_timeout"
|
||||
+ android:summary="@string/wifi_timeout_summary"
|
||||
+ android:entries="@array/wifi_timeout_entries"
|
||||
+ android:entryValues="@array/wifi_timeout_values"
|
||||
+ settings:controller="com.android.settings.wifi.WifiTimeoutPreferenceController"/>
|
||||
+
|
||||
<SwitchPreferenceCompat
|
||||
android:key="notify_open_networks"
|
||||
android:title="@string/wifi_notify_open_networks"
|
||||
diff --git a/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java b/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java
|
||||
new file mode 100644
|
||||
index 00000000000..7116c905190
|
||||
--- /dev/null
|
||||
+++ b/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java
|
||||
@@ -0,0 +1,115 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2020 The Calyx Institute
|
||||
+ *
|
||||
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
+ * you may not use this file except in compliance with the License.
|
||||
+ * You may obtain a copy of the License at
|
||||
+ *
|
||||
+ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
+ *
|
||||
+ * Unless required by applicable law or agreed to in writing, software
|
||||
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
+ * See the License for the specific language governing permissions and
|
||||
+ * limitations under the License.
|
||||
+ */
|
||||
+
|
||||
+package com.android.settings.wifi;
|
||||
+
|
||||
+import android.content.Context;
|
||||
+import android.net.wifi.WifiManager;
|
||||
+import android.provider.Settings;
|
||||
+import android.util.Log;
|
||||
+
|
||||
+import androidx.preference.ListPreference;
|
||||
+import androidx.preference.Preference;
|
||||
+
|
||||
+import com.android.settings.R;
|
||||
+import com.android.settings.core.BasePreferenceController;
|
||||
+import com.android.settings.core.PreferenceControllerMixin;
|
||||
+
|
||||
+public class WifiTimeoutPreferenceController extends BasePreferenceController implements
|
||||
+ PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
|
||||
+ private static final String TAG = "WifiTimeoutPrefCtrl";
|
||||
+
|
||||
+ public static final int FALLBACK_WIFI_TIMEOUT_VALUE = 0;
|
||||
+
|
||||
+ private final String mWifiTimeoutKey;
|
||||
+
|
||||
+ protected WifiManager mWifiManager;
|
||||
+
|
||||
+ public WifiTimeoutPreferenceController(Context context, String key) {
|
||||
+ super(context, key);
|
||||
+ mWifiTimeoutKey = key;
|
||||
+
|
||||
+ mWifiManager = context.getSystemService(WifiManager.class);
|
||||
+ if (mWifiManager == null) {
|
||||
+ Log.e(TAG, "Wifi is not supported on this device");
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int getAvailabilityStatus() {
|
||||
+ return mWifiManager != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getPreferenceKey() {
|
||||
+ return mWifiTimeoutKey;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void updateState(Preference preference) {
|
||||
+ final ListPreference timeoutListPreference = (ListPreference) preference;
|
||||
+ final long currentTimeout = Settings.Global.getLong(mContext.getContentResolver(),
|
||||
+ Settings.Global.WIFI_OFF_TIMEOUT, FALLBACK_WIFI_TIMEOUT_VALUE);
|
||||
+ timeoutListPreference.setValue(String.valueOf(currentTimeout));
|
||||
+ updateTimeoutPreferenceDescription(timeoutListPreference,
|
||||
+ Long.parseLong(timeoutListPreference.getValue()));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
+ try {
|
||||
+ long value = Long.parseLong((String) newValue);
|
||||
+ Settings.Global.putLong(mContext.getContentResolver(), Settings.Global.WIFI_OFF_TIMEOUT, value);
|
||||
+ updateTimeoutPreferenceDescription((ListPreference) preference, value);
|
||||
+ } catch (NumberFormatException e) {
|
||||
+ Log.e(TAG, "could not persist wifi timeout setting", e);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ public static CharSequence getTimeoutDescription(
|
||||
+ long currentTimeout, CharSequence[] entries, CharSequence[] values) {
|
||||
+ if (currentTimeout < 0 || entries == null || values == null
|
||||
+ || values.length != entries.length) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ for (int i = 0; i < values.length; i++) {
|
||||
+ long timeout = Long.parseLong(values[i].toString());
|
||||
+ if (currentTimeout == timeout) {
|
||||
+ return entries[i];
|
||||
+ }
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ private void updateTimeoutPreferenceDescription(ListPreference preference,
|
||||
+ long currentTimeout) {
|
||||
+ final CharSequence[] entries = preference.getEntries();
|
||||
+ final CharSequence[] values = preference.getEntryValues();
|
||||
+ final CharSequence timeoutDescription = getTimeoutDescription(
|
||||
+ currentTimeout, entries, values);
|
||||
+ String summary = "";
|
||||
+ if (timeoutDescription != null) {
|
||||
+ if (currentTimeout != 0)
|
||||
+ summary = mContext.getString(R.string.wifi_timeout_summary, timeoutDescription);
|
||||
+ else
|
||||
+ summary = mContext.getString(R.string.wifi_timeout_summary2);
|
||||
+ }
|
||||
+ preference.setSummary(summary);
|
||||
+ }
|
||||
+}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Port AXP auto-reboot, Wi-Fi/Bluetooth timeouts, and sensitive QS tiles.
|
||||
|
||||
AXP Patch.sh order @ 7b735844, applied with --fuzz=0:
|
||||
0014-Automatic_Reboot.patch
|
||||
0015-System_Server_Extensions.patch
|
||||
0015-WiFi_Timeout.patch
|
||||
0015-Bluetooth_Timeout.patch
|
||||
0015-Bluetooth_Timeout-Fix.patch
|
||||
0041-tile_restrictions.patch
|
||||
|
||||
A16 adaptations after the exact hunks:
|
||||
* privapp-permissions gained READ_SYSTEM_GRAMMATICAL_GENDER, so the REBOOT
|
||||
line is inserted between REAL_GET_TASKS and REQUEST_NETWORK_SCORES.
|
||||
* onKeyguardExitFinished() now dismisses the keyguard interactor before the
|
||||
closing brace. The AXP cancel stays immediately after sendUserPresentBroadcast().
|
||||
* QSTileImpl no longer imports QsInCompose, so KeyguardStateController is
|
||||
imported after QSLogger.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
TREE = pathlib.Path(os.environ.get("SUNFISH_A16_TREE", "/srv/rom/src/lineage"))
|
||||
PATCH_DIR = pathlib.Path(__file__).resolve().parent / "a15-src" / "frameworks_base"
|
||||
BASE = TREE / "frameworks/base"
|
||||
|
||||
XML = BASE / "data/etc/com.android.systemui.xml"
|
||||
KEYGUARD = (
|
||||
BASE / "packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java"
|
||||
)
|
||||
QSTILE = BASE / "packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java"
|
||||
|
||||
XML_OLD = """ <permission name="android.permission.REAL_GET_TASKS"/>
|
||||
<permission name="android.permission.REQUEST_NETWORK_SCORES"/>
|
||||
"""
|
||||
XML_NEW = """ <permission name="android.permission.REAL_GET_TASKS"/>
|
||||
<permission name="android.permission.REBOOT"/>
|
||||
<permission name="android.permission.REQUEST_NETWORK_SCORES"/>
|
||||
"""
|
||||
|
||||
KEYGUARD_OLD = """ adjustStatusBarLocked();
|
||||
sendUserPresentBroadcast();
|
||||
|
||||
if (!KeyguardWmStateRefactor.isEnabled()) {
|
||||
mKeyguardInteractor.dismissKeyguard();
|
||||
}
|
||||
"""
|
||||
KEYGUARD_NEW = """ adjustStatusBarLocked();
|
||||
sendUserPresentBroadcast();
|
||||
cancelDoRebootForOwnerAfterTimeoutIfEnabled();
|
||||
|
||||
if (!KeyguardWmStateRefactor.isEnabled()) {
|
||||
mKeyguardInteractor.dismissKeyguard();
|
||||
}
|
||||
"""
|
||||
|
||||
IMPORT_OLD = "import com.android.systemui.qs.logging.QSLogger;\n"
|
||||
IMPORT_NEW = (
|
||||
"import com.android.systemui.qs.logging.QSLogger;\n"
|
||||
"import com.android.systemui.statusbar.policy.KeyguardStateController;\n"
|
||||
)
|
||||
|
||||
|
||||
def replace_once(path: pathlib.Path, old: str, new: str) -> None:
|
||||
text = path.read_text()
|
||||
if new in text:
|
||||
return
|
||||
if old not in text:
|
||||
sys.exit(f"anchor missing in {path}")
|
||||
path.write_text(text.replace(old, new, 1))
|
||||
|
||||
|
||||
def apply_patch(name: str) -> None:
|
||||
patch = PATCH_DIR / name
|
||||
if not patch.is_file():
|
||||
sys.exit(f"missing {patch}")
|
||||
result = subprocess.run(
|
||||
[
|
||||
"patch",
|
||||
"-p1",
|
||||
"--forward",
|
||||
"--fuzz=0",
|
||||
"--batch",
|
||||
"--no-backup-if-mismatch",
|
||||
"-d",
|
||||
str(BASE),
|
||||
"-i",
|
||||
str(patch),
|
||||
],
|
||||
check=False,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
sys.stdout.write(result.stdout)
|
||||
sys.stdout.write(result.stderr)
|
||||
if result.returncode != 0 and "FAILED" in result.stderr + result.stdout:
|
||||
print(f"patch_incomplete: {name}")
|
||||
elif result.returncode != 0:
|
||||
print(f"patch_exit_{result.returncode}: {name}")
|
||||
|
||||
|
||||
def clean_rejects() -> None:
|
||||
for path in (XML, KEYGUARD, QSTILE):
|
||||
for suffix in (".rej", ".orig"):
|
||||
extra = path.with_name(path.name + suffix)
|
||||
if extra.exists():
|
||||
extra.unlink()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
apply_patch("0014-Automatic_Reboot.patch")
|
||||
replace_once(XML, XML_OLD, XML_NEW)
|
||||
replace_once(KEYGUARD, KEYGUARD_OLD, KEYGUARD_NEW)
|
||||
for name in (
|
||||
"0015-System_Server_Extensions.patch",
|
||||
"0015-WiFi_Timeout.patch",
|
||||
"0015-Bluetooth_Timeout.patch",
|
||||
"0015-Bluetooth_Timeout-Fix.patch",
|
||||
"0041-tile_restrictions.patch",
|
||||
):
|
||||
apply_patch(name)
|
||||
replace_once(QSTILE, IMPORT_OLD, IMPORT_NEW)
|
||||
clean_rejects()
|
||||
required = [
|
||||
"settings_reboot_after_timeout",
|
||||
"android.permission.REBOOT",
|
||||
"cancelDoRebootForOwnerAfterTimeoutIfEnabled();",
|
||||
"class SystemServerExt",
|
||||
"class WifiAutoOff",
|
||||
"class BluetoothAutoOff",
|
||||
"import android.bluetooth.BluetoothProfile;",
|
||||
"WIFI_OFF_TIMEOUT",
|
||||
"BLUETOOTH_OFF_TIMEOUT",
|
||||
"isAllowedWhenLocked",
|
||||
"KeyguardStateController",
|
||||
]
|
||||
blob = "\n".join(
|
||||
path.read_text()
|
||||
for path in (
|
||||
BASE / "core/java/android/provider/Settings.java",
|
||||
XML,
|
||||
KEYGUARD,
|
||||
BASE / "services/core/java/com/android/server/ext/SystemServerExt.java",
|
||||
BASE / "services/core/java/com/android/server/ext/WifiAutoOff.java",
|
||||
BASE / "services/core/java/com/android/server/ext/BluetoothAutoOff.java",
|
||||
QSTILE,
|
||||
)
|
||||
)
|
||||
missing = [item for item in required if item not in blob]
|
||||
if missing:
|
||||
sys.exit("missing after apply: " + ", ".join(missing))
|
||||
print("done: SUNFISH_A16_009_A15_REBOOT_TIMEOUTS")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Settings UI for the AXP reboot and radio timeouts.
|
||||
|
||||
Apply after apply_009_a15_reboot_timeouts.py. Order matches AXP:
|
||||
bluetooth arrays land before the Wi-Fi arrays, which insert after them.
|
||||
|
||||
0006 needs fuzz 2. A16's strings.xml has an empty line where the A15
|
||||
patch has a single space, and connected_devices.xml shifted by a few
|
||||
lines. The preference still lands on the connected-devices screen.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
TREE = pathlib.Path(os.environ.get("SUNFISH_A16_TREE", "/srv/rom/src/lineage"))
|
||||
PATCH_DIR = pathlib.Path(__file__).resolve().parent / "a15-src" / "settings"
|
||||
SETTINGS = TREE / "packages/apps/Settings"
|
||||
|
||||
|
||||
MARKERS = (
|
||||
"res/xml/security_dashboard_settings.xml",
|
||||
"res/xml/connected_devices.xml",
|
||||
"res/xml/wifi_configure_settings.xml",
|
||||
"res/values/arrays.xml",
|
||||
"res/values/strings.xml",
|
||||
)
|
||||
|
||||
|
||||
def already_applied() -> bool:
|
||||
try:
|
||||
blob = "\n".join((SETTINGS / name).read_text() for name in MARKERS)
|
||||
except OSError:
|
||||
return False
|
||||
needles = (
|
||||
'android:key="auto_reboot"',
|
||||
'android:key="bluetooth_timeout"',
|
||||
'android:key="wifi_timeout"',
|
||||
'name="auto_reboot_entries"',
|
||||
'name="bluetooth_timeout_entries"',
|
||||
'name="wifi_timeout_entries"',
|
||||
)
|
||||
return all(needle in blob for needle in needles)
|
||||
|
||||
|
||||
def apply_patch(name: str, fuzz: str) -> None:
|
||||
result = subprocess.run(
|
||||
[
|
||||
"patch",
|
||||
"-p1",
|
||||
"--forward",
|
||||
f"--fuzz={fuzz}",
|
||||
"--batch",
|
||||
"--no-backup-if-mismatch",
|
||||
"-d",
|
||||
str(SETTINGS),
|
||||
"-i",
|
||||
str(PATCH_DIR / name),
|
||||
],
|
||||
check=False,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
sys.stdout.write(result.stdout)
|
||||
sys.stdout.write(result.stderr)
|
||||
for path in SETTINGS.rglob("*.rej"):
|
||||
path.unlink()
|
||||
if "FAILED" in result.stdout + result.stderr and not already_applied():
|
||||
sys.exit(f"failed: {name}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if already_applied():
|
||||
print("done: SUNFISH_A16_009_A15_SETTINGS_TIMEOUTS")
|
||||
return
|
||||
apply_patch("0005-Automatic_Reboot.patch", "0")
|
||||
apply_patch("0006-Bluetooth_Timeout.patch", "2")
|
||||
apply_patch("0007-WiFi_Timeout.patch", "0")
|
||||
if not already_applied():
|
||||
sys.exit("missing settings timeout markers")
|
||||
print("done: SUNFISH_A16_009_A15_SETTINGS_TIMEOUTS")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Port four AXP LOS 22.2 userspace changes onto LOS 23 / Android 16.
|
||||
|
||||
Sources, Patch.sh @ 7b735844 (AXP A15):
|
||||
android_frameworks_base/0004-Fingerprint_Lockout.patch
|
||||
android_frameworks_base/0007-Always_Restict_Serial.patch
|
||||
android_frameworks_base/0042-minimal_screenshot_exif.patch
|
||||
android_bootable_recovery/0001-No_SerialNum_Restrictions.patch
|
||||
|
||||
Sunfish fingerprint HAL is HIDL 2.2, and HidlToAidlSensorAdapter still uses
|
||||
LockoutFrameworkImpl, so the AXP constant change is the live path.
|
||||
|
||||
A16 adaptations, behavior unchanged:
|
||||
* CheckPackageMetadata returns bool, so a serial constraint returns false.
|
||||
The A15 patch returned INSTALL_ERROR, which does not compile here.
|
||||
* Screenshot EXIF drops the now-unused Build and DateTimeFormatter imports.
|
||||
A16 ErrorProne treats unused imports as errors.
|
||||
|
||||
Already satisfied on this tree, so this script does not touch them:
|
||||
* 0028-Remove_Legacy_Package_Query: A16 already removed the
|
||||
MATCH_UNINSTALLED_PACKAGES / MATCH_ANY_USER branch.
|
||||
* system/core 0002-ptrace_scope: apply_009_yama_ptrace.py already writes
|
||||
persist.native_debug.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
TREE = pathlib.Path(os.environ.get("SUNFISH_A16_TREE", "/srv/rom/src/lineage"))
|
||||
|
||||
LOCKOUT = (
|
||||
TREE
|
||||
/ "frameworks/base/services/core/java/com/android/server/biometrics"
|
||||
/ "sensors/fingerprint/hidl/LockoutFrameworkImpl.java"
|
||||
)
|
||||
AMS = (
|
||||
TREE
|
||||
/ "frameworks/base/services/core/java/com/android/server/am"
|
||||
/ "ActivityManagerService.java"
|
||||
)
|
||||
EXIF = (
|
||||
TREE
|
||||
/ "frameworks/base/packages/SystemUI/src/com/android/systemui"
|
||||
/ "screenshot/ImageExporter.java"
|
||||
)
|
||||
RECOVERY = TREE / "bootable/recovery/install/install.cpp"
|
||||
|
||||
LOCKOUT_OLD = "private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;"
|
||||
LOCKOUT_NEW = "private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 5;"
|
||||
|
||||
SERIAL_OLD = """ // We deprecated Build.SERIAL and it is not accessible to
|
||||
// Instant Apps and target APIs higher than O MR1. Since access to the serial
|
||||
// is now behind a permission we push down the value.
|
||||
final String buildSerial = (!appInfo.isInstantApp()
|
||||
&& appInfo.targetSdkVersion < Build.VERSION_CODES.P)
|
||||
? sTheRealBuildSerial : Build.UNKNOWN;
|
||||
"""
|
||||
SERIAL_NEW = """ // AXP/GrapheneOS 0007: deprecated Build.SERIAL stays UNKNOWN.
|
||||
// Callers use Build.getSerial(), which requires READ_PHONE_STATE.
|
||||
final String buildSerial = Build.UNKNOWN;
|
||||
"""
|
||||
|
||||
EXIF_OLD = """ exif.setAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID, uniqueId.toString());
|
||||
|
||||
exif.setAttribute(ExifInterface.TAG_SOFTWARE, "Android " + Build.DISPLAY);
|
||||
exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, Integer.toString(width));
|
||||
exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, Integer.toString(height));
|
||||
|
||||
String dateTime = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(captureTime);
|
||||
String subSec = DateTimeFormatter.ofPattern("SSS").format(captureTime);
|
||||
String timeZone = DateTimeFormatter.ofPattern("xxx").format(captureTime);
|
||||
|
||||
exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, dateTime);
|
||||
exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL, subSec);
|
||||
exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, timeZone);
|
||||
"""
|
||||
EXIF_NEW = """ // AXP/Calyx 0042: width and height only. No OS name, unique id, or time.
|
||||
exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, Integer.toString(width));
|
||||
exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, Integer.toString(height));
|
||||
"""
|
||||
|
||||
RECOVERY_OLD = """ // We allow the package to not have any serialno; and we also allow it to carry multiple serial
|
||||
// numbers split by "|"; e.g. serialno=serialno1|serialno2|serialno3 ... We will fail the
|
||||
// verification if the device's serialno doesn't match any of these carried numbers.
|
||||
|
||||
auto pkg_serial_no = get_value(metadata, "serialno");
|
||||
if (!pkg_serial_no.empty()) {
|
||||
auto device_serial_no = android::base::GetProperty("ro.serialno", "");
|
||||
bool serial_number_match = false;
|
||||
for (const auto& number : android::base::Split(pkg_serial_no, "|")) {
|
||||
if (device_serial_no == android::base::Trim(number)) {
|
||||
serial_number_match = true;
|
||||
}
|
||||
}
|
||||
if (!serial_number_match) {
|
||||
LOG(ERROR) << "Package is for serial " << pkg_serial_no;
|
||||
return false;
|
||||
}
|
||||
} else if (ota_type == OtaType::BRICK) {
|
||||
"""
|
||||
RECOVERY_NEW = """ // AXP/GrapheneOS 0001: any serialno constraint is rejected.
|
||||
// A16 CheckPackageMetadata returns bool; A15 returned INSTALL_ERROR.
|
||||
auto pkg_serial_no = get_value(metadata, "serialno");
|
||||
if (!pkg_serial_no.empty()) {
|
||||
LOG(ERROR) << "Serial number constraint not permitted: " << pkg_serial_no;
|
||||
return false;
|
||||
} else if (ota_type == OtaType::BRICK) {
|
||||
"""
|
||||
|
||||
|
||||
def replace_once(path: pathlib.Path, old: str, new: str) -> None:
|
||||
text = path.read_text()
|
||||
if new in text:
|
||||
return
|
||||
if old not in text:
|
||||
sys.exit(f"anchor missing in {path}")
|
||||
path.write_text(text.replace(old, new, 1))
|
||||
|
||||
|
||||
def drop_import(text: str, import_line: str, still_used: str) -> str:
|
||||
if import_line not in text:
|
||||
return text
|
||||
body = text.replace(import_line + "\n", "", 1)
|
||||
if still_used in body:
|
||||
return text
|
||||
return body
|
||||
|
||||
|
||||
def apply_exif() -> None:
|
||||
text = EXIF.read_text()
|
||||
if EXIF_NEW in text:
|
||||
updated = text
|
||||
elif EXIF_OLD not in text:
|
||||
sys.exit(f"anchor missing in {EXIF}")
|
||||
else:
|
||||
updated = text.replace(EXIF_OLD, EXIF_NEW, 1)
|
||||
updated = drop_import(updated, "import android.os.Build;", "Build.")
|
||||
updated = drop_import(
|
||||
updated, "import java.time.format.DateTimeFormatter;", "DateTimeFormatter"
|
||||
)
|
||||
EXIF.write_text(updated)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
replace_once(LOCKOUT, LOCKOUT_OLD, LOCKOUT_NEW)
|
||||
replace_once(AMS, SERIAL_OLD, SERIAL_NEW)
|
||||
apply_exif()
|
||||
replace_once(RECOVERY, RECOVERY_OLD, RECOVERY_NEW)
|
||||
print("done: SUNFISH_A16_009_A15_SMALL")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""009: AXP ship identity on LOS 23.2.
|
||||
|
||||
- AXP Patch.sh (LineageOS-22.2:405) replaces LINEAGE_BUILDTYPE := UNOFFICIAL
|
||||
with its own build type in vendor/lineage/config/*.mk. Same here, with PRO
|
||||
(GOLDEN ro.lineage.releasetype=PRO).
|
||||
- Drops the old TARGET_UNOFFICIAL_BUILD_ID insert from lineage_sunfish.mk.
|
||||
- Vendor SPL stays the LOS 23.2 2023-09-05 (AXP GOLDEN ships 2023-08-05).
|
||||
- build/make/core/config.mk only tags release-keys when the default cert is
|
||||
under vendor/lineage-priv/. Copies the project certs there (same keys, so
|
||||
an existing install updates without a wipe).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
TREE = pathlib.Path("/srv/rom/src/lineage")
|
||||
SRC_CERTS = TREE / "vendor/sunfish-a16/certs"
|
||||
DST_CERTS = TREE / "vendor/lineage-priv/keys"
|
||||
OLD_ID = (
|
||||
"# sunfish-a16: must precede vendor/lineage version.mk (via inherit below)\n"
|
||||
"TARGET_UNOFFICIAL_BUILD_ID := AXP-Resurection-Pro\n\n"
|
||||
)
|
||||
# LOS 23.2 writes 2023-09-05. AXP GOLDEN uses 2023-08-05, but keymaster key
|
||||
# blobs on an existing install are bound to the vendor patch level and refuse
|
||||
# a rollback: vdc cryptfs init_user0 fails and recovery asks for a wipe
|
||||
# (LAB 2026-10-07 17:15 build). Never lower the vendor SPL on an upgrade.
|
||||
VENDOR_SPL_LOS = (
|
||||
"# Set Vendor SPL to match platform\n"
|
||||
"VENDOR_SECURITY_PATCH = 2023-09-05\n"
|
||||
)
|
||||
VENDOR_SPL_ROLLBACK = (
|
||||
"# Stock sunfish vendor SPL (TQ3A.230805), same as AXP GOLDEN.\n"
|
||||
"# System PLATFORM_SECURITY_PATCH stays the LOS 23.2 ASB.\n"
|
||||
"VENDOR_SECURITY_PATCH = 2023-08-05\n"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
version = TREE / "vendor/lineage/config/version.mk"
|
||||
text = version.read_text()
|
||||
if "LINEAGE_BUILDTYPE := PRO\n" not in text:
|
||||
old = " LINEAGE_BUILDTYPE := UNOFFICIAL\n"
|
||||
if old not in text:
|
||||
sys.exit(f"anchor missing in {version}")
|
||||
version.write_text(text.replace(old, " LINEAGE_BUILDTYPE := PRO\n", 1))
|
||||
|
||||
device_mk = TREE / "device/google/sunfish/lineage_sunfish.mk"
|
||||
text = device_mk.read_text()
|
||||
if OLD_ID in text:
|
||||
device_mk.write_text(text.replace(OLD_ID, "", 1))
|
||||
if "TARGET_UNOFFICIAL_BUILD_ID" in device_mk.read_text():
|
||||
sys.exit(f"TARGET_UNOFFICIAL_BUILD_ID still set in {device_mk}")
|
||||
|
||||
common = TREE / "device/google/sunfish/device-common.mk"
|
||||
text = common.read_text()
|
||||
if VENDOR_SPL_ROLLBACK in text:
|
||||
text = text.replace(VENDOR_SPL_ROLLBACK, VENDOR_SPL_LOS, 1)
|
||||
common.write_text(text)
|
||||
if VENDOR_SPL_LOS not in text:
|
||||
sys.exit(f"vendor SPL is not 2023-09-05 in {common}")
|
||||
|
||||
if not (SRC_CERTS / "releasekey.pk8").is_file():
|
||||
sys.exit(f"missing {SRC_CERTS}/releasekey.pk8")
|
||||
DST_CERTS.mkdir(parents=True, exist_ok=True)
|
||||
for src in SRC_CERTS.iterdir():
|
||||
if src.name.startswith("avb"):
|
||||
continue
|
||||
dst = DST_CERTS / src.name
|
||||
if dst.is_symlink() or dst.exists():
|
||||
dst.unlink()
|
||||
if src.is_symlink():
|
||||
dst.symlink_to(src.readlink())
|
||||
else:
|
||||
shutil.copy2(src, dst)
|
||||
print("axp identity applied")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Hide /proc/cmdline from unprivileged processes.
|
||||
|
||||
AOSP and GOLDEN's init do this in first-stage init, immediately after
|
||||
mounting proc:
|
||||
|
||||
CHECKCALL(chmod("/proc/cmdline", 0440));
|
||||
|
||||
The failure text is exactly `chmod("/proc/cmdline", 0440) failed`, which
|
||||
is present in GOLDEN's /system/bin/init and absent from the LOS 23.2 init
|
||||
that left the file mode 0444. LOS 23.2 already chmod's /proc/bootconfig
|
||||
the same way. init.rc only chowns cmdline to root:radio.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
TREE = pathlib.Path(os.environ.get("ANDROID_BUILD_TOP", "/srv/rom/src/lineage"))
|
||||
INIT = TREE / "system/core/init/first_stage_init.cpp"
|
||||
OLD = """#undef MAKE_STR
|
||||
std::string cmdline;
|
||||
android::base::ReadFileToString("/proc/cmdline", &cmdline);
|
||||
"""
|
||||
NEW = """#undef MAKE_STR
|
||||
// Don't expose the raw commandline to unprivileged processes.
|
||||
CHECKCALL(chmod("/proc/cmdline", 0440));
|
||||
std::string cmdline;
|
||||
android::base::ReadFileToString("/proc/cmdline", &cmdline);
|
||||
"""
|
||||
|
||||
|
||||
def main() -> None:
|
||||
text = INIT.read_text()
|
||||
if NEW in text:
|
||||
print(f"already applied: {INIT}")
|
||||
return
|
||||
if OLD not in text:
|
||||
sys.exit(f"cmdline chmod anchor missing in {INIT}")
|
||||
INIT.write_text(text.replace(OLD, NEW, 1))
|
||||
print(f"patched cmdline mode: {INIT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Drop Google carrier apps and the AOSP IMS entitlement/RCS packages.
|
||||
|
||||
inherit-product merges PRODUCT_PACKAGES per makefile, so a filter-out never
|
||||
sees names added by sunfish-vendor.mk. Remove the package lines where they
|
||||
are added. CarrierSettings/*.pb copy rules stay: their tokens are paths, not
|
||||
the CarrierSettings module. Qualcomm radio packages stay (ims, atfwd,
|
||||
qcrilmsgtunnel, uceShimService, EuiccSupportPixel).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
TARGETS: tuple[tuple[str, tuple[str, ...], tuple[str, ...]], ...] = (
|
||||
(
|
||||
"vendor/google/sunfish/sunfish-vendor.mk",
|
||||
(
|
||||
"CarrierServices",
|
||||
"CarrierSettings",
|
||||
"CarrierWifi",
|
||||
"CarrierSetup",
|
||||
"RilConfigService",
|
||||
"grilservice",
|
||||
),
|
||||
(
|
||||
"EuiccSupportPixel",
|
||||
"EuiccSupportPixelPermissions",
|
||||
"ims",
|
||||
"QtiTelephonyService",
|
||||
"atfwd",
|
||||
"qcrilmsgtunnel",
|
||||
"uceShimService",
|
||||
),
|
||||
),
|
||||
(
|
||||
"device/google/sunfish/device-lineage.mk",
|
||||
("PresencePolling", "RcsService"),
|
||||
("GoogleParts",),
|
||||
),
|
||||
(
|
||||
"build/make/target/product/telephony_product.mk",
|
||||
("ImsServiceEntitlement",),
|
||||
("Dialer",),
|
||||
),
|
||||
)
|
||||
|
||||
_RCS_STUB = "# RCS\nPRODUCT_PACKAGES += \\\n\n"
|
||||
|
||||
|
||||
def _token(line: str) -> str:
|
||||
return line.strip().rstrip("\\").strip()
|
||||
|
||||
|
||||
def _has(text: str, name: str) -> bool:
|
||||
return any(_token(line) == name for line in text.splitlines())
|
||||
|
||||
|
||||
def apply_file(path: Path, packages: tuple[str, ...], keep: tuple[str, ...]) -> str:
|
||||
text = path.read_text()
|
||||
for name in keep:
|
||||
if not _has(text, name):
|
||||
raise SystemExit(f"FAIL: {path} lost {name}")
|
||||
removed = {name: 0 for name in packages}
|
||||
kept: list[str] = []
|
||||
for line in text.splitlines(keepends=True):
|
||||
token = _token(line)
|
||||
if token in removed:
|
||||
removed[token] += 1
|
||||
continue
|
||||
kept.append(line)
|
||||
if all(count == 0 for count in removed.values()):
|
||||
return "already"
|
||||
if any(count != 1 for count in removed.values()):
|
||||
raise SystemExit(f"FAIL: unexpected removals in {path}: {removed}")
|
||||
new = "".join(kept)
|
||||
if path.name == "device-lineage.mk":
|
||||
new = new.replace(_RCS_STUB, "", 1)
|
||||
for name in keep:
|
||||
if not _has(new, name):
|
||||
raise SystemExit(f"FAIL: edit would drop {name} from {path}")
|
||||
for name in packages:
|
||||
if _has(new, name):
|
||||
raise SystemExit(f"FAIL: {name} still listed in {path}")
|
||||
path.write_text(new)
|
||||
return "dropped"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
|
||||
results: list[str] = []
|
||||
for rel, packages, keep in TARGETS:
|
||||
path = top / rel
|
||||
if not path.is_file():
|
||||
print(f"FAIL: missing {path}", file=sys.stderr)
|
||||
return 1
|
||||
state = apply_file(path, packages, keep)
|
||||
results.append(f"{state} {rel}")
|
||||
print("OK: " + "; ".join(results))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Drop the Google eUICC LPA from the sunfish vendor makefile.
|
||||
|
||||
inherit-product merges PRODUCT_PACKAGES per makefile, so a filter-out in
|
||||
apps.mk never sees the names added by sunfish-vendor.mk. A Soong phony
|
||||
does not set PACKAGES.*.OVERRIDES. The 20261009T073753Z and 20261009T090831Z
|
||||
zips still installed both APKs. Remove the two entries where they are added.
|
||||
EuiccSupportPixel and EuiccSupportPixelPermissions stay.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PACKAGES = ("EuiccGoogle", "EuiccGoogleOverlay")
|
||||
KEEP = ("EuiccSupportPixel", "EuiccSupportPixelPermissions")
|
||||
|
||||
|
||||
def _package_line(line: str) -> str | None:
|
||||
stripped = line.strip().rstrip("\\").strip()
|
||||
if stripped in PACKAGES:
|
||||
return stripped
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
|
||||
mk = top / "vendor/google/sunfish/sunfish-vendor.mk"
|
||||
if not mk.is_file():
|
||||
print(f"FAIL: missing {mk}", file=sys.stderr)
|
||||
return 1
|
||||
text = mk.read_text()
|
||||
for name in KEEP:
|
||||
if name not in text:
|
||||
print(f"FAIL: {mk} lost {name}", file=sys.stderr)
|
||||
return 1
|
||||
removed = {name: 0 for name in PACKAGES}
|
||||
kept: list[str] = []
|
||||
for line in text.splitlines(keepends=True):
|
||||
hit = _package_line(line)
|
||||
if hit is None:
|
||||
kept.append(line)
|
||||
continue
|
||||
removed[hit] += 1
|
||||
if all(count == 0 for count in removed.values()):
|
||||
print(f"OK: already applied ({mk})")
|
||||
return 0
|
||||
if any(count != 1 for count in removed.values()):
|
||||
print(f"FAIL: unexpected removals {removed}", file=sys.stderr)
|
||||
return 1
|
||||
new = "".join(kept)
|
||||
for name in KEEP:
|
||||
if name not in new:
|
||||
print(f"FAIL: edit would drop {name}", file=sys.stderr)
|
||||
return 1
|
||||
for name in PACKAGES:
|
||||
if f" {name} \\" in new or f" {name}\n" in new:
|
||||
print(f"FAIL: {name} still listed", file=sys.stderr)
|
||||
return 1
|
||||
mk.write_text(new)
|
||||
print(f"OK: dropped {', '.join(PACKAGES)} from {mk}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Drop commands present on LAB and absent on GOLDEN.
|
||||
|
||||
Keeps the GPS binaries loc_launcher, lowi-server and xtra-daemon.
|
||||
bpftool, alloctop and apexd-lsof.sh are not product packages: bpfloader,
|
||||
dumpstate and apexd require them. Remove only those required entries.
|
||||
pbtombstone stays in PRODUCT_HOST_PACKAGES; only the system copy goes.
|
||||
Qualcomm diag binaries and imsrcsd go. imsdatadaemon and the other IMS
|
||||
call daemons stay.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
MAKE_DROPS: tuple[tuple[str, tuple[str, ...], tuple[str, ...]], ...] = (
|
||||
(
|
||||
"vendor/google/sunfish/sunfish-vendor.mk",
|
||||
(
|
||||
"cnss_diag",
|
||||
"diag_klog",
|
||||
"diag_mdlog",
|
||||
"diag_socket_log",
|
||||
"diag_uart_log",
|
||||
"ssr_diag",
|
||||
"test_diag",
|
||||
"imsrcsd",
|
||||
),
|
||||
(
|
||||
"loc_launcher",
|
||||
"lowi-server",
|
||||
"xtra-daemon",
|
||||
"cnss-daemon",
|
||||
"imsdatadaemon",
|
||||
"imsqmidaemon",
|
||||
"ims_rtp_daemon",
|
||||
),
|
||||
),
|
||||
(
|
||||
"device/google/sunfish/device-lineage.mk",
|
||||
("disable_configstore",),
|
||||
("chre_daemon_msm",),
|
||||
),
|
||||
)
|
||||
|
||||
HOST_SAFE = {"pbtombstone"}
|
||||
|
||||
BP_LINES: tuple[tuple[str, str], ...] = (
|
||||
("system/bpf/loader/Android.bp", '"bpftool",'),
|
||||
("frameworks/native/cmds/dumpstate/Android.bp", '"alloctop",'),
|
||||
("system/apex/apexd/Android.bp", 'required: ["apexd-lsof.sh"],'),
|
||||
)
|
||||
|
||||
|
||||
def _token(line: str) -> str:
|
||||
return line.strip().rstrip("\\").strip()
|
||||
|
||||
|
||||
def _has(text: str, name: str) -> bool:
|
||||
return any(_token(line) == name for line in text.splitlines())
|
||||
|
||||
|
||||
def _assignment(line: str, current: str | None) -> str | None:
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("PRODUCT_HOST_PACKAGES") and "+=" in stripped:
|
||||
return "PRODUCT_HOST_PACKAGES"
|
||||
if stripped.startswith("PRODUCT_PACKAGES") and "+=" in stripped:
|
||||
return "PRODUCT_PACKAGES"
|
||||
return current
|
||||
|
||||
|
||||
def apply_make(path: Path, packages: tuple[str, ...], keep: tuple[str, ...]) -> str:
|
||||
text = path.read_text()
|
||||
for name in keep:
|
||||
if not _has(text, name):
|
||||
raise SystemExit(f"FAIL: {path} lost {name}")
|
||||
removed = {name: 0 for name in packages}
|
||||
rc_removed = 0
|
||||
kept: list[str] = []
|
||||
assignment: str | None = None
|
||||
for line in text.splitlines(keepends=True):
|
||||
assignment = _assignment(line, assignment)
|
||||
token = _token(line)
|
||||
if token in removed and not (token in HOST_SAFE and assignment == "PRODUCT_HOST_PACKAGES"):
|
||||
removed[token] += 1
|
||||
continue
|
||||
if "imsrcsd.rc:" in line:
|
||||
rc_removed += 1
|
||||
continue
|
||||
kept.append(line)
|
||||
pending = [name for name, count in removed.items() if count == 0]
|
||||
if pending and any(count for count in removed.values()):
|
||||
raise SystemExit(f"FAIL: partial removal in {path}: {removed}")
|
||||
if not any(count for count in removed.values()) and rc_removed == 0:
|
||||
return "already"
|
||||
if any(count != 1 for count in removed.values()):
|
||||
raise SystemExit(f"FAIL: unexpected removals in {path}: {removed}")
|
||||
if "imsrcsd" in packages and rc_removed != 1:
|
||||
raise SystemExit(f"FAIL: imsrcsd.rc removals={rc_removed} in {path}")
|
||||
new = "".join(kept)
|
||||
for name in keep:
|
||||
if not _has(new, name):
|
||||
raise SystemExit(f"FAIL: edit would drop {name} from {path}")
|
||||
for name in packages:
|
||||
if _has(new, name):
|
||||
raise SystemExit(f"FAIL: {name} still listed in {path}")
|
||||
if "imsrcsd.rc:" in new:
|
||||
raise SystemExit(f"FAIL: imsrcsd.rc still copied from {path}")
|
||||
path.write_text(new)
|
||||
return "dropped"
|
||||
|
||||
|
||||
def apply_pbtombstone(path: Path) -> str:
|
||||
text = path.read_text()
|
||||
if not _has(text, "logcat") or not _has(text, "fastboot"):
|
||||
raise SystemExit(f"FAIL: {path} lost logcat or fastboot")
|
||||
removed = 0
|
||||
kept: list[str] = []
|
||||
assignment: str | None = None
|
||||
for line in text.splitlines(keepends=True):
|
||||
assignment = _assignment(line, assignment)
|
||||
token = _token(line)
|
||||
if token == "pbtombstone" and assignment == "PRODUCT_PACKAGES":
|
||||
removed += 1
|
||||
continue
|
||||
kept.append(line)
|
||||
if removed == 0:
|
||||
if not any(
|
||||
_token(line) == "pbtombstone" and True
|
||||
for line in text.splitlines()
|
||||
):
|
||||
raise SystemExit(f"FAIL: {path} has no pbtombstone at all")
|
||||
return "already"
|
||||
if removed != 1:
|
||||
raise SystemExit(f"FAIL: pbtombstone system removals={removed}")
|
||||
new = "".join(kept)
|
||||
host_hits = 0
|
||||
assignment = None
|
||||
for line in new.splitlines():
|
||||
assignment = _assignment(line, assignment)
|
||||
if _token(line) == "pbtombstone" and assignment == "PRODUCT_HOST_PACKAGES":
|
||||
host_hits += 1
|
||||
if host_hits != 1:
|
||||
raise SystemExit(f"FAIL: host pbtombstone count={host_hits}")
|
||||
path.write_text(new)
|
||||
return "dropped"
|
||||
|
||||
|
||||
def apply_bp(path: Path, needle: str) -> str:
|
||||
text = path.read_text()
|
||||
hits = [line for line in text.splitlines() if line.strip() == needle]
|
||||
if not hits:
|
||||
return "already"
|
||||
if len(hits) != 1:
|
||||
raise SystemExit(f"FAIL: {needle} appears {len(hits)} times in {path}")
|
||||
new = "".join(
|
||||
line for line in text.splitlines(keepends=True) if line.strip() != needle
|
||||
)
|
||||
if needle in new:
|
||||
raise SystemExit(f"FAIL: {needle} still present in {path}")
|
||||
path.write_text(new)
|
||||
return "dropped"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
|
||||
results: list[str] = []
|
||||
for rel, packages, keep in MAKE_DROPS:
|
||||
path = top / rel
|
||||
if not path.is_file():
|
||||
print(f"FAIL: missing {path}", file=sys.stderr)
|
||||
return 1
|
||||
results.append(f"{apply_make(path, packages, keep)} {rel}")
|
||||
pb = top / "build/make/target/product/base_system.mk"
|
||||
if not pb.is_file():
|
||||
print(f"FAIL: missing {pb}", file=sys.stderr)
|
||||
return 1
|
||||
results.append(f"{apply_pbtombstone(pb)} {pb.relative_to(top)}")
|
||||
for rel, needle in BP_LINES:
|
||||
path = top / rel
|
||||
if not path.is_file():
|
||||
print(f"FAIL: missing {path}", file=sys.stderr)
|
||||
return 1
|
||||
results.append(f"{apply_bp(path, needle)} {rel}")
|
||||
print("OK: " + "; ".join(results))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Drop Lineage tools that AXP.OS PRO sunfish does not ship.
|
||||
|
||||
GOLDEN has no /system_ext/bin/{bash,htop,nano,unrar} and no
|
||||
/system/addon.d/50-lineage.sh. It does ship curl and vim, so those tokens
|
||||
stay. Qualcomm diag binaries are a separate change.
|
||||
|
||||
inherit-product merges PRODUCT_PACKAGES per makefile, so filter-out cannot
|
||||
remove names added here. Delete the exact package-token lines.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
TARGETS: tuple[tuple[str, tuple[str, ...], tuple[str, ...]], ...] = (
|
||||
(
|
||||
"vendor/lineage/config/common.mk",
|
||||
("bash", "htop", "nano", "50-lineage.sh"),
|
||||
("curl", "getcap", "setcap", "vim", "nano_recovery"),
|
||||
),
|
||||
(
|
||||
"vendor/lineage/config/common_mobile_full.mk",
|
||||
("unrar",),
|
||||
("zstd",),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _token(line: str) -> str:
|
||||
return line.strip().rstrip("\\").strip()
|
||||
|
||||
|
||||
def _has(text: str, name: str) -> bool:
|
||||
return any(_token(line) == name for line in text.splitlines())
|
||||
|
||||
|
||||
def apply_file(path: Path, packages: tuple[str, ...], keep: tuple[str, ...]) -> str:
|
||||
text = path.read_text()
|
||||
for name in keep:
|
||||
if not _has(text, name):
|
||||
raise SystemExit(f"FAIL: {path} lost {name}")
|
||||
removed = {name: 0 for name in packages}
|
||||
kept: list[str] = []
|
||||
for line in text.splitlines(keepends=True):
|
||||
token = _token(line)
|
||||
if token in removed:
|
||||
removed[token] += 1
|
||||
continue
|
||||
kept.append(line)
|
||||
if all(count == 0 for count in removed.values()):
|
||||
return "already"
|
||||
if any(count != 1 for count in removed.values()):
|
||||
raise SystemExit(f"FAIL: unexpected removals in {path}: {removed}")
|
||||
new = "".join(kept)
|
||||
for name in keep:
|
||||
if not _has(new, name):
|
||||
raise SystemExit(f"FAIL: edit would drop {name} from {path}")
|
||||
for name in packages:
|
||||
if _has(new, name):
|
||||
raise SystemExit(f"FAIL: {name} still listed in {path}")
|
||||
path.write_text(new)
|
||||
return "dropped"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
|
||||
results: list[str] = []
|
||||
for rel, packages, keep in TARGETS:
|
||||
path = top / rel
|
||||
if not path.is_file():
|
||||
print(f"FAIL: missing {path}", file=sys.stderr)
|
||||
return 1
|
||||
results.append(f"{apply_file(path, packages, keep)} {rel}")
|
||||
print("OK: " + "; ".join(results))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,576 @@
|
||||
#!/usr/bin/env python3
|
||||
"""009: SUPL privacy, plus an optional stop for LPP / XTRA / IZat.
|
||||
|
||||
- Never send IMSI or MSISDN to a SUPL server (AXP/Divest), including emergency.
|
||||
LOS 23 already skips that outside an emergency call.
|
||||
- Settings toggle "Force disable SUPL", default off. On forces SUPL_MODE=0.
|
||||
- GNSS NTP in sunfish gps.conf: 2.android.pool.ntp.org.
|
||||
- Settings toggle "Stop extra location assistance", default off.
|
||||
Off keeps Lineage LPP, XTRA and IZat. On sets LPP_PROFILE=0 (LPPE is
|
||||
carried by that LPP session), skips the framework XTRA download, and
|
||||
stops loc_launcher. The vendor binaries stay on the image.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
MARKER = "sunfish-a16-009-supl-privacy"
|
||||
TREE = pathlib.Path("/srv/rom/src/lineage")
|
||||
|
||||
|
||||
def must(path: pathlib.Path, old: str, new: str, already: str) -> None:
|
||||
text = path.read_text()
|
||||
if already in text:
|
||||
return
|
||||
if old not in text:
|
||||
sys.exit(f"anchor missing in {path}")
|
||||
path.write_text(text.replace(old, new, 1))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
settings = TREE / "frameworks/base/core/java/android/provider/Settings.java"
|
||||
gnss_cfg = (
|
||||
TREE
|
||||
/ "frameworks/base/services/core/java/com/android/server/location/gnss/GnssConfiguration.java"
|
||||
)
|
||||
gnss_prov = (
|
||||
TREE
|
||||
/ "frameworks/base/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java"
|
||||
)
|
||||
loc_xml = TREE / "packages/apps/Settings/res/xml/location_settings.xml"
|
||||
strings = TREE / "packages/apps/Settings/res/values/strings.xml"
|
||||
gps = TREE / "device/google/sunfish/gps.conf"
|
||||
init_rc = TREE / "device/google/sunfish/init.hardware.rc"
|
||||
controller = (
|
||||
TREE
|
||||
/ "packages/apps/Settings/src/com/android/settings/location/ForceDisableSuplPrefController.java"
|
||||
)
|
||||
|
||||
must(
|
||||
settings,
|
||||
""" public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
|
||||
""",
|
||||
""" public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
|
||||
|
||||
/**
|
||||
* Force disable Secure User Plane Location (SUPL), 0 or 1.
|
||||
* Default 0 keeps carrier SUPL. AXP/Graphene toggle, """
|
||||
+ MARKER
|
||||
+ """.
|
||||
* @hide
|
||||
*/
|
||||
@Readable
|
||||
public static final String FORCE_DISABLE_SUPL = "force_disable_supl";
|
||||
|
||||
/** @hide */
|
||||
public static final int FORCE_DISABLE_SUPL_DEFAULT = 0;
|
||||
""",
|
||||
already="FORCE_DISABLE_SUPL",
|
||||
)
|
||||
|
||||
must(
|
||||
settings,
|
||||
""" /** @hide */
|
||||
public static final int FORCE_DISABLE_SUPL_DEFAULT = 0;
|
||||
""",
|
||||
""" /** @hide */
|
||||
public static final int FORCE_DISABLE_SUPL_DEFAULT = 0;
|
||||
|
||||
/**
|
||||
* Stop LPP, the framework XTRA download, and loc_launcher
|
||||
* (xtra-daemon, lowi, IZat). 0 or 1. Default 0 keeps Lineage
|
||||
* assistance. Binaries stay installed. """
|
||||
+ MARKER
|
||||
+ """.
|
||||
* @hide
|
||||
*/
|
||||
@Readable
|
||||
public static final String FORCE_DISABLE_LOCATION_ASSIST =
|
||||
"force_disable_location_assist";
|
||||
|
||||
/** @hide */
|
||||
public static final int FORCE_DISABLE_LOCATION_ASSIST_DEFAULT = 0;
|
||||
""",
|
||||
already="FORCE_DISABLE_LOCATION_ASSIST",
|
||||
)
|
||||
|
||||
must(
|
||||
gnss_cfg,
|
||||
"import android.os.SystemProperties;\n",
|
||||
"import android.os.SystemProperties;\nimport android.provider.Settings;\n",
|
||||
already="import android.provider.Settings;\n",
|
||||
)
|
||||
must(
|
||||
gnss_cfg,
|
||||
"import android.util.Log;\n",
|
||||
"import android.util.Log;\nimport android.util.Slog;\n",
|
||||
already="import android.util.Slog;\n",
|
||||
)
|
||||
must(
|
||||
gnss_cfg,
|
||||
""" loadPropertiesFromGpsDebugConfig(mProperties, DEBUG_PROPERTIES_VENDOR_FILE);
|
||||
loadPropertiesFromGpsDebugConfig(mProperties, DEBUG_PROPERTIES_SYSTEM_FILE);
|
||||
""",
|
||||
""" loadPropertiesFromGpsDebugConfig(mProperties, DEBUG_PROPERTIES_VENDOR_FILE);
|
||||
loadPropertiesFromGpsDebugConfig(mProperties, DEBUG_PROPERTIES_SYSTEM_FILE);
|
||||
applyConfigOverrides(mContext, mProperties); /* """
|
||||
+ MARKER
|
||||
+ """ */
|
||||
""",
|
||||
already="applyConfigOverrides(mContext, mProperties)",
|
||||
)
|
||||
if "void applyConfigOverrides(" not in gnss_cfg.read_text():
|
||||
text = gnss_cfg.read_text()
|
||||
needle = " private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds);\n"
|
||||
insert = needle + """
|
||||
/** """ + MARKER + """: optional SUPL off. Default leaves carrier SUPL_MODE. */
|
||||
private static void applyConfigOverrides(Context ctx, Properties props) {
|
||||
String key = Settings.Global.FORCE_DISABLE_SUPL;
|
||||
int def = Settings.Global.FORCE_DISABLE_SUPL_DEFAULT;
|
||||
if (Settings.Global.getInt(ctx.getContentResolver(), key, def) == 1) {
|
||||
props.setProperty(CONFIG_SUPL_MODE, "0");
|
||||
Slog.d(TAG, "SUPL is force disabled");
|
||||
}
|
||||
}
|
||||
"""
|
||||
if needle not in text:
|
||||
sys.exit("native_set_es_extension_sec missing")
|
||||
gnss_cfg.write_text(text.replace(needle, insert, 1))
|
||||
|
||||
must(
|
||||
gnss_cfg,
|
||||
""" if (Settings.Global.getInt(ctx.getContentResolver(), key, def) == 1) {
|
||||
props.setProperty(CONFIG_SUPL_MODE, "0");
|
||||
Slog.d(TAG, "SUPL is force disabled");
|
||||
}
|
||||
}
|
||||
""",
|
||||
""" if (Settings.Global.getInt(ctx.getContentResolver(), key, def) == 1) {
|
||||
props.setProperty(CONFIG_SUPL_MODE, "0");
|
||||
Slog.d(TAG, "SUPL is force disabled");
|
||||
}
|
||||
String assist = Settings.Global.FORCE_DISABLE_LOCATION_ASSIST;
|
||||
int assistDef = Settings.Global.FORCE_DISABLE_LOCATION_ASSIST_DEFAULT;
|
||||
if (Settings.Global.getInt(ctx.getContentResolver(), assist, assistDef) == 1) {
|
||||
// LPPE is carried by the LPP session. This HAL only has a setter
|
||||
// for LPP_PROFILE, and this override runs after carrier config.
|
||||
props.setProperty(CONFIG_LPP_PROFILE, "0");
|
||||
Slog.d(TAG, "LPP assistance stopped");
|
||||
}
|
||||
}
|
||||
""",
|
||||
already="LPP assistance stopped",
|
||||
)
|
||||
|
||||
must(
|
||||
gnss_prov,
|
||||
""" mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Global.getUriFor(Settings.Global.ASSISTED_GPS_ENABLED),
|
||||
false,
|
||||
new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
toggleXtraDaemon();
|
||||
}
|
||||
}, UserHandle.USER_ALL);
|
||||
""",
|
||||
""" mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Global.getUriFor(Settings.Global.ASSISTED_GPS_ENABLED),
|
||||
false,
|
||||
new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
toggleXtraDaemon();
|
||||
}
|
||||
}, UserHandle.USER_ALL);
|
||||
|
||||
/* """
|
||||
+ MARKER
|
||||
+ """ */
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Global.getUriFor(Settings.Global.FORCE_DISABLE_SUPL),
|
||||
false,
|
||||
new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
reloadGpsProperties();
|
||||
}
|
||||
}, UserHandle.USER_ALL);
|
||||
""",
|
||||
already="Settings.Global.FORCE_DISABLE_SUPL",
|
||||
)
|
||||
|
||||
must(
|
||||
gnss_prov,
|
||||
""" Settings.Global.getUriFor(Settings.Global.FORCE_DISABLE_SUPL),
|
||||
false,
|
||||
new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
reloadGpsProperties();
|
||||
}
|
||||
}, UserHandle.USER_ALL);
|
||||
""",
|
||||
""" Settings.Global.getUriFor(Settings.Global.FORCE_DISABLE_SUPL),
|
||||
false,
|
||||
new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
reloadGpsProperties();
|
||||
}
|
||||
}, UserHandle.USER_ALL);
|
||||
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Global.getUriFor(Settings.Global.FORCE_DISABLE_LOCATION_ASSIST),
|
||||
false,
|
||||
new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
reloadGpsProperties();
|
||||
}
|
||||
}, UserHandle.USER_ALL);
|
||||
""",
|
||||
already="Settings.Global.FORCE_DISABLE_LOCATION_ASSIST",
|
||||
)
|
||||
|
||||
must(
|
||||
gnss_prov,
|
||||
""" toggleXtraDaemon();
|
||||
}
|
||||
|
||||
public GnssLocationProvider(Context context, GnssNative gnssNative,
|
||||
""",
|
||||
""" applyLocationAssistPrivacy();
|
||||
toggleXtraDaemon();
|
||||
}
|
||||
|
||||
public GnssLocationProvider(Context context, GnssNative gnssNative,
|
||||
""",
|
||||
already="applyLocationAssistPrivacy();",
|
||||
)
|
||||
|
||||
must(
|
||||
gnss_prov,
|
||||
""" if (!isAssistedGpsEnabled()) {
|
||||
// PSDS download disabled by system setting, don't try
|
||||
Log.d(TAG, "handleDownloadPsdsData() called when PSDS disabled by system setting");
|
||||
return;
|
||||
}
|
||||
""",
|
||||
""" if (!isAssistedGpsEnabled()) {
|
||||
// PSDS download disabled by system setting, don't try
|
||||
Log.d(TAG, "handleDownloadPsdsData() called when PSDS disabled by system setting");
|
||||
return;
|
||||
}
|
||||
if (Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.FORCE_DISABLE_LOCATION_ASSIST,
|
||||
Settings.Global.FORCE_DISABLE_LOCATION_ASSIST_DEFAULT) == 1) {
|
||||
Log.d(TAG, "handleDownloadPsdsData() skipped: location assistance stopped");
|
||||
return;
|
||||
}
|
||||
""",
|
||||
already="location assistance stopped",
|
||||
)
|
||||
|
||||
must(
|
||||
gnss_prov,
|
||||
""" private void toggleXtraDaemon() {
|
||||
Log.i(TAG, "Toggling xtra-daemon via property");
|
||||
SystemProperties.set("persist.sys.xtra-daemon.enabled",
|
||||
Boolean.toString(isAssistedGpsEnabled()));
|
||||
}
|
||||
""",
|
||||
""" /** """
|
||||
+ MARKER
|
||||
+ """ Stop loc_launcher. Does not delete xtra-daemon, lowi or IZat. */
|
||||
private void applyLocationAssistPrivacy() {
|
||||
boolean stop = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.FORCE_DISABLE_LOCATION_ASSIST,
|
||||
Settings.Global.FORCE_DISABLE_LOCATION_ASSIST_DEFAULT) == 1;
|
||||
SystemProperties.set("persist.sys.axp.loc_privacy", stop ? "1" : "0");
|
||||
}
|
||||
|
||||
private void toggleXtraDaemon() {
|
||||
Log.i(TAG, "Toggling xtra-daemon via property");
|
||||
SystemProperties.set("persist.sys.xtra-daemon.enabled",
|
||||
Boolean.toString(isAssistedGpsEnabled()));
|
||||
}
|
||||
""",
|
||||
already="persist.sys.axp.loc_privacy",
|
||||
)
|
||||
|
||||
old_setid = """ TelephonyManager phone = (TelephonyManager)
|
||||
mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
int type = AGPS_SETID_TYPE_NONE;
|
||||
String setId = null;
|
||||
final Boolean isEmergency = mNIHandler.getInEmergency();
|
||||
|
||||
// Unless we are in an emergency, do not provide sensitive subscriber information
|
||||
// to SUPL servers.
|
||||
if (!isEmergency) {
|
||||
mGnssNative.setAgpsSetId(type, "");
|
||||
return;
|
||||
}
|
||||
|
||||
int subId = SubscriptionManager.getDefaultDataSubscriptionId();
|
||||
if (mGnssConfiguration.isActiveSimEmergencySuplEnabled() && isEmergency
|
||||
&& mNetworkConnectivityHandler.getActiveSubId() >= 0) {
|
||||
subId = mNetworkConnectivityHandler.getActiveSubId();
|
||||
}
|
||||
if (SubscriptionManager.isValidSubscriptionId(subId)) {
|
||||
phone = phone.createForSubscriptionId(subId);
|
||||
}
|
||||
if ((flags & AGPS_REQUEST_SETID_IMSI) == AGPS_REQUEST_SETID_IMSI) {
|
||||
setId = phone.getSubscriberId();
|
||||
if (setId != null) {
|
||||
// This means the framework has the SIM card.
|
||||
type = AGPS_SETID_TYPE_IMSI;
|
||||
}
|
||||
} else if ((flags & AGPS_REQUEST_SETID_MSISDN) == AGPS_REQUEST_SETID_MSISDN) {
|
||||
setId = phone.getLine1Number();
|
||||
if (setId != null) {
|
||||
// This means the framework has the SIM card.
|
||||
type = AGPS_SETID_TYPE_MSISDN;
|
||||
}
|
||||
}
|
||||
|
||||
mGnssNative.setAgpsSetId(type, (setId == null) ? "" : setId);
|
||||
"""
|
||||
new_setid = """ // """ + MARKER + """: AXP/Divest. Never send IMSI or the phone number
|
||||
// to a SUPL server, including during an emergency call. LOS 23 already
|
||||
// skips this outside emergency. Control-plane A-GNSS is unchanged.
|
||||
// flags is read so the unused-parameter check stays quiet.
|
||||
if (flags != 0 && Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "ignoring SUPL set-id request flags=" + flags);
|
||||
}
|
||||
mGnssNative.setAgpsSetId(AGPS_SETID_TYPE_NONE, "");
|
||||
"""
|
||||
prov = gnss_prov.read_text()
|
||||
if old_setid in prov:
|
||||
gnss_prov.write_text(prov.replace(old_setid, new_setid, 1))
|
||||
elif "ignoring SUPL set-id request flags" not in prov:
|
||||
short = """ // """ + MARKER + """: AXP/Divest. Never send IMSI or the phone number
|
||||
// to a SUPL server, including during an emergency call. LOS 23 already
|
||||
// skips this outside emergency. Control-plane A-GNSS is unchanged.
|
||||
mGnssNative.setAgpsSetId(AGPS_SETID_TYPE_NONE, "");
|
||||
"""
|
||||
if short not in prov:
|
||||
sys.exit("setAgpsSetId block missing")
|
||||
gnss_prov.write_text(prov.replace(short, new_setid, 1))
|
||||
|
||||
must(
|
||||
loc_xml,
|
||||
""" settings:controller="com.android.settings.location.LocationServicesPreferenceController"/>
|
||||
|
||||
</com.android.settingslib.widget.UntitledPreferenceCategory>
|
||||
""",
|
||||
""" settings:controller="com.android.settings.location.LocationServicesPreferenceController"/>
|
||||
|
||||
<!-- """
|
||||
+ MARKER
|
||||
+ """: default off, so carrier SUPL stays -->
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="force_disable_supl"
|
||||
android:title="@string/force_disable_supl_title"
|
||||
android:summary="@string/force_disable_supl_summary"
|
||||
settings:controller="com.android.settings.location.ForceDisableSuplPrefController"/>
|
||||
|
||||
</com.android.settingslib.widget.UntitledPreferenceCategory>
|
||||
""",
|
||||
already='android:key="force_disable_supl"',
|
||||
)
|
||||
|
||||
must(
|
||||
loc_xml,
|
||||
""" settings:controller="com.android.settings.location.ForceDisableSuplPrefController"/>
|
||||
|
||||
</com.android.settingslib.widget.UntitledPreferenceCategory>
|
||||
""",
|
||||
""" settings:controller="com.android.settings.location.ForceDisableSuplPrefController"/>
|
||||
|
||||
<!-- """
|
||||
+ MARKER
|
||||
+ """: default off. On stops LPP, XTRA and IZat. Binaries stay. -->
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="force_disable_location_assist"
|
||||
android:title="@string/force_disable_location_assist_title"
|
||||
android:summary="@string/force_disable_location_assist_summary"
|
||||
settings:controller="com.android.settings.location.ForceDisableLocationAssistPrefController"/>
|
||||
|
||||
</com.android.settingslib.widget.UntitledPreferenceCategory>
|
||||
""",
|
||||
already='android:key="force_disable_location_assist"',
|
||||
)
|
||||
|
||||
must(
|
||||
strings,
|
||||
""" <string name="location_services_preference_title">Location services</string>
|
||||
""",
|
||||
""" <string name="location_services_preference_title">Location services</string>
|
||||
<!-- """
|
||||
+ MARKER
|
||||
+ """ -->
|
||||
<string name="force_disable_supl_title">Force disable Secure User Plane Location (SUPL)</string>
|
||||
<string name="force_disable_supl_summary">Always disable SUPL assisted location support regardless of carrier configuration or emergency call status. Does not disable control-plane A-GNSS. IMSI and the phone number are never sent to a SUPL server.</string>
|
||||
""",
|
||||
already="force_disable_supl_title",
|
||||
)
|
||||
|
||||
must(
|
||||
strings,
|
||||
""" <string name="force_disable_supl_summary">Always disable SUPL assisted location support regardless of carrier configuration or emergency call status. Does not disable control-plane A-GNSS. IMSI and the phone number are never sent to a SUPL server.</string>
|
||||
""",
|
||||
""" <string name="force_disable_supl_summary">Always disable SUPL assisted location support regardless of carrier configuration or emergency call status. Does not disable control-plane A-GNSS. IMSI and the phone number are never sent to a SUPL server.</string>
|
||||
<string name="force_disable_location_assist_title">Stop extra location assistance</string>
|
||||
<string name="force_disable_location_assist_summary">Stops LTE positioning (LPP/LPPE), XTRA orbit downloads, and Qualcomm IZat. Satellite fixes keep working. The programs stay installed. SUPL has its own switch.</string>
|
||||
""",
|
||||
already="force_disable_location_assist_title",
|
||||
)
|
||||
|
||||
if not controller.exists():
|
||||
controller.write_text(
|
||||
"""package com.android.settings.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
/** """
|
||||
+ MARKER
|
||||
+ """ AXP/Graphene toggle. Default off. */
|
||||
public class ForceDisableSuplPrefController extends TogglePreferenceController {
|
||||
|
||||
public ForceDisableSuplPrefController(Context ctx, String key) {
|
||||
super(ctx, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!Process.myUserHandle().isSystem()) {
|
||||
return DISABLED_FOR_USER;
|
||||
}
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return Settings.Global.getInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Global.FORCE_DISABLE_SUPL,
|
||||
Settings.Global.FORCE_DISABLE_SUPL_DEFAULT) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
return Settings.Global.putInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Global.FORCE_DISABLE_SUPL,
|
||||
isChecked ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliceHighlightMenuRes() {
|
||||
return NO_RES;
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assist_controller = (
|
||||
TREE
|
||||
/ "packages/apps/Settings/src/com/android/settings/location/ForceDisableLocationAssistPrefController.java"
|
||||
)
|
||||
if not assist_controller.exists():
|
||||
assist_controller.write_text(
|
||||
"""package com.android.settings.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
/** """
|
||||
+ MARKER
|
||||
+ """ Default off. Stops assistance; does not delete blobs. */
|
||||
public class ForceDisableLocationAssistPrefController extends TogglePreferenceController {
|
||||
|
||||
public ForceDisableLocationAssistPrefController(Context ctx, String key) {
|
||||
super(ctx, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!Process.myUserHandle().isSystem()) {
|
||||
return DISABLED_FOR_USER;
|
||||
}
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return Settings.Global.getInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Global.FORCE_DISABLE_LOCATION_ASSIST,
|
||||
Settings.Global.FORCE_DISABLE_LOCATION_ASSIST_DEFAULT) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
return Settings.Global.putInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Global.FORCE_DISABLE_LOCATION_ASSIST,
|
||||
isChecked ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliceHighlightMenuRes() {
|
||||
return NO_RES;
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
must(
|
||||
init_rc,
|
||||
"""on property:persist.sys.xtra-daemon.enabled=*
|
||||
restart loc_launcher
|
||||
""",
|
||||
"""# """
|
||||
+ MARKER
|
||||
+ """
|
||||
# 1 stops loc_launcher (xtra-daemon, lowi, IZat). Binaries stay.
|
||||
# The xtra property must not restart the service while that stop is on.
|
||||
on property:persist.sys.axp.loc_privacy=1
|
||||
stop loc_launcher
|
||||
exec_background -- /system/bin/toybox killall xtra-daemon lowi-server
|
||||
|
||||
on property:persist.sys.axp.loc_privacy=0
|
||||
start loc_launcher
|
||||
|
||||
on property:sys.boot_completed=1 && property:persist.sys.axp.loc_privacy=1
|
||||
stop loc_launcher
|
||||
exec_background -- /system/bin/toybox killall xtra-daemon lowi-server
|
||||
|
||||
on property:persist.sys.xtra-daemon.enabled=* && property:persist.sys.axp.loc_privacy=0
|
||||
restart loc_launcher
|
||||
""",
|
||||
already="persist.sys.axp.loc_privacy",
|
||||
)
|
||||
|
||||
raw = gps.read_bytes()
|
||||
old_ntp = b"NTP_SERVER = time.google.com"
|
||||
new_ntp = b"NTP_SERVER = 2.android.pool.ntp.org"
|
||||
if new_ntp not in raw:
|
||||
if old_ntp not in raw:
|
||||
sys.exit("NTP_SERVER line missing")
|
||||
raw = raw.replace(old_ntp, new_ntp, 1)
|
||||
gps.write_bytes(raw)
|
||||
print("supl privacy applied")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
"""009: AXP Yama ptrace_scope, active at 2.
|
||||
|
||||
AXP LOS 22 init.rc sets /proc/sys/kernel/yama/ptrace_scope from
|
||||
persist.native_debug (true -> 0, false -> 2). GOLDEN's kernel has Yama
|
||||
off, so those lines do nothing there. This tree builds Yama, so init
|
||||
writes 2 at boot. persist.native_debug=true still opens ptrace.
|
||||
|
||||
Writing that sysctl needs CAP_SYS_PTRACE. AOSP neverallows it for init;
|
||||
the exception is the one in AXP's ptrace_scope sepolicy patch. The node
|
||||
is labeled proc_security, which init may already read and write.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
TREE = pathlib.Path("/srv/rom/src/lineage")
|
||||
MARKER = "sunfish-a16-009-yama-ptrace"
|
||||
|
||||
RC_BOOT_OLD = " write /proc/sys/kernel/randomize_va_space 2\n"
|
||||
RC_BOOT_NEW = (
|
||||
" write /proc/sys/kernel/randomize_va_space 2\n"
|
||||
" # sunfish-a16-009-yama-ptrace: AXP scope 2 unless native debug is on.\n"
|
||||
" write /proc/sys/kernel/yama/ptrace_scope 2\n"
|
||||
)
|
||||
RC_PROP_OLD = (
|
||||
" write /proc/sys/net/ipv4/tcp_default_init_rwnd ${net.tcp_def_init_rwnd}\n"
|
||||
)
|
||||
RC_PROP_NEW = (
|
||||
" write /proc/sys/net/ipv4/tcp_default_init_rwnd ${net.tcp_def_init_rwnd}\n"
|
||||
"\n"
|
||||
"on property:persist.native_debug=true\n"
|
||||
" write /proc/sys/kernel/yama/ptrace_scope 0\n"
|
||||
"\n"
|
||||
"on property:persist.native_debug=false\n"
|
||||
" write /proc/sys/kernel/yama/ptrace_scope 2\n"
|
||||
)
|
||||
|
||||
GENFS_OLD = "genfscon proc /sys/kernel/usermodehelper u:object_r:usermodehelper:s0\n"
|
||||
GENFS_NEW = (
|
||||
"genfscon proc /sys/kernel/usermodehelper u:object_r:usermodehelper:s0\n"
|
||||
"genfscon proc /sys/kernel/yama/ptrace_scope u:object_r:proc_security:s0\n"
|
||||
)
|
||||
|
||||
PROP_OLD = "persist.debug. u:object_r:persist_debug_prop:s0\n"
|
||||
PROP_NEW = (
|
||||
"persist.debug. u:object_r:persist_debug_prop:s0\n"
|
||||
"persist.native_debug u:object_r:system_prop:s0\n"
|
||||
)
|
||||
|
||||
INIT_OLD = """allow init {
|
||||
proc_security
|
||||
}:file rw_file_perms;
|
||||
"""
|
||||
INIT_NEW = """allow init {
|
||||
proc_security
|
||||
}:file rw_file_perms;
|
||||
|
||||
# sunfish-a16-009-yama-ptrace: CAP_SYS_PTRACE to set ptrace_scope.
|
||||
allow init self:global_capability_class_set sys_ptrace;
|
||||
"""
|
||||
|
||||
NEVER_END = "} self:global_capability_class_set sys_ptrace;"
|
||||
|
||||
|
||||
def must(path: pathlib.Path, old: str, new: str, already: str) -> None:
|
||||
text = path.read_text()
|
||||
if already in text:
|
||||
return
|
||||
if old not in text:
|
||||
sys.exit(f"anchor missing in {path}")
|
||||
path.write_text(text.replace(old, new, 1))
|
||||
|
||||
|
||||
def loosen_sys_ptrace(path: pathlib.Path) -> None:
|
||||
text = path.read_text()
|
||||
idx = text.find(NEVER_END)
|
||||
if idx < 0:
|
||||
return
|
||||
start = text.rfind("neverallow {", 0, idx)
|
||||
if start < 0:
|
||||
sys.exit(f"neverallow block missing in {path}")
|
||||
block = text[start:idx]
|
||||
if "\n -init\n" in block:
|
||||
return
|
||||
if "\n domain\n" not in block:
|
||||
sys.exit(f"domain line missing in sys_ptrace neverallow {path}")
|
||||
new_block = block.replace("\n domain\n", "\n domain\n -init\n", 1)
|
||||
path.write_text(text[:start] + new_block + text[idx:])
|
||||
|
||||
|
||||
def main() -> None:
|
||||
rc = TREE / "system/core/rootdir/init.rc"
|
||||
must(rc, RC_BOOT_OLD, RC_BOOT_NEW, MARKER)
|
||||
must(rc, RC_PROP_OLD, RC_PROP_NEW, "on property:persist.native_debug=true")
|
||||
|
||||
se = TREE / "system/sepolicy"
|
||||
must(
|
||||
se / "private/init.te",
|
||||
INIT_OLD,
|
||||
INIT_NEW,
|
||||
"allow init self:global_capability_class_set sys_ptrace;",
|
||||
)
|
||||
loosen_sys_ptrace(se / "private/domain.te")
|
||||
must(se / "private/genfs_contexts", GENFS_OLD, GENFS_NEW, "yama/ptrace_scope")
|
||||
must(se / "private/property_contexts", PROP_OLD, PROP_NEW, "persist.native_debug")
|
||||
|
||||
api = se / "prebuilts/api"
|
||||
for domain in api.glob("*/private/domain.te"):
|
||||
loosen_sys_ptrace(domain)
|
||||
for genfs in api.glob("*/private/genfs_contexts"):
|
||||
text = genfs.read_text()
|
||||
if "yama/ptrace_scope" in text or GENFS_OLD not in text:
|
||||
continue
|
||||
genfs.write_text(text.replace(GENFS_OLD, GENFS_NEW, 1))
|
||||
for props in api.glob("*/private/property_contexts"):
|
||||
text = props.read_text()
|
||||
if "persist.native_debug" in text or PROP_OLD not in text:
|
||||
continue
|
||||
props.write_text(text.replace(PROP_OLD, PROP_NEW, 1))
|
||||
|
||||
# Current policy must still exclude init, or the write is neverallowed.
|
||||
live = (se / "private/domain.te").read_text()
|
||||
end = live.find(NEVER_END)
|
||||
start = live.rfind("neverallow {", 0, end)
|
||||
if "\n -init\n" not in live[start:end]:
|
||||
sys.exit("init still neverallowed from sys_ptrace")
|
||||
print("yama ptrace_scope applied")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -69,12 +69,12 @@ guarantee). Do not relock the bootloader on this Magisk track.
|
||||
|
||||
## Branding
|
||||
|
||||
`TARGET_UNOFFICIAL_BUILD_ID=AXP-Resurection-Pro` is set at the **top** of
|
||||
`device/google/sunfish/lineage_sunfish.mk` (before inheriting
|
||||
`vendor/lineage` → `version.mk`). Helper:
|
||||
`apply_009_unofficial_build_id.py`. **Pro** = full Magisk resurrection track
|
||||
(not a future slim/minimal variant). Mirrors GOLDEN’s `…-sunfish-pro` /
|
||||
releasetype `PRO` intent without patching `vendor/lineage/config/version.mk`.
|
||||
Ship identity follows AXP/GOLDEN, not Lineage's unofficial channel.
|
||||
GOLDEN (`22.2-2026.003-sunfish-pro`) reports `ro.lineage.releasetype=PRO`,
|
||||
`ro.build.tags=release-keys`, and a version ending in `sunfish-pro`.
|
||||
`TARGET_UNOFFICIAL_BUILD_ID` forces Lineage `UNOFFICIAL` and is not that
|
||||
scheme. The next bacon sets `PRO` + `release-keys` + `…-sunfish-pro`.
|
||||
**Pro** remains the full Magisk track (AXP PRO, not SLIM).
|
||||
|
||||
`branding.mk` adds `ro.axp.resurection.track=pro`. Ship scripts also publish
|
||||
`AXP-Resurection-Pro-<ver>-<date>-sunfish.zip` as a hardlink/copy of the
|
||||
|
||||
@@ -30,7 +30,9 @@ while IFS= read -r rel || [[ -n "$rel" ]]; do
|
||||
fail=$((fail + 1))
|
||||
continue
|
||||
fi
|
||||
args=(-p1 --batch -d "$KERNEL_DIR")
|
||||
# Never let GNU patch auto-answer "Assuming -R": the LOS base can already
|
||||
# contain a patch's intended result. Reversing it is a regression, not OK.
|
||||
args=(-p1 --batch --forward -d "$KERNEL_DIR")
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
args+=(--dry-run)
|
||||
fi
|
||||
|
||||
@@ -6,8 +6,13 @@
|
||||
# off; CONFIG_LSM already lists yama but SECURITY_YAMA was unset so the LSM
|
||||
# never built.
|
||||
#
|
||||
# Does NOT apply high-offset CVE leftovers (nf_tables deliberately skipped) or
|
||||
# CVE-2022-49980 (denylist until lock-scope port).
|
||||
# Does NOT apply high-offset CVE leftovers (nf_tables deliberately skipped).
|
||||
# CVE-2022-49980's one-liner stays on the denylist. The bacon applies
|
||||
# ports/cve-2022-49980-lock-scope.patch after the CVE lists.
|
||||
#
|
||||
# USERFAULTFD: GOLDEN's running 4.14.355 config leaves it unset. LOS 23
|
||||
# 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.
|
||||
#
|
||||
# Usage: apply_uss5_1_defconfig.sh <kernel-tree>
|
||||
set -euo pipefail
|
||||
@@ -17,15 +22,14 @@ DEF="$K/arch/arm64/configs/sunfish_defconfig"
|
||||
test -f "$DEF"
|
||||
test -f "$K/security/yama/Kconfig"
|
||||
|
||||
for k in SECURITY_YAMA SLAB_FREELIST_RANDOM; do
|
||||
for k in SECURITY_YAMA SLAB_FREELIST_RANDOM USERFAULTFD HAVE_ARCH_USERFAULTFD_MINOR; do
|
||||
sed -i "/CONFIG_${k}/d" "$DEF"
|
||||
# drop "# CONFIG_FOO is not set" lines too (sed above only matches CONFIG_FOO=)
|
||||
sed -i "/# CONFIG_${k} is not set/d" "$DEF"
|
||||
done
|
||||
|
||||
{
|
||||
echo "CONFIG_SECURITY_YAMA=y"
|
||||
echo "CONFIG_SLAB_FREELIST_RANDOM=y"
|
||||
echo "# CONFIG_USERFAULTFD is not set"
|
||||
} >> "$DEF"
|
||||
|
||||
echo "OK: US-S5-1 defconfig (YAMA + SLAB_FREELIST_RANDOM) on $K"
|
||||
echo "OK: US-S5-1 defconfig (YAMA + SLAB_FREELIST_RANDOM, USERFAULTFD off) on $K"
|
||||
@@ -1,6 +1,7 @@
|
||||
# relative to AXP kernel_patches
|
||||
# LAB 2026-10-01: alone ⇒ Google-logo hang (udc_lock recursive deadlock on
|
||||
# msm-4.14; needs lock-scope companion, not one-liner). Do not re-apply.
|
||||
# msm-4.14). Companion is ports/cve-2022-49980-lock-scope.patch (UAF fix
|
||||
# plus uevents outside the lock). Do not apply the one-liner by itself.
|
||||
CVE-2022-49980/ANY/0001.patch
|
||||
CVE-2017-15832/qcacld-3.0/0001.patch
|
||||
CVE-2021-29648/^5.11/0001.patch
|
||||
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
From: Daniel Micay <danielmicay@gmail.com>
|
||||
Subject: [PATCH] add vmalloc alloc_size attributes
|
||||
|
||||
Port of AXP/Graphene
|
||||
0008-Graphene-Kernel_Hardening-allocsize/4.14/0007.patch onto the
|
||||
Lineage msm-4.14 vmalloc.h. That header gained vmalloc_user_node_flags
|
||||
and dropped the pgprot argument of __vmalloc, so the upstream hunk no
|
||||
longer applies. The attribute is the same one AXP adds: alloc_size(1)
|
||||
on the size argument.
|
||||
|
||||
Signed-off-by: Daniel Micay <danielmicay@gmail.com>
|
||||
---
|
||||
include/linux/vmalloc.h | 22 +++++++++++-----------
|
||||
1 file changed, 11 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/include/linux/vmalloc.h
|
||||
+++ b/include/linux/vmalloc.h
|
||||
@@ -76,20 +76,20 @@
|
||||
static inline unsigned long vmalloc_nr_pages(void) { return 0; }
|
||||
#endif
|
||||
|
||||
-extern void *vmalloc(unsigned long size);
|
||||
-extern void *vzalloc(unsigned long size);
|
||||
-extern void *vmalloc_user(unsigned long size);
|
||||
-extern void *vmalloc_node(unsigned long size, int node);
|
||||
-extern void *vzalloc_node(unsigned long size, int node);
|
||||
-extern void *vmalloc_user_node_flags(unsigned long size, int node, gfp_t flags);
|
||||
-extern void *vmalloc_exec(unsigned long size);
|
||||
-extern void *vmalloc_32(unsigned long size);
|
||||
-extern void *vmalloc_32_user(unsigned long size);
|
||||
-extern void *__vmalloc(unsigned long size, gfp_t gfp_mask);
|
||||
+extern void *vmalloc(unsigned long size) __attribute__((alloc_size(1)));
|
||||
+extern void *vzalloc(unsigned long size) __attribute__((alloc_size(1)));
|
||||
+extern void *vmalloc_user(unsigned long size) __attribute__((alloc_size(1)));
|
||||
+extern void *vmalloc_node(unsigned long size, int node) __attribute__((alloc_size(1)));
|
||||
+extern void *vzalloc_node(unsigned long size, int node) __attribute__((alloc_size(1)));
|
||||
+extern void *vmalloc_user_node_flags(unsigned long size, int node, gfp_t flags) __attribute__((alloc_size(1)));
|
||||
+extern void *vmalloc_exec(unsigned long size) __attribute__((alloc_size(1)));
|
||||
+extern void *vmalloc_32(unsigned long size) __attribute__((alloc_size(1)));
|
||||
+extern void *vmalloc_32_user(unsigned long size) __attribute__((alloc_size(1)));
|
||||
+extern void *__vmalloc(unsigned long size, gfp_t gfp_mask) __attribute__((alloc_size(1)));
|
||||
extern void *__vmalloc_node_range(unsigned long size, unsigned long align,
|
||||
unsigned long start, unsigned long end, gfp_t gfp_mask,
|
||||
pgprot_t prot, unsigned long vm_flags, int node,
|
||||
- const void *caller);
|
||||
+ const void *caller) __attribute__((alloc_size(1)));
|
||||
#ifndef CONFIG_MMU
|
||||
extern void *__vmalloc_node_flags(unsigned long size, int node, gfp_t flags);
|
||||
static inline void *__vmalloc_node_flags_caller(unsigned long size, int node,
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
From: Alan Stern <stern@rowland.harvard.edu>
|
||||
Subject: [PATCH] USB: gadget: fix udc_lock scope around usb_udc_uevent
|
||||
|
||||
CVE-2022-49980 (2191c00855b0) takes udc_lock inside usb_udc_uevent().
|
||||
On msm-4.14 that callback still runs synchronously from device_add()
|
||||
and kobject_uevent() while the same thread already holds udc_lock, so
|
||||
the mutex deadlocks and the phone hangs on the Google logo.
|
||||
|
||||
Linux 6.1 usb_add_gadget() / gadget_bind_driver() only hold udc_lock
|
||||
around the list and the driver pointer. This keeps the 4.14 call
|
||||
shape and moves those uevents outside the lock, then applies the
|
||||
UAF fix. Do not apply 2191c00855b0 without this.
|
||||
|
||||
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
|
||||
---
|
||||
|
||||
--- a/drivers/usb/gadget/udc/core.c
|
||||
+++ b/drivers/usb/gadget/udc/core.c
|
||||
@@ -1229,11 +1229,18 @@
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_add_tail(&udc->list, &udc_list);
|
||||
+ mutex_unlock(&udc_lock);
|
||||
|
||||
+ /*
|
||||
+ * device_add() emits a uevent that calls usb_udc_uevent(), which
|
||||
+ * takes udc_lock. Same for device_del() on the error path. v6.1
|
||||
+ * usb_add_gadget() only holds the mutex around the list update.
|
||||
+ */
|
||||
ret = device_add(&udc->dev);
|
||||
if (ret)
|
||||
goto err_unlist_udc;
|
||||
|
||||
+ mutex_lock(&udc_lock);
|
||||
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
|
||||
udc->vbus = true;
|
||||
|
||||
@@ -1247,9 +1254,11 @@
|
||||
return 0;
|
||||
|
||||
err_del_udc:
|
||||
+ mutex_unlock(&udc_lock);
|
||||
device_del(&udc->dev);
|
||||
|
||||
err_unlist_udc:
|
||||
+ mutex_lock(&udc_lock);
|
||||
list_del(&udc->list);
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
@@ -1313,7 +1322,14 @@
|
||||
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
|
||||
udc->driver->function);
|
||||
|
||||
+ /*
|
||||
+ * Caller holds udc_lock. usb_udc_uevent() takes it too, so the
|
||||
+ * CHANGE uevent has to run outside the lock. Driver is still bound;
|
||||
+ * the callback re-locks to read it.
|
||||
+ */
|
||||
+ mutex_unlock(&udc_lock);
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
|
||||
+ mutex_lock(&udc_lock);
|
||||
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
udc->driver->disconnect(udc->gadget);
|
||||
@@ -1383,7 +1399,10 @@
|
||||
}
|
||||
usb_udc_connect_control(udc);
|
||||
|
||||
+ /* Same deadlock as remove: uevent re-enters udc_lock. */
|
||||
+ mutex_unlock(&udc_lock);
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
|
||||
+ mutex_lock(&udc_lock);
|
||||
return 0;
|
||||
err1:
|
||||
if (ret != -EISNAM)
|
||||
@@ -1611,13 +1630,14 @@
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (udc->driver) {
|
||||
+ mutex_lock(&udc_lock);
|
||||
+ if (udc->driver)
|
||||
ret = add_uevent_var(env, "USB_UDC_DRIVER=%s",
|
||||
udc->driver->function);
|
||||
- if (ret) {
|
||||
- dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n");
|
||||
- return ret;
|
||||
- }
|
||||
+ mutex_unlock(&udc_lock);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n");
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
From: Daniel Micay <danielmicay@gmail.com>
|
||||
Subject: [PATCH] make sysctl constants read-only
|
||||
|
||||
Port of AXP/Graphene ro/4.14/0007.patch and ro/4.14/0034.patch.
|
||||
Declarations still match; hunk context does not, and this tree has no
|
||||
net/decnet/sysctl_net_decnet.c. Same __read_only (__ro_after_init)
|
||||
annotations AXP applies, on the files that exist here.
|
||||
|
||||
Signed-off-by: Daniel Micay <danielmicay@gmail.com>
|
||||
---
|
||||
|
||||
--- a/fs/pipe.c
|
||||
+++ b/fs/pipe.c
|
||||
@@ -53,7 +53,7 @@
|
||||
/*
|
||||
* Minimum pipe size, as required by POSIX
|
||||
*/
|
||||
-unsigned int pipe_min_size = PAGE_SIZE;
|
||||
+unsigned int pipe_min_size __read_only = PAGE_SIZE;
|
||||
|
||||
/* Maximum allocatable pages per user. Hard limit is unset by default, soft
|
||||
* matches default values.
|
||||
--- a/kernel/sysctl.c
|
||||
+++ b/kernel/sysctl.c
|
||||
@@ -121,48 +121,48 @@
|
||||
|
||||
/* Constants used for minimum and maximum */
|
||||
#ifdef CONFIG_LOCKUP_DETECTOR
|
||||
-static int sixty = 60;
|
||||
+static int sixty __read_only = 60;
|
||||
#endif
|
||||
|
||||
-static int __maybe_unused neg_one = -1;
|
||||
-static int __maybe_unused neg_three = -3;
|
||||
+static int __maybe_unused neg_one __read_only = -1;
|
||||
+static int __maybe_unused neg_three __read_only = -3;
|
||||
|
||||
-static int zero;
|
||||
-static int __maybe_unused one = 1;
|
||||
-static int __maybe_unused two = 2;
|
||||
-static int __maybe_unused three = 3;
|
||||
-static int __maybe_unused four = 4;
|
||||
+static int zero __read_only;
|
||||
+static int __maybe_unused one __read_only = 1;
|
||||
+static int __maybe_unused two __read_only = 2;
|
||||
+static int __maybe_unused three __read_only = 3;
|
||||
+static int __maybe_unused four __read_only = 4;
|
||||
static int int_max = INT_MAX;
|
||||
-static unsigned long zero_ul;
|
||||
-static unsigned long one_ul = 1;
|
||||
-static unsigned long long_max = LONG_MAX;
|
||||
-static int one_hundred = 100;
|
||||
-static int one_thousand = 1000;
|
||||
+static unsigned long zero_ul __read_only;
|
||||
+static unsigned long one_ul __read_only = 1;
|
||||
+static unsigned long long_max __read_only = LONG_MAX;
|
||||
+static int one_hundred __read_only = 100;
|
||||
+static int one_thousand __read_only = 1000;
|
||||
#ifdef CONFIG_SCHED_WALT
|
||||
-static int two_million = 2000000;
|
||||
+static int two_million __read_only = 2000000;
|
||||
#endif
|
||||
#ifdef CONFIG_PRINTK
|
||||
-static int ten_thousand = 10000;
|
||||
+static int ten_thousand __read_only = 10000;
|
||||
#endif
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
-static int six_hundred_forty_kb = 640 * 1024;
|
||||
+static int six_hundred_forty_kb __read_only = 640 * 1024;
|
||||
#endif
|
||||
static int __maybe_unused two_hundred_million __read_only = 200000000;
|
||||
static int two_hundred_fifty_five __read_only = 255;
|
||||
|
||||
/* this is needed for the proc_doulongvec_minmax of vm_dirty_bytes */
|
||||
-static unsigned long dirty_bytes_min = 2 * PAGE_SIZE;
|
||||
+static unsigned long dirty_bytes_min __read_only = 2 * PAGE_SIZE;
|
||||
|
||||
/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
|
||||
-static int maxolduid = 65535;
|
||||
-static int minolduid;
|
||||
+static int maxolduid __read_only = 65535;
|
||||
+static int minolduid __read_only;
|
||||
|
||||
-static int ngroups_max = NGROUPS_MAX;
|
||||
+static int ngroups_max __read_only = NGROUPS_MAX;
|
||||
static const int cap_last_cap = CAP_LAST_CAP;
|
||||
|
||||
/*this is needed for proc_doulongvec_minmax of sysctl_hung_task_timeout_secs */
|
||||
#ifdef CONFIG_DETECT_HUNG_TASK
|
||||
-static unsigned long hung_task_timeout_max = (LONG_MAX/HZ);
|
||||
+static unsigned long hung_task_timeout_max __read_only = (LONG_MAX/HZ);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_INOTIFY_USER
|
||||
@@ -332,19 +332,19 @@
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SCHED_DEBUG
|
||||
-static int min_sched_granularity_ns = 100000; /* 100 usecs */
|
||||
-static int max_sched_granularity_ns = NSEC_PER_SEC; /* 1 second */
|
||||
-static int min_wakeup_granularity_ns; /* 0 usecs */
|
||||
-static int max_wakeup_granularity_ns = NSEC_PER_SEC; /* 1 second */
|
||||
+static int min_sched_granularity_ns __read_only = 100000; /* 100 usecs */
|
||||
+static int max_sched_granularity_ns __read_only = NSEC_PER_SEC; /* 1 second */
|
||||
+static int min_wakeup_granularity_ns __read_only; /* 0 usecs */
|
||||
+static int max_wakeup_granularity_ns __read_only = NSEC_PER_SEC; /* 1 second */
|
||||
#ifdef CONFIG_SMP
|
||||
-static int min_sched_tunable_scaling = SCHED_TUNABLESCALING_NONE;
|
||||
-static int max_sched_tunable_scaling = SCHED_TUNABLESCALING_END-1;
|
||||
+static int min_sched_tunable_scaling __read_only = SCHED_TUNABLESCALING_NONE;
|
||||
+static int max_sched_tunable_scaling __read_only = SCHED_TUNABLESCALING_END-1;
|
||||
#endif /* CONFIG_SMP */
|
||||
#endif /* CONFIG_SCHED_DEBUG */
|
||||
|
||||
#ifdef CONFIG_COMPACTION
|
||||
-static int min_extfrag_threshold;
|
||||
-static int max_extfrag_threshold = 1000;
|
||||
+static int min_extfrag_threshold __read_only;
|
||||
+static int max_extfrag_threshold __read_only = 1000;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
--- a/ipc/mq_sysctl.c
|
||||
+++ b/ipc/mq_sysctl.c
|
||||
@@ -47,11 +47,11 @@
|
||||
#define proc_mq_dointvec_minmax NULL
|
||||
#endif
|
||||
|
||||
-static int msg_max_limit_min = MIN_MSGMAX;
|
||||
-static int msg_max_limit_max = HARD_MSGMAX;
|
||||
+static int msg_max_limit_min __read_only = MIN_MSGMAX;
|
||||
+static int msg_max_limit_max __read_only = HARD_MSGMAX;
|
||||
|
||||
-static int msg_maxsize_limit_min = MIN_MSGSIZEMAX;
|
||||
-static int msg_maxsize_limit_max = HARD_MSGSIZEMAX;
|
||||
+static int msg_maxsize_limit_min __read_only = MIN_MSGSIZEMAX;
|
||||
+static int msg_maxsize_limit_max __read_only = HARD_MSGSIZEMAX;
|
||||
|
||||
static struct ctl_table mq_sysctls[] = {
|
||||
{
|
||||
--- a/lib/test_sysctl.c
|
||||
+++ b/lib/test_sysctl.c
|
||||
@@ -36,8 +36,8 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
-static int i_zero;
|
||||
-static int i_one_hundred = 100;
|
||||
+static int i_zero __read_only;
|
||||
+static int i_one_hundred __read_only = 100;
|
||||
|
||||
struct test_sysctl_data {
|
||||
int int_0001;
|
||||
--- a/net/ax25/sysctl_net_ax25.c
|
||||
+++ b/net/ax25/sysctl_net_ax25.c
|
||||
@@ -12,21 +12,21 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <net/ax25.h>
|
||||
|
||||
-static int min_ipdefmode[1], max_ipdefmode[] = {1};
|
||||
-static int min_axdefmode[1], max_axdefmode[] = {1};
|
||||
-static int min_backoff[1], max_backoff[] = {2};
|
||||
-static int min_conmode[1], max_conmode[] = {2};
|
||||
-static int min_window[] = {1}, max_window[] = {7};
|
||||
-static int min_ewindow[] = {1}, max_ewindow[] = {63};
|
||||
-static int min_t1[] = {1}, max_t1[] = {30000};
|
||||
-static int min_t2[] = {1}, max_t2[] = {20000};
|
||||
-static int min_t3[1], max_t3[] = {3600000};
|
||||
-static int min_idle[1], max_idle[] = {65535000};
|
||||
-static int min_n2[] = {1}, max_n2[] = {31};
|
||||
-static int min_paclen[] = {1}, max_paclen[] = {512};
|
||||
-static int min_proto[1], max_proto[] = { AX25_PROTO_MAX };
|
||||
+static int min_ipdefmode[1], max_ipdefmode[] __read_only = {1};
|
||||
+static int min_axdefmode[1], max_axdefmode[] __read_only = {1};
|
||||
+static int min_backoff[1], max_backoff[] __read_only = {2};
|
||||
+static int min_conmode[1], max_conmode[] __read_only = {2};
|
||||
+static int min_window[] __read_only = {1}, max_window[] __read_only = {7};
|
||||
+static int min_ewindow[] __read_only = {1}, max_ewindow[] __read_only = {63};
|
||||
+static int min_t1[] __read_only = {1}, max_t1[] __read_only = {30000};
|
||||
+static int min_t2[] __read_only = {1}, max_t2[] __read_only = {20000};
|
||||
+static int min_t3[1], max_t3[] __read_only = {3600000};
|
||||
+static int min_idle[1], max_idle[] __read_only = {65535000};
|
||||
+static int min_n2[] __read_only = {1}, max_n2[] __read_only = {31};
|
||||
+static int min_paclen[] __read_only = {1}, max_paclen[] __read_only = {512};
|
||||
+static int min_proto[1], max_proto[] __read_only = { AX25_PROTO_MAX };
|
||||
#ifdef CONFIG_AX25_DAMA_SLAVE
|
||||
-static int min_ds_timeout[1], max_ds_timeout[] = {65535000};
|
||||
+static int min_ds_timeout[1], max_ds_timeout[] __read_only = {65535000};
|
||||
#endif
|
||||
|
||||
static const struct ctl_table ax25_param_table[] = {
|
||||
--- a/net/core/sysctl_net_core.c
|
||||
+++ b/net/core/sysctl_net_core.c
|
||||
@@ -23,14 +23,14 @@
|
||||
#include <net/busy_poll.h>
|
||||
#include <net/pkt_sched.h>
|
||||
|
||||
-static int zero = 0;
|
||||
-static int one = 1;
|
||||
-static int two __maybe_unused = 2;
|
||||
-static int min_sndbuf = SOCK_MIN_SNDBUF;
|
||||
-static int min_rcvbuf = SOCK_MIN_RCVBUF;
|
||||
-static int max_skb_frags = MAX_SKB_FRAGS;
|
||||
-static long long_one __maybe_unused = 1;
|
||||
-static long long_max __maybe_unused = LONG_MAX;
|
||||
+static int zero __read_only = 0;
|
||||
+static int one __read_only = 1;
|
||||
+static int two __maybe_unused __read_only = 2;
|
||||
+static int min_sndbuf __read_only = SOCK_MIN_SNDBUF;
|
||||
+static int min_rcvbuf __read_only = SOCK_MIN_RCVBUF;
|
||||
+static int max_skb_frags __read_only = MAX_SKB_FRAGS;
|
||||
+static long long_one __maybe_unused __read_only = 1;
|
||||
+static long long_max __maybe_unused __read_only = LONG_MAX;
|
||||
|
||||
static int net_msg_warn; /* Unused, but still a sysctl */
|
||||
|
||||
--- a/net/ipv4/sysctl_net_ipv4.c
|
||||
+++ b/net/ipv4/sysctl_net_ipv4.c
|
||||
@@ -28,33 +28,33 @@
|
||||
#include <net/protocol.h>
|
||||
#include <net/netevent.h>
|
||||
|
||||
-static int zero;
|
||||
-static int one = 1;
|
||||
-static int three = 3;
|
||||
-static int four = 4;
|
||||
-static int hundred = 100;
|
||||
-static int thousand = 1000;
|
||||
-static int gso_max_segs = GSO_MAX_SEGS;
|
||||
-static int tcp_retr1_max = 255;
|
||||
-static int ip_local_port_range_min[] = { 1, 1 };
|
||||
-static int ip_local_port_range_max[] = { 65535, 65535 };
|
||||
-static int tcp_adv_win_scale_min = -31;
|
||||
-static int tcp_adv_win_scale_max = 31;
|
||||
-static int tcp_min_snd_mss_min = TCP_MIN_SND_MSS;
|
||||
-static int tcp_min_snd_mss_max = 65535;
|
||||
+static int zero __read_only;
|
||||
+static int one __read_only = 1;
|
||||
+static int three __read_only = 3;
|
||||
+static int four __read_only = 4;
|
||||
+static int hundred __read_only = 100;
|
||||
+static int thousand __read_only = 1000;
|
||||
+static int gso_max_segs __read_only = GSO_MAX_SEGS;
|
||||
+static int tcp_retr1_max __read_only = 255;
|
||||
+static int ip_local_port_range_min[] __read_only = { 1, 1 };
|
||||
+static int ip_local_port_range_max[] __read_only = { 65535, 65535 };
|
||||
+static int tcp_adv_win_scale_min __read_only = -31;
|
||||
+static int tcp_adv_win_scale_max __read_only = 31;
|
||||
+static int tcp_min_snd_mss_min __read_only = TCP_MIN_SND_MSS;
|
||||
+static int tcp_min_snd_mss_max __read_only = 65535;
|
||||
static int ip_privileged_port_min;
|
||||
-static int ip_privileged_port_max = 65535;
|
||||
-static int ip_ttl_min = 1;
|
||||
-static int ip_ttl_max = 255;
|
||||
-static int tcp_syn_retries_min = 1;
|
||||
-static int tcp_syn_retries_max = MAX_TCP_SYNCNT;
|
||||
-static int ip_ping_group_range_min[] = { 0, 0 };
|
||||
-static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
|
||||
-static int one_day_secs = 24 * 3600;
|
||||
-static int tcp_delack_seg_min = TCP_DELACK_MIN;
|
||||
-static int tcp_delack_seg_max = 60;
|
||||
+static int ip_privileged_port_max __read_only = 65535;
|
||||
+static int ip_ttl_min __read_only = 1;
|
||||
+static int ip_ttl_max __read_only = 255;
|
||||
+static int tcp_syn_retries_min __read_only = 1;
|
||||
+static int tcp_syn_retries_max __read_only = MAX_TCP_SYNCNT;
|
||||
+static int ip_ping_group_range_min[] __read_only = { 0, 0 };
|
||||
+static int ip_ping_group_range_max[] __read_only = { GID_T_MAX, GID_T_MAX };
|
||||
+static int one_day_secs __read_only = 24 * 3600;
|
||||
+static int tcp_delack_seg_min __read_only = TCP_DELACK_MIN;
|
||||
+static int tcp_delack_seg_max __read_only = 60;
|
||||
static int tcp_use_userconfig_min;
|
||||
-static int tcp_use_userconfig_max = 1;
|
||||
+static int tcp_use_userconfig_max __read_only = 1;
|
||||
|
||||
/* obsolete */
|
||||
static int sysctl_tcp_low_latency __read_mostly;
|
||||
--- a/net/ipv6/sysctl_net_ipv6.c
|
||||
+++ b/net/ipv6/sysctl_net_ipv6.c
|
||||
@@ -22,9 +22,9 @@
|
||||
#endif
|
||||
|
||||
static int zero;
|
||||
-static int one = 1;
|
||||
-static int auto_flowlabels_min;
|
||||
-static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
|
||||
+static int one __read_only = 1;
|
||||
+static int auto_flowlabels_min __read_only;
|
||||
+static int auto_flowlabels_max __read_only = IP6_AUTO_FLOW_LABEL_MAX;
|
||||
|
||||
static int proc_rt6_multipath_hash_policy(struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
--- a/net/netrom/sysctl_net_netrom.c
|
||||
+++ b/net/netrom/sysctl_net_netrom.c
|
||||
@@ -15,22 +15,22 @@
|
||||
/*
|
||||
* Values taken from NET/ROM documentation.
|
||||
*/
|
||||
-static int min_quality[] = {0}, max_quality[] = {255};
|
||||
-static int min_obs[] = {0}, max_obs[] = {255};
|
||||
-static int min_ttl[] = {0}, max_ttl[] = {255};
|
||||
-static int min_t1[] = {5 * HZ};
|
||||
-static int max_t1[] = {600 * HZ};
|
||||
-static int min_n2[] = {2}, max_n2[] = {127};
|
||||
-static int min_t2[] = {1 * HZ};
|
||||
-static int max_t2[] = {60 * HZ};
|
||||
-static int min_t4[] = {1 * HZ};
|
||||
-static int max_t4[] = {1000 * HZ};
|
||||
-static int min_window[] = {1}, max_window[] = {127};
|
||||
-static int min_idle[] = {0 * HZ};
|
||||
-static int max_idle[] = {65535 * HZ};
|
||||
-static int min_route[] = {0}, max_route[] = {1};
|
||||
-static int min_fails[] = {1}, max_fails[] = {10};
|
||||
-static int min_reset[] = {0}, max_reset[] = {1};
|
||||
+static int min_quality[] __read_only = {0}, max_quality[] __read_only = {255};
|
||||
+static int min_obs[] __read_only = {0}, max_obs[] __read_only = {255};
|
||||
+static int min_ttl[] __read_only = {0}, max_ttl[] __read_only = {255};
|
||||
+static int min_t1[] __read_only = {5 * HZ};
|
||||
+static int max_t1[] __read_only = {600 * HZ};
|
||||
+static int min_n2[] __read_only = {2}, max_n2[] __read_only = {127};
|
||||
+static int min_t2[] __read_only = {1 * HZ};
|
||||
+static int max_t2[] __read_only = {60 * HZ};
|
||||
+static int min_t4[] __read_only = {1 * HZ};
|
||||
+static int max_t4[] __read_only = {1000 * HZ};
|
||||
+static int min_window[] __read_only = {1}, max_window[] __read_only = {127};
|
||||
+static int min_idle[] __read_only = {0 * HZ};
|
||||
+static int max_idle[] __read_only = {65535 * HZ};
|
||||
+static int min_route[] __read_only = {0}, max_route[] __read_only = {1};
|
||||
+static int min_fails[] __read_only = {1}, max_fails[] __read_only = {10};
|
||||
+static int min_reset[] __read_only = {0}, max_reset[] __read_only = {1};
|
||||
|
||||
static struct ctl_table_header *nr_table_header;
|
||||
|
||||
--- a/net/phonet/sysctl.c
|
||||
+++ b/net/phonet/sysctl.c
|
||||
@@ -35,8 +35,8 @@
|
||||
#define DYNAMIC_PORT_MAX 0x7f
|
||||
|
||||
static DEFINE_SEQLOCK(local_port_range_lock);
|
||||
-static int local_port_range_min[2] = {0, 0};
|
||||
-static int local_port_range_max[2] = {1023, 1023};
|
||||
+static int local_port_range_min[2] __read_only = {0, 0};
|
||||
+static int local_port_range_max[2] __read_only = {1023, 1023};
|
||||
static int local_port_range[2] = {DYNAMIC_PORT_MIN, DYNAMIC_PORT_MAX};
|
||||
static struct ctl_table_header *phonet_table_hrd;
|
||||
|
||||
--- a/net/rose/sysctl_net_rose.c
|
||||
+++ b/net/rose/sysctl_net_rose.c
|
||||
@@ -12,15 +12,15 @@
|
||||
#include <net/ax25.h>
|
||||
#include <net/rose.h>
|
||||
|
||||
-static int min_timer[] = {1 * HZ};
|
||||
-static int max_timer[] = {300 * HZ};
|
||||
-static int min_idle[] = {0 * HZ};
|
||||
-static int max_idle[] = {65535 * HZ};
|
||||
-static int min_route[1], max_route[] = {1};
|
||||
-static int min_ftimer[] = {60 * HZ};
|
||||
-static int max_ftimer[] = {600 * HZ};
|
||||
-static int min_maxvcs[] = {1}, max_maxvcs[] = {254};
|
||||
-static int min_window[] = {1}, max_window[] = {7};
|
||||
+static int min_timer[] __read_only = {1 * HZ};
|
||||
+static int max_timer[] __read_only = {300 * HZ};
|
||||
+static int min_idle[] __read_only = {0 * HZ};
|
||||
+static int max_idle[] __read_only = {65535 * HZ};
|
||||
+static int min_route[1], max_route[] __read_only = {1};
|
||||
+static int min_ftimer[] __read_only = {60 * HZ};
|
||||
+static int max_ftimer[] __read_only = {600 * HZ};
|
||||
+static int min_maxvcs[] __read_only = {1}, max_maxvcs[] = {254};
|
||||
+static int min_window[] __read_only = {1}, max_window[] = {7};
|
||||
|
||||
static struct ctl_table_header *rose_table_header;
|
||||
|
||||
--- a/net/sctp/sysctl.c
|
||||
+++ b/net/sctp/sysctl.c
|
||||
@@ -40,21 +40,21 @@
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
-static int zero = 0;
|
||||
-static int one = 1;
|
||||
-static int timer_max = 86400000; /* ms in one day */
|
||||
-static int int_max = INT_MAX;
|
||||
-static int sack_timer_min = 1;
|
||||
-static int sack_timer_max = 500;
|
||||
-static int addr_scope_max = SCTP_SCOPE_POLICY_MAX;
|
||||
-static int rwnd_scale_max = 16;
|
||||
-static int rto_alpha_min = 0;
|
||||
-static int rto_beta_min = 0;
|
||||
-static int rto_alpha_max = 1000;
|
||||
-static int rto_beta_max = 1000;
|
||||
+static int zero __read_only = 0;
|
||||
+static int one __read_only = 1;
|
||||
+static int timer_max __read_only = 86400000; /* ms in one day */
|
||||
+static int int_max __read_only = INT_MAX;
|
||||
+static int sack_timer_min __read_only = 1;
|
||||
+static int sack_timer_max __read_only = 500;
|
||||
+static int addr_scope_max __read_only = SCTP_SCOPE_POLICY_MAX;
|
||||
+static int rwnd_scale_max __read_only = 16;
|
||||
+static int rto_alpha_min __read_only = 0;
|
||||
+static int rto_beta_min __read_only = 0;
|
||||
+static int rto_alpha_max __read_only = 1000;
|
||||
+static int rto_beta_max __read_only = 1000;
|
||||
|
||||
-static unsigned long max_autoclose_min = 0;
|
||||
-static unsigned long max_autoclose_max =
|
||||
+static unsigned long max_autoclose_min __read_only = 0;
|
||||
+static unsigned long max_autoclose_max __read_only =
|
||||
(MAX_SCHEDULE_TIMEOUT / HZ > UINT_MAX)
|
||||
? UINT_MAX : MAX_SCHEDULE_TIMEOUT / HZ;
|
||||
|
||||
--- a/net/x25/sysctl_net_x25.c
|
||||
+++ b/net/x25/sysctl_net_x25.c
|
||||
@@ -13,8 +13,8 @@
|
||||
#include <linux/init.h>
|
||||
#include <net/x25.h>
|
||||
|
||||
-static int min_timer[] = { 1 * HZ };
|
||||
-static int max_timer[] = { 300 * HZ };
|
||||
+static int min_timer[] __read_only = { 1 * HZ };
|
||||
+static int max_timer[] __read_only = { 300 * HZ };
|
||||
|
||||
static struct ctl_table_header *x25_table_header;
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
0008-Graphene-Kernel_Hardening-misc/4.14/0005.patch
|
||||
0008-Graphene-Kernel_Hardening-misc/4.14/0010.patch
|
||||
0008-Graphene-Kernel_Hardening-misc/4.14/0018.patch
|
||||
# misc/0021 is already present in LOS @ 28f9290ae067 (KSYM_NAME_LEN=192).
|
||||
# Applying it with GNU patch's reverse detection silently shrinks it to 128
|
||||
# under CONFIG_CFI_CLANG, so it must remain omitted.
|
||||
0008-Graphene-Kernel_Hardening-random/4.14/0002.patch
|
||||
0008-Graphene-Kernel_Hardening-random/4.14/0007.patch
|
||||
0008-Graphene-Kernel_Hardening-random/4.14/0012.patch
|
||||
|
||||
@@ -30,13 +30,19 @@ Stage1+combo4+CVE apply.
|
||||
```text
|
||||
zcat /proc/config.gz | grep CONFIG_SECURITY_YAMA=y
|
||||
zcat /proc/config.gz | grep CONFIG_SLAB_FREELIST_RANDOM=y
|
||||
cat /proc/sys/kernel/yama/ptrace_scope # expect 1 (default)
|
||||
cat /proc/sys/kernel/yama/ptrace_scope # expect 2 (ship policy)
|
||||
pidof magiskd # still up
|
||||
```
|
||||
|
||||
The ship script now applies `apply_009_yama_ptrace.py`, so the current LAB
|
||||
policy is `ptrace_scope=2`, not the kernel default `1`. Matching AXP's
|
||||
`persist.native_debug` 0/2 toggle is a separate userspace change and must not
|
||||
be mixed into the CVE-2022-49980 boot test.
|
||||
|
||||
## Out of scope here
|
||||
|
||||
- `CVE-2022-49980` lock-scope port (still denylist)
|
||||
- The original `CVE-2022-49980/ANY/0001.patch` remains denylisted. The next
|
||||
isolated bacon applies `ports/cve-2022-49980-lock-scope.patch` instead.
|
||||
- nf_tables high-offset CVEs
|
||||
- Stage1 slab FAIL patches (`0010`/`0018` still reject on this tree)
|
||||
- `hardened_malloc` / bootloader lock
|
||||
@@ -0,0 +1,414 @@
|
||||
#!/usr/bin/env python3
|
||||
"""012: Graphene hardened_malloc on LOS 23.2, the AXP GRAPHENE_MALLOC set.
|
||||
|
||||
Source of the integration is GrapheneOS platform_bionic branch 16:
|
||||
64-bit libc uses hardened_malloc, 32-bit stays Scudo, and SurfaceFlinger
|
||||
plus camera executables stay on Scudo. AXP documents that camera exception
|
||||
as applying to every camera executable, which on sunfish is
|
||||
/vendor/bin/hw/android.hardware.camera.provider@2.7-service-google rather
|
||||
than the newer Pixel apex path.
|
||||
|
||||
Library pin: GrapheneOS/hardened_malloc 1d7fc7ffe0f8 (branch 14, 2025-04-03),
|
||||
the newest branch that is not the Android 17 line. device_has_arm_mte is
|
||||
removed because LOS soong has no such product variable and sunfish has no MTE.
|
||||
|
||||
Class region size is 2GiB, as DivestOS/AXP set it, because the sunfish
|
||||
kernel has a 39-bit address space (GOLDEN: VA_BITS=39 with hardened_malloc).
|
||||
|
||||
system/core init.rc raises vm.max_map_count to 1048576, same as GrapheneOS 16.
|
||||
frameworks/av no longer sets RLIMIT_AS on this tree, so that AXP/Pie hunk
|
||||
does not apply.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
MARKER = "SUNFISH_A16_012_HARDENED_MALLOC"
|
||||
PIN = "1d7fc7ffe0f8068a32ab2ba33df7515e7bad3f5f"
|
||||
REPO = "https://github.com/GrapheneOS/hardened_malloc.git"
|
||||
REGION_48BIT = "-DCONFIG_CLASS_REGION_SIZE=34359738368"
|
||||
REGION_39BIT = "-DCONFIG_CLASS_REGION_SIZE=2147483648"
|
||||
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
TREE = pathlib.Path(os.environ.get("ANDROID_BUILD_TOP", "/srv/rom/src/lineage"))
|
||||
|
||||
|
||||
def must_replace(path: pathlib.Path, old: str, new: str, label: str) -> None:
|
||||
text = path.read_text()
|
||||
if new in text:
|
||||
print(f"already applied: {path} ({label})")
|
||||
return
|
||||
if old not in text:
|
||||
sys.exit(f"anchor missing for {label} in {path}")
|
||||
path.write_text(text.replace(old, new, 1))
|
||||
print(f"patched {label}: {path}")
|
||||
|
||||
|
||||
def rewrite_region_comment(bp: pathlib.Path) -> None:
|
||||
text = bp.read_text()
|
||||
stale = f'{REGION_39BIT}", // 32GiB'
|
||||
fresh = f'{REGION_39BIT}", // 2GiB, 39-bit VA (DivestOS/AXP)'
|
||||
if stale not in text:
|
||||
return
|
||||
bp.write_text(text.replace(stale, fresh))
|
||||
print(f"region comment: {bp}")
|
||||
|
||||
|
||||
def ensure_library() -> None:
|
||||
dest = TREE / "external/hardened_malloc"
|
||||
if (dest / ".git").is_dir():
|
||||
head = subprocess.check_output(
|
||||
["git", "-C", dest, "rev-parse", "HEAD"], text=True
|
||||
).strip()
|
||||
bp_text = (dest / "Android.bp").read_text()
|
||||
if head == PIN and MARKER in bp_text and REGION_39BIT in bp_text:
|
||||
rewrite_region_comment(dest / "Android.bp")
|
||||
print(f"hardened_malloc already at {PIN}")
|
||||
return
|
||||
subprocess.check_call(["git", "-C", dest, "fetch", "--depth", "1", "origin", PIN])
|
||||
subprocess.check_call(["git", "-C", dest, "checkout", "--detach", PIN])
|
||||
else:
|
||||
if dest.exists():
|
||||
sys.exit(f"{dest} exists and is not a git checkout")
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.check_call(
|
||||
["git", "clone", "--depth", "1", REPO, str(dest)]
|
||||
)
|
||||
subprocess.check_call(["git", "-C", dest, "fetch", "--depth", "1", "origin", PIN])
|
||||
subprocess.check_call(["git", "-C", dest, "checkout", "--detach", PIN])
|
||||
bp = dest / "Android.bp"
|
||||
text = bp.read_text()
|
||||
mte = """ device_has_arm_mte: {
|
||||
cflags: ["-DHAS_ARM_MTE", "-march=armv8-a+dotprod+memtag"]
|
||||
},
|
||||
"""
|
||||
if mte in text:
|
||||
text = text.replace(mte, "")
|
||||
# sunfish msm-4.14 is CONFIG_ARM64_VA_BITS=39. The 32GiB default class
|
||||
# region needs 48-bit VA and aborts every 64-bit process at boot.
|
||||
# DivestOS/AXP Patch.sh: sed 's/34359738368/2147483648/' Android.bp.
|
||||
text = text.replace(REGION_48BIT, REGION_39BIT)
|
||||
text = text.replace(
|
||||
f'{REGION_39BIT}", // 32GiB',
|
||||
f'{REGION_39BIT}", // 2GiB, 39-bit VA (DivestOS/AXP)',
|
||||
)
|
||||
bp.write_text(text)
|
||||
text = bp.read_text()
|
||||
if REGION_39BIT not in text:
|
||||
sys.exit(f"CONFIG_CLASS_REGION_SIZE is not 2GiB in {bp}")
|
||||
if MARKER not in text:
|
||||
text = text.replace(
|
||||
"cc_library {\n",
|
||||
f"cc_library {{\n // {MARKER}: sunfish has no MTE; LOS soong has no device_has_arm_mte.\n",
|
||||
1,
|
||||
)
|
||||
bp.write_text(text)
|
||||
head = subprocess.check_output(
|
||||
["git", "-C", dest, "rev-parse", "HEAD"], text=True
|
||||
).strip()
|
||||
if head != PIN:
|
||||
sys.exit(f"hardened_malloc HEAD {head} != {PIN}")
|
||||
print(f"hardened_malloc {PIN}")
|
||||
|
||||
|
||||
def patch_bionic() -> None:
|
||||
wrapper = TREE / "bionic/libc/bionic/h_malloc_wrapper.cpp"
|
||||
src = HERE / "h_malloc_wrapper.cpp"
|
||||
if not wrapper.exists() or wrapper.read_text() != src.read_text():
|
||||
wrapper.write_text(src.read_text())
|
||||
print(f"installed {wrapper}")
|
||||
|
||||
must_replace(
|
||||
TREE / "bionic/libc/Android.bp",
|
||||
"""cc_defaults {
|
||||
name: "libc_native_allocator_defaults",
|
||||
|
||||
whole_static_libs: [
|
||||
"libscudo",
|
||||
],
|
||||
cflags: [
|
||||
"-DUSE_SCUDO",
|
||||
],
|
||||
header_libs: ["gwp_asan_headers"],
|
||||
product_variables: {
|
||||
malloc_low_memory: {
|
||||
cflags: ["-UUSE_SCUDO"],
|
||||
whole_static_libs: [
|
||||
"libjemalloc5",
|
||||
"libc_jemalloc_wrapper",
|
||||
],
|
||||
exclude_static_libs: [
|
||||
"libscudo",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
""",
|
||||
f"""cc_defaults {{
|
||||
// {MARKER}
|
||||
name: "libc_native_allocator_defaults",
|
||||
|
||||
header_libs: ["gwp_asan_headers"],
|
||||
whole_static_libs: ["libscudo"],
|
||||
|
||||
// GrapheneOS 16: hardened_malloc on 64-bit, Scudo on 32-bit.
|
||||
multilib: {{
|
||||
lib32: {{
|
||||
cflags: ["-DUSE_SCUDO"],
|
||||
}},
|
||||
lib64: {{
|
||||
srcs: ["bionic/h_malloc_wrapper.cpp"],
|
||||
cflags: [
|
||||
"-DH_MALLOC_PREFIX",
|
||||
"-DUSE_H_MALLOC",
|
||||
"-DUSE_SCUDO",
|
||||
],
|
||||
whole_static_libs: [
|
||||
"libhardened_malloc",
|
||||
],
|
||||
}},
|
||||
}},
|
||||
}}
|
||||
""",
|
||||
"allocator-defaults",
|
||||
)
|
||||
|
||||
must_replace(
|
||||
TREE / "bionic/libc/bionic/malloc_common.h",
|
||||
"""#else // __has_feature(hwaddress_sanitizer)
|
||||
|
||||
#if defined(USE_SCUDO)
|
||||
""",
|
||||
f"""#else // __has_feature(hwaddress_sanitizer)
|
||||
|
||||
// {MARKER}
|
||||
#ifdef __LP64__
|
||||
#ifndef USE_H_MALLOC
|
||||
#error missing USE_H_MALLOC
|
||||
#endif
|
||||
|
||||
#include "h_malloc.h"
|
||||
#define Malloc(function) h_ ## function
|
||||
__BEGIN_DECLS
|
||||
int h_malloc_info(int options, FILE* fp);
|
||||
__END_DECLS
|
||||
|
||||
#if defined(USE_SCUDO)
|
||||
#include "scudo.h"
|
||||
void InitNativeAllocatorDispatch(libc_globals* globals);
|
||||
#endif
|
||||
|
||||
#define BOTH_H_MALLOC_AND_SCUDO
|
||||
|
||||
#else // 32-bit
|
||||
|
||||
#if defined(USE_SCUDO)
|
||||
""",
|
||||
"malloc-header",
|
||||
)
|
||||
header = TREE / "bionic/libc/bionic/malloc_common.h"
|
||||
text = header.read_text()
|
||||
tail = """#define Malloc(function) je_ ## function
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
const MallocDispatch* NativeAllocatorDispatch();
|
||||
"""
|
||||
tail_new = """#define Malloc(function) je_ ## function
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __LP64__
|
||||
|
||||
#endif // __has_feature(hwaddress_sanitizer)
|
||||
|
||||
const MallocDispatch* NativeAllocatorDispatch();
|
||||
"""
|
||||
if tail_new not in text:
|
||||
if tail not in text:
|
||||
sys.exit(f"header close anchor missing in {header}")
|
||||
header.write_text(text.replace(tail, tail_new, 1))
|
||||
|
||||
must_replace(
|
||||
TREE / "bionic/libc/bionic/malloc_common.cpp",
|
||||
"""const MallocDispatch* NativeAllocatorDispatch() {
|
||||
return &__libc_malloc_default_dispatch;
|
||||
}
|
||||
""",
|
||||
f"""#if defined(BOTH_H_MALLOC_AND_SCUDO)
|
||||
// {MARKER}: camera executables and surfaceflinger stay on Scudo.
|
||||
|
||||
#define ScudoMalloc(function) scudo_ ## function
|
||||
|
||||
static constexpr MallocDispatch __scudo_malloc_dispatch __attribute__((unused)) = {{
|
||||
ScudoMalloc(calloc),
|
||||
ScudoMalloc(free),
|
||||
ScudoMalloc(mallinfo),
|
||||
ScudoMalloc(malloc),
|
||||
ScudoMalloc(malloc_usable_size),
|
||||
ScudoMalloc(memalign),
|
||||
ScudoMalloc(posix_memalign),
|
||||
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
|
||||
ScudoMalloc(pvalloc),
|
||||
#endif
|
||||
ScudoMalloc(realloc),
|
||||
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
|
||||
ScudoMalloc(valloc),
|
||||
#endif
|
||||
ScudoMalloc(malloc_iterate),
|
||||
ScudoMalloc(malloc_disable),
|
||||
ScudoMalloc(malloc_enable),
|
||||
ScudoMalloc(mallopt),
|
||||
ScudoMalloc(aligned_alloc),
|
||||
ScudoMalloc(malloc_info),
|
||||
}};
|
||||
|
||||
static const MallocDispatch* native_allocator_dispatch;
|
||||
|
||||
void InitNativeAllocatorDispatch(libc_globals* globals) {{
|
||||
bool hardened_impl = true;
|
||||
if (globals->flags & GLOBAL_FLAG_DISABLE_HARDENED_MALLOC) {{
|
||||
hardened_impl = false;
|
||||
}} else if (getenv("DISABLE_HARDENED_MALLOC") != nullptr) {{
|
||||
hardened_impl = false;
|
||||
}}
|
||||
|
||||
const MallocDispatch* table = hardened_impl ?
|
||||
&__libc_malloc_default_dispatch :
|
||||
&__scudo_malloc_dispatch;
|
||||
|
||||
if (!hardened_impl) {{
|
||||
globals->malloc_dispatch_table = __scudo_malloc_dispatch;
|
||||
globals->current_dispatch_table = &globals->malloc_dispatch_table;
|
||||
globals->default_dispatch_table = &globals->malloc_dispatch_table;
|
||||
}}
|
||||
|
||||
native_allocator_dispatch = table;
|
||||
}}
|
||||
|
||||
const MallocDispatch* NativeAllocatorDispatch() {{
|
||||
return native_allocator_dispatch;
|
||||
}}
|
||||
#else
|
||||
const MallocDispatch* NativeAllocatorDispatch() {{
|
||||
return &__libc_malloc_default_dispatch;
|
||||
}}
|
||||
#endif
|
||||
""",
|
||||
"dispatch",
|
||||
)
|
||||
|
||||
must_replace(
|
||||
TREE / "bionic/libc/bionic/malloc_common_dynamic.cpp",
|
||||
"""void MallocInitImpl(libc_globals* globals) {
|
||||
char prop[PROP_VALUE_MAX];
|
||||
""",
|
||||
f"""void MallocInitImpl(libc_globals* globals) {{
|
||||
#if defined(BOTH_H_MALLOC_AND_SCUDO)
|
||||
// {MARKER}
|
||||
InitNativeAllocatorDispatch(globals);
|
||||
#endif
|
||||
char prop[PROP_VALUE_MAX];
|
||||
""",
|
||||
"malloc-init",
|
||||
)
|
||||
|
||||
must_replace(
|
||||
TREE / "bionic/libc/private/bionic_globals.h",
|
||||
""" MallocDispatch malloc_dispatch_table;
|
||||
};
|
||||
""",
|
||||
f""" MallocDispatch malloc_dispatch_table;
|
||||
// {MARKER}
|
||||
int flags;
|
||||
}};
|
||||
|
||||
#define GLOBAL_FLAG_DISABLE_HARDENED_MALLOC 1
|
||||
""",
|
||||
"globals",
|
||||
)
|
||||
|
||||
init = TREE / "bionic/libc/bionic/libc_init_dynamic.cpp"
|
||||
text = init.read_text()
|
||||
if '#include <string.h>' not in text:
|
||||
if '#include <stdlib.h>' not in text:
|
||||
sys.exit(f"stdlib include missing in {init}")
|
||||
text = text.replace(
|
||||
"#include <stdlib.h>\n",
|
||||
"#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n",
|
||||
1,
|
||||
)
|
||||
helper = f"""
|
||||
// {MARKER}: GrapheneOS disables hardened_malloc for surfaceflinger.
|
||||
// AXP expands the camera exception to every camera executable. sunfish runs
|
||||
// /vendor/bin/hw/android.hardware.camera.provider@2.7-service-google.
|
||||
static void init_hardened_malloc_exclusions(libc_globals* globals) {{
|
||||
char exe_path[500];
|
||||
ssize_t n = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
if (n <= 0) {{
|
||||
globals->flags = 0;
|
||||
return;
|
||||
}}
|
||||
exe_path[n] = '\\0';
|
||||
if (strcmp(exe_path, "/system/bin/surfaceflinger") == 0 ||
|
||||
strstr(exe_path, "camera") != nullptr) {{
|
||||
globals->flags = GLOBAL_FLAG_DISABLE_HARDENED_MALLOC;
|
||||
}} else {{
|
||||
globals->flags = 0;
|
||||
}}
|
||||
}}
|
||||
|
||||
"""
|
||||
# __libc_preinit_impl must stay noinline (stack protector guard setup).
|
||||
noinline = "__attribute__((noinline))\n"
|
||||
anchor = noinline + "static void __libc_preinit_impl() {"
|
||||
misplaced = noinline + helper + "static void __libc_preinit_impl() {"
|
||||
if misplaced in text:
|
||||
text = text.replace(misplaced, helper + anchor, 1)
|
||||
if "init_hardened_malloc_exclusions" not in text:
|
||||
if anchor not in text:
|
||||
sys.exit(f"preinit anchor missing in {init}")
|
||||
text = text.replace(anchor, helper + anchor, 1)
|
||||
if anchor not in text:
|
||||
sys.exit(f"__libc_preinit_impl lost noinline in {init}")
|
||||
old_mutate = " __libc_globals.mutate(__libc_init_malloc);\n"
|
||||
new_mutate = """ __libc_globals.mutate([](libc_globals* globals) {
|
||||
init_hardened_malloc_exclusions(globals);
|
||||
__libc_init_malloc(globals);
|
||||
});
|
||||
"""
|
||||
if "init_hardened_malloc_exclusions(globals)" not in text:
|
||||
if old_mutate not in text:
|
||||
sys.exit(f"mutate anchor missing in {init}")
|
||||
text = text.replace(old_mutate, new_mutate, 1)
|
||||
init.write_text(text)
|
||||
print(f"patched exclusions: {init}")
|
||||
|
||||
|
||||
def patch_init_rc() -> None:
|
||||
must_replace(
|
||||
TREE / "system/core/rootdir/init.rc",
|
||||
" symlink /sys/class/block/${dev.mnt.rootdisk.data} /dev/sys/block/by-name/rootdisk\n",
|
||||
" symlink /sys/class/block/${dev.mnt.rootdisk.data} /dev/sys/block/by-name/rootdisk\n\n"
|
||||
f" # {MARKER}: GrapheneOS 16 vm.max_map_count for hardened_malloc\n"
|
||||
" write /proc/sys/vm/max_map_count 1048576\n",
|
||||
"max-map-count",
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not (TREE / "bionic/libc/Android.bp").is_file():
|
||||
sys.exit(f"not an Android tree: {TREE}")
|
||||
ensure_library()
|
||||
patch_bionic()
|
||||
patch_init_rc()
|
||||
print(f"done: {MARKER}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <private/MallocXmlElem.h>
|
||||
|
||||
#include "h_malloc.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
int h_malloc_info(int options, FILE* fp);
|
||||
__END_DECLS
|
||||
|
||||
int h_malloc_info(int options, FILE* fp) {
|
||||
if (options != 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fflush(fp);
|
||||
int fd = fileno(fp);
|
||||
MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
|
||||
|
||||
// Dump all of the large allocations in the arenas.
|
||||
for (size_t i = 0; i < h_mallinfo_narenas(); i++) {
|
||||
struct mallinfo mi = h_mallinfo_arena_info(i);
|
||||
if (mi.hblkhd != 0) {
|
||||
MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i);
|
||||
{
|
||||
MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks);
|
||||
MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks);
|
||||
MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks);
|
||||
|
||||
size_t total = 0;
|
||||
for (size_t j = 0; j < h_mallinfo_nbins(); j++) {
|
||||
struct mallinfo mi = h_mallinfo_bin_info(i, j);
|
||||
if (mi.ordblks != 0) {
|
||||
MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j);
|
||||
MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks);
|
||||
MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks);
|
||||
MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks);
|
||||
total += mi.ordblks;
|
||||
}
|
||||
}
|
||||
MallocXmlElem(fd, "bins-total").Contents("%zu", total);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+5
@@ -1,8 +1,13 @@
|
||||
# US-S2 — project release keys (Gate G).
|
||||
# Soong requires paths inside ANDROID_BUILD_TOP (absolute $HOME/.android-certs panics).
|
||||
# Host: copy keys into vendor/sunfish-a16/certs/ (gitignored). See docs/009-hardening-signing-checklist.md
|
||||
# apply_009_axp_identity.py copies them to vendor/lineage-priv/keys, the only
|
||||
# path for which build/make/core/config.mk sets BUILD_KEYS := release-keys.
|
||||
|
||||
_AXP_CERT_DIR := vendor/sunfish-a16/certs
|
||||
ifneq ($(wildcard vendor/lineage-priv/keys/releasekey.pk8),)
|
||||
_AXP_CERT_DIR := vendor/lineage-priv/keys
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard $(_AXP_CERT_DIR)/releasekey.pk8),)
|
||||
PRODUCT_DEFAULT_DEV_CERTIFICATE := $(_AXP_CERT_DIR)/releasekey
|
||||
|
||||
Reference in new issue
Block a user