Files
config_apk/README.md
2026-06-08 04:53:02 -07:00

8.9 KiB

pawlet_config_apk

Configuration APK for PawletOS — an Android 16 custom ROM for Raspberry Pi 4/5.

This APK is installed at first boot by ConfigProvisioner, which downloads it from the release URL in vendor.cfg, installs it, then reads its resources to apply system settings and APN entries. When baked into the ROM as a system app it also acts as a Partner customization source for Lawnchair.


Building

./gradlew assembleRelease
# Output: build/outputs/apk/release/pawlet_config_apk-release.apk

The APK is signed with the debug key for standalone builds. When baked into the ROM, AOSP signs it with the platform key via Android.bp.

Upload a release to https://git.oxmc.me/PawletOS/config_apk/releases and set config_apk_url in vendor.cfg to point at it.


Settings — res/xml/settings.xml

ConfigProvisioner reads this file after installing the APK and applies each entry using WRITE_SECURE_SETTINGS. All settings are optional; comment out anything you don't need.

Setting types

<settings>
    <secure name="key" value="value" />   <!-- Settings.Secure -->
    <system name="key" value="value" />   <!-- Settings.System -->
    <global name="key" value="value" />   <!-- Settings.Global -->
</settings>

Conditional blocks

Wrap any settings in <if> to apply them only when all listed conditions match. Nest <if> blocks for AND logic; use separate blocks at the same level for OR-like behaviour.

<if [conditions…]>
    <secure name="…" value="…" />
</if>
Attribute Match type Example
manufacturer exact, case-insensitive manufacturer="motorola"
brand exact, case-insensitive brand="motorola"
model substring, case-insensitive model="Raspberry Pi 5"
sdk exact API level sdk="36"
sdk_min minimum API level (inclusive) sdk_min="31"
sdk_max maximum API level (inclusive) sdk_max="34"
form_factor tablet | phone | flip | tv form_factor="tablet"
feature PackageManager feature string feature="android.hardware.telephony"

Telephony defaults

The telephony block in settings.xml is gated on android.hardware.telephony and sets safe defaults (mobile data on, roaming off, LTE preferred, VoLTE on). Edit the values directly — the <if> wrapper means they are silently skipped on devices without a SIM slot (e.g. RPi without a modem hat).


APNs

ConfigProvisioner applies APNs using WRITE_APN_SETTINGS after installing the APK. Two modes are supported; the multi-file mode takes precedence.

Mode 1 — Multi-file assets/apns/ (preferred)

Place XML files anywhere under assets/apns/. ConfigProvisioner walks the directory recursively and parses every .xml it finds. Files can be organised however you like:

assets/apns/
    global.xml              # hand-authored carrier-agnostic entries
    assets/apns/lineage/    # auto-populated from LineageOS submodule (see below)
        US.xml
        GB.xml
        …

Files must use the standard AOSP / LineageOS/android_vendor_apn format:

<apns version="8">
    <apn carrier="Carrier Name"
         mcc="310" mnc="260"
         apn="fast.t-mobile.com"
         type="default,supl"
         protocol="IPV4V6"
         roaming_protocol="IPV4V6"
         carrier_enabled="true" />
</apns>

Both carrier= (AOSP) and name= (custom) are accepted for the display name. Numeric is derived from mcc+mnc if a numeric= attribute is absent.

Supported attributes: carrier/name, mcc, mnc, apn, type, protocol, roaming_protocol, server, proxy, port, mmsc, mmsproxy, mmsport, user, password, authtype, bearer_bitmask, profile_id, mtu, modem_cognitive, max_conns, wait_time, max_conns_time, carrier_enabled, mvno_type, mvno_match_data

MVNO entries (same MCC/MNC, different mvno_type/mvno_match_data) are treated as distinct APNs. Deduplication key: numeric + apn + mvno_type + mvno_match_data.

Using LineageOS/android_vendor_apn as a submodule

The submodule goes at the repo root, not inside assets/. Gradle copies only the *.xml files into the APK, leaving scripts, schemas, and licence files behind.

git submodule add https://github.com/LineageOS/android_vendor_apn lineage_apn
git submodule update --init

