mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-02-18 23:18:29 +00:00
Support patching full ODIN firmware
This commit is contained in:
parent
ceb21ced2b
commit
c10b376575
@ -25,14 +25,12 @@ public class Const {
|
|||||||
EXTERNAL_PATH.mkdirs();
|
EXTERNAL_PATH.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String BUSYBOX_PATH = "/sbin/.magisk/busybox";
|
|
||||||
public static final String TMP_FOLDER_PATH = "/dev/tmp";
|
public static final String TMP_FOLDER_PATH = "/dev/tmp";
|
||||||
public static final String MAGISK_LOG = "/cache/magisk.log";
|
public static final String MAGISK_LOG = "/cache/magisk.log";
|
||||||
public static final String MANAGER_CONFIGS = ".tmp.magisk.config";
|
public static final String MANAGER_CONFIGS = ".tmp.magisk.config";
|
||||||
|
|
||||||
// Versions
|
// Versions
|
||||||
public static final int UPDATE_SERVICE_VER = 1;
|
public static final int UPDATE_SERVICE_VER = 1;
|
||||||
public static final int MIN_MODULE_VER = 1500;
|
|
||||||
public static final int SNET_EXT_VER = 12;
|
public static final int SNET_EXT_VER = 12;
|
||||||
|
|
||||||
public static final int USER_ID = Process.myUid() / 100000;
|
public static final int USER_ID = Process.myUid() / 100000;
|
||||||
@ -42,10 +40,8 @@ public class Const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ID {
|
public static class ID {
|
||||||
public static final int UPDATE_SERVICE_ID = 1;
|
|
||||||
public static final int FETCH_ZIP = 2;
|
public static final int FETCH_ZIP = 2;
|
||||||
public static final int SELECT_BOOT = 3;
|
public static final int SELECT_BOOT = 3;
|
||||||
public static final int ONBOOT_SERVICE_ID = 6;
|
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
|
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
|
||||||
@ -88,14 +84,13 @@ public class Const {
|
|||||||
public static final String INTENT_SET_NAME = "filename";
|
public static final String INTENT_SET_NAME = "filename";
|
||||||
public static final String INTENT_SET_LINK = "link";
|
public static final String INTENT_SET_LINK = "link";
|
||||||
public static final String FLASH_ACTION = "action";
|
public static final String FLASH_ACTION = "action";
|
||||||
public static final String FLASH_SET_BOOT = "boot";
|
|
||||||
public static final String BROADCAST_MANAGER_UPDATE = "manager_update";
|
public static final String BROADCAST_MANAGER_UPDATE = "manager_update";
|
||||||
public static final String BROADCAST_REBOOT = "reboot";
|
public static final String BROADCAST_REBOOT = "reboot";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Value {
|
public static class Value {
|
||||||
public static final String FLASH_ZIP = "flash";
|
public static final String FLASH_ZIP = "flash";
|
||||||
public static final String PATCH_BOOT = "patch";
|
public static final String PATCH_FILE = "patch";
|
||||||
public static final String FLASH_MAGISK = "magisk";
|
public static final String FLASH_MAGISK = "magisk";
|
||||||
public static final String FLASH_INACTIVE_SLOT = "slot";
|
public static final String FLASH_INACTIVE_SLOT = "slot";
|
||||||
public static final String UNINSTALL = "uninstall";
|
public static final String UNINSTALL = "uninstall";
|
||||||
|
@ -125,8 +125,8 @@ public class FlashActivity extends BaseActivity {
|
|||||||
case Const.Value.FLASH_INACTIVE_SLOT:
|
case Const.Value.FLASH_INACTIVE_SLOT:
|
||||||
new SecondSlot().exec();
|
new SecondSlot().exec();
|
||||||
break;
|
break;
|
||||||
case Const.Value.PATCH_BOOT:
|
case Const.Value.PATCH_FILE:
|
||||||
new PatchBoot(uri).exec();
|
new PatchFile(uri).exec();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -254,17 +254,17 @@ public class FlashActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PatchBoot extends BaseInstaller {
|
private class PatchFile extends BaseInstaller {
|
||||||
|
|
||||||
private Uri uri;
|
private Uri uri;
|
||||||
|
|
||||||
PatchBoot(Uri u) {
|
PatchFile(Uri u) {
|
||||||
uri = u;
|
uri = u;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean operations() {
|
protected boolean operations() {
|
||||||
return copyBoot(uri) && extractZip() && patchBoot() && storeBoot();
|
return extractZip() && handleFile(uri) && patchBoot() && storeBoot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
|
|||||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||||
Intent i = new Intent(activity, ClassMap.get(FlashActivity.class))
|
Intent i = new Intent(activity, ClassMap.get(FlashActivity.class))
|
||||||
.setData(data.getData())
|
.setData(data.getData())
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_FILE);
|
||||||
activity.startActivity(i);
|
activity.startActivity(i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -23,6 +23,7 @@ import com.topjohnwu.superuser.io.SuFileInputStream;
|
|||||||
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
||||||
|
|
||||||
import org.kamranzafar.jtar.TarEntry;
|
import org.kamranzafar.jtar.TarEntry;
|
||||||
|
import org.kamranzafar.jtar.TarHeader;
|
||||||
import org.kamranzafar.jtar.TarInputStream;
|
import org.kamranzafar.jtar.TarInputStream;
|
||||||
import org.kamranzafar.jtar.TarOutputStream;
|
import org.kamranzafar.jtar.TarOutputStream;
|
||||||
|
|
||||||
@ -30,11 +31,11 @@ import java.io.BufferedInputStream;
|
|||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
@ -42,10 +43,13 @@ import java.util.zip.ZipInputStream;
|
|||||||
|
|
||||||
public abstract class MagiskInstaller {
|
public abstract class MagiskInstaller {
|
||||||
|
|
||||||
private List<String> console, logs;
|
|
||||||
protected String srcBoot;
|
protected String srcBoot;
|
||||||
|
protected File destFile;
|
||||||
protected File installDir;
|
protected File installDir;
|
||||||
|
|
||||||
|
private List<String> console, logs;
|
||||||
|
private boolean isTar = false;
|
||||||
|
|
||||||
private class ProgressLog implements DownloadProgressListener {
|
private class ProgressLog implements DownloadProgressListener {
|
||||||
|
|
||||||
private int prev = -1;
|
private int prev = -1;
|
||||||
@ -166,38 +170,101 @@ public abstract class MagiskInstaller {
|
|||||||
} else {
|
} else {
|
||||||
init64.delete();
|
init64.delete();
|
||||||
}
|
}
|
||||||
|
Shell.sh("cd " + installDir, "chmod 755 *").exec();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean copyBoot(Uri bootUri) {
|
private TarEntry newEntry(String name, long size) {
|
||||||
srcBoot = new File(installDir, "boot.img").getPath();
|
console.add("-- Writing: " + name);
|
||||||
console.add("- Copying image to cache");
|
return new TarEntry(TarHeader.createHeader(name, size, 0, false, 0644));
|
||||||
// Copy boot image to local
|
}
|
||||||
try (InputStream in = App.self.getContentResolver().openInputStream(bootUri);
|
|
||||||
OutputStream out = new FileOutputStream(srcBoot)) {
|
|
||||||
if (in == null)
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
|
|
||||||
InputStream src;
|
private void handleTar(InputStream in) throws IOException {
|
||||||
if (Utils.getNameFromUri(App.self, bootUri).endsWith(".tar")) {
|
console.add("- Processing tar file");
|
||||||
// Extract boot.img from tar
|
boolean vbmeta = false;
|
||||||
TarInputStream tar = new TarInputStream(new BufferedInputStream(in));
|
try (TarInputStream tarIn = new TarInputStream(in);
|
||||||
|
TarOutputStream tarOut = new TarOutputStream(destFile)) {
|
||||||
TarEntry entry;
|
TarEntry entry;
|
||||||
while ((entry = tar.getNextEntry()) != null) {
|
while ((entry = tarIn.getNextEntry()) != null) {
|
||||||
if (entry.getName().equals("boot.img"))
|
if (entry.getName().contains("boot.img")
|
||||||
break;
|
|| entry.getName().contains("recovery.img")) {
|
||||||
|
String name = entry.getName();
|
||||||
|
console.add("-- Extracting: " + name);
|
||||||
|
File extract = new File(installDir, name);
|
||||||
|
try (FileOutputStream fout = new FileOutputStream(extract)) {
|
||||||
|
ShellUtils.pump(tarIn, fout);
|
||||||
}
|
}
|
||||||
src = tar;
|
if (name.contains(".lz4")) {
|
||||||
|
console.add("-- Decompressing: " + name);
|
||||||
|
Shell.sh("./magiskboot --decompress " + extract).to(console).exec();
|
||||||
|
}
|
||||||
|
} else if (entry.getName().contains("vbmeta.img")) {
|
||||||
|
vbmeta = true;
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate(256);
|
||||||
|
buf.put("AVB0".getBytes()); // magic
|
||||||
|
buf.putInt(1); // required_libavb_version_major
|
||||||
|
buf.putInt(120, 2); // flags
|
||||||
|
buf.position(128); // release_string
|
||||||
|
buf.put("avbtool 1.1.0".getBytes());
|
||||||
|
tarOut.putNextEntry(newEntry("vbmeta.img", 256));
|
||||||
|
tarOut.write(buf.array());
|
||||||
} else {
|
} else {
|
||||||
// Direct copy raw image
|
console.add("-- Writing: " + entry.getName());
|
||||||
src = new BufferedInputStream(in);
|
tarOut.putNextEntry(entry);
|
||||||
|
ShellUtils.pump(tarIn, tarOut);
|
||||||
}
|
}
|
||||||
ShellUtils.pump(src, out);
|
}
|
||||||
} catch (FileNotFoundException e) {
|
SuFile boot = new SuFile(installDir, "boot.img");
|
||||||
console.add("! Invalid Uri");
|
SuFile recovery = new SuFile(installDir, "recovery.img");
|
||||||
|
if (vbmeta && recovery.exists() && boot.exists()) {
|
||||||
|
// Install Magisk to recovery
|
||||||
|
srcBoot = recovery.getPath();
|
||||||
|
// Repack boot image to prevent restore
|
||||||
|
Shell.sh(
|
||||||
|
"./magiskboot --unpack boot.img",
|
||||||
|
"./magiskboot --repack boot.img",
|
||||||
|
"./magiskboot --cleanup",
|
||||||
|
"mv new-boot.img boot.img").exec();
|
||||||
|
try (InputStream sin = new SuFileInputStream(boot)) {
|
||||||
|
tarOut.putNextEntry(newEntry("boot.img", boot.length()));
|
||||||
|
ShellUtils.pump(sin, tarOut);
|
||||||
|
}
|
||||||
|
boot.delete();
|
||||||
|
} else {
|
||||||
|
if (!boot.exists()) {
|
||||||
|
console.add("! No boot image found");
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
srcBoot = boot.getPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean handleFile(Uri uri) {
|
||||||
|
try (InputStream in = new BufferedInputStream(App.self.getContentResolver().openInputStream(uri))) {
|
||||||
|
in.mark(500);
|
||||||
|
byte[] magic = new byte[5];
|
||||||
|
if (in.skip(257) != 257 || in.read(magic) != magic.length) {
|
||||||
|
console.add("! Invalid file");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
in.reset();
|
||||||
|
if (Arrays.equals(magic, "ustar".getBytes())) {
|
||||||
|
isTar = true;
|
||||||
|
destFile = new File(Const.EXTERNAL_PATH, "magisk_patched.tar");
|
||||||
|
handleTar(in);
|
||||||
|
} else {
|
||||||
|
// Raw image
|
||||||
|
srcBoot = new File(installDir, "boot.img").getPath();
|
||||||
|
destFile = new File(Const.EXTERNAL_PATH, "magisk_patched.img");
|
||||||
|
console.add("- Copying image to cache");
|
||||||
|
try (OutputStream out = new FileOutputStream(srcBoot)) {
|
||||||
|
ShellUtils.pump(in, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
console.add("! Copy failed");
|
console.add("! Process error");
|
||||||
|
e.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -215,7 +282,7 @@ public abstract class MagiskInstaller {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Shell.sh("cd " + installDir, Utils.fmt(
|
if (!Shell.sh(Utils.fmt(
|
||||||
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b RECOVERYMODE=%b " +
|
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b RECOVERYMODE=%b " +
|
||||||
"sh update-binary sh boot_patch.sh %s",
|
"sh update-binary sh boot_patch.sh %s",
|
||||||
Config.keepEnc, Config.keepVerity, Config.recovery, srcBoot))
|
Config.keepEnc, Config.keepVerity, Config.recovery, srcBoot))
|
||||||
@ -253,35 +320,30 @@ public abstract class MagiskInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean storeBoot() {
|
protected boolean storeBoot() {
|
||||||
File patched = new File(installDir, "new-boot.img");
|
SuFile patched = new SuFile(installDir, "new-boot.img");
|
||||||
String fmt = Config.get(Config.Key.BOOT_FORMAT);
|
|
||||||
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + fmt);
|
|
||||||
dest.getParentFile().mkdirs();
|
|
||||||
OutputStream os;
|
|
||||||
try {
|
try {
|
||||||
switch (fmt) {
|
OutputStream os;
|
||||||
case ".img.tar":
|
if (isTar) {
|
||||||
os = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
|
os = new TarOutputStream(destFile, true);
|
||||||
((TarOutputStream) os).putNextEntry(new TarEntry(patched, "boot.img"));
|
((TarOutputStream) os).putNextEntry(newEntry(
|
||||||
break;
|
srcBoot.contains("recovery") ? "recovery.img" : "boot.img",
|
||||||
default:
|
patched.length()));
|
||||||
case ".img":
|
} else {
|
||||||
os = new BufferedOutputStream(new FileOutputStream(dest));
|
os = new BufferedOutputStream(new FileOutputStream(destFile));
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
try (InputStream in = new SuFileInputStream(patched)) {
|
try (InputStream in = new SuFileInputStream(patched);
|
||||||
ShellUtils.pump(in, os);
|
OutputStream out = os) {
|
||||||
os.close();
|
ShellUtils.pump(in, out);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
console.add("! Failed to store boot to " + dest);
|
console.add("! Failed to output to " + destFile);
|
||||||
return false;
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
Shell.sh("rm -f " + patched).exec();
|
patched.delete();
|
||||||
console.add("");
|
console.add("");
|
||||||
console.add("****************************");
|
console.add("****************************");
|
||||||
console.add(" Patched image is placed in ");
|
console.add(" Output file is placed in ");
|
||||||
console.add(" " + dest + " ");
|
console.add(" " + destFile + " ");
|
||||||
console.add("****************************");
|
console.add("****************************");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user