#!/sbin/sh

VERSION=18.1.20180909
NANODROID_UPGRADE=0
NANODROID_LIST=/data/adb/NanoDroid_FileList

GOOGLE_APPS="AMAPNetworkLocation
BaiduNetworkLocation
BlankStore
com.qualcomm.location
DejaVuBackend
DejaVuNlpBackend
FDroidPriv
GmsCoreSetupPrebuilt
GmsCore_update
GmsDroidGuard
GoogleFeedback
GoogleLoginService
GoogleOneTimeInitializer
GoogleServicesFramework
GoogleTTS
IchnaeaNlpBackend
LegacyNetworkLocation
MarketUpdater
MarkupGoogle
MozillaNlpBackend
NetworkLocation
NominatimGeocoderBackend
NominatimNlpBackend
PlayGames
PlayStore
PrebuiltGmsCore
UnifiedNlp
Velvet
Vending
WhisperPush
YouTube"

GOOGLE_DATA="com.amap.android.location
com.baidu.location
com.google.android.feedback
com.google.android.gsf.login
com.google.android.location
com.google.android.youtube
com.mgoogle.android.gms
com.qualcomm.location
org.microg.nlp
org.microg.unifiednlp"

##########################################################################################
# Misc. Functions
##########################################################################################

detect_bootmode () {
	ps | grep zygote | grep -qv grep && BOOTMODE=true || BOOTMODE=false
	${BOOTMODE} || ps -A | grep zygote | grep -qv grep && BOOTMODE=true
}

ui_print() {
	if ${BOOTMODE}; then
		echo "${@}"
	else 
		echo -n -e "ui_print ${@}\n" >> /proc/self/fd/${OUTFD}
		echo -n -e "ui_print\n" >> /proc/self/fd/${OUTFD}
	fi
}

show_progress() {
	${BOOTMODE} || echo "progress ${1} ${2}" >> /proc/self/fd/${OUTFD}
}

set_progress() {
	${BOOTMODE} || echo "set_progress ${1}" >> /proc/self/fd/${OUTFD}
}

grep_prop() {
	REGEX="${1}"
	shift
	FILES="${@}"
	[ -z "${@}" ] && FILES='/system/build.prop'
	sed -n "s/^${REGEX}=//p" ${FILES} | \
		head -n 1
}

grep_cmdline() {
	local REGEX="s/^${1}=//p"
	sed -E 's/ +/\n/g' /proc/cmdline | \
		sed -n "${REGEX}" 2>/dev/null
}

is_mounted() {
	if [ ! -z "${2}" ]; then
		cat /proc/mounts | grep ${1} | grep ${2}, >/dev/null
	else	cat /proc/mounts | grep ${1} >/dev/null
	fi
	return $?
}

set_perm() {
	chown ${2}:${3} ${1} || error "failed change owner for ${1}"
	chmod ${4} ${1} || error "failed to change mode for ${1}"
	if [ ! -z "${5}" ]; then
		chcon ${5} ${1} 2>/dev/null
	else	chcon 'u:object_r:system_file:s0' ${1} 2>/dev/null
	fi
}

set_perm_recursive() {
	find ${1} -type d 2>/dev/null | while read dir; do
		set_perm ${dir} ${2} ${3} ${4} ${6}
	done
	find ${1} -type f 2>/dev/null | while read file; do
		set_perm ${file} ${2} ${3} ${5} ${6}
	done
}

set_perm_data () {
	if [ "${1}" = "-r" ]; then
		echo "    perm: data [recursive] {${2}}"
		set_perm_recursive ${2} 0 0 0755 0644
	else
		echo "    perm: data [single] {${1}}"
		set_perm ${1} 0 0 0644
	fi
}

set_perm_bin () {
	if [ "${1}" = "-r" ]; then
		echo "    perm: exec [recursive] {${2}}"
		set_perm_recursive ${2} 0 0 0755 0755
	else
		echo "    perm: exec [single] {${1}}"
		set_perm ${1} 0 0 0755
	fi
}

installinfo_add () {
	if [ "${MODE}" = "SYSTEM" ]; then
		for file in ${@}; do
			echo ${file} >> ${NANODROID_LIST}
		done
	fi
}

detect_outfd () {
	if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
		# We will have to manually find out OUTFD
		for FD in `ls /proc/$$/fd`; do
			if readlink /proc/$$/fd/$FD | grep -q pipe; then
				if ps | grep -v grep | grep -q " 3 $FD "; then
					OUTFD=$FD
					break
				fi
			fi
		done
	fi
}

show_banner () {
	ui_print " "
	ui_print "*****************************"
	ui_print "   NanoDroid ${VERSION}   "
	ui_print "      created by Nanolx      "
	case ${MODID} in
		NanoDroid )
			ui_print "       Full package          "
		;;
		NanoDroid_microG )
			ui_print "       microG package        "
		;;
		NanoDroid_FDroid )
			ui_print "       F-Droid package       "
		;;
	esac
	ui_print "*****************************"
	ui_print " "
}

error () {
	ui_print " !!"
	ui_print "${@}"
	ui_print " !!"

	if [ "${MODE}" = "MAGISK" ]; then
		rm -rf "${MODPATH}"
		unmount_magisk_img
	fi

	${BOOTMODE} || recovery_cleanup
	nanodroid_storelogs

	exit 1
}

