Skip to content

Instantly share code, notes, and snippets.

@hubciorz
Created January 15, 2026 09:33
Show Gist options
  • Select an option

  • Save hubciorz/c69efe4d1483ae18e6de32fcf7b3b327 to your computer and use it in GitHub Desktop.

Select an option

Save hubciorz/c69efe4d1483ae18e6de32fcf7b3b327 to your computer and use it in GitHub Desktop.
#!/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