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)