# taken from Magisk, with minor modifications for NanoDroid
mount_partitions () {
	SLOT=$(grep_cmdline androidboot.slot_suffix)
	if [ -z ${SLOT} ]; then
		SLOT=_$(grep_cmdline androidboot.slot)
		[ "${SLOT}" = "_" ] && SLOT=
	fi

	is_mounted /data || mount /data || error "failed to mount /data!"

	${BOOTMODE} || mount -o bind /dev/urandom /dev/random

	! is_mounted /system && mount -o rw /system

	if [ ! -f /system/build.prop ]; then
		SYSTEMBLOCK=$(find /dev/block -iname system${SLOT} | head -n 1)
		mount -t ext4 -o rw ${SYSTEMBLOCK} /system
	fi

	[ -f /system/build.prop ] || is_mounted /system || error "failed to mount /system (unsupported A/B device?)"

	if [ -f /system/init ]; then
		mkdir /system_root 2>/dev/null
		mount --move /system /system_root
		mount -o bind /system_root/system /system
	fi

	[ ! -f /system/build.prop ] && error "failed to mount /system (unsupported A/B device?)"

	if [ -L /system/vendor ]; then
		! is_mounted /vendor && mount /vendor
		if ! is_mounted /vendor; then
			VENDORBLOCK=$(find /dev/block -iname vendor${SLOT} | head -n 1)
			mount -t ext4 -o ro ${VENDORBLOCK} /vendor
		fi
	elif [ ! ${BOOTMODE} -a -d /system/vendor -a ! -e /vendor ]; then
		### XXX work-around required for some ROMs
		echo " xxx compat /vendor link created!"
		ln -sf /system/vendor /vendor
	fi

	mount | awk '{print $1 " on " $3}'
}

migrate_magisk_custom () {
	if [ ! -d /data/adb/ ]; then
		mkdir -p /data/adb/
		chown root:root /data/adb
		chmod 0644 /data/adb
	fi
	cp -r ${1} /data/adb/magisk
}

detect_mode () {
	[ -f /data/adb/.nanodroid-list ] && mv /data/adb/.nanodroid-list "${NANODROID_LIST}"

	case "${nanodroid_forcesystem}" in
		1 )
			MODE_DETECT=forced
			MODE=SYSTEM

			${BOOTMODE} && error "Forced System Mode installation requested, but device in BOOTMODE"
		;;

		0 | *)
			MODE_DETECT=detected

			if [ -f "${NANODROID_LIST}" ]; then
				MODE=SYSTEM

				${BOOTMODE} && error "System Mode installation detected, but device in BOOTMODE"
			elif [ -f /data/adb/magisk/magisk ]; then
				MODE=MAGISK
			elif [ -f /data/.magisk/magisk ]; then
				migrate_magisk_custom /data/.magisk/
				MODE=MAGISK
			elif [ -f /data/magisk/magisk ]; then
				migrate_magisk_custom /data/magisk/
				MODE=MAGISK
			elif ! ${BOOTMODE}; then
				MODE=SYSTEM
			else
				error "Magisk 15.0+ not found and device in BOOTMODE"
			fi
		;;
	esac

	ui_print " > Mode   | ${MODE}            | ${MODE_DETECT}"
	ui_print " "

	if [ "${MODE}" = "SYSTEM" ]; then
		if [ -f "${NANODROID_LIST}" -a "$(wc -l < "${NANODROID_LIST}")" -gt 1 ]; then
			NANODROID_UPGRADE=1
		else	NANODROID_UPGRADE=0
		fi

		recovery_actions
	fi

	[ "${MODE}" = "MAGISK" ] && magisk_install_setup

	if [ "${nanodroid_fonts}" -eq 1 ]; then
		if test -h ${RESTORE_PATH}/system/fonts/Roboto-Regular.ttf; then
			CUSTOM_FONT="$(basename $(readlink ${RESTORE_PATH}/system/fonts/Roboto-Regular.ttf) .ttf)"
			ui_print " << Detected NanoDroid-Font (${CUSTOM_FONT})"
		fi
	fi

	if [ "${MODE}" = "MAGISK" ]; then
		case ${MODID} in
			NanoDroid | NanoDroid_microG )
				magisk_install_preinst
			;;
		esac
	else	unset MODPATH
	fi

	if [ "${ROM_NO_XBIN}" -eq 0 ]; then
		NANODROID_BINDIR="${MODPATH}/system/xbin"
	else	NANODROID_BINDIR="${MODPATH}/system/bin"
	fi
}

unpack_zip () {
	TMPDIR=/dev/tmp
	TMP_LIBDIR=${TMPDIR}/nanodroid-lib
	INSTALLER=${TMPDIR}/install

	rm -rf ${INSTALLER}
	mkdir -p ${INSTALLER} || error "failed to prepare environment!"

	ui_print " > prepare installation"
	unzip -o "${ZIP}" -d "${INSTALLER}" || \
		error "failed to prepare environment!"
}

nanodroid_finalize () {
	case ${MODID} in
		NanoDroid | NanoDroid_microG )
			if [ "${nanodroid_overlay}" -eq 1 ]; then
				ui_print " << with /system applications override"
				${NANODROID_BINDIR}/nanodroid-overlay --create
			else
				ui_print " << without /system applications override"
			fi
		;;
	esac

	if [ "${MODE}" = "MAGISK" ]; then
		magisk_install_finish
	else
		install_addond
		recovery_cleanup

		sort ${NANODROID_LIST} | uniq > /tmp/nanodroid-list
		mv /tmp/nanodroid-list ${NANODROID_LIST}
	fi

	rm -rf ${INSTALLER}

	ui_print " "
	ui_print " Thanks for using NanoDroid "
	ui_print " "

	sync
	nanodroid_storelogs

	if ! ${BOOTMODE} ; then
		umount -l /system_root 2>/dev/null
		umount -l /system 2>/dev/null
		umount -l /vendor 2>/dev/null
		umount -l /dev/random 2>/dev/null
	fi
}

##########################################################################################
# Architecture detection
##########################################################################################

detect_arch () {
	SDK_VERSION=$(grep_prop ro.build.version.sdk)
	CUSTOM_ROM=0

	ABI=$(grep_prop ro.product.cpu.abi | cut -c-3)
	ABI2=$(grep_prop ro.product.cpu.abi2 | cut -c-3)
	ABILONG=$(grep_prop ro.product.cpu.abi)

	ARCH=arm

	[ "$ABI" = "x86" ] && ARCH=x86
	[ "$ABI2" = "x86" ] && ARCH=x86
	[ "$ABILONG" = "arm64-v8a" ] && ARCH=arm64
	[ "$ABILONG" = "x86_64" ] && ARCH=x64

	case ${ARCH} in
		arm )		BIN_ARCH="arm"
				LIB_ARCHES="armeabi-v7a armeabi"
				SWIPE_LIBDIR=lib ;;
		arm64 )		BIN_ARCH="arm"
				LIB_ARCHES="arm64-v8a armeabi-v7a armeabi"
				SWIPE_LIBDIR=lib64 ;;
		x86 )		BIN_ARCH="x86"
				LIB_ARCHES="x86 armeabi-v7a armeabi"
				SWIPE_LIBDIR=lib ;;
		x86_64 )	BIN_ARCH="x86"
				LIB_ARCHES="x86_64 x86 armeabi-v7a armeabi"
				SWIPE_LIBDIR=lib64 ;;
	esac

	chmod +x "${INSTALLER}/system/xbin/unzip.${BIN_ARCH}"

	case ${SDK_VERSION} in
		19 )		GSYNC_VER=K	;;
		21 | 22 )	GSYNC_VER=L	;;
		23 )		GSYNC_VER=M	;;
		24 | 25 )	GSYNC_VER=N	;;
		26 | 27 )	GSYNC_VER=O	;;
		28 )		GSYNC_VER=P	;;
		* )	GSYNC_UNSUPPORTED=1	;;
	esac

	if [[ "${SDK_VERSION}" -lt 21 ]]; then
		UNFOLD_APP_DIR=1
	else	UNFOLD_APP_DIR=0
	fi

	if [[ "${SDK_VERSION}" -lt 19 ]]; then
		ui_print " "
		ui_print " ++ Installing on pre-KitKat ROM, full"
		ui_print " ++ compatibility is not garantueed!"
		ui_print " "
	fi

	if [ ! -d /system/xbin ]; then
		ROM_NO_XBIN=1
		echo " ++ ROM has no /system/xbin, using /system/bin instead"
	else	ROM_NO_XBIN=0
	fi

	for pattern in dev-keys test-keys type=userdebug; do
		grep -q "${pattern}" /system/build.prop && CUSTOM_ROM=1
	done

	[ "${CUSTOM_ROM}" -eq 1 ] && \
		ui_print " > Custom ROM detected" \
		|| ui_print " > Stock ROM detected"

	[ ! -d /data/adb ] && mkdir /data/adb
}

setup_busybox () {
	OLD_PATH=${PATH}

	mkdir -p ${INSTALLER}/busybox
	ln -s ${INSTALLER}/busybox.${BIN_ARCH} ${INSTALLER}/busybox/busybox

	chmod 0755 ${INSTALLER}/busybox.${BIN_ARCH}
	${INSTALLER}/busybox.${BIN_ARCH} --install -s ${INSTALLER}/busybox/

	export PATH="${INSTALLER}/busybox:${PATH}"
}

# Taken from Magisk, used for System Mode installation
recovery_actions () {
	mv /sbin /sbin_tmp
	OLD_LD_LIB=$LD_LIBRARY_PATH
	OLD_LD_PRE=$LD_PRELOAD
	unset LD_LIBRARY_PATH
	unset LD_PRELOAD
}

# Taken from Magisk, used for System Mode installation
recovery_cleanup () {
	mv /sbin_tmp /sbin 2>/dev/null
	[ -z $OLD_PATH ] || export PATH=$OLD_PATH
	[ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB
	[ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE
}

##########################################################################################
# Magisk Mode Installation
##########################################################################################

magisk_install_setup () {
	ui_print "******************************"
	ui_print "Powered by Magisk (@topjohnwu)"
	ui_print "******************************"
	ui_print " "
	ui_print " > setup Magisk environment"

	MOUNTPATH=${TMPDIR}/magisk_img
	MAGISKBIN=/data/adb/magisk

	[ -f ${MAGISKBIN}/util_functions.sh ] || \
		error "Magisk version 15.0 or newer is required"

	. ${MAGISKBIN}/util_functions.sh

	MIN_VER=$(grep_prop minMagisk ${INSTALLER}/module.prop)
	[ ! -z ${MAGISK_VER_CODE} -a ${MAGISK_VER_CODE} -ge ${MIN_VER} ] || \
		error "Magisk 15.0 or newer is required"

	${BOOTMODE} && IMG=/data/adb/magisk_merge.img || IMG=/data/adb/magisk.img

	MODPATH=${MOUNTPATH}/${MODID}
	MAGISKPATH=/sbin/.core/img
	REALPATH=${MAGISKPATH}/${MODID}

	${BOOTMODE} && boot_actions || recovery_actions
	${BOOTMODE} && RESTORE_PATH=${REALPATH} || RESTORE_PATH=${MODPATH}

	request_size_check "${INSTALLER}"
	mount_magisk_img

	case ${MODID} in
		NanoDroid )
			# check for sub-modules  which are not
			# supposed to be installed alongside
			for dir in NanoDroid_microG NanoDroid_FDroid; do
				if [ -d ${MOUNTPATH}/${dir} -o -d ${MAGISKPATH}/${dir} ]; then
					ui_print " !! ${dir} module detected - uninstalling!"
					rm -rf ${MOUNTPATH}/${dir} ${MAGISKPATH}/${dir}
					NANODROID_UPGRADE=1
				fi
			done
		;;

		NanoDroid_microG | NanoDroid_FDroid )
			# check for Full package and abort if found
			[ -d ${MOUNTPATH}/NanoDroid -o -d ${MAGISKPATH}/NanoDroid ] && \
				error "${MODID} can't be installed along side the Full package"
		;;

		* )
			error "unknown module intended to be installed!?"
		;;
	esac

	[ -d ${MOUNTPATH}/${MODID} -o -d ${MAGISKPATH}/${MODID} ] && NANODROID_UPGRADE=1
}

