Files
MessagesModPlus/scripts/messages_mod.sh

486 lines
15 KiB
Bash
Executable File

#!/system/bin/sh
# Messages Mod+ - Main Operations Script
# Runs on-device with root privileges
# Usage: messages_mod.sh <command> [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 <command> [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 <t> List backups (sms|rcs|attachments)"
echo " delete-backup <p> Delete a backup file or directory"
echo ""
exit 1
;;
esac