Skip to content

Android 16 Framework Patcher #2102

Android 16 Framework Patcher

Android 16 Framework Patcher #2102

Workflow file for this run

name: Android 16 Framework Patcher
on:
workflow_dispatch:
inputs:
api_level:
description: 'Android API level (Android 16 is API 36)'
required: true
default: '36'
type: string
device_codename:
description: 'Device codename (for zip file naming, e.g., "munch"). Optional, will use sanitized device_name if not provided.'
required: false
type: string
device_name:
description: 'Full device name (for release description, e.g., "Redmi Note 11T Pro")'
required: true
type: string
version_name:
description: 'ROM/firmware version identifier'
required: true
type: string
framework_url:
description: 'URL to framework.jar (required for: signature_verification, kaorios_toolbox)'
required: false
type: string
services_url:
description: 'URL to services.jar (required for: signature_verification, disable_secure_flag)'
required: false
type: string
miui_services_url:
description: 'URL to miui-services.jar (required for: signature_verification, cn_notification_fix, disable_secure_flag, add_gboard)'
required: false
type: string
miui_framework_url:
description: 'URL to miui-framework.jar (required for: add_gboard)'
required: false
type: string
user_id:
description: 'Telegram User ID to notify (optional)'
required: false
type: string
features:
description: 'Comma-separated list of features to enable (e.g., "disable_signature_verification,cn_notification_fix")'
required: false
type: string
default: 'disable_signature_verification'
jobs:
patch:
name: Android 16 Framework Patcher
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y p7zip-full wget zip python3 python3-pip
pip3 install gdown
- name: Prepare tools directory
run: |
mkdir -p tools
if [ ! -f tools/apktool.jar ]; then
wget -O tools/apktool.jar https://github.com/iBotPeaches/Apktool/releases/download/v2.9.3/apktool_2.9.3.jar
fi
- name: Download framework JARs
run: |
# Smart download function that handles Google Drive links
download_file() {
url=$1
output=$2
echo "Downloading $output from $url"
if echo "$url" | grep -q "drive.google.com"; then
echo "Detected Google Drive link, using gdown..."
gdown --fuzzy -O "$output" "$url"
else
echo "Using wget for download..."
wget -O "$output" "$url"
fi
}
# Determine which JARs are needed based on features
FEATURES="${{ github.event.inputs.features }}"
NEED_FRAMEWORK=0
NEED_SERVICES=0
NEED_MIUI_SERVICES=0
NEED_MIUI_FRAMEWORK=0
# Signature verification needs all 3
if [[ "$FEATURES" == *"disable_signature_verification"* ]]; then
NEED_FRAMEWORK=1
NEED_SERVICES=1
NEED_MIUI_SERVICES=1
fi
# Kaorios toolbox only needs framework.jar
if [[ "$FEATURES" == *"kaorios_toolbox"* ]]; then
NEED_FRAMEWORK=1
fi
# CN notification fix only needs miui-services.jar
if [[ "$FEATURES" == *"cn_notification_fix"* ]]; then
NEED_MIUI_SERVICES=1
fi
# Disable secure flag needs services.jar and miui-services.jar
if [[ "$FEATURES" == *"disable_secure_flag"* ]]; then
NEED_SERVICES=1
NEED_MIUI_SERVICES=1
fi
# Add Gboard needs miui-services.jar and miui-framework.jar
if [[ "$FEATURES" == *"add_gboard"* ]]; then
NEED_MIUI_SERVICES=1
NEED_MIUI_FRAMEWORK=1
fi
echo "============================================"
echo "Feature-based JAR requirements:"
echo " framework.jar: $([ $NEED_FRAMEWORK -eq 1 ] && echo 'REQUIRED' || echo 'not needed')"
echo " services.jar: $([ $NEED_SERVICES -eq 1 ] && echo 'REQUIRED' || echo 'not needed')"
echo " miui-services.jar: $([ $NEED_MIUI_SERVICES -eq 1 ] && echo 'REQUIRED' || echo 'not needed')"
echo " miui-framework.jar: $([ $NEED_MIUI_FRAMEWORK -eq 1 ] && echo 'REQUIRED' || echo 'not needed')"
echo "============================================"
# Download required JARs
if [ $NEED_FRAMEWORK -eq 1 ]; then
if [ -z "${{ github.event.inputs.framework_url }}" ]; then
echo "❌ framework.jar URL is required but not provided!"
exit 1
fi
download_file "${{ github.event.inputs.framework_url }}" "framework.jar"
fi
if [ $NEED_SERVICES -eq 1 ]; then
if [ -z "${{ github.event.inputs.services_url }}" ]; then
echo "❌ services.jar URL is required but not provided!"
exit 1
fi
download_file "${{ github.event.inputs.services_url }}" "services.jar"
fi
if [ $NEED_MIUI_SERVICES -eq 1 ]; then
if [ -z "${{ github.event.inputs.miui_services_url }}" ]; then
echo "❌ miui-services.jar URL is required but not provided!"
exit 1
fi
download_file "${{ github.event.inputs.miui_services_url }}" "miui-services.jar"
fi
if [ $NEED_MIUI_FRAMEWORK -eq 1 ]; then
if [ -z "${{ github.event.inputs.miui_framework_url }}" ]; then
echo "❌ miui-framework.jar URL is required but not provided!"
exit 1
fi
download_file "${{ github.event.inputs.miui_framework_url }}" "miui-framework.jar"
fi
echo "Validating downloaded JAR files..."
for jar in framework.jar services.jar miui-services.jar miui-framework.jar; do
if [ -f "$jar" ]; then
file_size=$(stat -c%s "$jar")
echo "Checking $jar (${file_size} bytes)..."
if [ $file_size -lt 1500000 ]; then
echo "❌ Error: $jar is too small (${file_size} bytes). Download might have failed."
exit 1
fi
echo "✅ $jar validated successfully"
fi
done
echo "All required JAR files downloaded and validated!"
- name: Set safe device codename
id: set_codename
run: |
RAW_CODENAME="${{ github.event.inputs.device_codename }}"
if [ -z "$RAW_CODENAME" ]; then
RAW_CODENAME="${{ github.event.inputs.device_name }}"
fi
# Sanitize: Remove spaces and other invalid tag characters
SAFE_CODENAME=$(echo "$RAW_CODENAME" | sed 's/[^a-zA-Z0-9._-]/_/g')
echo "codename=${SAFE_CODENAME}" >> $GITHUB_OUTPUT
- name: Run Android 16 patcher
run: |
LOG_FILE="build_log_android16.txt"
: > "$LOG_FILE"
exec > >(tee -a "$LOG_FILE") 2>&1
set -o pipefail
chmod +x scripts/patcher_a16.sh
# Build feature flags
FEATURE_FLAGS=""
if [[ "${{ github.event.inputs.features }}" == *disable_signature_verification* ]]; then
FEATURE_FLAGS="$FEATURE_FLAGS --disable-signature-verification"
fi
if [[ "${{ github.event.inputs.features }}" == *cn_notification_fix* ]]; then
FEATURE_FLAGS="$FEATURE_FLAGS --cn-notification-fix"
fi
if [[ "${{ github.event.inputs.features }}" == *disable_secure_flag* ]]; then
FEATURE_FLAGS="$FEATURE_FLAGS --disable-secure-flag"
fi
if [[ "${{ github.event.inputs.features }}" == *kaorios_toolbox* ]]; then
FEATURE_FLAGS="$FEATURE_FLAGS --kaorios-toolbox"
fi
if [[ "${{ github.event.inputs.features }}" == *add_gboard* ]]; then
FEATURE_FLAGS="$FEATURE_FLAGS --add-gboard"
fi
# If no features selected, default to signature bypass
if [ -z "$FEATURE_FLAGS" ]; then
FEATURE_FLAGS="--disable-signature-verification"
fi
# Build JAR flags based on which JARs are present
JAR_FLAGS=""
if [ -f "framework.jar" ]; then
JAR_FLAGS="$JAR_FLAGS --framework"
fi
if [ -f "services.jar" ]; then
JAR_FLAGS="$JAR_FLAGS --services"
fi
if [ -f "miui-services.jar" ]; then
JAR_FLAGS="$JAR_FLAGS --miui-services"
fi
if [ -f "miui-framework.jar" ]; then
JAR_FLAGS="$JAR_FLAGS --miui-framework"
fi
echo "Running patcher with JAR flags: $JAR_FLAGS"
echo "Feature flags: $FEATURE_FLAGS"
./scripts/patcher_a16.sh \
"${{ github.event.inputs.api_level }}" \
"${{ steps.set_codename.outputs.codename }}" \
"${{ github.event.inputs.version_name }}" \
$JAR_FLAGS $FEATURE_FLAGS
- name: Verify module creation
run: |
echo "Checking if module was created properly..."
MODULE_FILE=$(ls Framework-Patcher-${{ steps.set_codename.outputs.codename }}*.zip 2>/dev/null || echo "")
if [ -z "$MODULE_FILE" ]; then
echo "❌ No module ZIP found!"
exit 1
fi
echo "✅ Module ZIP found: $MODULE_FILE"
echo "Module contents:"
unzip -l "$MODULE_FILE" | head -20
echo "Checking for MMT-Extended files:"
unzip -l "$MODULE_FILE" | grep -E "(module\.prop|customize\.sh|common/functions\.sh|META-INF)" || echo "❌ Missing MMT-Extended files"
- name: Set Release Info
id: release_info
run: |
# Format version name to be git tag friendly
SAFE_VERSION=$(echo "${{ github.event.inputs.version_name }}" | sed 's/[^a-zA-Z0-9._-]/_/g')
# Add timestamp to ensure unique tags
TIMESTAMP=$(date +'%Y%m%d-%H%M%S')
RELEASE_TAG="${{ steps.set_codename.outputs.codename }}_${SAFE_VERSION}_${TIMESTAMP}"
echo "tag=${RELEASE_TAG}" >> $GITHUB_OUTPUT
echo "name=Android 16 Framework Patch for ${{ github.event.inputs.device_name }} (${{ github.event.inputs.version_name }})" >> $GITHUB_OUTPUT
- name: Find Module ZIP
id: find_zip
run: |
# Find the module ZIP
ZIP_FILE=$(ls Framework-Patcher-${{ steps.set_codename.outputs.codename }}*.zip 2>/dev/null || echo "")
if [ -z "$ZIP_FILE" ]; then
echo "No module ZIP found!"
exit 1
fi
echo "file_path=${ZIP_FILE}" >> $GITHUB_OUTPUT
- name: Delete Existing Release
uses: actions/github-script@v6
continue-on-error: true
with:
script: |
try {
const releases = await github.rest.repos.listReleases({
owner: context.repo.owner,
repo: context.repo.repo
});
const existingRelease = releases.data.find(r => r.tag_name === '${{ steps.release_info.outputs.tag }}');
if (existingRelease) {
console.log('Found existing release, deleting...');
await github.rest.repos.deleteRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: existingRelease.id
});
// Delete the tag
try {
await github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'tags/${{ steps.release_info.outputs.tag }}'
});
} catch (e) {
console.log('Tag might not exist, continuing...');
}
}
} catch (e) {
console.log('No existing release found or error occurred:', e.message);
}
- name: Generate Release Body
id: generate_release_body
run: |
# Build features list
FEATURES_LIST=()
if [[ "${{ github.event.inputs.features }}" == *disable_signature_verification* ]]; then
FEATURES_LIST+=("- Signature Verification Bypass")
fi
if [[ "${{ github.event.inputs.features }}" == *cn_notification_fix* ]]; then
FEATURES_LIST+=("- CN Notification Fix")
fi
if [[ "${{ github.event.inputs.features }}" == *disable_secure_flag* ]]; then
FEATURES_LIST+=("- Disable Secure Flag")
fi
if [[ "${{ github.event.inputs.features }}" == *kaorios_toolbox* ]]; then
FEATURES_LIST+=("- Kaorios Toolbox (Play Integrity Fix) ")
fi
if [[ "${{ github.event.inputs.features }}" == *add_gboard* ]]; then
FEATURES_LIST+=("- Add Gboard Support")
fi
if [ ${#FEATURES_LIST[@]} -eq 0 ]; then
FEATURES="- Signature Verification Bypass (default)"
else
FEATURES=$(printf '%s\n' "${FEATURES_LIST[@]}")
fi
# Create release body
cat > /tmp/release_body.md << RELEASE_EOF
Android 16 Framework Patcher for ${{ github.event.inputs.device_name }}
Device: ${{ github.event.inputs.device_name }}
Version: ${{ github.event.inputs.version_name }}
Android API: ${{ github.event.inputs.api_level }}
Important: This module is built specifically for ${{ github.event.inputs.device_name }} running ${{ github.event.inputs.version_name }}. Installing on other devices or ROM versions may cause boot loops or system issues.
Compatible with Magisk, KernelSU, and SUFS
Features:
FEATURES_PLACEHOLDER
Source files:
- Framework: ${{ github.event.inputs.framework_url }}
- Services: ${{ github.event.inputs.services_url }}
- MIUI Services: ${{ github.event.inputs.miui_services_url }}
Built: $(date +'%Y-%m-%d %H:%M:%S UTC')
Workflow: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
Changelog: https://github.com/${{ github.repository }}/blob/master/CHANGELOG.md
RELEASE_EOF
# Replace features placeholder with actual features (handle multiline properly)
# Create temporary file with features
echo "$FEATURES" > /tmp/features.txt
# Use awk to replace placeholder with file content
awk '{
if ($0 ~ /FEATURES_PLACEHOLDER/) {
while ((getline line < "/tmp/features.txt") > 0) print line
close("/tmp/features.txt")
} else {
print $0
}
}' /tmp/release_body.md > /tmp/release_body_final.md
mv /tmp/release_body_final.md /tmp/release_body.md
# Output to GitHub
echo "release_body<<EOF" >> $GITHUB_OUTPUT
cat /tmp/release_body.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.release_info.outputs.tag }}
name: ${{ steps.release_info.outputs.name }}
files: ${{ steps.find_zip.outputs.file_path }}
body: ${{ steps.generate_release_body.outputs.release_body }}
- name: Upload patched jars
uses: actions/upload-artifact@v4
with:
name: android16-patched-jars-${{ github.run_id }}
if-no-files-found: warn
path: |
framework_patched.jar
services_patched.jar
miui-services_patched.jar
miui-framework_patched.jar
retention-days: 7
- name: Upload module zip
if: success()
uses: actions/upload-artifact@v4
with:
name: android16-module-${{ github.run_id }}
if-no-files-found: warn
path: Framework-Patcher-${{ steps.set_codename.outputs.codename }}*.zip
retention-days: 7
- name: Upload build log
if: always()
uses: actions/upload-artifact@v4
with:
name: android16-build-log-${{ github.run_id }}
if-no-files-found: warn
path: build_log_android16.txt
retention-days: 7
- name: Send Telegram Notification (Success)
if: success() && github.event.inputs.user_id != ''
run: |
escape_mdv2() {
echo "$1" | sed -e 's/\\/\\\\/g' -e 's/\./\\./g' -e 's/\-/\\-/g' -e 's/!/\\!/g' -e 's/#/\\#/g' -e 's/{/\\{/g' -e 's/}/\\}/g' -e 's/(/\\(/g' -e 's/)/\\)/g'
}
DEVICE_CODENAME_ESCAPED=$(escape_mdv2 "${{ steps.set_codename.outputs.codename }}")
DEVICE_NAME_ESCAPED=$(escape_mdv2 "${{ github.event.inputs.device_name }}")
VERSION_NAME_ESCAPED=$(escape_mdv2 "${{ github.event.inputs.version_name }}")
API_LEVEL_ESCAPED=$(escape_mdv2 "${{ github.event.inputs.api_level }}")
MESSAGE=$(printf "Android 16 framework patch is ready for your device:\n\n> *Device:* \`%s\`\n> *Codename:* \`%s\`\n> *Version:* \`%s\`\n> *Android API:* \`%s\`\n\nModule compatible with Magisk, KSU, and SUFS" \
"$DEVICE_NAME_ESCAPED" \
"$DEVICE_CODENAME_ESCAPED" \
"$VERSION_NAME_ESCAPED" \
"$API_LEVEL_ESCAPED")
RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/${{ steps.release_info.outputs.tag }}"
REPLY_MARKUP="{\"inline_keyboard\":[[{\"text\":\"Click here to download\",\"url\":\"$RELEASE_URL\"}],[{\"text\":\"Support me\",\"url\":\"https://buymeacoffee.com/jefino\"}]]}"
curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
-d chat_id=${{ github.event.inputs.user_id }} \
-d text="$MESSAGE" \
-d parse_mode="MarkdownV2" \
-d reply_markup="$REPLY_MARKUP"
- name: Send Telegram Notification (Failure)
if: failure() && github.event.inputs.user_id != ''
run: |
escape_mdv2() {
echo "$1" | sed -e 's/\\/\\\\/g' -e 's/\./\\./g' -e 's/\-/\\-/g' -e 's/!/\\!/g' -e 's/#/\\#/g' -e 's/{/\\{/g' -e 's/}/\\}/g' -e 's/(/\\(/g' -e 's/)/\\)/g'
}
DEVICE_CODENAME="${{ steps.set_codename.outputs.codename }}"
if [ -z "$DEVICE_CODENAME" ]; then
DEVICE_CODENAME="${{ github.event.inputs.device_codename }}"
fi
DEVICE_CODENAME_ESCAPED=$(escape_mdv2 "$DEVICE_CODENAME")
DEVICE_NAME_ESCAPED=$(escape_mdv2 "${{ github.event.inputs.device_name }}")
VERSION_NAME_ESCAPED=$(escape_mdv2 "${{ github.event.inputs.version_name }}")
WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
MESSAGE=$(printf "Android 16 framework patch failed for your device:\n\n> *Device:* \`%s\`\n> *Codename:* \`%s\`\n> *Version:* \`%s\`\n\nCheckout logs below\." \
"$DEVICE_NAME_ESCAPED" \
"$DEVICE_CODENAME_ESCAPED" \
"$VERSION_NAME_ESCAPED")
REPLY_MARKUP="{\"inline_keyboard\":[[{\"text\":\"View Workflow Run\",\"url\":\"$WORKFLOW_URL\"}]]}"
curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
-d chat_id=${{ github.event.inputs.user_id }} \
-d text="$MESSAGE" \
-d parse_mode="MarkdownV2" \
-d reply_markup="$REPLY_MARKUP"
LOG_FILE="build_log_android16.txt"
if [ ! -s "$LOG_FILE" ]; then
LOG_FILE="failure_summary_android16.txt"
{
echo "Build log file was not generated."
echo "Device: ${{ github.event.inputs.device_name }}"
echo "Codename: $DEVICE_CODENAME"
echo "Version: ${{ github.event.inputs.version_name }}"
echo "Workflow run: $WORKFLOW_URL"
echo "Please open the workflow URL for full runner logs."
} > "$LOG_FILE"
fi
curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument" \
-F chat_id="${{ github.event.inputs.user_id }}" \
-F document=@"$LOG_FILE" \
-F caption="Android 16 failure log"