commit 3f24145e949cf9dce30155fc6edbb9b0bbbc1afb Author: sssnake Date: Tue Mar 31 03:25:19 2026 -0700 Initial release: Miracast Enabler KernelSU module v1.0.0 Enables Wi-Fi Display (Miracast) on Pixel phones where Google disabled it in software. Uses fabricated overlays, system properties, and sysconfig XML. Includes WebUI for KernelSU manager. Tested hardware: Pixel 10 Pro Fold (rango), Tensor G5, BCM4390. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..2e0a9ad --- /dev/null +++ b/build.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# Build Miracast Enabler KernelSU/Magisk module zip +# +# The module uses fabricated overlays at runtime (no APK needed). +# If you have a local aapt2 binary (e.g. pulled from the phone), +# set AAPT2=/path/to/aapt2 and FRAMEWORK_RES=/path/to/framework-res.apk +# to also build an RRO APK as a fallback for pre-Android 12 devices. +set -e + +cd "$(dirname "$0")" + +MODULE_ZIP="miracast-enabler-v1.0.0.zip" + +echo "=== Miracast Enabler Build ===" +echo "" + +# Optional: build RRO APK if tools are available +AAPT2="${AAPT2:-tools/aapt2}" +FRAMEWORK_RES="${FRAMEWORK_RES:-tools/framework-res.apk}" + +if [ -x "$AAPT2" ] && [ -f "$FRAMEWORK_RES" ]; then + echo "[1/3] Building RRO overlay APK..." + + OVERLAY_DIR="overlay" + rm -rf "$OVERLAY_DIR/build" + mkdir -p "$OVERLAY_DIR/build" + + "$AAPT2" compile \ + --dir "$OVERLAY_DIR/res" \ + -o "$OVERLAY_DIR/build/compiled.zip" + + "$AAPT2" link \ + --manifest "$OVERLAY_DIR/AndroidManifest.xml" \ + -I "$FRAMEWORK_RES" \ + -o "$OVERLAY_DIR/build/MiracastEnablerOverlay.apk" \ + "$OVERLAY_DIR/build/compiled.zip" + + # Sign if possible + KEYSTORE="build-key.jks" + KEY_ALIAS="miracast" + KEY_PASS="miracast-build" + + if ! [ -f "$KEYSTORE" ] && command -v keytool >/dev/null 2>&1; then + keytool -genkeypair \ + -keystore "$KEYSTORE" \ + -alias "$KEY_ALIAS" \ + -keyalg RSA -keysize 2048 -validity 10000 \ + -storepass "$KEY_PASS" -keypass "$KEY_PASS" \ + -dname "CN=Miracast Enabler,O=Module,C=US" + fi + + if [ -f "$KEYSTORE" ]; then + if command -v apksigner >/dev/null 2>&1; then + apksigner sign \ + --ks "$KEYSTORE" --ks-key-alias "$KEY_ALIAS" \ + --ks-pass "pass:$KEY_PASS" --key-pass "pass:$KEY_PASS" \ + --out "$OVERLAY_DIR/MiracastEnablerOverlay.apk" \ + "$OVERLAY_DIR/build/MiracastEnablerOverlay.apk" + elif command -v jarsigner >/dev/null 2>&1; then + cp "$OVERLAY_DIR/build/MiracastEnablerOverlay.apk" "$OVERLAY_DIR/MiracastEnablerOverlay.apk" + jarsigner -keystore "$KEYSTORE" \ + -storepass "$KEY_PASS" -keypass "$KEY_PASS" \ + "$OVERLAY_DIR/MiracastEnablerOverlay.apk" "$KEY_ALIAS" + else + cp "$OVERLAY_DIR/build/MiracastEnablerOverlay.apk" "$OVERLAY_DIR/MiracastEnablerOverlay.apk" + fi + else + cp "$OVERLAY_DIR/build/MiracastEnablerOverlay.apk" "$OVERLAY_DIR/MiracastEnablerOverlay.apk" + fi + + mkdir -p system/vendor/overlay + cp "$OVERLAY_DIR/MiracastEnablerOverlay.apk" system/vendor/overlay/ + echo " RRO APK built: $(du -h system/vendor/overlay/MiracastEnablerOverlay.apk | cut -f1)" + rm -rf "$OVERLAY_DIR/build" +else + echo "[1/3] Skipping RRO APK (no local aapt2 or framework-res.apk)" + echo " Module will use fabricated overlays on Android 12+" + echo " To build APK: pull aapt2 from phone with ./pull-aapt2.sh" + rm -rf system/vendor/overlay +fi + +echo "[2/3] Packaging module..." +rm -f "$MODULE_ZIP" + +# Build file list +FILES=( + module.prop + customize.sh + post-fs-data.sh + service.sh + system.prop + system/ + webroot/ +) + +# Include overlay source for on-device build fallback +FILES+=(overlay/AndroidManifest.xml overlay/res/) + +# Include pre-built APK if it exists +if [ -f "system/vendor/overlay/MiracastEnablerOverlay.apk" ]; then + FILES+=(system/vendor/overlay/MiracastEnablerOverlay.apk) +fi + +zip -r9 "$MODULE_ZIP" "${FILES[@]}" \ + -x "overlay/build/*" "*.git*" "build.sh" "build-key.jks" \ + "pull-aapt2.sh" "tools/*" "README*" + +echo "[3/3] Done" +echo "" +echo "=== $MODULE_ZIP ($(du -h "$MODULE_ZIP" | cut -f1)) ===" +echo "Flash via KernelSU or Magisk manager" diff --git a/customize.sh b/customize.sh new file mode 100644 index 0000000..6ca0c5a --- /dev/null +++ b/customize.sh @@ -0,0 +1,118 @@ +#!/system/bin/sh +# Miracast Enabler - Installation Script + +SKIPUNZIP=0 + +ui_print "================================================" +ui_print " Miracast Enabler v1.0.0" +ui_print "================================================" +ui_print "" + +# Detect device +DEVICE=$(getprop ro.product.device) +MODEL=$(getprop ro.product.model) +SOC=$(getprop ro.soc.model) +ANDROID=$(getprop ro.build.version.release) +API=$(getprop ro.build.version.sdk) + +ui_print "- Device: $MODEL ($DEVICE)" +ui_print "- SoC: $SOC" +ui_print "- Android: $ANDROID (API $API)" +ui_print "" + +# Pixel detection +case "$DEVICE" in + rango|comet|cometl|caiman|komodo|tokay|blazer|husky|shiba|felix|tangorpro|lynx|panther|cheetah|oriole|raven|bluejay) + ui_print "- Pixel device detected" + ;; + *) + ui_print "! Non-Pixel device — module may still work" + ;; +esac + +# Foldable messaging +case "$DEVICE" in + rango|comet|cometl|felix) + ui_print "- Foldable detected: inner display is default Miracast source" + ui_print "- Change display source via WebUI in KernelSU manager" + ;; +esac +ui_print "" + +# --- Build RRO APK on-device as fallback for pre-Android 12 --- +# Android 12+ uses fabricated overlays (no APK needed) +# Pre-Android 12 needs a real RRO APK, built right here on the phone +if [ "$API" -lt 31 ]; then + ui_print "- Android < 12: building RRO overlay APK on-device..." + + AAPT2=$(which aapt2 2>/dev/null) + if [ -z "$AAPT2" ]; then + # aapt2 is typically in the build-tools or framework + for candidate in \ + /system/bin/aapt2 \ + /system/xbin/aapt2 \ + /data/adb/aapt2; do + if [ -x "$candidate" ]; then + AAPT2="$candidate" + break + fi + done + fi + + if [ -n "$AAPT2" ] && [ -x "$AAPT2" ]; then + ui_print "- Found aapt2: $AAPT2" + OVERLAY_BUILD="$MODPATH/overlay_build" + mkdir -p "$OVERLAY_BUILD" + + # Compile resources + "$AAPT2" compile \ + --dir "$MODPATH/overlay/res" \ + -o "$OVERLAY_BUILD/compiled.zip" 2>/dev/null + + if [ $? -eq 0 ]; then + # Link into APK + "$AAPT2" link \ + --manifest "$MODPATH/overlay/AndroidManifest.xml" \ + -I /system/framework/framework-res.apk \ + -o "$OVERLAY_BUILD/overlay.apk" \ + "$OVERLAY_BUILD/compiled.zip" 2>/dev/null + + if [ $? -eq 0 ]; then + mkdir -p "$MODPATH/system/vendor/overlay" + cp "$OVERLAY_BUILD/overlay.apk" "$MODPATH/system/vendor/overlay/MiracastEnablerOverlay.apk" + chmod 644 "$MODPATH/system/vendor/overlay/MiracastEnablerOverlay.apk" + ui_print "- RRO overlay APK built and installed" + else + ui_print "! aapt2 link failed — overlay APK not built" + fi + else + ui_print "! aapt2 compile failed — overlay APK not built" + fi + rm -rf "$OVERLAY_BUILD" + else + ui_print "! aapt2 not found on device" + ui_print "! Pre-Android 12 will rely on system properties only" + fi +else + ui_print "- Android 12+: will use fabricated overlays (no APK needed)" +fi + +ui_print "" +ui_print "- Setting permissions..." + +set_perm_recursive $MODPATH 0 0 0755 0644 +set_perm $MODPATH/post-fs-data.sh 0 0 0755 +set_perm $MODPATH/service.sh 0 0 0755 +set_perm_recursive $MODPATH/system/etc 0 0 0755 0644 + +if [ -f "$MODPATH/system/vendor/overlay/MiracastEnablerOverlay.apk" ]; then + set_perm "$MODPATH/system/vendor/overlay/MiracastEnablerOverlay.apk" 0 0 0644 +fi + +ui_print "" +ui_print "- Installation complete!" +ui_print "- Reboot to activate Miracast" +ui_print "- After reboot: Settings > Connected devices >" +ui_print " Connection preferences > Cast" +ui_print "- WebUI available in KernelSU manager" +ui_print "" diff --git a/miracast-enabler-v1.0.0.zip b/miracast-enabler-v1.0.0.zip new file mode 100644 index 0000000..a23e378 Binary files /dev/null and b/miracast-enabler-v1.0.0.zip differ diff --git a/module.prop b/module.prop new file mode 100644 index 0000000..319c080 --- /dev/null +++ b/module.prop @@ -0,0 +1,6 @@ +id=miracast-enabler +name=Miracast Enabler +version=v1.0.0 +versionCode=1 +author=snake +description=Enables Miracast (Wi-Fi Display) on Pixel phones and other devices where it is disabled by default diff --git a/overlay/AndroidManifest.xml b/overlay/AndroidManifest.xml new file mode 100644 index 0000000..7dca89d --- /dev/null +++ b/overlay/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/overlay/build/compiled.zip b/overlay/build/compiled.zip new file mode 100644 index 0000000..ac81998 Binary files /dev/null and b/overlay/build/compiled.zip differ diff --git a/overlay/res/values/config.xml b/overlay/res/values/config.xml new file mode 100644 index 0000000..34715bd --- /dev/null +++ b/overlay/res/values/config.xml @@ -0,0 +1,8 @@ + + + + true + + + true + diff --git a/post-fs-data.sh b/post-fs-data.sh new file mode 100644 index 0000000..e6b0f22 --- /dev/null +++ b/post-fs-data.sh @@ -0,0 +1,21 @@ +#!/system/bin/sh +# Miracast Enabler - post-fs-data +# Runs early in boot before most services start + +MODDIR=${0%/*} + +# Core Wi-Fi Display props +resetprop persist.debug.wfd.enable 1 +resetprop persist.sys.wfd.virtual 0 + +# HDCP — disable by default for maximum compatibility +resetprop persist.sys.wfd.nohdcp 1 +resetprop wlan.wfd.hdcp disable + +# Tensor / Pixel: force GPU composition for virtual displays +# HWC on Tensor chips does not correctly handle virtual display layers, +# which causes black screen or green artifacts during Miracast +resetprop debug.sf.enable_hwc_vds 0 + +# Wi-Fi Direct concurrency — needed on Pixel to allow P2P alongside STA +resetprop wifi.direct.interface p2p-dev-wlan0 diff --git a/pull-aapt2.sh b/pull-aapt2.sh new file mode 100755 index 0000000..57b8779 --- /dev/null +++ b/pull-aapt2.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Pull aapt2 from a connected Android device for local builds +# Usage: ./pull-aapt2.sh +set -e + +DEST="tools/aapt2" + +if ! adb get-state >/dev/null 2>&1; then + echo "ERROR: No device connected via ADB" + exit 1 +fi + +echo "Searching for aapt2 on device..." + +# Find aapt2 binary on the device +DEVICE_PATH=$(adb shell "find /system /vendor /apex -name 'aapt2' -type f 2>/dev/null" | tr -d '\r' | head -1) + +if [ -z "$DEVICE_PATH" ]; then + echo "aapt2 not found as standalone binary, checking APEX modules..." + # On newer Android, build tools live inside APEX + DEVICE_PATH=$(adb shell "find /apex/com.android.sdkext /apex/com.android.art -name 'aapt2' 2>/dev/null" | tr -d '\r' | head -1) +fi + +if [ -z "$DEVICE_PATH" ]; then + echo "aapt2 not found on device." + echo "Trying framework-res.apk instead (for on-device overlay builds)..." + DEVICE_PATH="/system/framework/framework-res.apk" + DEST="tools/framework-res.apk" +fi + +echo "Pulling: $DEVICE_PATH" +mkdir -p "$(dirname "$DEST")" +adb pull "$DEVICE_PATH" "$DEST" + +if [ -f "$DEST" ]; then + chmod +x "$DEST" 2>/dev/null + echo "Saved to: $DEST" + file "$DEST" + + if [[ "$DEST" == *aapt2 ]]; then + VERSION=$("./$DEST" version 2>&1 || true) + echo "Version: $VERSION" + fi +fi diff --git a/service.sh b/service.sh new file mode 100644 index 0000000..2324e53 --- /dev/null +++ b/service.sh @@ -0,0 +1,174 @@ +#!/system/bin/sh +# Miracast Enabler - late service +# Runs after boot is completed + +MODDIR=${0%/*} +LOGFILE="$MODDIR/miracast.log" + +mlog() { + echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOGFILE" + log -t MiracastEnabler "$1" +} + +# Truncate log on each boot +echo "" > "$LOGFILE" +mlog "Waiting for boot..." + +# Wait for boot to complete +while [ "$(getprop sys.boot_completed)" != "1" ]; do + sleep 1 +done +sleep 3 + +mlog "Boot complete, applying Miracast configuration" + +# --- Core WFD properties --- +# Some vendors reset these during boot, so re-apply +resetprop persist.debug.wfd.enable 1 +resetprop persist.sys.wfd.virtual 0 +resetprop persist.sys.wfd.nohdcp 1 +resetprop wlan.wfd.hdcp disable + +# Tensor / Pixel: force GPU composition for virtual display surfaces +# PowerVR (G5) and Mali (G4 and earlier) HWC implementations don't +# handle virtual display layers for Miracast, causing black/green frames +resetprop debug.sf.enable_hwc_vds 0 + +# Wi-Fi Direct concurrency — needed on Pixel to allow P2P alongside STA +resetprop wifi.direct.interface p2p-dev-wlan0 + +# --- Device detection --- +DEVICE=$(getprop ro.product.device) +SOC=$(getprop ro.soc.model) +PLATFORM=$(getprop ro.board.platform) +API=$(getprop ro.build.version.sdk) +GPU=$(getprop ro.hardware.egl) + +mlog "Device=$DEVICE SoC=$SOC Platform=$PLATFORM API=$API GPU=$GPU" + +# --- Tensor SoC family tweaks --- +case "$SOC" in + Tensor*) + # High GO intent so Pixel acts as Group Owner in P2P negotiation + # This ensures the Pixel controls the channel and timing + resetprop wifi.direct.go_intent 15 + mlog "Tensor SoC: set P2P GO intent=15" + ;; +esac + +# --- Pixel 10 Pro Fold (rango) — Tensor G5, Broadcom BCM4390 --- +# Confirmed: codename=rango, platform=laguna, gpu=powervr, wifi=bcmdhd4390 +# Display IDs: 0 (inner), 3 (outer) +# Wi-Fi features include WFD_R2 — hardware fully supports Miracast +case "$DEVICE" in + rango) + mlog "Pixel 10 Pro Fold (rango) detected" + + # Inner display (ID 0) as default Miracast source + if [ -z "$(getprop persist.sys.wfd.display_id)" ]; then + resetprop persist.sys.wfd.display_id 0 + fi + # Default 1080p30 — the inner panel is 2076x2152 which is too wide + # for most Miracast receivers; 1080p is the safe maximum + if [ -z "$(getprop persist.sys.wfd.resolution)" ]; then + resetprop persist.sys.wfd.resolution 7 + fi + + # Tensor G5 / laguna platform: PowerVR GPU composition + resetprop debug.sf.enable_hwc_vds 0 + + # BCM4390 Wi-Fi: P2P supplicant already supports VHT and DFS + # channels (p2p_go_vht=1, p2p_dfs_chan_enable=1 in vendor config) + # but 6GHz is disabled for P2P (p2p_6ghz_disable=1), which is fine + # since most Miracast receivers don't support 6GHz + + # Force single-channel concurrency to prevent the BCM4390 from + # splitting STA and P2P across bands which can cause latency spikes + resetprop persist.vendor.wifi.wfd.scc 1 + + mlog "rango: inner display=0, outer display=3, wifi=BCM4390, gpu=PowerVR" + ;; +esac + +# --- Pixel 9 Pro Fold (comet/cometl) — Tensor G4 --- +case "$DEVICE" in + comet|cometl) + mlog "Pixel 9 Pro Fold detected" + if [ -z "$(getprop persist.sys.wfd.display_id)" ]; then + resetprop persist.sys.wfd.display_id 0 + fi + if [ -z "$(getprop persist.sys.wfd.resolution)" ]; then + resetprop persist.sys.wfd.resolution 7 + fi + ;; +esac + +# --- Original Pixel Fold (felix) — Tensor G2 --- +case "$DEVICE" in + felix) + mlog "Pixel Fold (1st gen) detected" + if [ -z "$(getprop persist.sys.wfd.display_id)" ]; then + resetprop persist.sys.wfd.display_id 0 + fi + if [ -z "$(getprop persist.sys.wfd.resolution)" ]; then + resetprop persist.sys.wfd.resolution 7 + fi + ;; +esac + +# --- Pixel 9 series (non-fold) — Tensor G4 --- +case "$DEVICE" in + caiman|komodo|tokay|blazer) + mlog "Pixel 9 series detected ($DEVICE)" + # No foldable display concerns, standard single display + ;; +esac + +# --- Pixel 10 series (non-fold) --- +# Platform "laguna" is Tensor G5; other Pixel 10 variants share it +case "$PLATFORM" in + laguna) + if [ "$DEVICE" != "rango" ]; then + mlog "Tensor G5 device ($DEVICE) on laguna platform" + resetprop debug.sf.enable_hwc_vds 0 + resetprop persist.vendor.wifi.wfd.scc 1 + fi + ;; +esac + +# --- Fabricated overlay (Android 12+, API 31+) --- +# Primary method to flip config_enableWifiDisplay in the framework +# No APK compilation needed — fabricated overlays are created at runtime +# Requires root, which KernelSU provides +if [ "$API" -ge 31 ]; then + mlog "API $API >= 31, creating fabricated overlays" + + # TYPE_INT_BOOLEAN = 0x12, true = 0xFFFFFFFF + cmd overlay fabricate --target android --name MiracastEnablerWifiDisplay \ + android:bool/config_enableWifiDisplay 0x12 0xFFFFFFFF 2>/dev/null + RET1=$? + + cmd overlay fabricate --target android --name MiracastEnablerProtectedBuffers \ + android:bool/config_wifiDisplaySupportsProtectedBuffers 0x12 0xFFFFFFFF 2>/dev/null + RET2=$? + + # Enable the fabricated overlays + cmd overlay enable --user current com.android.shell:MiracastEnablerWifiDisplay 2>/dev/null + cmd overlay enable --user current com.android.shell:MiracastEnablerProtectedBuffers 2>/dev/null + + # Verify and log + WFD_STATE=$(cmd overlay list 2>/dev/null | grep MiracastEnabler) + mlog "Fabricate WifiDisplay ret=$RET1, ProtectedBuffers ret=$RET2" + mlog "Overlay state: $WFD_STATE" +else + mlog "API $API < 31, fabricated overlays not available" + mlog "Relying on system properties and sysconfig XML only" +fi + +# --- Fallback: enable pre-installed RRO APK if present --- +if [ -f "$MODDIR/system/vendor/overlay/MiracastEnablerOverlay.apk" ]; then + cmd overlay enable --user current com.miracast.enabler.overlay 2>/dev/null + mlog "Enabled RRO APK overlay" +fi + +mlog "Miracast enabler service complete" diff --git a/system.prop b/system.prop new file mode 100644 index 0000000..a7eeaaa --- /dev/null +++ b/system.prop @@ -0,0 +1,11 @@ +# Core Miracast / Wi-Fi Display +persist.debug.wfd.enable=1 +persist.sys.wfd.virtual=0 +persist.sys.wfd.nohdcp=1 +wlan.wfd.hdcp=disable + +# Tensor / Pixel: GPU composition for virtual displays +debug.sf.enable_hwc_vds=0 + +# Wi-Fi Direct concurrency +wifi.direct.interface=p2p-dev-wlan0 diff --git a/system/etc/permissions/miracast-enabler.xml b/system/etc/permissions/miracast-enabler.xml new file mode 100644 index 0000000..70f3dcc --- /dev/null +++ b/system/etc/permissions/miracast-enabler.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/system/etc/sysconfig/miracast-enabler.xml b/system/etc/sysconfig/miracast-enabler.xml new file mode 100644 index 0000000..9ff33d2 --- /dev/null +++ b/system/etc/sysconfig/miracast-enabler.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/webroot/index.html b/webroot/index.html new file mode 100644 index 0000000..016b422 --- /dev/null +++ b/webroot/index.html @@ -0,0 +1,643 @@ + + + + + + Miracast Enabler + + + +
+