magisk_install_preinst () {
	if [ -f ${RESTORE_PATH}/system.prop ]; then
		ui_print " << backing up module's system.prop"
		cp ${RESTORE_PATH}/system.prop ${TMPDIR}/system.prop
	fi

	if [ -f ${RESTORE_PATH}/system/framework/services.jar ]; then
		cp ${RESTORE_PATH}/system/framework/services.jar ${TMPDIR}/services.jar
	fi

	${BOOTMODE} || rm -rf "${MODPATH}"

	for dir in app etc priv-app bin; do
		mkdir -p "${MODPATH}/system/${dir}"
	done
}

magisk_install_postinst () {
	if [ -f ${TMPDIR}/system.prop ]; then
		ui_print " << restoring system.prop"
		cp ${TMPDIR}/system.prop ${MODPATH}/system.prop
	fi

	if [ -f ${TMPDIR}/services.jar ]; then
		ui_print " << restoring patched services.jar"
		mkdir -p ${MODPATH}/system/framework
		cp ${TMPDIR}/services.jar ${MODPATH}/system/framework/services.jar
	fi
}

magisk_install_finish () {
	# Magisk Module files
	mktouch ${MODPATH}/auto_mount
	cp -af ${INSTALLER}/module.prop ${MODPATH}/module.prop

	if [[ -f ${INSTALLER}/common/service.sh ]]; then
		cp -af ${INSTALLER}/common/service.sh ${MODPATH}/service.sh
		chmod +x ${MODPATH}/service.sh
	fi

	if ${BOOTMODE}; then
		# Update info for Magisk Manager
		mktouch /sbin/.core/img/${MODID}/update
		cp -af ${INSTALLER}/module.prop /sbin/.core/img/${MODID}/module.prop
	fi

	case ${MODID} in
		NanoDroid | NanoDroid_microG )
			magisk_install_postinst
		;;
	esac

	if [ "${nanodroid_fonts}" -eq 1 -a ! -z ${CUSTOM_FONT} ]; then
		ui_print " << Restore NanoDroid-Font (${CUSTOM_FONT})"
		${NANODROID_BINDIR}/nanodroid-font -s "${CUSTOM_FONT}"
	fi

	unmount_magisk_img
	${BOOTMODE} || recovery_cleanup
}

##########################################################################################
# Generic Installation
##########################################################################################

install_microg () {
	# install microG if requested
	# respectively force if it's the microG package
	if [ "${nanodroid_microg}" -eq 1 -o "${MODID}" = "NanoDroid_microG" ]; then

		# first get rid of conflicting packages
		for app in ${GOOGLE_APPS}; do
			${NANODROID_BINDIR}/nanodroid-overlay --add ${app}

			rm -rf /data/dalvik-cache/*/system"@priv-app@${app}"[@\.]*@classes.*
			rm -rf /data/dalvik-cache/*/system"@app@${app}"[@\.]*@classes.*
		done

		for app in ${GOOGLE_DATA}; do
			rm -rf /data/data/${app}
			rm -rf /data/user/*/${app}
			rm -rf /data/user_de/*/${app}
			rm -rf /data/app/${app}-*
		done

		if [ "${NANODROID_UPGRADE}" -eq 0 ]; then
			# this removes original Play Services
			# only if this is a fresh NanoDroid installation
			rm -rf /data/dalvik-cache/*/system"@priv-app@GmsCore"[@\.]*@classes.*

			for app in com.google.android.gms \
					com.google.android.gsf \
					com.android.vending; do
				rm -rf /data/data/${app}
				rm -rf /data/user/*/${app}
				rm -rf /data/user_de/*/${app}
				rm -rf /data/app/${app}-*
			done

			for file in /data/system/users/*/runtime-permissions.xml; do
				if grep -q "com.google.android.gms" "${file}"; then
					rm -f "${file}"
					echo "  upgrade: removing ${file}"
				fi
			done
		fi

		case ${nanodroid_nlpbackend} in
			0 ) NLPBACKEND=""
			    ui_print " << with microG [Nominatim]" ;;
			1 ) NLPBACKEND="nlpBackendDejavu"
			    ui_print " << with microG [Déjà Vu, Nominatim]" ;;
			2 ) NLPBACKEND="nlpBackendIchnaea"
			    ui_print " << with microG [Ichnaea, Nominatim]" ;;
			3 ) NLPBACKEND="nlpBackendDejavu nlpBackendIchnaea"
			    ui_print " << with microG [Déjà Vu, Ichnaea, Nominatim]" ;;
		esac

		for app in ${NLPBACKEND} nlpBackendNominatim GmsCore GsfProxy DroidGuard; do
			nanodroid_install_apk ${app}
		done

		nanodroid_install_file etc/permissions/features.xml
		nanodroid_install_file etc/permissions/privapp-permissions-google.xml
		nanodroid_install_file etc/default-permissions/microg-permissions.xml
		nanodroid_install_file etc/sysconfig/microg-a5k.xml

	else	ui_print " << without microG"
	fi
}

install_mapsv1 () {
	# install mapsv1 if requested
	if [ "${nanodroid_mapsv1}" -eq 1 ]; then
		ui_print " << with Maps API version 1"
		nanodroid_install_file etc/permissions/com.google.android.maps.xml
		nanodroid_install_dir framework
	else	ui_print " << without Maps API version 1"
	fi
}

