Created
January 15, 2026 09:33
-
-
Save hubciorz/c69efe4d1483ae18e6de32fcf7b3b327 to your computer and use it in GitHub Desktop.
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
| #!/bin/bash | |
| # | |
| # check_16kb_alignment.sh - Check Android APK/AAB for 16KB page size support | |
| # | |
| # Usage: ./check_16kb_alignment.sh <file.apk|file.aab> | |
| # | |
| set -e | |
| # Colors | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| BOLD='\033[1m' | |
| NC='\033[0m' # No Color | |
| cleanup() { | |
| if [ -n "${TEMP_DIR}" ] && [ -d "${TEMP_DIR}" ]; then | |
| rm -rf "${TEMP_DIR}" | |
| fi | |
| } | |
| trap cleanup EXIT | |
| usage() { | |
| echo "Usage: $0 <file.apk|file.aab>" | |
| echo "" | |
| echo "Check Android APK/AAB for 16KB page size support." | |
| echo "For AAB files, bundletool must be installed." | |
| exit 1 | |
| } | |
| check_dependencies() { | |
| if ! command -v objdump &> /dev/null; then | |
| echo -e "${RED}Error: objdump not found${NC}" | |
| exit 1 | |
| fi | |
| if ! command -v unzip &> /dev/null; then | |
| echo -e "${RED}Error: unzip not found${NC}" | |
| exit 1 | |
| fi | |
| } | |
| # Find zipalign | |
| find_zipalign() { | |
| if command -v zipalign &> /dev/null; then | |
| echo "zipalign" | |
| return | |
| fi | |
| local sdk_paths=( | |
| "$ANDROID_HOME/build-tools" | |
| "$ANDROID_SDK_ROOT/build-tools" | |
| "$HOME/Library/Android/sdk/build-tools" | |
| "$HOME/Android/Sdk/build-tools" | |
| ) | |
| for sdk_path in "${sdk_paths[@]}"; do | |
| if [ -d "$sdk_path" ]; then | |
| local latest=$(ls -1 "$sdk_path" 2>/dev/null | sort -V | tail -1) | |
| if [ -n "$latest" ] && [ -f "$sdk_path/$latest/zipalign" ]; then | |
| echo "$sdk_path/$latest/zipalign" | |
| return | |
| fi | |
| fi | |
| done | |
| echo "" | |
| } | |
| convert_aab_to_apk() { | |
| local aab_file="$1" | |
| local output_dir="$2" | |
| if ! command -v bundletool &> /dev/null; then | |
| echo -e "${RED}Error: bundletool not found. Install with: brew install bundletool${NC}" >&2 | |
| exit 1 | |
| fi | |
| echo -e "${BLUE}Converting AAB to APK...${NC}" >&2 | |
| local apks_file="${output_dir}/app.apks" | |
| bundletool build-apks --bundle="$aab_file" --output="$apks_file" --mode=universal >/dev/null 2>&1 | |
| unzip -q "$apks_file" -d "${output_dir}/apks" | |
| local apk_path="${output_dir}/apks/universal.apk" | |
| echo "$apk_path" | |
| } | |
| check_arch_alignment() { | |
| local lib_dir="$1" | |
| local arch="$2" | |
| local total=0 | |
| local aligned=0 | |
| local unaligned_libs=() | |
| if [ ! -d "$lib_dir" ]; then | |
| echo "MISSING|0|0|" | |
| return | |
| fi | |
| for so_file in "$lib_dir"/*.so; do | |
| [ -f "$so_file" ] || continue | |
| total=$((total + 1)) | |
| local align=$(objdump -p "$so_file" 2>/dev/null | grep LOAD | awk '{ print $NF }' | head -1) | |
| if [[ $align =~ 2\*\*(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,}) ]]; then | |
| aligned=$((aligned + 1)) | |
| else | |
| unaligned_libs+=("$(basename "$so_file")") | |
| fi | |
| done | |
| if [ $total -eq 0 ]; then | |
| echo "EMPTY|0|0|" | |
| elif [ $aligned -eq $total ]; then | |
| echo "ALIGNED|$aligned|$total|" | |
| else | |
| echo "UNALIGNED|$aligned|$total|${unaligned_libs[*]}" | |
| fi | |
| } | |
| print_header() { | |
| echo "" | |
| echo -e "${BOLD}╔════════════════════════════════════════════════════════════════════╗${NC}" | |
| echo -e "${BOLD}║ 16KB Page Size Alignment Check Report ║${NC}" | |
| echo -e "${BOLD}╚════════════════════════════════════════════════════════════════════╝${NC}" | |
| echo "" | |
| } | |
| print_table() { | |
| local apk_file="$1" | |
| local zip_status="$2" | |
| local arm64_result="$3" | |
| local x86_64_result="$4" | |
| local armv7_result="$5" | |
| local x86_result="$6" | |
| echo -e "${BOLD}File:${NC} $(basename "$apk_file")" | |
| echo "" | |
| # Zip alignment status | |
| echo -e "${BOLD}┌────────────────────────────────────────────────────────────────────┐${NC}" | |
| echo -e "${BOLD}│ APK Zip Alignment │${NC}" | |
| echo -e "${BOLD}├────────────────────────────────────────────────────────────────────┤${NC}" | |
| if [ "$zip_status" = "OK" ]; then | |
| echo -e "│ ✅ Verification successful │" | |
| elif [ "$zip_status" = "SKIP" ]; then | |
| echo -e "│ ⏭️ Skipped (zipalign not found) │" | |
| else | |
| echo -e "│ ❌ Verification failed │" | |
| fi | |
| echo -e "${BOLD}└────────────────────────────────────────────────────────────────────┘${NC}" | |
| echo "" | |
| # ELF alignment table | |
| echo -e "${BOLD}┌──────────────┬────────────────────────────┬───────────┬──────────────┐${NC}" | |
| echo -e "${BOLD}│ Architecture │ Status │ Libraries │ Required │${NC}" | |
| echo -e "${BOLD}├──────────────┼────────────────────────────┼───────────┼──────────────┤${NC}" | |
| print_arch_row "arm64-v8a" "$arm64_result" "Yes" | |
| print_arch_row "x86_64" "$x86_64_result" "Yes" | |
| print_arch_row "armeabi-v7a" "$armv7_result" "No (32-bit)" | |
| print_arch_row "x86" "$x86_result" "No (32-bit)" | |
| echo -e "${BOLD}└──────────────┴────────────────────────────┴───────────┴──────────────┘${NC}" | |
| } | |
| print_arch_row() { | |
| local arch="$1" | |
| local result="$2" | |
| local required="$3" | |
| IFS='|' read -r status aligned total unaligned_libs <<< "$result" | |
| local status_text | |
| local libs_text | |
| case "$status" in | |
| "ALIGNED") | |
| status_text="✅ ALIGNED (16KB) " | |
| libs_text="${aligned}/${total}" | |
| ;; | |
| "UNALIGNED") | |
| if [ "$required" = "Yes" ]; then | |
| status_text="❌ UNALIGNED (4KB) " | |
| else | |
| status_text="🟡 UNALIGNED (4KB) " | |
| fi | |
| libs_text="${aligned}/${total}" | |
| ;; | |
| "MISSING") | |
| status_text="⬜ Not present " | |
| libs_text="-" | |
| ;; | |
| "EMPTY") | |
| status_text="⬜ Empty " | |
| libs_text="-" | |
| ;; | |
| esac | |
| # Print row with fixed-width status text (padding included in status_text) | |
| echo "│ $(printf '%-12s' "$arch") │ ${status_text}│ $(printf '%-9s' "$libs_text") │ $(printf '%-12s' "$required") │" | |
| } | |
| print_summary() { | |
| local arm64_result="$1" | |
| local x86_64_result="$2" | |
| IFS='|' read -r arm64_status _ _ _ <<< "$arm64_result" | |
| IFS='|' read -r x86_64_status _ _ _ <<< "$x86_64_result" | |
| echo "" | |
| echo -e "${BOLD}Summary${NC}" | |
| echo "─────────" | |
| local all_ok=true | |
| if [ "$arm64_status" != "ALIGNED" ] && [ "$arm64_status" != "MISSING" ]; then | |
| all_ok=false | |
| fi | |
| if [ "$x86_64_status" != "ALIGNED" ] && [ "$x86_64_status" != "MISSING" ]; then | |
| all_ok=false | |
| fi | |
| if [ "$all_ok" = true ]; then | |
| echo -e "${GREEN}✅ This APK supports 16KB page size${NC}" | |
| echo "" | |
| echo "All 64-bit libraries (arm64-v8a, x86_64) are properly aligned." | |
| echo "32-bit libraries don't require 16KB alignment." | |
| else | |
| echo -e "${RED}❌ This APK does NOT support 16KB page size${NC}" | |
| echo "" | |
| echo "Some 64-bit libraries are not aligned to 16KB boundaries." | |
| echo "Rebuild native libraries with: -Wl,-z,max-page-size=16384" | |
| fi | |
| echo "" | |
| } | |
| print_unaligned_details() { | |
| local arm64_result="$1" | |
| local x86_64_result="$2" | |
| IFS='|' read -r arm64_status _ _ arm64_unaligned <<< "$arm64_result" | |
| IFS='|' read -r x86_64_status _ _ x86_64_unaligned <<< "$x86_64_result" | |
| if [ "$arm64_status" = "UNALIGNED" ] || [ "$x86_64_status" = "UNALIGNED" ]; then | |
| echo -e "${BOLD}Unaligned 64-bit libraries:${NC}" | |
| echo "============================" | |
| if [ "$arm64_status" = "UNALIGNED" ] && [ -n "$arm64_unaligned" ]; then | |
| echo -e "${YELLOW}arm64-v8a:${NC}" | |
| for lib in $arm64_unaligned; do | |
| echo " - $lib" | |
| done | |
| fi | |
| if [ "$x86_64_status" = "UNALIGNED" ] && [ -n "$x86_64_unaligned" ]; then | |
| echo -e "${YELLOW}x86_64:${NC}" | |
| for lib in $x86_64_unaligned; do | |
| echo " - $lib" | |
| done | |
| fi | |
| echo "" | |
| fi | |
| } | |
| # Main | |
| if [ $# -ne 1 ]; then | |
| usage | |
| fi | |
| INPUT_FILE="$1" | |
| if [ ! -f "$INPUT_FILE" ]; then | |
| echo -e "${RED}Error: File not found: $INPUT_FILE${NC}" | |
| exit 1 | |
| fi | |
| check_dependencies | |
| TEMP_DIR=$(mktemp -d) | |
| APK_FILE="" | |
| case "$INPUT_FILE" in | |
| *.aab) | |
| APK_FILE=$(convert_aab_to_apk "$INPUT_FILE" "$TEMP_DIR") | |
| ;; | |
| *.apk) | |
| APK_FILE="$INPUT_FILE" | |
| ;; | |
| *) | |
| echo -e "${RED}Error: File must be .apk or .aab${NC}" | |
| exit 1 | |
| ;; | |
| esac | |
| EXTRACT_DIR="${TEMP_DIR}/extracted" | |
| unzip -q "$APK_FILE" -d "$EXTRACT_DIR" | |
| ZIPALIGN=$(find_zipalign) | |
| ZIP_STATUS="SKIP" | |
| if [ -n "$ZIPALIGN" ]; then | |
| if "$ZIPALIGN" -c -P 16 4 "$APK_FILE" &>/dev/null; then | |
| ZIP_STATUS="OK" | |
| else | |
| ZIP_STATUS="FAIL" | |
| fi | |
| fi | |
| ARM64_RESULT=$(check_arch_alignment "$EXTRACT_DIR/lib/arm64-v8a" "arm64-v8a") | |
| X86_64_RESULT=$(check_arch_alignment "$EXTRACT_DIR/lib/x86_64" "x86_64") | |
| ARMV7_RESULT=$(check_arch_alignment "$EXTRACT_DIR/lib/armeabi-v7a" "armeabi-v7a") | |
| X86_RESULT=$(check_arch_alignment "$EXTRACT_DIR/lib/x86" "x86") | |
| print_header | |
| print_table "$INPUT_FILE" "$ZIP_STATUS" "$ARM64_RESULT" "$X86_64_RESULT" "$ARMV7_RESULT" "$X86_RESULT" | |
| print_unaligned_details "$ARM64_RESULT" "$X86_64_RESULT" | |
| print_summary "$ARM64_RESULT" "$X86_64_RESULT" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment