diff --git a/app/src/main/assets/magisk_uninstaller.sh b/app/src/main/assets/magisk_uninstaller.sh new file mode 100644 index 000000000..5eec40851 --- /dev/null +++ b/app/src/main/assets/magisk_uninstaller.sh @@ -0,0 +1,149 @@ +#!/system/bin/sh + +TMPDIR=/tmp +($BOOTMODE) && TMPDIR=/dev/tmp + +BINDIR=/data/magisk +CHROMEDIR=$BINDIR/chromeos + +NEWBOOT=$TMPDIR/boottmp/new-boot.img +UNPACKDIR=$TMPDIR/boottmp/bootunpack +RAMDISK=$TMPDIR/boottmp/ramdisk + +SYSTEMLIB=/system/lib +[ -d /system/lib64 ] && SYSTEMLIB=/system/lib64 + +ui_print() { + echo "$1" +} + +grep_prop() { + REGEX="s/^$1=//p" + shift + FILES=$@ + if [ -z "$FILES" ]; then + FILES='/system/build.prop' + fi + cat $FILES 2>/dev/null | sed -n $REGEX | head -n 1 +} + +find_boot_image() { + if [ -z "$BOOTIMAGE" ]; then + for PARTITION in kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do + BOOTIMAGE=`readlink /dev/block/by-name/$PARTITION || readlink /dev/block/platform/*/by-name/$PARTITION || readlink /dev/block/platform/*/*/by-name/$PARTITION` + if [ ! -z "$BOOTIMAGE" ]; then break; fi + done + fi + if [ -z "$BOOTIMAGE" ]; then + FSTAB="/etc/recovery.fstab" + [ ! -f "$FSTAB" ] && FSTAB="/etc/recovery.fstab.bak" + [ -f "$FSTAB" ] && BOOTIMAGE=`grep -E '\b/boot\b' "$FSTAB" | grep -oE '/dev/[a-zA-Z0-9_./-]*'` + fi +} + +unpack_boot() { + rm -rf $UNPACKDIR $RAMDISK 2>/dev/null + mkdir -p $UNPACKDIR + mkdir -p $RAMDISK + cd $UNPACKDIR + LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/bootimgtools --extract $1 + + cd $RAMDISK + $BINDIR/busybox gunzip -c < $UNPACKDIR/ramdisk.gz | cpio -i +} + +repack_boot() { + cd $RAMDISK + find . | cpio -o -H newc 2>/dev/null | gzip -9 > $UNPACKDIR/ramdisk.gz + cd $UNPACKDIR + LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/bootimgtools --repack $BOOTIMAGE + if [ -f chromeos ]; then + echo " " > config + echo " " > bootloader + LD_LIBRARY_PATH=$SYSTEMLIB $CHROMEDIR/futility vbutil_kernel --pack new-boot.img.signed --keyblock $CHROMEDIR/kernel.keyblock --signprivate $CHROMEDIR/kernel_data_key.vbprivk --version 1 --vmlinuz new-boot.img --config config --arch arm --bootloader bootloader --flags 0x1 + rm -f new-boot.img + mv new-boot.img.signed new-boot.img + fi + if ($SAMSUNG); then + SAMSUNG_CHECK=$(cat new-boot.img | grep SEANDROIDENFORCE) + if [ $? -ne 0 ]; then + echo -n "SEANDROIDENFORCE" >> new-boot.img + fi + fi + if ($LGE_G); then + # Prevent secure boot error on LG G2/G3. + # Just for know, It's a pattern which bootloader verifies at boot. Thanks to LG hackers. + echo -n -e "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79" >> new-boot.img + fi + mv new-boot.img $NEWBOOT +} + +# Set permissions +chmod -R 755 $CHROMEDIR/futility $BINDIR + +# Find the boot image +find_boot_image +if [ -z "$BOOTIMAGE" ]; then + ui_print "! Unable to detect boot image" + exit 1 +fi + +ui_print "- Found Boot Image: $BOOTIMAGE" + +# Detect special vendors +SAMSUNG=false +SAMSUNG_CHECK=$(cat /system/build.prop | grep "ro.build.fingerprint=" | grep -i "samsung") +if [ $? -eq 0 ]; then + SAMSUNG=true +fi +LGE_G=false +RBRAND=$(grep_prop ro.product.brand) +RMODEL=$(grep_prop ro.product.device) +if [ "$RBRAND" = "lge" ] || [ "$RBRAND" = "LGE" ]; then + if [ "$RMODEL" = "*D80*" ] || + [ "$RMODEL" = "*S98*" ] || + [ "$RMODEL" = "*D85*" ] || + [ "$RMODEL" = "*F40*" ]; then + LGE_G=true + ui_print "! Bump device detected" + fi +fi + +# First unpack the boot image +unpack_boot $BOOTIMAGE + +SUPERSU=false +[ -f sbin/launch_daemonsu.sh ] && SUPERSU=true + +if ($SUPERSU); then + ui_print "- SuperSU patched image detected" + rm -f magisk sbin/init.magisk.rc sbin/magic_mask.sh + repack_boot +else + if [ -f /data/stock_boot.img ]; then + ui_print "- Boot image backup found!" + NEWBOOT=/data/stock_boot.img + else + ui_print "! Boot image backup unavailable" + if [ -d ".backup" ]; then + ui_print "- Restoring ramdisk with backup" + cp -af .backup/. . + fi + rm -f magisk sbin/init.magisk.rc sbin/magic_mask.sh + repack_boot + fi +fi + +chmod 644 $NEWBOOT + +ui_print "- Flashing stock/reverted image" +[ ! -L "$BOOTIMAGE" ] && dd if=/dev/zero of=$BOOTIMAGE bs=4096 2>/dev/null +dd if=$NEWBOOT of=$BOOTIMAGE bs=4096 + +ui_print "- Removing Magisk files" +rm -rf /cache/magisk.log /cache/last_magisk.log /cache/magiskhide.log /cache/.disable_magisk \ + /cache/magisk /cache/magisk_merge /cache/magisk_mount /cache/unblock /cache/magisk_uninstaller.sh \ + /data/Magisk.apk /data/magisk.apk /data/magisk.img /data/magisk_merge.img \ + /data/busybox /data/magisk /data/custom_ramdisk_patch.sh 2>/dev/null + +($BOOTMODE) && reboot diff --git a/app/src/main/java/com/topjohnwu/magisk/InstallFragment.java b/app/src/main/java/com/topjohnwu/magisk/InstallFragment.java index aedc39ec8..cd0cf51c0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/InstallFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/InstallFragment.java @@ -1,8 +1,10 @@ package com.topjohnwu.magisk; +import android.app.ProgressDialog; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.CountDownTimer; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.CardView; @@ -17,8 +19,13 @@ import android.widget.TextView; import com.topjohnwu.magisk.receivers.MagiskDlReceiver; import com.topjohnwu.magisk.utils.CallbackHandler; +import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Utils; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -28,12 +35,16 @@ import butterknife.Unbinder; public class InstallFragment extends Fragment implements CallbackHandler.EventListener { + + private static final String UNINSTALLER = "magisk_uninstaller.sh"; + private Unbinder unbinder; @BindView(R.id.current_version_title) TextView currentVersionTitle; @BindView(R.id.install_title) TextView installTitle; @BindView(R.id.block_spinner) Spinner spinner; @BindView(R.id.detect_bootimage) Button detectButton; @BindView(R.id.flash_button) CardView flashButton; + @BindView(R.id.uninstall_button) CardView uninstallButton; @BindView(R.id.keep_force_enc) CheckBox keepEncChkbox; @BindView(R.id.keep_verity) CheckBox keepVerityChkbox; @@ -56,14 +67,13 @@ public class InstallFragment extends Fragment implements CallbackHandler.EventLi bootImage = Global.Data.blockList.get(spinner.getSelectedItemPosition()); } String filename = "Magisk-v" + Global.Info.remoteMagiskVersion + ".zip"; - String finalBootImage = bootImage; Utils.getAlertDialogBuilder(getActivity()) .setTitle(getString(R.string.repo_install_title, getString(R.string.magisk))) .setMessage(getString(R.string.repo_install_msg, filename)) .setCancelable(true) .setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive( getActivity(), - new MagiskDlReceiver(finalBootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()), + new MagiskDlReceiver(bootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()), Global.Info.magiskLink, Utils.getLegalFilename(filename))) .setNeutralButton(R.string.check_release_notes, (dialog, which) -> { @@ -72,6 +82,44 @@ public class InstallFragment extends Fragment implements CallbackHandler.EventLi .setNegativeButton(R.string.no_thanks, null) .show(); }); + uninstallButton.setOnClickListener(vi -> { + Utils.getAlertDialogBuilder(getActivity()) + .setTitle("Uninstall Magisk") + .setMessage("This will remove all modules, MagiskSU, and potentially re-encrypt your device\nAre you sure to process?") + .setPositiveButton(R.string.yes, (dialogInterface, i) -> { + try { + InputStream in = getActivity().getAssets().open(UNINSTALLER); + File uninstaller = new File(getActivity().getCacheDir().getAbsolutePath() + "/" + UNINSTALLER); + FileOutputStream out = new FileOutputStream(uninstaller); + byte[] bytes = new byte[1024]; + int read; + while ((read = in.read(bytes)) != -1) + out.write(bytes, 0, read); + in.close(); + out.close(); + ProgressDialog progress = new ProgressDialog(getActivity()); + progress.setTitle(R.string.reboot); + progress.show(); + new CountDownTimer(5000, 1000) { + @Override + public void onTick(long millisUntilFinished) { + progress.setMessage(getString(R.string.reboot_countdown, millisUntilFinished / 1000)); + } + + @Override + public void onFinish() { + progress.setMessage(getString(R.string.reboot_countdown, 0)); + Shell.su(true, "cp -af " + uninstaller + " /cache/" + UNINSTALLER, + "reboot"); + } + }.start(); + } catch (IOException e) { + e.printStackTrace(); + } + }) + .setNegativeButton(R.string.no_thanks, null) + .show(); + }); if (Global.Events.blockDetectionDone.isTriggered) { updateUI(); } diff --git a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java index f0e2a043c..ddb79b038 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java @@ -1,6 +1,5 @@ package com.topjohnwu.magisk; -import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.ListPreference; diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Async.java b/app/src/main/java/com/topjohnwu/magisk/utils/Async.java index 4dafd1df7..33f6507c8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Async.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Async.java @@ -284,7 +284,7 @@ public class Async { Utils.getAlertDialogBuilder(mContext) .setTitle(R.string.reboot_title) .setMessage(R.string.reboot_msg) - .setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.sh("su -c reboot")) + .setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.su(true, "reboot")) .setNegativeButton(R.string.no_thanks, null) .show(); } diff --git a/app/src/main/res/layout/fragment_install.xml b/app/src/main/res/layout/fragment_install.xml index e2a930b64..685c0e044 100644 --- a/app/src/main/res/layout/fragment_install.xml +++ b/app/src/main/res/layout/fragment_install.xml @@ -213,6 +213,35 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 390763f78..265250cbd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -46,6 +46,8 @@ Installed Magisk Version: v%1$s Latest Magisk Version: v%1$.1f Magiskify + Uninstall + Rebooting in %1$d (No info provided)