install_gsync () {
	# install GSync if requested
	if [ "${nanodroid_gsync}" -eq 1 ]; then
		if [ "${GSYNC_UNSUPPORTED}" -eq 1 ]; then
			ui_print " << Google Sync Adapters are unsupported on pre-KitKat ROMs"
		else
			GSYNC_BASE="${INSTALLER}/gsync/${GSYNC_VER}"
			ui_print " << with Google Sync Adapters (${GSYNC_VER})"

			for app in CalendarSync ContactsSync; do
				echo "    installing app: Google${app}"
				mkdir -p "${MODPATH}/system/app/Google${app}"

				cp "${GSYNC_BASE}/app/Google${app}/Google${app}.apk" \
					"${MODPATH}/system/app/Google${app}/"  || \
					error "failed to install Google${app}"

				set_perm_data -r "${MODPATH}/system/app/Google${app}"

				installinfo_add "/system/app/Google${app}/Google${app}.apk"
			done

			echo "    installing app: GoogleBackupTransport"
			mkdir -p "${MODPATH}/system/priv-app/GoogleBackupTransport"

			cp "${GSYNC_BASE}/priv-app/GoogleBackupTransport/GoogleBackupTransport.apk" \
				"${MODPATH}/system/priv-app/GoogleBackupTransport/" || \
				error "failed to install GoogleBackupTransport"

			set_perm_data -r "${MODPATH}/system/priv-app/GoogleBackupTransport"

			installinfo_add "/system/priv-app/GoogleBackupTransport/GoogleBackupTransport.apk"

			cp -r "${INSTALLER}/gsync/etc/permissions/" "${MODPATH}/system/etc/"
			set_perm_data -r "${MODPATH}/system/etc/permissions/"

			find "${INSTALLER}/gsync/etc/permissions/" -type f | \
					sed -e "s,${INSTALLER}/gsync/etc,/system/etc,g" | \
					installinfo_add
		fi
	else	ui_print " << without Google Sync Adapters"
	fi
}

install_fdroid () {
	# install F-Droid if requested
	# respectively force if it's the F-Droid package
	if [ "${nanodroid_fdroid}" -eq 1 -o "${MODID}" = "NanoDroid_FDroid" ]; then
		ui_print " << with F-Droid"
		nanodroid_install_apk FDroid
		nanodroid_install_apk FDroidPrivileged
		nanodroid_install_file etc/permissions/org.fdroid.fdroid.privileged.xml
	else	ui_print " << without F-Droid"
	fi
}

install_apps () {
	# install apps if requested
	if [ "${nanodroid_apps}" -eq 1 ]; then
		ui_print " << with applications"
		APP_STRING=" <<>"

		INSTALL_APPS=$(cat "${cfg_apps}")
		for app in ${INSTALL_APPS}; do
			[[ -d "${INSTALLER}/system/app/${app}" ]] && \
				APP_STRING="${APP_STRING} ${app}, "
		done

		ui_print "${APP_STRING}"

		for app in ${INSTALL_APPS}; do
			if [[ ${app} = Adaway && -d /system/app/AdAway ]]; then
				if [ "${nanodroid_override}" -eq 0 ]; then
					ui_print " <<> app: Adaway already provided by ROM (as AdAway)"
				else	nanodroid_install_apk Adaway
					echo "   + removing conflicting AdAway"
					${NANODROID_BINDIR}/nanodroid-overlay --add AdAway
				fi
			elif [[ ${app} = OmniClock && -d /system/app/OmniClockOSS ]]; then
				if [ "${nanodroid_override}" -eq 0 ]; then
					ui_print " <<> app: OmniClock already provided by ROM (as OmniClockOSS)"
				else
					nanodroid_install_apk OmniClock
					echo "   + removing conflicting OmniClockOSS"
					${NANODROID_BINDIR}/nanodroid-overlay --add OmniClockOSS
				fi
			elif [[ -d /system/app/${app} && ! -d ${RESTORE_PATH}/system/app/${app} ]]; then
				if [ "${nanodroid_override}" -eq 0 ]; then
					ui_print " <<> app: ${app} already provided by ROM"
				else	nanodroid_install_apk ${app}
				fi
			elif [[ -d "${INSTALLER}/system/app/${app}" ]]; then
				nanodroid_install_apk ${app}
			else	ui_print " <<>> app: ${app} missing"
			fi
		done
	else	ui_print " << without applications"
	fi
}

install_store () {
	NANODROID_APP_STORE=""
	NANODROID_REM_STORE=""

	# install Yalp Store or Play Store whichever requested
	case "${nanodroid_play}" in
		0 )
			ui_print " << without App Store"
		;;

		1 )
			ui_print " << with Play Store"
			NANODROID_APP_STORE="Phonesky"
			NANODROID_REM_STORE="FakeStore"
		;;

		2 )
			ui_print " << with Yalp Store"
			NANODROID_APP_STORE="YalpStore"
			NANODROID_REM_STORE=""
		;;

		3 )
			ui_print " << with Fake Store"
			NANODROID_APP_STORE="FakeStore"
			NANODROID_REM_STORE="Phonesky"
		;;

		4 )
			ui_print " << with Yalp Store and Fake Store"
			NANODROID_APP_STORE="YalpStore FakeStore"
			NANODROID_REM_STORE="Phonesky"
		;;
	esac

	if [ ! -z "${NANODROID_APP_STORE}" ]; then
		for app in ${NANODROID_APP_STORE}; do
			nanodroid_install_apk ${app}
		done
	fi

	if [ ! -z "${NANODROID_REM_STORE}" ]; then
		for app in ${NANODROID_REM_STORE}; do
			${NANODROID_BINDIR}/nanodroid-overlay --add ${app}
		done

		if [ "${NANODROID_UPGRADE}" -eq 0 ]; then
			rm -rf /data/data/com.android.vending
			rm -rf /data/user/*/com.android.vending
			rm -rf /data/user_de/*/com.android.vending
			rm -rf /data/app/com.android.vending-*
		fi
	fi
}