build.gradle automatically copies lineage_apn/**/*.xmlbuild/generated/assets/apns/lineage/ before the asset merge step. If the submodule is not initialised the copy task is a no-op — the build still succeeds.

To add another APN source, add one entry to apnSubmodules in build.gradle:

def apnSubmodules = [
    'lineage_apn':   'lineage',
    'my_custom_apn': 'custom',   // git submodule add <url> my_custom_apn
]

Mode 2 — Single-file res/xml/apns.xml (fallback)

Used only when assets/apns/ is absent or empty. Same APN attribute set as above, plus a numeric= attribute as an alternative to separate mcc/mnc.

<apns>
    <apn name="My Carrier"
         numeric="31026"
         mcc="310" mnc="26"
         apn="internet"
         type="default,supl"
         protocol="IPV4V6"
         roaming_protocol="IPV4V6"
         carrier_enabled="1" />
</apns>

Launcher layout

Two paths exist depending on whether the APK is a system app or a user app.

Path A — Partner customization (system app only)

When the APK is baked into the ROM, Lawnchair discovers it via MATCH_SYSTEM_ONLY and reads res/xml/partner_default_layout.xml directly. Edit that file to set the default home screen.

Path B — ContentProvider (user app, installed by ConfigProvisioner)

ConfigProvisioner writes Settings.Secure["launcher3.layout.provider"] = "app.pawlet.config.layout". Lawnchair then requests the layout from LayoutProvider, which serves res/raw/partner_default_layout.xml as raw bytes.

Edit res/raw/partner_default_layout.xml for the ContentProvider path (the file in res/xml/ is for the system-app path). Both files exist separately because res/xml/ is AAPT2-compiled to binary while res/raw/ is copied as-is.

<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto">
    <favorite
        launcher:packageName="com.vivaldi.browser"
        launcher:className="com.vivaldi.browser.ChromeTabbedActivity"
        launcher:screen="0" launcher:x="0" launcher:y="0" />
</favorites>

Icon pack — res/xml/appfilter.xml

Standard ADW/Nova/Lawnchair icon pack format. Map component names to drawable resource names:

<resources>
    <item component="ComponentInfo{com.example.app/com.example.app.MainActivity}"
          drawable="ic_example" />
</resources>

Place the corresponding drawables in res/drawable-nodpi/. The IconPackActivity is a no-display activity that satisfies launcher icon pack discovery queries.


ROM integration (system app path)

Android.bp

android_app {
    name: "PawletConfig",
    srcs: [],
    resource_dirs: ["res"],
    certificate: "platform",
    sdk_version: "current",
    privileged: false,
    optimize: { enabled: false },
}

Product makefile

Add to pawlet_rpi4.mk and pawlet_rpi5.mk:

PRODUCT_PACKAGES += PawletConfig

Local manifest

Add to android_local_manifest/pawletos.xml:

<project name="PawletOS/config_apk"
         path="vendor/pawlet/config_apk"
         remote="pawlet"
         revision="master" />

ConfigProvisioner integration

ConfigProvisioner (dev.oxmc.configprovisioner) is the system app that drives this APK. Relevant vendor.cfg keys:

Key Default Description
config_apk_url HTTPS URL to download the APK from
config_apk_package app.pawlet.config Package name to read settings from after install

After a successful install ConfigProvisioner:

  1. Reads res/xml/settings.xml from the installed package and applies each entry via Settings.Secure/System/Global.putString()
  2. Reads APNs from assets/apns/ (or res/xml/apns.xml) and inserts into content://telephony/carriers

Both steps are no-ops if the relevant resources are absent or empty.


Device Owner (optional, currently disabled)

PawletDeviceAdminReceiver is declared but not activated automatically. Activation requires an explicit adb command on a freshly wiped device before any accounts are added:

adb shell dpm set-device-owner app.pawlet.config/.PawletDeviceAdminReceiver

When active as Device Owner the app can push managed configurations to other apps via DevicePolicyManager.setApplicationRestrictions(). The relevant code in ConfigReceiver.kt is commented out pending this being needed.


Android API compatibility

API level Android version Notes
36 Android 16 Primary target (PawletOS)
29 Android 10 Minimum (minSdk)