Android 16 Framework Patcher #2102
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |