diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2198eaf --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sub_projects/Loki"] + path = sub_projects/Loki + url = https://github.com/efidroid/modules_loki.git diff --git a/app/build.gradle b/app/build.gradle index 9fef9a2..d91d79e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,6 +36,11 @@ android { versionNameSuffix "-debug" } } + externalNativeBuild { + ndkBuild { + path 'src/main/cpp/Android.mk' + } + } } dependencies { diff --git a/app/src/main/cpp/Android.mk b/app/src/main/cpp/Android.mk new file mode 100644 index 0000000..05aec00 --- /dev/null +++ b/app/src/main/cpp/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH := $(call my-dir) + +# LOKI usable only for ARMv7-based phones +ifeq ($(TARGET_ARCH_ABI), armeabi-v7a) + LOKI_PATH := $(abspath $(LOCAL_PATH)/../../../../sub_projects/Loki) + + include $(CLEAR_VARS) + LOCAL_MODULE := loki_wrapper + LOCAL_SRC_FILES := loki_wrapper.c + LOCAL_STATIC_LIBRARIES := libloki_static + LOCAL_C_INCLUDES += $(LOKI_PATH) + include $(BUILD_SHARED_LIBRARY) + + include $(LOKI_PATH)/Android.mk +endif diff --git a/app/src/main/cpp/loki_wrapper.c b/app/src/main/cpp/loki_wrapper.c new file mode 100644 index 0000000..0a53bfc --- /dev/null +++ b/app/src/main/cpp/loki_wrapper.c @@ -0,0 +1,24 @@ +#include +#include + +JNIEXPORT jboolean JNICALL +Java_org_efidroid_efidroidmanager_patching_LokiPatcher_nativePatchImage(JNIEnv *env, + jclass class, + jstring imageType_, + jstring aBootImage_, + jstring in_, jstring out_) { + const char *imageType = (*env)->GetStringUTFChars(env, imageType_, 0); + const char *aBootImage = (*env)->GetStringUTFChars(env, aBootImage_, 0); + const char *in = (*env)->GetStringUTFChars(env, in_, 0); + const char *out = (*env)->GetStringUTFChars(env, out_, 0); + + // loki_patch() returns '0' on successful exit + int result = loki_patch(imageType, aBootImage, in, out); + + (*env)->ReleaseStringUTFChars(env, imageType_, imageType); + (*env)->ReleaseStringUTFChars(env, aBootImage_, aBootImage); + (*env)->ReleaseStringUTFChars(env, in_, in); + (*env)->ReleaseStringUTFChars(env, out_, out); + + return (jboolean)(result == 0); +} diff --git a/app/src/main/java/org/efidroid/efidroidmanager/RootToolsEx.java b/app/src/main/java/org/efidroid/efidroidmanager/RootToolsEx.java index bc1ff88..c441dd7 100644 --- a/app/src/main/java/org/efidroid/efidroidmanager/RootToolsEx.java +++ b/app/src/main/java/org/efidroid/efidroidmanager/RootToolsEx.java @@ -676,14 +676,7 @@ public static void init(Context context) { int lastVersionCode = sp.getInt(AppConstants.SHAREDPREFS_GLOBAL_LAST_APP_VERSION, 0); try { - ArrayList abis = new ArrayList<>(); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { - abis.addAll(Arrays.asList(Build.SUPPORTED_ABIS)); - } - else { - abis.add(Build.CPU_ABI); - abis.add(Build.CPU_ABI2); - } + List abis = Util.getABIs(); InputStream is = null; for(String abi : abis) { diff --git a/app/src/main/java/org/efidroid/efidroidmanager/Util.java b/app/src/main/java/org/efidroid/efidroidmanager/Util.java index 7ef0013..358f685 100644 --- a/app/src/main/java/org/efidroid/efidroidmanager/Util.java +++ b/app/src/main/java/org/efidroid/efidroidmanager/Util.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; +import android.os.Build; import android.support.design.widget.AppBarLayout; import android.util.TypedValue; import android.view.View; @@ -20,6 +21,8 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class Util { public static OperatingSystemEditActivity.MultibootPartitionInfo getPartitionInfoByName(ArrayList list, String name) { @@ -35,6 +38,18 @@ public static String name2path(String name) { return name.replaceAll("\\W+", "_"); } + public static List getABIs() { + ArrayList abis = new ArrayList<>(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + abis.addAll(Arrays.asList(Build.SUPPORTED_ABIS)); + } + else { + abis.add(Build.CPU_ABI); + abis.add(Build.CPU_ABI2); + } + return abis; + } + public static byte[] longToBytes(long x) { ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE/Byte.SIZE); buffer.order(ByteOrder.LITTLE_ENDIAN); diff --git a/app/src/main/java/org/efidroid/efidroidmanager/patching/DummyPatcher.java b/app/src/main/java/org/efidroid/efidroidmanager/patching/DummyPatcher.java new file mode 100644 index 0000000..3b91151 --- /dev/null +++ b/app/src/main/java/org/efidroid/efidroidmanager/patching/DummyPatcher.java @@ -0,0 +1,33 @@ +package org.efidroid.efidroidmanager.patching; + + +import android.content.Context; + +import org.efidroid.efidroidmanager.models.DeviceInfo; +import org.efidroid.efidroidmanager.types.FSTabEntry; + +final class DummyPatcher extends Patcher { + DummyPatcher(DeviceInfo deviceInfo, Context context) { + super(deviceInfo, context); + } + + @Override + public void prepareEnvironment(String updateDir) throws Exception { + + } + + @Override + public boolean isPatchRequired(FSTabEntry entry) { + return false; + } + + @Override + public void patchImage(FSTabEntry destEntry, String image) throws Exception { + + } + + @Override + public void cleanupEnvironment() { + + } +} diff --git a/app/src/main/java/org/efidroid/efidroidmanager/patching/LokiPatcher.java b/app/src/main/java/org/efidroid/efidroidmanager/patching/LokiPatcher.java new file mode 100644 index 0000000..93e9973 --- /dev/null +++ b/app/src/main/java/org/efidroid/efidroidmanager/patching/LokiPatcher.java @@ -0,0 +1,103 @@ +package org.efidroid.efidroidmanager.patching; + +import android.content.Context; + +import com.stericson.roottools.RootTools; + +import org.efidroid.efidroidmanager.RootToolsEx; +import org.efidroid.efidroidmanager.Util; +import org.efidroid.efidroidmanager.models.DeviceInfo; +import org.efidroid.efidroidmanager.types.FSTabEntry; + +import java.util.List; + +class LokiPatcher extends Patcher { + private enum ImageType { + BOOT("boot", "lokiboot"), + RECOVERY("recovery", "lokirecovery"); + + private final String mName, mFlag; + + ImageType(String name, String flag) { + mName = name; + mFlag = flag; + } + + String getName() { + return mName; + } + + String getFlag() { + return mFlag; + } + } + + static { + // LOKI usable only for ARMv7-based phones + List abis = Util.getABIs(); + if (abis.contains("armeabi-v7a")) { + System.loadLibrary("loki_wrapper"); + for (ImageType imageType : ImageType.values()) { + PatcherStorage.registerPatcher(imageType.getFlag(), LokiPatcher.class); + } + } + } + + private static final String ABootFlag = "lokiaboot"; + + private String aBootImage; + + public LokiPatcher(DeviceInfo deviceInfo, Context context) { + super(deviceInfo, context); + } + + @Override + public void prepareEnvironment(String updateDir) throws Exception { + FSTabEntry aBootEntry = null; + for (FSTabEntry entry : getDeviceInfo().getFSTab().getFSTabEntries()) { + if (entry.getFfMgrFlags().contains(ABootFlag)) { + aBootEntry = entry; + } + } + if (aBootEntry == null) { + throw new Exception("ABoot " + ABootFlag + " entry not found (bad multiboot.fstab)"); + } + aBootImage = updateDir + "/aboot.img"; + RootToolsEx.dd(aBootEntry.getBlkDevice(), aBootImage); + } + + @Override + public boolean isPatchRequired(FSTabEntry entry) { + for (ImageType imageType : ImageType.values()) { + if (entry.getFfMgrFlags().contains(imageType.getFlag())) { + return true; + } + } + return false; + } + + private static native boolean nativePatchImage(String imageType, String aBootImage, String in, String out); + + @Override + public void patchImage(FSTabEntry destEntry, String image) throws Exception { + boolean isSuccessfulPatch = false; + String outputImage = null; + for (ImageType imageType : ImageType.values()) { + if (destEntry.getFfMgrFlags().contains(imageType.getFlag())) { + outputImage = image.substring(0, image.lastIndexOf('/')) + "/" + imageType.getName() + ".img"; + isSuccessfulPatch = nativePatchImage(imageType.getName(), aBootImage, image, outputImage); + } + } + if (isSuccessfulPatch) { + RootTools.copyFile(outputImage, image, false, true); + RootTools.deleteFileOrDirectory(outputImage, false); + } else { + throw new Exception("Image patch error"); + } + } + + @Override + public void cleanupEnvironment() { + RootTools.deleteFileOrDirectory(aBootImage, false); + } +} diff --git a/app/src/main/java/org/efidroid/efidroidmanager/patching/Patcher.java b/app/src/main/java/org/efidroid/efidroidmanager/patching/Patcher.java new file mode 100644 index 0000000..e145ada --- /dev/null +++ b/app/src/main/java/org/efidroid/efidroidmanager/patching/Patcher.java @@ -0,0 +1,33 @@ +package org.efidroid.efidroidmanager.patching; + +import android.content.Context; + +import org.efidroid.efidroidmanager.models.DeviceInfo; +import org.efidroid.efidroidmanager.types.FSTabEntry; + +public abstract class Patcher { + private final Context mContext; + private final DeviceInfo mDeviceInfo; + + Patcher(DeviceInfo deviceInfo, Context context) { + mContext = context; + mDeviceInfo = deviceInfo; + } + + protected Context getContext() { + return mContext; + } + + protected DeviceInfo getDeviceInfo() { + return mDeviceInfo; + } + + public abstract void prepareEnvironment(String updateDir) throws Exception; + + public abstract boolean isPatchRequired(FSTabEntry entry); + + // replaces original image with patched + public abstract void patchImage(FSTabEntry destEntry, String image) throws Exception; + + public abstract void cleanupEnvironment(); +} diff --git a/app/src/main/java/org/efidroid/efidroidmanager/patching/PatcherStorage.java b/app/src/main/java/org/efidroid/efidroidmanager/patching/PatcherStorage.java new file mode 100644 index 0000000..93713f9 --- /dev/null +++ b/app/src/main/java/org/efidroid/efidroidmanager/patching/PatcherStorage.java @@ -0,0 +1,43 @@ +package org.efidroid.efidroidmanager.patching; + +import android.content.Context; + +import org.efidroid.efidroidmanager.models.DeviceInfo; +import org.efidroid.efidroidmanager.types.FSTabEntry; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; + +public final class PatcherStorage { + // FSTab flag to patcher + private static final Map> patchers = new HashMap<>(); + + static void registerPatcher(String flag, Class patcher) { + patchers.put(flag, patcher); + } + + // selects patcher using FSTab flags. If no patcher selected, returns a DummyPatcher instance + public static Patcher selectPatcher(DeviceInfo deviceInfo, Context context) throws Exception { + for (FSTabEntry entry : deviceInfo.getFSTab().getFSTabEntries()) { + for (String flag : patchers.keySet()) { + if (entry.getFfMgrFlags().contains(flag)) { + Class patcherClass = patchers.get(flag); + if (patcherClass == null) { + throw new Exception("Patcher registered but not found"); + } + try { + Constructor constructor = patcherClass.getDeclaredConstructor(DeviceInfo.class, Context.class); + return (Patcher) constructor.newInstance(deviceInfo, context); + } catch (NoSuchMethodException e) { + throw new Exception("Cannot instantiate patcher"); + } + } + } + } + return new DummyPatcher(deviceInfo, context); + } + + private PatcherStorage() { + } +} diff --git a/app/src/main/java/org/efidroid/efidroidmanager/tasks/EFIDroidInstallServiceTask.java b/app/src/main/java/org/efidroid/efidroidmanager/tasks/EFIDroidInstallServiceTask.java index 6c47dec..f143877 100644 --- a/app/src/main/java/org/efidroid/efidroidmanager/tasks/EFIDroidInstallServiceTask.java +++ b/app/src/main/java/org/efidroid/efidroidmanager/tasks/EFIDroidInstallServiceTask.java @@ -8,6 +8,8 @@ import org.efidroid.efidroidmanager.RootToolsEx; import org.efidroid.efidroidmanager.Util; import org.efidroid.efidroidmanager.models.DeviceInfo; +import org.efidroid.efidroidmanager.patching.Patcher; +import org.efidroid.efidroidmanager.patching.PatcherStorage; import org.efidroid.efidroidmanager.services.GenericProgressIntentService; import org.efidroid.efidroidmanager.types.FSTabEntry; import org.efidroid.efidroidmanager.types.InstallationEntry; @@ -115,6 +117,9 @@ private String downloadUpdate(String urlString) throws Exception { } private void doInstall(String updateDir) throws Exception { + // determine appropriate patcher + Patcher patcher = PatcherStorage.selectPatcher(mDeviceInfo, getService().getApplicationContext()); + // get esp parent directory String espParent = mDeviceInfo.getESPDir(false); if (espParent == null) @@ -161,14 +166,21 @@ private void doInstall(String updateDir) throws Exception { } } + patcher.prepareEnvironment(updateDir); + // install for (FSTabEntry entry : mDeviceInfo.getFSTab().getFSTabEntries()) { if (!entry.isUEFI()) continue; String file = updateDir + "/" + entry.getName() + ".img"; + if (patcher.isPatchRequired(entry)) { + patcher.patchImage(entry, file); + } RootToolsEx.dd(file, entry.getBlkDevice()); } + + patcher.cleanupEnvironment(); } public void onProcess(Bundle extras) { diff --git a/app/src/main/java/org/efidroid/efidroidmanager/types/FSTabEntry.java b/app/src/main/java/org/efidroid/efidroidmanager/types/FSTabEntry.java index c40d8e6..770ca3b 100644 --- a/app/src/main/java/org/efidroid/efidroidmanager/types/FSTabEntry.java +++ b/app/src/main/java/org/efidroid/efidroidmanager/types/FSTabEntry.java @@ -5,6 +5,9 @@ import org.efidroid.efidroidmanager.RootToolsEx; +import java.util.Arrays; +import java.util.List; + public class FSTabEntry implements Parcelable { final String mBlkDevice; final String mMountPoint; @@ -91,33 +94,20 @@ public String getMountFlags() { return mMountFlags; } - public String getFfMgrFlags() { - return mFfMgrFlags; + public List getFfMgrFlags() { + return Arrays.asList(mFfMgrFlags.split(",")); } public boolean isMultiboot() { - String[] parts = mFfMgrFlags.split(","); - for (String part : parts) { - if (part.equals("multiboot")) - return true; - } - - return false; + return getFfMgrFlags().contains("multiboot"); } public boolean isUEFI() { - String[] parts = mFfMgrFlags.split(","); - for (String part : parts) { - if (part.equals("uefi")) - return true; - } - - return false; + return getFfMgrFlags().contains("uefi"); } public String getESP() { - String[] parts = mFfMgrFlags.split(","); - for (String part : parts) { + for (String part : getFfMgrFlags()) { if (!part.startsWith("esp")) continue; diff --git a/sub_projects/Loki b/sub_projects/Loki new file mode 160000 index 0000000..784e86f --- /dev/null +++ b/sub_projects/Loki @@ -0,0 +1 @@ +Subproject commit 784e86f981b7b1c30bd0d6b401f071e47e738eb8