#!/system/bin/sh # Messages Mod+ - Main Operations Script # Runs on-device with root privileges # Usage: messages_mod.sh [args] MODDIR="${MODDIR:-$(dirname "$(dirname "$(readlink -f "$0")")")}" BACKUP_DIR="/sdcard/MessagesModPlus" MSG_PKG="com.google.android.apps.messaging" TELEPHONY_PKG="com.android.providers.telephony" CARRIER_PKG="com.google.android.ims" SMS_DB="/data/data/${TELEPHONY_PKG}/databases/mmssms.db" RCS_DB="/data/data/${MSG_PKG}/databases/bugle_db" ATTACH_DIR="/data/data/${MSG_PKG}/files" log() { echo "[MessagesModPlus] $1"; } err() { echo "[MessagesModPlus:ERROR] $1" >&2; } timestamp() { date +%Y%m%d_%H%M%S; } ensure_backup_dir() { mkdir -p "${BACKUP_DIR}/sms" "${BACKUP_DIR}/rcs" "${BACKUP_DIR}/attachments" "${BACKUP_DIR}/apks" } get_apk_paths() { pm path "$MSG_PKG" 2>/dev/null | sed 's/^package://' } get_system_apk_path() { dumpsys package "$MSG_PKG" | grep "codePath=" | grep -E "/system|/product|/system_ext|/vendor" | head -1 | sed 's/.*codePath=//' | tr -d ' ' } is_system_app() { dumpsys package "$MSG_PKG" | grep "pkgFlags" | head -1 | grep -q "SYSTEM" } is_updated_system_app() { dumpsys package "$MSG_PKG" | grep "pkgFlags" | head -1 | grep -q "UPDATED_SYSTEM_APP" } is_package_enabled() { pm list packages -e | grep -q "$MSG_PKG" } get_status() { local status="unknown" local system_path="" local user_path="" local enabled="false" if ! pm list packages | grep -q "$MSG_PKG"; then echo "NOT_INSTALLED|none|none|false" return fi system_path=$(get_system_apk_path) user_path=$(dumpsys package "$MSG_PKG" | grep "codePath=" | grep "/data/app" | head -1 | sed 's/.*codePath=//' | tr -d ' ') if is_package_enabled; then enabled="true" fi if is_updated_system_app; then status="MODDED" elif is_system_app; then status="SYSTEM" else status="USER" fi echo "${status}|${system_path:-none}|${user_path:-none}|${enabled}" } cmd_status() { local info info=$(get_status) local state=$(echo "$info" | cut -d'|' -f1) local sys_path=$(echo "$info" | cut -d'|' -f2) local usr_path=$(echo "$info" | cut -d'|' -f3) local enabled=$(echo "$info" | cut -d'|' -f4) echo "STATE=${state}" echo "SYSTEM_PATH=${sys_path}" echo "USER_PATH=${usr_path}" echo "ENABLED=${enabled}" # Backup info local sms_count=0 local rcs_count=0 local attach_count=0 [ -d "${BACKUP_DIR}/sms" ] && sms_count=$(ls -1 "${BACKUP_DIR}/sms"/*.db 2>/dev/null | wc -l) [ -d "${BACKUP_DIR}/rcs" ] && rcs_count=$(ls -1 "${BACKUP_DIR}/rcs"/*.db 2>/dev/null | wc -l) [ -d "${BACKUP_DIR}/attachments" ] && attach_count=$(ls -1d "${BACKUP_DIR}/attachments"/*/ 2>/dev/null | wc -l) echo "SMS_BACKUPS=${sms_count}" echo "RCS_BACKUPS=${rcs_count}" echo "ATTACH_BACKUPS=${attach_count}" } cmd_backup_sms() { ensure_backup_dir if [ ! -f "$SMS_DB" ]; then err "SMS database not found at ${SMS_DB}" return 1 fi local ts=$(timestamp) local dest="${BACKUP_DIR}/sms/mmssms_${ts}.db" # Force a checkpoint so WAL data is flushed cp "$SMS_DB" "$dest" 2>/dev/null [ -f "${SMS_DB}-wal" ] && cp "${SMS_DB}-wal" "${dest}-wal" 2>/dev/null [ -f "${SMS_DB}-journal" ] && cp "${SMS_DB}-journal" "${dest}-journal" 2>/dev/null if [ -f "$dest" ]; then local size=$(du -h "$dest" | cut -f1) log "SMS backup saved: ${dest} (${size})" echo "BACKUP=${dest}" return 0 else err "Failed to create SMS backup" return 1 fi } cmd_backup_rcs() { ensure_backup_dir if [ ! -f "$RCS_DB" ]; then err "RCS database not found at ${RCS_DB}" return 1 fi local ts=$(timestamp) local dest="${BACKUP_DIR}/rcs/bugle_db_${ts}.db" cp "$RCS_DB" "$dest" 2>/dev/null [ -f "${RCS_DB}-wal" ] && cp "${RCS_DB}-wal" "${dest}-wal" 2>/dev/null [ -f "${RCS_DB}-journal" ] && cp "${RCS_DB}-journal" "${dest}-journal" 2>/dev/null if [ -f "$dest" ]; then local size=$(du -h "$dest" | cut -f1) log "RCS backup saved: ${dest} (${size})" echo "BACKUP=${dest}" return 0 else err "Failed to create RCS backup" return 1 fi } cmd_backup_attachments() { ensure_backup_dir if [ ! -d "$ATTACH_DIR" ]; then err "Attachments directory not found at ${ATTACH_DIR}" return 1 fi local ts=$(timestamp) local dest="${BACKUP_DIR}/attachments/${ts}" mkdir -p "$dest" # Copy all media/attachment files local count=0 # bugle attachments if [ -d "/data/data/${MSG_PKG}/files/photoEditor" ]; then cp -r "/data/data/${MSG_PKG}/files/photoEditor" "$dest/" 2>/dev/null fi # Main media storage used by Messages local media_dir="/data/user_de/0/${MSG_PKG}/files" if [ -d "$media_dir" ]; then cp -r "$media_dir" "$dest/user_de_files" 2>/dev/null fi # MMS parts (images, videos, audio from MMS) local parts_dir="/data/data/${TELEPHONY_PKG}/app_parts" if [ -d "$parts_dir" ]; then cp -r "$parts_dir" "$dest/mms_parts" 2>/dev/null fi # Also grab from the standard media location local bugle_media="/data/data/${MSG_PKG}/cache" if [ -d "$bugle_media" ]; then cp -r "$bugle_media" "$dest/cache" 2>/dev/null fi count=$(find "$dest" -type f 2>/dev/null | wc -l) local size=$(du -sh "$dest" 2>/dev/null | cut -f1) log "Attachments saved: ${dest} (${count} files, ${size})" echo "BACKUP=${dest}" echo "FILE_COUNT=${count}" return 0 } cmd_restore_sms() { local backup_file="$1" if [ -z "$backup_file" ]; then # Use most recent backup backup_file=$(ls -t "${BACKUP_DIR}/sms"/*.db 2>/dev/null | head -1) fi if [ -z "$backup_file" ] || [ ! -f "$backup_file" ]; then err "No SMS backup found" return 1 fi # Stop telephony provider temporarily am force-stop "$TELEPHONY_PKG" 2>/dev/null cp "$backup_file" "$SMS_DB" [ -f "${backup_file}-wal" ] && cp "${backup_file}-wal" "${SMS_DB}-wal" [ -f "${backup_file}-journal" ] && cp "${backup_file}-journal" "${SMS_DB}-journal" # Fix ownership - telephony db is owned by radio local radio_uid=$(stat -c '%u' /data/data/${TELEPHONY_PKG}/ 2>/dev/null || echo "1001") local radio_gid=$(stat -c '%g' /data/data/${TELEPHONY_PKG}/ 2>/dev/null || echo "1001") chown "${radio_uid}:${radio_gid}" "$SMS_DB" "${SMS_DB}-wal" "${SMS_DB}-journal" 2>/dev/null chmod 660 "$SMS_DB" "${SMS_DB}-wal" "${SMS_DB}-journal" 2>/dev/null restorecon "$SMS_DB" "${SMS_DB}-wal" "${SMS_DB}-journal" 2>/dev/null log "SMS database restored from ${backup_file}" log "Restart Messages app or reboot for changes to take effect" echo "RESTORED=${backup_file}" return 0 } cmd_restore_rcs() { local backup_file="$1" if [ -z "$backup_file" ]; then backup_file=$(ls -t "${BACKUP_DIR}/rcs"/*.db 2>/dev/null | head -1) fi if [ -z "$backup_file" ] || [ ! -f "$backup_file" ]; then err "No RCS backup found" return 1 fi am force-stop "$MSG_PKG" 2>/dev/null cp "$backup_file" "$RCS_DB" [ -f "${backup_file}-wal" ] && cp "${backup_file}-wal" "${RCS_DB}-wal" [ -f "${backup_file}-journal" ] && cp "${backup_file}-journal" "${RCS_DB}-journal" # Fix ownership to match Messages app uid local msg_uid=$(stat -c '%u' "/data/data/${MSG_PKG}/" 2>/dev/null) local msg_gid=$(stat -c '%g' "/data/data/${MSG_PKG}/" 2>/dev/null) if [ -n "$msg_uid" ]; then chown "${msg_uid}:${msg_gid}" "$RCS_DB" "${RCS_DB}-wal" "${RCS_DB}-journal" 2>/dev/null chmod 660 "$RCS_DB" "${RCS_DB}-wal" "${RCS_DB}-journal" 2>/dev/null restorecon "$RCS_DB" "${RCS_DB}-wal" "${RCS_DB}-journal" 2>/dev/null fi log "RCS database restored from ${backup_file}" echo "RESTORED=${backup_file}" return 0 } cmd_list_backups() { local type="$1" case "$type" in sms) ls -1t "${BACKUP_DIR}/sms"/*.db 2>/dev/null | while read f; do local size=$(du -h "$f" | cut -f1) local name=$(basename "$f") echo "${f}|${name}|${size}" done ;; rcs) ls -1t "${BACKUP_DIR}/rcs"/*.db 2>/dev/null | while read f; do local size=$(du -h "$f" | cut -f1) local name=$(basename "$f") echo "${f}|${name}|${size}" done ;; attachments) ls -1dt "${BACKUP_DIR}/attachments"/*/ 2>/dev/null | while read d; do local size=$(du -sh "$d" 2>/dev/null | cut -f1) local count=$(find "$d" -type f 2>/dev/null | wc -l) local name=$(basename "$d") echo "${d}|${name}|${size}|${count} files" done ;; esac } cmd_delete_backup() { local path="$1" # Safety: only allow deleting from our backup directory case "$path" in ${BACKUP_DIR}/*) if [ -f "$path" ]; then rm -f "$path" "${path}-wal" "${path}-journal" log "Deleted backup: ${path}" elif [ -d "$path" ]; then rm -rf "$path" log "Deleted backup directory: ${path}" else err "Backup not found: ${path}" return 1 fi ;; *) err "Refusing to delete outside backup directory: ${path}" return 1 ;; esac } cmd_apply_mod() { log "Starting Messages Mod+ apply..." # Verify package exists if ! pm list packages | grep -q "$MSG_PKG"; then err "Google Messages not installed" return 1 fi # Check if already modded if is_updated_system_app; then log "Messages is already running as user-space updated system app" echo "ALREADY_MODDED=true" return 0 fi # Step 1: Get current APK paths before disabling log "Collecting APK paths..." local apk_paths=$(get_apk_paths) if [ -z "$apk_paths" ]; then err "Could not find Messages APK paths" return 1 fi # Step 2: Save APKs to backup ensure_backup_dir local apk_dir="${BACKUP_DIR}/apks" rm -f "${apk_dir}"/*.apk 2>/dev/null local apk_count=0 local apk_list="" echo "$apk_paths" | while read apk_path; do if [ -f "$apk_path" ]; then local apk_name=$(basename "$apk_path") cp "$apk_path" "${apk_dir}/${apk_name}" log "Saved: ${apk_name}" fi done # Build the install command from saved APKs apk_list=$(ls -1 "${apk_dir}"/*.apk 2>/dev/null | tr '\n' ' ') if [ -z "$apk_list" ]; then err "No APKs were saved, aborting" return 1 fi apk_count=$(ls -1 "${apk_dir}"/*.apk 2>/dev/null | wc -l) log "Saved ${apk_count} APK split(s)" # Step 3: Disable the system app log "Disabling system Messages..." pm disable-user --user 0 "$MSG_PKG" 2>&1 # Step 4: Reinstall as user-space app log "Installing as user-space app..." local install_result # pm install requires -t for test and --bypass-low-target-sdk-block on newer Android # Use session-based install for split APKs local session_id=$(pm install-create 2>&1 | grep -oE '[0-9]+') if [ -z "$session_id" ]; then err "Failed to create install session" pm enable "$MSG_PKG" 2>/dev/null return 1 fi local idx=0 for apk in ${apk_dir}/*.apk; do local apk_size=$(stat -c '%s' "$apk" 2>/dev/null || wc -c < "$apk") local apk_name=$(basename "$apk") pm install-write -S "$apk_size" "$session_id" "$apk_name" "$apk" 2>&1 idx=$((idx + 1)) done install_result=$(pm install-commit "$session_id" 2>&1) if echo "$install_result" | grep -qi "success"; then log "Installation successful" else # Fallback: try direct install log "Session install returned: ${install_result}, trying direct method..." local apk_args="" for apk in ${apk_dir}/*.apk; do apk_args="${apk_args} ${apk}" done install_result=$(pm install $apk_args 2>&1) if ! echo "$install_result" | grep -qi "success"; then err "Installation failed: ${install_result}" pm enable "$MSG_PKG" 2>/dev/null return 1 fi fi # Step 5: Re-enable log "Re-enabling package..." pm enable "$MSG_PKG" 2>&1 # Step 6: Verify if is_updated_system_app; then log "Messages Mod+ applied successfully!" log "Messages is now running as a user-space app" echo "RESULT=success" return 0 else err "Mod may not have applied correctly - check package state" echo "RESULT=uncertain" return 1 fi } cmd_uninstall() { log "Reverting Messages to stock system app..." if ! pm list packages | grep -q "$MSG_PKG"; then err "Google Messages not installed" return 1 fi if ! is_updated_system_app; then log "Messages is already running as stock system app" echo "ALREADY_STOCK=true" return 0 fi # Uninstall updates reverts to the system version log "Removing user-space updates..." pm uninstall-updates "$MSG_PKG" 2>&1 || { # Alternative method pm install-existing "$MSG_PKG" 2>/dev/null cmd install -r --user 0 "$MSG_PKG" 2>/dev/null } # Make sure it's enabled pm enable "$MSG_PKG" 2>/dev/null if ! is_updated_system_app; then log "Successfully reverted to stock system Messages" echo "RESULT=success" return 0 else err "Revert may not have completed - check package state" echo "RESULT=uncertain" return 1 fi } # Command router case "$1" in status) cmd_status ;; backup-sms) cmd_backup_sms ;; backup-rcs) cmd_backup_rcs ;; backup-attach) cmd_backup_attachments ;; restore-sms) cmd_restore_sms "$2" ;; restore-rcs) cmd_restore_rcs "$2" ;; list-backups) cmd_list_backups "$2" ;; delete-backup) cmd_delete_backup "$2" ;; apply) cmd_apply_mod ;; uninstall) cmd_uninstall ;; *) echo "Messages Mod+ v1.0.0" echo "" echo "Usage: $0 [args]" echo "" echo "Commands:" echo " status Show current Messages state" echo " apply Apply mod (reinstall as user-space app)" echo " uninstall Revert to stock system app" echo " backup-sms Backup SMS/MMS database" echo " backup-rcs Backup RCS database" echo " backup-attach Backup message attachments" echo " restore-sms [f] Restore SMS/MMS database (latest or specific file)" echo " restore-rcs [f] Restore RCS database (latest or specific file)" echo " list-backups List backups (sms|rcs|attachments)" echo " delete-backup

Delete a backup file or directory" echo "" exit 1 ;; esac