2017-08-31 03:07:33 +08:00
|
|
|
package com.topjohnwu.magisk.asyncs;
|
|
|
|
|
|
|
|
|
|
import android.app.Activity;
|
2018-05-13 18:14:10 +08:00
|
|
|
import android.app.ProgressDialog;
|
2017-08-31 03:07:33 +08:00
|
|
|
import android.net.Uri;
|
|
|
|
|
import android.os.Build;
|
|
|
|
|
import android.text.TextUtils;
|
2017-11-18 03:55:47 +08:00
|
|
|
import android.view.View;
|
2018-05-13 18:14:10 +08:00
|
|
|
import android.widget.Toast;
|
2017-08-31 03:07:33 +08:00
|
|
|
|
2017-11-18 03:55:47 +08:00
|
|
|
import com.topjohnwu.magisk.FlashActivity;
|
2017-08-31 03:07:33 +08:00
|
|
|
import com.topjohnwu.magisk.MagiskManager;
|
2018-05-13 18:14:10 +08:00
|
|
|
import com.topjohnwu.magisk.R;
|
2017-09-30 03:04:23 +08:00
|
|
|
import com.topjohnwu.magisk.container.TarEntry;
|
2017-11-06 04:41:23 +08:00
|
|
|
import com.topjohnwu.magisk.utils.Const;
|
2017-09-04 01:57:45 +08:00
|
|
|
import com.topjohnwu.magisk.utils.Utils;
|
2017-08-31 03:07:33 +08:00
|
|
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
2018-01-21 06:07:24 +08:00
|
|
|
import com.topjohnwu.superuser.Shell;
|
2018-02-12 23:07:35 +08:00
|
|
|
import com.topjohnwu.superuser.ShellUtils;
|
|
|
|
|
import com.topjohnwu.superuser.io.SuFileInputStream;
|
2018-01-31 04:00:11 +08:00
|
|
|
import com.topjohnwu.utils.SignBoot;
|
2017-08-31 03:07:33 +08:00
|
|
|
|
2017-09-04 01:57:45 +08:00
|
|
|
import org.kamranzafar.jtar.TarInputStream;
|
2017-09-03 17:24:05 +08:00
|
|
|
import org.kamranzafar.jtar.TarOutputStream;
|
|
|
|
|
|
2017-08-31 03:07:33 +08:00
|
|
|
import java.io.BufferedInputStream;
|
2017-09-03 17:24:05 +08:00
|
|
|
import java.io.BufferedOutputStream;
|
2017-08-31 03:07:33 +08:00
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
import java.io.OutputStream;
|
2018-05-13 18:14:10 +08:00
|
|
|
import java.util.AbstractList;
|
2017-08-31 03:07:33 +08:00
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
2017-09-03 00:10:14 +08:00
|
|
|
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|
|
|
|
|
|
|
|
|
private static final int PATCH_MODE = 0;
|
2018-06-26 00:29:01 +08:00
|
|
|
public static final int DIRECT_MODE = 1;
|
2018-05-13 18:14:10 +08:00
|
|
|
private static final int FIX_ENV_MODE = 2;
|
2018-06-26 00:29:01 +08:00
|
|
|
public static final int SECOND_SLOT_MODE = 3;
|
2017-08-31 03:07:33 +08:00
|
|
|
|
2018-06-25 19:46:41 +08:00
|
|
|
private Uri bootUri, mZip;
|
2017-11-18 03:55:47 +08:00
|
|
|
private List<String> console, logs;
|
2018-06-25 19:46:41 +08:00
|
|
|
private String mBoot;
|
2017-09-03 00:10:14 +08:00
|
|
|
private int mode;
|
2018-06-25 19:46:41 +08:00
|
|
|
private File installDir;
|
2018-05-13 18:14:10 +08:00
|
|
|
private ProgressDialog dialog;
|
2018-06-25 19:46:41 +08:00
|
|
|
private MagiskManager mm;
|
2017-08-31 03:07:33 +08:00
|
|
|
|
2018-05-13 18:14:10 +08:00
|
|
|
public InstallMagisk(Activity context, Uri zip) {
|
2017-08-31 03:07:33 +08:00
|
|
|
super(context);
|
2018-05-13 18:14:10 +08:00
|
|
|
mZip = zip;
|
2018-06-25 19:46:41 +08:00
|
|
|
mm = MagiskManager.get();
|
2018-05-13 18:14:10 +08:00
|
|
|
mode = FIX_ENV_MODE;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-26 00:29:01 +08:00
|
|
|
public InstallMagisk(Activity context, List<String> console, List<String> logs, Uri zip, int mode) {
|
2018-05-13 18:14:10 +08:00
|
|
|
this(context, zip);
|
2017-11-18 03:55:47 +08:00
|
|
|
this.console = console;
|
|
|
|
|
this.logs = logs;
|
2018-06-26 00:29:01 +08:00
|
|
|
this.mode = mode;
|
2017-09-03 00:10:14 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-13 18:14:10 +08:00
|
|
|
public InstallMagisk(FlashActivity context, List<String> console, List<String> logs, Uri zip, Uri boot) {
|
2018-06-26 00:29:01 +08:00
|
|
|
this(context, console, logs, zip, PATCH_MODE);
|
2018-06-25 19:46:41 +08:00
|
|
|
bootUri = boot;
|
2017-08-31 03:07:33 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-13 18:14:10 +08:00
|
|
|
@Override
|
|
|
|
|
protected void onPreExecute() {
|
|
|
|
|
if (mode == FIX_ENV_MODE) {
|
2018-06-27 05:58:56 +08:00
|
|
|
Activity a = getActivity();
|
|
|
|
|
dialog = ProgressDialog.show(a, a.getString(R.string.setup_title), a.getString(R.string.setup_msg));
|
2018-05-13 18:14:10 +08:00
|
|
|
console = new NOPList<>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void extractFiles(String arch) throws IOException {
|
|
|
|
|
console.add("- Extracting files");
|
|
|
|
|
try (InputStream in = mm.getContentResolver().openInputStream(mZip)) {
|
|
|
|
|
if (in == null) throw new FileNotFoundException();
|
|
|
|
|
BufferedInputStream buf = new BufferedInputStream(in);
|
|
|
|
|
buf.mark(Integer.MAX_VALUE);
|
2018-06-25 19:46:41 +08:00
|
|
|
ZipUtils.unzip(buf, installDir, arch + "/", true);
|
2018-05-13 18:14:10 +08:00
|
|
|
buf.reset();
|
2018-06-25 19:46:41 +08:00
|
|
|
ZipUtils.unzip(buf, installDir, "common/", true);
|
2018-05-13 18:14:10 +08:00
|
|
|
buf.reset();
|
2018-06-25 19:46:41 +08:00
|
|
|
ZipUtils.unzip(buf, installDir, "chromeos/", false);
|
2018-05-13 18:14:10 +08:00
|
|
|
buf.reset();
|
2018-06-25 19:46:41 +08:00
|
|
|
ZipUtils.unzip(buf, installDir, "META-INF/com/google/android/update-binary", true);
|
2018-05-13 18:14:10 +08:00
|
|
|
buf.close();
|
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
|
console.add("! Invalid Uri");
|
|
|
|
|
throw e;
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
console.add("! Cannot unzip zip");
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
Shell.Sync.sh(Utils.fmt("chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk",
|
2018-06-25 19:46:41 +08:00
|
|
|
installDir, installDir, installDir));
|
2018-05-13 18:14:10 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-25 19:46:41 +08:00
|
|
|
private boolean dumpBoot() {
|
2018-07-04 17:15:26 +08:00
|
|
|
console.add("- Copying image locally");
|
2018-06-25 19:46:41 +08:00
|
|
|
// Copy boot image to local
|
|
|
|
|
try (InputStream in = mm.getContentResolver().openInputStream(bootUri);
|
|
|
|
|
OutputStream out = new FileOutputStream(mBoot)
|
|
|
|
|
) {
|
|
|
|
|
if (in == null)
|
|
|
|
|
throw new FileNotFoundException();
|
|
|
|
|
|
|
|
|
|
InputStream src;
|
|
|
|
|
if (Utils.getNameFromUri(mm, bootUri).endsWith(".tar")) {
|
|
|
|
|
// Extract boot.img from tar
|
|
|
|
|
TarInputStream tar = new TarInputStream(new BufferedInputStream(in));
|
|
|
|
|
org.kamranzafar.jtar.TarEntry entry;
|
|
|
|
|
while ((entry = tar.getNextEntry()) != null) {
|
|
|
|
|
if (entry.getName().equals("boot.img"))
|
|
|
|
|
break;
|
2018-05-13 18:14:10 +08:00
|
|
|
}
|
2018-06-25 19:46:41 +08:00
|
|
|
src = tar;
|
|
|
|
|
} else {
|
|
|
|
|
// Direct copy raw image
|
|
|
|
|
src = new BufferedInputStream(in);
|
|
|
|
|
}
|
|
|
|
|
ShellUtils.pump(src, out);
|
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
|
console.add("! Invalid Uri");
|
|
|
|
|
return false;
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
console.add("! Copy failed");
|
|
|
|
|
return false;
|
2018-05-13 18:14:10 +08:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 19:46:41 +08:00
|
|
|
private File patchBoot() throws IOException {
|
2018-05-13 18:14:10 +08:00
|
|
|
boolean isSigned;
|
2018-06-25 19:46:41 +08:00
|
|
|
try (InputStream in = new SuFileInputStream(mBoot)) {
|
2018-05-13 18:14:10 +08:00
|
|
|
isSigned = SignBoot.verifySignature(in, null);
|
|
|
|
|
if (isSigned) {
|
|
|
|
|
console.add("- Boot image is signed with AVB 1.0");
|
|
|
|
|
}
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
console.add("! Unable to check signature");
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Patch boot image
|
|
|
|
|
Shell.Sync.sh(console, logs,
|
2018-06-25 19:46:41 +08:00
|
|
|
"cd " + installDir,
|
|
|
|
|
Utils.fmt("KEEPFORCEENCRYPT=%b KEEPVERITY=%b sh update-binary indep " +
|
|
|
|
|
"boot_patch.sh %s || echo 'Failed!'",
|
|
|
|
|
mm.keepEnc, mm.keepVerity, mBoot));
|
2018-05-13 18:14:10 +08:00
|
|
|
|
|
|
|
|
if (TextUtils.equals(console.get(console.size() - 1), "Failed!"))
|
2018-06-25 19:46:41 +08:00
|
|
|
return null;
|
2018-05-13 18:14:10 +08:00
|
|
|
|
2018-06-25 19:46:41 +08:00
|
|
|
Shell.Sync.sh("mv bin/busybox busybox",
|
|
|
|
|
"rm -rf magisk.apk bin boot.img update-binary",
|
2018-05-13 18:14:10 +08:00
|
|
|
"cd /");
|
|
|
|
|
|
2018-06-25 19:46:41 +08:00
|
|
|
File patched = new File(installDir, "new-boot.img");
|
2018-05-13 18:14:10 +08:00
|
|
|
if (isSigned) {
|
|
|
|
|
console.add("- Signing boot image with test keys");
|
2018-06-25 19:46:41 +08:00
|
|
|
File signed = new File(installDir, "signed.img");
|
2018-06-27 01:08:48 +08:00
|
|
|
try (InputStream in = new SuFileInputStream(patched);
|
2018-05-13 18:14:10 +08:00
|
|
|
OutputStream out = new BufferedOutputStream(new FileOutputStream(signed))
|
|
|
|
|
) {
|
|
|
|
|
SignBoot.doSignature("/boot", in, out, null, null);
|
|
|
|
|
}
|
2018-06-27 01:08:48 +08:00
|
|
|
Shell.Sync.su("mv -f " + signed + " " + patched);
|
2018-05-13 18:14:10 +08:00
|
|
|
}
|
2018-06-25 19:46:41 +08:00
|
|
|
return patched;
|
2018-05-13 18:14:10 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-25 19:46:41 +08:00
|
|
|
private void outputBoot(File patched) throws IOException {
|
2018-05-13 18:14:10 +08:00
|
|
|
switch (mode) {
|
|
|
|
|
case PATCH_MODE:
|
|
|
|
|
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + mm.bootFormat);
|
|
|
|
|
dest.getParentFile().mkdirs();
|
|
|
|
|
OutputStream out;
|
|
|
|
|
switch (mm.bootFormat) {
|
|
|
|
|
case ".img.tar":
|
|
|
|
|
out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
|
2018-06-25 19:46:41 +08:00
|
|
|
((TarOutputStream) out).putNextEntry(new TarEntry(patched, "boot.img"));
|
2018-05-13 18:14:10 +08:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
case ".img":
|
|
|
|
|
out = new BufferedOutputStream(new FileOutputStream(dest));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-06-27 01:08:48 +08:00
|
|
|
try (InputStream in = new SuFileInputStream(patched)) {
|
2018-05-13 18:14:10 +08:00
|
|
|
ShellUtils.pump(in, out);
|
|
|
|
|
out.close();
|
|
|
|
|
}
|
2018-07-04 17:15:26 +08:00
|
|
|
Shell.Sync.su("rm -f " + patched);
|
2018-05-13 18:14:10 +08:00
|
|
|
console.add("");
|
2018-07-04 17:15:26 +08:00
|
|
|
console.add("****************************");
|
|
|
|
|
console.add(" Patched image is placed in ");
|
2018-05-13 18:14:10 +08:00
|
|
|
console.add(" " + dest + " ");
|
2018-07-04 17:15:26 +08:00
|
|
|
console.add("****************************");
|
2018-05-13 18:14:10 +08:00
|
|
|
break;
|
2018-06-26 00:29:01 +08:00
|
|
|
case SECOND_SLOT_MODE:
|
2018-05-13 18:14:10 +08:00
|
|
|
case DIRECT_MODE:
|
2018-07-04 17:15:26 +08:00
|
|
|
Shell.Sync.sh(console, logs,
|
|
|
|
|
Utils.fmt("direct_install %s %s %s", patched, mBoot, installDir));
|
|
|
|
|
if (!mm.keepVerity)
|
|
|
|
|
Shell.Sync.sh(console, logs, "find_dtbo_image", "patch_dtbo_image");
|
2018-05-13 18:14:10 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-31 03:07:33 +08:00
|
|
|
@Override
|
|
|
|
|
protected Boolean doInBackground(Void... voids) {
|
2018-05-13 18:14:10 +08:00
|
|
|
if (mode == FIX_ENV_MODE) {
|
2018-06-25 19:46:41 +08:00
|
|
|
installDir = new File("/data/adb/magisk");
|
2018-07-04 17:15:26 +08:00
|
|
|
Shell.Sync.sh("rm -rf /data/adb/magisk/*");
|
2018-05-13 18:14:10 +08:00
|
|
|
} else {
|
2018-06-25 19:46:41 +08:00
|
|
|
installDir = new File(
|
2018-05-13 18:14:10 +08:00
|
|
|
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
|
|
|
|
|
mm.createDeviceProtectedStorageContext() : mm)
|
|
|
|
|
.getFilesDir().getParent()
|
|
|
|
|
, "install");
|
2018-06-25 19:46:41 +08:00
|
|
|
Shell.Sync.sh("rm -rf " + installDir);
|
|
|
|
|
installDir.mkdirs();
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-26 00:29:01 +08:00
|
|
|
switch (mode) {
|
|
|
|
|
case PATCH_MODE:
|
|
|
|
|
mBoot = new File(installDir, "boot.img").getAbsolutePath();
|
|
|
|
|
if (!dumpBoot())
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
case DIRECT_MODE:
|
2018-07-04 17:15:26 +08:00
|
|
|
console.add("- Detecting target image");
|
2018-06-26 00:29:01 +08:00
|
|
|
mBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
|
|
|
|
|
break;
|
|
|
|
|
case SECOND_SLOT_MODE:
|
2018-07-04 17:15:26 +08:00
|
|
|
console.add("- Detecting target image");
|
2018-06-26 00:29:01 +08:00
|
|
|
char slot[] = ShellUtils.fastCmd("echo $SLOT").toCharArray();
|
|
|
|
|
if (slot[1] == 'a') slot[1] = 'b';
|
|
|
|
|
else slot[1] = 'a';
|
|
|
|
|
mBoot = ShellUtils.fastCmd("SLOT=" + String.valueOf(slot),
|
|
|
|
|
"find_boot_image", "echo \"$BOOTIMAGE\"");
|
|
|
|
|
Shell.Async.su("mount_partitions");
|
|
|
|
|
break;
|
2018-05-13 18:14:10 +08:00
|
|
|
}
|
2018-06-26 00:29:01 +08:00
|
|
|
if (mBoot == null) {
|
2018-07-04 17:15:26 +08:00
|
|
|
console.add("! Unable to detect target image");
|
2018-06-26 00:29:01 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-04 17:15:26 +08:00
|
|
|
console.add("- Target image: " + mBoot);
|
2017-08-31 03:07:33 +08:00
|
|
|
|
|
|
|
|
List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
|
|
|
|
|
String arch;
|
2018-04-29 14:39:03 +08:00
|
|
|
|
|
|
|
|
if (mm.remoteMagiskVersionCode >= Const.MAGISK_VER.SEPOL_REFACTOR) {
|
|
|
|
|
// 32-bit only
|
|
|
|
|
if (abis.contains("x86")) arch = "x86";
|
|
|
|
|
else arch = "arm";
|
|
|
|
|
} else {
|
|
|
|
|
if (abis.contains("x86_64")) arch = "x64";
|
|
|
|
|
else if (abis.contains("arm64-v8a")) arch = "arm64";
|
|
|
|
|
else if (abis.contains("x86")) arch = "x86";
|
|
|
|
|
else arch = "arm";
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-22 13:54:27 +08:00
|
|
|
console.add("- Device platform: " + Build.SUPPORTED_ABIS[0]);
|
2017-08-31 03:07:33 +08:00
|
|
|
|
|
|
|
|
try {
|
2018-05-13 18:14:10 +08:00
|
|
|
extractFiles(arch);
|
|
|
|
|
if (mode == FIX_ENV_MODE) {
|
2018-07-04 17:15:26 +08:00
|
|
|
Shell.Sync.sh("fix_env");
|
2018-05-13 18:14:10 +08:00
|
|
|
} else {
|
2018-06-25 19:46:41 +08:00
|
|
|
File patched = patchBoot();
|
|
|
|
|
if (patched == null)
|
2017-09-03 00:10:14 +08:00
|
|
|
return false;
|
2018-06-25 19:46:41 +08:00
|
|
|
outputBoot(patched);
|
2018-05-13 18:14:10 +08:00
|
|
|
console.add("- All done!");
|
|
|
|
|
}
|
2017-08-31 03:07:33 +08:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPostExecute(Boolean result) {
|
2018-05-13 18:14:10 +08:00
|
|
|
if (mode == FIX_ENV_MODE) {
|
|
|
|
|
dialog.dismiss();
|
|
|
|
|
MagiskManager.toast(result ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
|
|
|
|
|
} else {
|
|
|
|
|
// Running in FlashActivity
|
|
|
|
|
FlashActivity activity = (FlashActivity) getActivity();
|
|
|
|
|
if (!result) {
|
2018-06-25 19:46:41 +08:00
|
|
|
Shell.Async.sh("rm -rf " + installDir);
|
2018-05-13 18:14:10 +08:00
|
|
|
console.add("! Installation failed");
|
|
|
|
|
activity.reboot.setVisibility(View.GONE);
|
|
|
|
|
}
|
|
|
|
|
activity.buttonPanel.setVisibility(View.VISIBLE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class NOPList<E> extends AbstractList<E> {
|
|
|
|
|
@Override
|
|
|
|
|
public E get(int index) {
|
|
|
|
|
return null;
|
2017-11-18 03:55:47 +08:00
|
|
|
}
|
2018-05-13 18:14:10 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int size() {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void add(int index, E element) {}
|
2017-08-31 03:07:33 +08:00
|
|
|
}
|
|
|
|
|
}
|