install_zelda () {
	# install Zelda Ringtones when requested
	if [ "${nanodroid_zelda}" -eq 1 ]; then
		ui_print " << with Zelda sounds"
		nanodroid_install_dir media
	else	ui_print " << without Zelda sounds"
	fi
}

install_bash () {
	if [ "${nanodroid_bash}" -eq 1 ]; then
		ui_print " << with GNU Bash Shell"
		nanodroid_install_file etc/bash_logout
		nanodroid_install_file etc/bashrc

		nanodroid_install_file xbin/bash bin
		nanodroid_install_file xbin/bash.bin.${BIN_ARCH} bin
		nanodroid_install_file xbin/bashbug.${BIN_ARCH} bin
		nanodroid_install_file xbin/less bin
		nanodroid_install_file xbin/less.bin.${BIN_ARCH} bin
	else	ui_print " << without GNU Bash Shell"
	fi
}

install_nanodroidscripts () {
	ui_print " << with NanoDroid Scripts"

	for script in npem nupd nutl novl \
		nanodroid-perm nanodroid-upd \
		nanodroid-util nanodroid-overlay; do
		nanodroid_install_file xbin/${script} bin
	done

	nanodroid_substitute xbin/nanodroid-overlay

	if [ "${MODE}" = "MAGISK" ]; then
		for script in nprp nanodroid-prop; do
			nanodroid_install_file xbin/${script} bin
		done

		nanodroid_substitute xbin/nanodroid-prop
	fi
}

install_fonts () {
	if [ "${nanodroid_fonts}" -eq 1 ]; then
		ui_print " << with Nintendo Fonts"
		nanodroid_install_file xbin/nanodroid-font bin
		nanodroid_substitute xbin/nanodroid-font
		nanodroid_install_file xbin/nfnt bin

		nanodroid_install_dir fonts
	fi
}

install_nano () {
	if [ "${nanodroid_nano}" -eq 1 ]; then
		ui_print " << with GNU Nano Terminal Editor"
		nanodroid_install_dir etc/terminfo

		nanodroid_install_file xbin/nano bin
		nanodroid_install_file xbin/nano.bin bin
	else	ui_print " << without GNU Nano Terminal Editor"
	fi
}

install_shell_utils () {
	if [ "${nanodroid_utils}" -eq 1 ]; then
		ui_print " << with Shell Utilities"

		# architecture dependend stuff
		for binary in col colcrt colrm column findfs findmnt \
			hexdump lessecho lesskey look lsblk lscpu lsipc \
			lslocks lsns ncal setterm unzip whereis; do
			nanodroid_install_file xbin/${binary}.${BIN_ARCH} bin
		done
	else	ui_print " << without Shell Utilities"
	fi
}