Miracast Enabler

+
v1.0.0
+
+ +
+
+ Reading device state... +
+ + +
+
Miracast
+ +
+
+
Wi-Fi Display
+
Master toggle for Miracast
+
+ +
+ +
+
+
Disable HDCP
+
Skip DRM handshake (fixes many receivers)
+
+ +
+ +
+
+
Virtual Display
+
Use virtual display instead of overlay
+
+ +
+
+ + +
+
Quality
+ +
+
+
Max Resolution
+
Caps output resolution for stability
+
+ +
+ +
+
+
WLAN HDCP
+
Wi-Fi level HDCP control
+
+ +
+
+ + +
+
Pixel / Tensor Tweaks
+ +
+
+
Force P2P Concurrency
+
Allow Wi-Fi Direct alongside station mode
+
+ +
+ +
+
+
Force Display Composition
+
Use GPU composition for wireless display
+
+ +
+ +
+
+
Fold Display Source
+
Which display to mirror on foldables
+
+ +
+
+ + +
+
Framework Overlay
+ +
+
+
config_enableWifiDisplay
+
Framework feature flag (fabricated overlay)
+
+ +
+ +
+
+
config_wifiDisplaySupportsProtectedBuffers
+
Allow DRM content over wireless display
+
+ +
+ +
+
+
Overlay Status
+
Checking...
+
+ +
+
+ + +
+
Device Info
+
+
+
Device
+
+
+
+
SoC
+
+
+
+
Android
+
+
+
+
Wi-Fi Chip
+
+
+
+
+ + +
+
Log
+
Initializing...
+
+ + +
+
+ + + +