install_initd () {
	if [ "${nanodroid_init}" -eq 1 ]; then
		ui_print " << with init scripts"

		if [ "${MODE}" = "SYSTEM" ]; then
			INIT_BASE="${MODPATH}/system/etc/init.d"
		else	INIT_BASE="${MODPATH}/init.d"
		fi

		mkdir -p "${INIT_BASE}"
		cp "${INSTALLER}/system/etc/init.d"/* "${INIT_BASE}/"
		set_perm_bin "${INIT_BASE}"

		for init in fstrim logcat logscleaner sqlite external_sd; do
			installinfo_add "/system/etc/init.d/${init}"
		done
	else	ui_print " << without init scripts"
	fi
}

install_swipe () {
	if [ "${nanodroid_swipe}" -eq 1 ]; then
		ui_print " << with swipe libraries"

		SWIPE_SRC="${INSTALLER}/swipe/${ARCH}"
		SWIPE_DEST="/system/${SWIPE_LIBDIR}"

		mkdir -p "${MODPATH}${SWIPE_DEST}"

		for lib in latinimegoogle keyboarddecoder; do
			cp "${SWIPE_SRC}/libjni_${lib}.so" "${MODPATH}${SWIPE_DEST}" || \
				error "failed to install libjni_${lib}.so"
			set_perm_data "${MODPATH}${SWIPE_DEST}/libjni_${lib}.so"

			installinfo_add "${SWIPE_DEST}/libjni_${lib}.so"
		done
	else	ui_print " << without swipe libraries"
	fi
}

install_addond () {
	if [ -d /system/addon.d ]; then
		ui_print " << System Mode OTA survival"

		cp "${INSTALLER}/CommonAddon" "/system/addon.d/91-nanodroid.sh" || \
			error "failed to install System Mode OTA survival"

		chmod 0755 "/system/addon.d/91-nanodroid.sh"
		sed -e "s|@MODID@|${MODID}|g" -i "/system/addon.d/91-nanodroid.sh"
	else
		ui_print " << ROM does not have addon.d support"
	fi
}

##########################################################################################
# Advanced Installation Functions
##########################################################################################

nanodroid_install_dir () {
	dir="${1}"
	dest="/system/${dir}"

	[ ! -d "${INSTALLER}/system/${dir}" ] && \
		error "dir: ${dir} not found"

	echo "    installing dir: ${dir}"

	mkdir -p "${MODPATH}${dest}"
	cp -r "${INSTALLER}/system/${dir}"/* "${MODPATH}${dest}/" || \
		error "failed to install ${dir}"

	case ${2} in
		bin ) set_perm_bin -r "${MODPATH}${dest}" ;;
		*   ) set_perm_data -r "${MODPATH}${dest}" ;;
	esac

	find "${INSTALLER}/system/${dir}" -type f | \
			sed -e "s,^${INSTALLER},,g" | \
			installinfo_add
}

nanodroid_install_apk () {
	app=${1}
	unset prefix

	if [ -f "${INSTALLER}/system/app/${app}/${app}.apk" ]; then
		dir=system/app
	elif [ -f "${INSTALLER}/system/priv-app/${app}/${app}.apk" ]; then
		dir=system/priv-app
	else
		error "app ${app} not found"
	fi

	source="${INSTALLER}/${dir}/${app}/${app}.apk"

	if [ "${UNFOLD_APP_DIR}" -eq 1 ]; then
		dest="${dir}"
		mkdir -p "${MODPATH}/${dir}"
	else
		dest="${dir}/${app}"
		mkdir -p "${MODPATH}/${dir}/${app}"
	fi

	echo "    installing app: ${app}"
	cp "${source}" "${MODPATH}/${dest}" || \
		error "failed to install ${source} to ${dest}"

	set_perm_data -r "${MODPATH}/${dest}"

	installinfo_add "/${dest}/${app}.apk"

	nanodroid_install_lib
}

nanodroid_install_file () {
	dir="$(dirname "${1}")"
	file="$(basename "${1}")"

	[ ! -f "${INSTALLER}/system/${dir}/${file}" ] && \
		error "file: ${file} not found"

	# if /system/xbin does not exist, use /system/bin
	[ "${dir}" = "xbin" -a "${ROM_NO_XBIN}" -eq 1 ] && xdir="bin" || xdir="${dir}"

	# strip off .${BIN_ARCH} from final filename (if any)
	xfile=$(basename ${file} .${BIN_ARCH})

	echo "    installing file: ${xfile}"

	mkdir -p "${MODPATH}/system/${xdir}/"
	cp "${INSTALLER}/system/${dir}/${file}" \
		"${MODPATH}/system/${xdir}/${xfile}" || \
		error "failed to install ${xfile}"

	case ${2} in
		bin ) set_perm_bin "${MODPATH}/system/${xdir}/${xfile}" ;;
		*   ) set_perm_data "${MODPATH}/system/${xdir}/${xfile}" ;;
	esac

	installinfo_add "/system/${xdir}/${xfile}"
}

nanodroid_install_lib () {
	apk="${MODPATH}/${dest}/${app}.apk"

	if [ "${UNFOLD_APP_DIR}" -eq 1 ]; then
		case ${ARCH} in
			arm | x86 )      libd=${MODPATH}/system/lib   ;;
			arm64 | x86_64 ) libd=${MODPATH}/system/lib64 ;;
		esac
	else
		libd=${MODPATH}/${dir}/${app}/lib
	fi

	rm -rf ${TMP_LIBDIR}
	mkdir -p ${TMP_LIBDIR}

	unset srcdir

	"${INSTALLER}/system/xbin/unzip.${BIN_ARCH}" \
		-oq ${apk} "lib/*" -d "${TMP_LIBDIR}" \
		2>/dev/null
	unzip_return=$?

	case ${unzip_return} in
		0  ) echo "      ${app} does have libs" ;;
		11 ) echo "      ${app} does not have any libs" ;;
		*  ) error "  !! installing libs for ${app} failed" ;;
	esac

	if [ -d ${TMP_LIBDIR}/lib ]; then
		for ARCHLIB in ${LIB_ARCHES}; do
			if [ -d ${TMP_LIBDIR}/lib/${ARCHLIB} ]; then
				srcdir=${TMP_LIBDIR}/lib/${ARCHLIB}
				break
			fi
		done

		if [ -d "${srcdir}" ]; then
			case ${srcdir} in
				*arm64*  ) echo "      ${app}: installing arm64 libs"
					   lib_arch=arm64 ;;
				*arm*	 ) echo "      ${app}: installing arm libs"
					   lib_arch=arm ;;
				*x86_64* ) echo "      ${app}: installing x86_64 libs"
					   lib_arch=x86_64 ;;
				*x86*	 ) echo "      ${app}: installing x86 libs"
					   lib_arch=x86 ;;
			esac

			echo "        from: ${srcdir}"
			echo "        into: ${libd}/${lib_arch}"
			mkdir -p ${libd}/${lib_arch}

			for lib in ${srcdir}/*; do
				echo "        library: $(basename ${lib})"
				cp ${lib} ${libd}/${lib_arch}/ || \
					error "installing libraries failed"

				set_perm_data ${libd}/${lib_arch}/$(basename ${lib})

				installinfo_add /${dir}/${app}/lib/${lib_arch}/$(basename ${lib})
			done
		fi
	fi
}

nanodroid_substitute () {
	dir="$(dirname "${1}")"
	file="$(basename "${1}")"

	# if /system/xbin does not exist, use /system/bin
	[ "${dir}" = "xbin" -a "${ROM_NO_XBIN}" -eq 1 ] && dir="bin"

	target="${MODPATH}/system/${dir}/${file}"

	[ ! -f "${target}" ] && error "file: ${file} not found"

	if grep -q @ZIPDIR@ "${target}"; then
		if [[ "${ZIPDIR}" == "/external_sd" ]]; then
			# /external_sd is already known by `nanodroid-overlay`
			# no need to add it a second time
			sed -e 's|@ZIPDIR@||' -i ${target}
			echo "    substitute: remove @ZIPDIR@ in ${target}"
		else
			sed -e "s|@ZIPDIR@|${ZIPDIR}|g" -i ${target}
			echo "    substitute: adjust @ZIPDIR@ in ${target}"
		fi
	fi

	if grep -q @MODPATH@ ${target}; then
		sed -e "s|@MODPATH@|${REALPATH}|g" -i ${target}
		echo "    substitute: adjust @MODPATH@ in ${target}"
	fi
}

nanodroid_storelogs () {

	curdate=$(date +%Y%m%d_%H.%M.%S)
	nanodroid_logfile="/data/adb/${MODID}_log_${curdate}"
	nanodroid_twrplog="/data/adb/${MODID}_twrp_${curdate}"

	echo "\
### VALUES ###
ZIP=${ZIP}
ZIPDIR=${ZIPDIR}
TMPDIR=${TMPDIR}
TMP_LIBDIR=${TMP_LIBDIR}
INSTALLER=${INSTALLER}
MODID=${MODID}
SDK_VERSION=${SDK_VERSION}
ARCH=${ARCH}
LIB_ARCHES=${LIB_ARCHES}
MODPATH=${MODPATH}
SWIPE_LIBDIR=${SWIPE_LIBDIR}
REALPATH=${REALPATH}
MODE=${MODE}
MAGISK_VER_CODE=${MAGISK_VER_CODE}
CUSTOM_FONT=${CUSTOM_FONT}
ROM_NO_XBIN=${ROM_NO_XBIN}
RESTORE_PATH=${RESTORE_PATH}
NANODROID_UPGRADE=${NANODROID_UPGRADE}
NANODROID_BINDIR=${NANODROID_BINDIR}
CUSTOM_ROM=${CUSTOM_ROM}

### SETUP ###
" > ${nanodroid_logfile}

cat "${cfg_setup}" >> ${nanodroid_logfile}

if [ ! -z "${cfg_apps}" ]; then

echo "
### APPS ###
" >> ${nanodroid_logfile}

cat "${cfg_apps}" >> ${nanodroid_logfile}

fi

if [ ! -z "${cfg_overlay}" ]; then

echo "
### OVERLAY ###
" >> ${nanodroid_logfile}

cat "${cfg_overlay}" >> ${nanodroid_logfile}

fi

${BOOTMODE} || cat /tmp/recovery.log > ${nanodroid_twrplog}

}

##########################################################################################
# NanoDroid Configuration File Handling
##########################################################################################

# check for configuration files
config_locations="/sdcard /external_sd ${ZIPDIR} /data"

get_config () {
	config=""
	config_exists=0
	for path in ${config_locations}; do
		if test -r "${path}/.nanodroid-${1}"; then
			config="${path}/.nanodroid-${1}"
			config_exists=1
			return
		fi
	done
}

set_prop () {
	echo "${1}=${2}" >> ${cfg_setup}
}

# check whether '.nanodroid-setup' has all required entries
# update format if required
check_cfg_setup () {
	source ${cfg_setup}

	[ -z ${nanodroid_fdroid} ] && set_prop nanodroid_fdroid 1
	[ -z ${nanodroid_microg} ] && set_prop nanodroid_microg 1
	[ -z ${nanodroid_apps} ] && set_prop nanodroid_apps 1
	[ -z ${nanodroid_play} ] && set_prop nanodroid_play 1

	if [ "${MODID}" = "NanoDroid" ]; then
		[ -z ${nanodroid_overlay} ] && set_prop nanodroid_overlay 1
	else	[ -z ${nanodroid_overlay} ] && set_prop nanodroid_overlay 0
	fi

	[ -z ${nanodroid_zelda} ] && set_prop nanodroid_zelda 1
	[ -z ${nanodroid_mapsv1} ] && set_prop nanodroid_mapsv1 1
	[ -z ${nanodroid_init} ] && set_prop nanodroid_init 1
	[ -z ${nanodroid_gsync} ] && set_prop nanodroid_gsync 0
	[ -z ${nanodroid_forcesystem} ] && set_prop nanodroid_forcesystem 0
	[ -z ${nanodroid_swipe} ] && set_prop nanodroid_swipe 0
	[ -z ${nanodroid_nlpbackend} ] && set_prop nanodroid_nlpbackend 1
	[ -z ${nanodroid_bash} ] && set_prop nanodroid_bash 1
	[ -z ${nanodroid_nano} ] && set_prop nanodroid_nano 1
	[ -z ${nanodroid_utils} ] && set_prop nanodroid_utils 1
	[ -z ${nanodroid_fonts} ] && set_prop nanodroid_fonts 1
	[ -z ${nanodroid_override} ] && set_prop nanodroid_override 0

	source ${cfg_setup}
}

# check whether '.nanodroid-apps' has new format
# and update if required
check_cfg_apps () {
	# <= 16.3 has KeePass DX instead of KeePassDroid, migrate
	if grep -q KeePassDroid "${cfg_apps}"; then
		sed -e 's/KeePassDroid/KeePassDX/g' -i "${cfg_apps}"
	fi

	# <= 17.0~beta1 && <= 17.2~beta1 has Bucket (LibreSubtratum) instead of Substratum, migrate
	if grep -q Bucket "${cfg_apps}"; then
		sed -e 's/Bucket/Substratum/g' -i "${cfg_apps}"
	fi
}

# check whether '.nanodroid-overlay' has new format
# and update if required
check_cfg_overlay () {
	return 0
}

get_cfg_setup () {
	get_config setup
	if [ "$config_exists" -eq 1 ]; then
		cfg_setup="${config}"
		check_cfg_setup
		ui_print " > Config | nanodroid-setup   | $(dirname ${cfg_setup})"
	else
		cfg_setup="${INSTALLER}/.nanodroid-setup"
		source "${cfg_setup}"
		ui_print " > Config | nanodroid-setup   | fallback"
	fi
}

get_cfg_overlay () {
	get_config overlay
	if [ "$config_exists" -eq 1 ]; then
		cfg_overlay="${config}"
		check_cfg_overlay
		ui_print " > Config | nanodroid-overlay | $(dirname ${cfg_overlay})"
	else
		cfg_overlay="${INSTALLER}/.nanodroid-overlay"
		ui_print " > Config | nanodroid-overlay | fallback"
	fi
}

get_cfg_apps () {
	get_config apps
	if [ "$config_exists" -eq 1 ]; then
		cfg_apps="${config}"
		check_cfg_apps
		ui_print " > Config | nanodroid-apps    | $(dirname ${cfg_apps})"
	else
		cfg_apps="${INSTALLER}/.nanodroid-apps"
		ui_print " > Config | nanodroid-apps    | fallback"
	fi
}
