diff --git a/.gitignore b/.gitignore index f0b6408bf..13f3c8a7c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ /build app/release *.hprof -app/.externalNativeBuild/ +.externalNativeBuild/ *.sh public.certificate.x509.pem private.key.pk8 diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 796b96d1c..000000000 --- a/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index fc52fa8b3..000000000 --- a/app/build.gradle +++ /dev/null @@ -1,67 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 27 - buildToolsVersion "27.0.1" - - defaultConfig { - applicationId "com.topjohnwu.magisk" - minSdkVersion 21 - targetSdkVersion 27 - versionCode 71 - versionName "5.4.3" - ndk { - moduleName 'zipadjust' - abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' - } - javaCompileOptions { - annotationProcessorOptions { - argument('butterknife.debuggable', 'false') - } - } - } - - buildTypes { - release { - minifyEnabled true - shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - dexOptions { - preDexLibraries true - javaMaxHeapSize "2g" - } - externalNativeBuild { - cmake { - path 'src/main/jni/CMakeLists.txt' - } - } - lintOptions { - disable 'MissingTranslation' - } -} - -repositories { - jcenter() - google() - maven { url "https://jitpack.io" } -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation project(':crypto') - implementation 'com.android.support:recyclerview-v7:27.0.2' - implementation 'com.android.support:cardview-v7:27.0.2' - implementation 'com.android.support:design:27.0.2' - implementation 'com.android.support:support-v4:27.0.2' - implementation 'com.jakewharton:butterknife:8.8.1' - implementation 'com.atlassian.commonmark:commonmark:0.10.0' - implementation 'org.kamranzafar:jtar:2.3' - implementation 'com.google.code.gson:gson:2.8.2' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' -} diff --git a/build.gradle b/build.gradle index a95a1e456..fc52fa8b3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,18 +1,67 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. +apply plugin: 'com.android.application' -buildscript { - repositories { - jcenter() - google() +android { + compileSdkVersion 27 + buildToolsVersion "27.0.1" + + defaultConfig { + applicationId "com.topjohnwu.magisk" + minSdkVersion 21 + targetSdkVersion 27 + versionCode 71 + versionName "5.4.3" + ndk { + moduleName 'zipadjust' + abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' + } + javaCompileOptions { + annotationProcessorOptions { + argument('butterknife.debuggable', 'false') + } + } } - dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + buildTypes { + release { + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + dexOptions { + preDexLibraries true + javaMaxHeapSize "2g" + } + externalNativeBuild { + cmake { + path 'src/main/jni/CMakeLists.txt' + } + } + lintOptions { + disable 'MissingTranslation' } } -task clean(type: Delete) { - delete rootProject.buildDir +repositories { + jcenter() + google() + maven { url "https://jitpack.io" } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation project(':crypto') + implementation 'com.android.support:recyclerview-v7:27.0.2' + implementation 'com.android.support:cardview-v7:27.0.2' + implementation 'com.android.support:design:27.0.2' + implementation 'com.android.support:support-v4:27.0.2' + implementation 'com.jakewharton:butterknife:8.8.1' + implementation 'com.atlassian.commonmark:commonmark:0.10.0' + implementation 'org.kamranzafar:jtar:2.3' + implementation 'com.google.code.gson:gson:2.8.2' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' } diff --git a/crypto/.gitignore b/crypto/.gitignore deleted file mode 100644 index 796b96d1c..000000000 --- a/crypto/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/crypto/build.gradle b/crypto/build.gradle deleted file mode 100644 index edfb254cd..000000000 --- a/crypto/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -apply plugin: 'java-library' - -apply plugin: 'com.github.johnrengelman.shadow' -apply plugin: 'java' - -sourceCompatibility = "1.8" -targetCompatibility = "1.8" - -jar { - manifest { - attributes 'Main-Class': 'com.topjohnwu.crypto.ZipSigner' - } -} - -shadowJar { - baseName = 'zipsigner' - classifier = null - version = 1.1 -} - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1' - } -} - -repositories { - jcenter() -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'org.bouncycastle:bcprov-jdk15on:1.58' - implementation 'org.bouncycastle:bcpkix-jdk15on:1.58' -} diff --git a/crypto/src/main/java/com/topjohnwu/crypto/ByteArrayStream.java b/crypto/src/main/java/com/topjohnwu/crypto/ByteArrayStream.java deleted file mode 100644 index ef6c7bc6d..000000000 --- a/crypto/src/main/java/com/topjohnwu/crypto/ByteArrayStream.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.topjohnwu.crypto; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class ByteArrayStream extends ByteArrayOutputStream { - public byte[] getBuf() { - return buf; - } - public synchronized void readFrom(InputStream is) { - readFrom(is, Integer.MAX_VALUE); - } - public synchronized void readFrom(InputStream is, int len) { - int read; - byte buffer[] = new byte[4096]; - try { - while ((read = is.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) { - write(buffer, 0, read); - len -= read; - } - } catch (IOException e) { - e.printStackTrace(); - } - } - public synchronized void writeTo(OutputStream out, int off, int len) throws IOException { - out.write(buf, off, len); - } - public ByteArrayInputStream getInputStream() { - return new ByteArrayInputStream(buf, 0, count); - } -} diff --git a/crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java b/crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java deleted file mode 100644 index 45bf1655c..000000000 --- a/crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.topjohnwu.crypto; - -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.GeneralSecurityException; -import java.security.Key; -import java.security.KeyFactory; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.Signature; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -class CryptoUtils { - - private static final Map ID_TO_ALG; - private static final Map ALG_TO_ID; - - static { - ID_TO_ALG = new HashMap<>(); - ALG_TO_ID = new HashMap<>(); - ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA256.getId(), "SHA256withECDSA"); - ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA384.getId(), "SHA384withECDSA"); - ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA512.getId(), "SHA512withECDSA"); - ID_TO_ALG.put(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "SHA1withRSA"); - ID_TO_ALG.put(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(), "SHA256withRSA"); - ID_TO_ALG.put(PKCSObjectIdentifiers.sha512WithRSAEncryption.getId(), "SHA512withRSA"); - ALG_TO_ID.put("SHA256withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256.getId()); - ALG_TO_ID.put("SHA384withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384.getId()); - ALG_TO_ID.put("SHA512withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512.getId()); - ALG_TO_ID.put("SHA1withRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption.getId()); - ALG_TO_ID.put("SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption.getId()); - ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId()); - } - - private static String getSignatureAlgorithm(Key key) throws Exception { - if ("EC".equals(key.getAlgorithm())) { - int curveSize; - KeyFactory factory = KeyFactory.getInstance("EC"); - if (key instanceof PublicKey) { - ECPublicKeySpec spec = factory.getKeySpec(key, ECPublicKeySpec.class); - curveSize = spec.getParams().getCurve().getField().getFieldSize(); - } else if (key instanceof PrivateKey) { - ECPrivateKeySpec spec = factory.getKeySpec(key, ECPrivateKeySpec.class); - curveSize = spec.getParams().getCurve().getField().getFieldSize(); - } else { - throw new InvalidKeySpecException(); - } - if (curveSize <= 256) { - return "SHA256withECDSA"; - } else if (curveSize <= 384) { - return "SHA384withECDSA"; - } else { - return "SHA512withECDSA"; - } - } else if ("RSA".equals(key.getAlgorithm())) { - return "SHA256withRSA"; - } else { - throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm()); - } - } - - static AlgorithmIdentifier getSignatureAlgorithmIdentifier(Key key) throws Exception { - String id = ALG_TO_ID.get(getSignatureAlgorithm(key)); - if (id == null) { - throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm()); - } - return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id)); - } - - static boolean verify(PublicKey key, byte[] input, byte[] signature, - AlgorithmIdentifier algId) throws Exception { - String algName = ID_TO_ALG.get(algId.getAlgorithm().getId()); - if (algName == null) { - throw new IllegalArgumentException("Unsupported algorithm " + algId.getAlgorithm()); - } - Signature verifier = Signature.getInstance(algName); - verifier.initVerify(key); - verifier.update(input); - return verifier.verify(signature); - } - - static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception { - Signature signer = Signature.getInstance(getSignatureAlgorithm(privateKey)); - signer.initSign(privateKey); - signer.update(input); - return signer.sign(); - } - - static X509Certificate readPublicKey(InputStream input) - throws IOException, GeneralSecurityException { - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - return (X509Certificate) cf.generateCertificate(input); - } finally { - input.close(); - } - } - - /** Read a PKCS#8 format private key. */ - static PrivateKey readPrivateKey(InputStream input) - throws IOException, GeneralSecurityException { - try { - byte[] buffer = new byte[4096]; - int size = input.read(buffer); - byte[] bytes = Arrays.copyOf(buffer, size); - /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */ - PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes); - /* - * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm - * OID and use that to construct a KeyFactory. - */ - ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded())); - PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject()); - String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId(); - return KeyFactory.getInstance(algOid).generatePrivate(spec); - } finally { - input.close(); - } - } -} diff --git a/crypto/src/main/java/com/topjohnwu/crypto/JarMap.java b/crypto/src/main/java/com/topjohnwu/crypto/JarMap.java deleted file mode 100644 index 9db773949..000000000 --- a/crypto/src/main/java/com/topjohnwu/crypto/JarMap.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.topjohnwu.crypto; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; -import java.util.Enumeration; -import java.util.LinkedHashMap; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.JarInputStream; -import java.util.jar.Manifest; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/* -* A universal random access interface for both JarFile and JarInputStream -* -* In the case when JarInputStream is provided to constructor, the whole stream -* will be loaded into memory for random access purposes. -* On the other hand, when a JarFile is provided, it simply works as a wrapper. -* */ - -public class JarMap implements Closeable, AutoCloseable { - private JarFile jarFile; - private JarInputStream jis; - private boolean isInputStream = false; - private LinkedHashMap bufMap; - - public JarMap(File file) throws IOException { - this(file, true); - } - - public JarMap(File file, boolean verify) throws IOException { - this(file, verify, ZipFile.OPEN_READ); - } - - public JarMap(File file, boolean verify, int mode) throws IOException { - jarFile = new JarFile(file, verify, mode); - } - - public JarMap(String name) throws IOException { - this(new File(name)); - } - - public JarMap(String name, boolean verify) throws IOException { - this(new File(name), verify); - } - - public JarMap(InputStream is) throws IOException { - this(is, true); - } - - public JarMap(InputStream is, boolean verify) throws IOException { - isInputStream = true; - bufMap = new LinkedHashMap<>(); - jis = new JarInputStream(is, verify); - JarEntry entry; - while ((entry = jis.getNextJarEntry()) != null) { - bufMap.put(entry.getName(), new JarMapEntry(entry, jis)); - } - } - - public File getFile() { - return isInputStream ? null : new File(jarFile.getName()); - } - - public Manifest getManifest() throws IOException { - return isInputStream ? jis.getManifest() : jarFile.getManifest(); - } - - public InputStream getInputStream(ZipEntry ze) throws IOException { - return isInputStream ? ((JarMapEntry) bufMap.get(ze.getName())).data.getInputStream() : - jarFile.getInputStream(ze); - } - - public OutputStream getOutputStream(ZipEntry ze) { - if (!isInputStream) // Only support InputStream mode - return null; - ByteArrayStream bs = ((JarMapEntry) bufMap.get(ze.getName())).data; - bs.reset(); - return bs; - } - - public byte[] getRawData(ZipEntry ze) throws IOException { - if (isInputStream) { - return ((JarMapEntry) bufMap.get(ze.getName())).data.toByteArray(); - } else { - ByteArrayStream bytes = new ByteArrayStream(); - bytes.readFrom(jarFile.getInputStream(ze)); - return bytes.toByteArray(); - } - } - - public Enumeration entries() { - return isInputStream ? Collections.enumeration(bufMap.values()) : jarFile.entries(); - } - - public ZipEntry getEntry(String name) { - return getJarEntry(name); - } - - public JarEntry getJarEntry(String name) { - return isInputStream ? bufMap.get(name) : jarFile.getJarEntry(name); - } - - @Override - public void close() throws IOException { - (isInputStream ? jis : jarFile).close(); - } - - private static class JarMapEntry extends JarEntry { - ByteArrayStream data; - JarMapEntry(JarEntry je, InputStream is) { - super(je); - data = new ByteArrayStream(); - data.readFrom(is); - } - } -} diff --git a/crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java b/crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java deleted file mode 100644 index 96c2aca5a..000000000 --- a/crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java +++ /dev/null @@ -1,502 +0,0 @@ -package com.topjohnwu.crypto; - -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.DEROutputStream; -import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; -import org.bouncycastle.cert.jcajce.JcaCertStore; -import org.bouncycastle.cms.CMSException; -import org.bouncycastle.cms.CMSProcessableByteArray; -import org.bouncycastle.cms.CMSSignedData; -import org.bouncycastle.cms.CMSSignedDataGenerator; -import org.bouncycastle.cms.CMSTypedData; -import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; -import org.bouncycastle.util.encoders.Base64; - -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.RandomAccessFile; -import java.security.DigestOutputStream; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.Security; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; -import java.util.regex.Pattern; - -/* -* Modified from from AOSP(Marshmallow) SignAPK.java -* */ - -public class SignAPK { - - private static final String CERT_SF_NAME = "META-INF/CERT.SF"; - private static final String CERT_SIG_NAME = "META-INF/CERT.%s"; - - public static Provider sBouncyCastleProvider; - // bitmasks for which hash algorithms we need the manifest to include. - private static final int USE_SHA1 = 1; - private static final int USE_SHA256 = 2; - - static { - sBouncyCastleProvider = new BouncyCastleProvider(); - Security.insertProviderAt(sBouncyCastleProvider, 1); - } - - public static void signZip(InputStream publicIn, InputStream privateIn, - JarMap input, File output, boolean minSign) throws Exception { - int alignment = 4; - BufferedOutputStream outputFile; - int hashes = 0; - X509Certificate publicKey = CryptoUtils.readPublicKey(publicIn); - hashes |= getDigestAlgorithm(publicKey); - - // Set the ZIP file timestamp to the starting valid time - // of the 0th certificate plus one hour (to match what - // we've historically done). - long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000; - PrivateKey privateKey = CryptoUtils.readPrivateKey(privateIn); - - outputFile = new BufferedOutputStream(new FileOutputStream(output)); - if (minSign) { - signWholeFile(input.getFile(), publicKey, privateKey, outputFile); - } else { - JarOutputStream outputJar = new JarOutputStream(outputFile); - // For signing .apks, use the maximum compression to make - // them as small as possible (since they live forever on - // the system partition). For OTA packages, use the - // default compression level, which is much much faster - // and produces output that is only a tiny bit larger - // (~0.1% on full OTA packages I tested). - outputJar.setLevel(9); - Manifest manifest = addDigestsToManifest(input, hashes); - copyFiles(manifest, input, outputJar, timestamp, alignment); - signFile(manifest, input, publicKey, privateKey, outputJar); - outputJar.close(); - } - input.close(); - outputFile.close(); - } - - /** - * Return one of USE_SHA1 or USE_SHA256 according to the signature - * algorithm specified in the cert. - */ - private static int getDigestAlgorithm(X509Certificate cert) { - String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US); - if ("SHA1WITHRSA".equals(sigAlg) || - "MD5WITHRSA".equals(sigAlg)) { // see "HISTORICAL NOTE" above. - return USE_SHA1; - } else if (sigAlg.startsWith("SHA256WITH")) { - return USE_SHA256; - } else { - throw new IllegalArgumentException("unsupported signature algorithm \"" + sigAlg + - "\" in cert [" + cert.getSubjectDN()); - } - } - /** Returns the expected signature algorithm for this key type. */ - private static String getSignatureAlgorithm(X509Certificate cert) { - String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US); - String keyType = cert.getPublicKey().getAlgorithm().toUpperCase(Locale.US); - if ("RSA".equalsIgnoreCase(keyType)) { - if (getDigestAlgorithm(cert) == USE_SHA256) { - return "SHA256withRSA"; - } else { - return "SHA1withRSA"; - } - } else if ("EC".equalsIgnoreCase(keyType)) { - return "SHA256withECDSA"; - } else { - throw new IllegalArgumentException("unsupported key type: " + keyType); - } - } - // Files matching this pattern are not copied to the output. - private static Pattern stripPattern = - Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" + - Pattern.quote(JarFile.MANIFEST_NAME) + ")$"); - - /** - * Add the hash(es) of every file to the manifest, creating it if - * necessary. - */ - private static Manifest addDigestsToManifest(JarMap jar, int hashes) - throws IOException, GeneralSecurityException { - Manifest input = jar.getManifest(); - Manifest output = new Manifest(); - Attributes main = output.getMainAttributes(); - if (input != null) { - main.putAll(input.getMainAttributes()); - } else { - main.putValue("Manifest-Version", "1.0"); - main.putValue("Created-By", "1.0 (Android SignApk)"); - } - MessageDigest md_sha1 = null; - MessageDigest md_sha256 = null; - if ((hashes & USE_SHA1) != 0) { - md_sha1 = MessageDigest.getInstance("SHA1"); - } - if ((hashes & USE_SHA256) != 0) { - md_sha256 = MessageDigest.getInstance("SHA256"); - } - byte[] buffer = new byte[4096]; - int num; - // We sort the input entries by name, and add them to the - // output manifest in sorted order. We expect that the output - // map will be deterministic. - TreeMap byName = new TreeMap(); - for (Enumeration e = jar.entries(); e.hasMoreElements(); ) { - JarEntry entry = e.nextElement(); - byName.put(entry.getName(), entry); - } - for (JarEntry entry: byName.values()) { - String name = entry.getName(); - if (!entry.isDirectory() && - (stripPattern == null || !stripPattern.matcher(name).matches())) { - InputStream data = jar.getInputStream(entry); - while ((num = data.read(buffer)) > 0) { - if (md_sha1 != null) md_sha1.update(buffer, 0, num); - if (md_sha256 != null) md_sha256.update(buffer, 0, num); - } - Attributes attr = null; - if (input != null) attr = input.getAttributes(name); - attr = attr != null ? new Attributes(attr) : new Attributes(); - if (md_sha1 != null) { - attr.putValue("SHA1-Digest", - new String(Base64.encode(md_sha1.digest()), "ASCII")); - } - if (md_sha256 != null) { - attr.putValue("SHA-256-Digest", - new String(Base64.encode(md_sha256.digest()), "ASCII")); - } - output.getEntries().put(name, attr); - } - } - return output; - } - - /** Write to another stream and track how many bytes have been - * written. - */ - private static class CountOutputStream extends FilterOutputStream { - private int mCount; - public CountOutputStream(OutputStream out) { - super(out); - mCount = 0; - } - @Override - public void write(int b) throws IOException { - super.write(b); - mCount++; - } - @Override - public void write(byte[] b, int off, int len) throws IOException { - super.write(b, off, len); - mCount += len; - } - public int size() { - return mCount; - } - } - /** Write a .SF file with a digest of the specified manifest. */ - private static void writeSignatureFile(Manifest manifest, OutputStream out, - int hash) - throws IOException, GeneralSecurityException { - Manifest sf = new Manifest(); - Attributes main = sf.getMainAttributes(); - main.putValue("Signature-Version", "1.0"); - main.putValue("Created-By", "1.0 (Android SignApk)"); - MessageDigest md = MessageDigest.getInstance( - hash == USE_SHA256 ? "SHA256" : "SHA1"); - PrintStream print = new PrintStream( - new DigestOutputStream(new ByteArrayOutputStream(), md), - true, "UTF-8"); - // Digest of the entire manifest - manifest.write(print); - print.flush(); - main.putValue(hash == USE_SHA256 ? "SHA-256-Digest-Manifest" : "SHA1-Digest-Manifest", - new String(Base64.encode(md.digest()), "ASCII")); - Map entries = manifest.getEntries(); - for (Map.Entry entry : entries.entrySet()) { - // Digest of the manifest stanza for this entry. - print.print("Name: " + entry.getKey() + "\r\n"); - for (Map.Entry att : entry.getValue().entrySet()) { - print.print(att.getKey() + ": " + att.getValue() + "\r\n"); - } - print.print("\r\n"); - print.flush(); - Attributes sfAttr = new Attributes(); - sfAttr.putValue(hash == USE_SHA256 ? "SHA-256-Digest" : "SHA1-Digest-Manifest", - new String(Base64.encode(md.digest()), "ASCII")); - sf.getEntries().put(entry.getKey(), sfAttr); - } - CountOutputStream cout = new CountOutputStream(out); - sf.write(cout); - // A bug in the java.util.jar implementation of Android platforms - // up to version 1.6 will cause a spurious IOException to be thrown - // if the length of the signature file is a multiple of 1024 bytes. - // As a workaround, add an extra CRLF in this case. - if ((cout.size() % 1024) == 0) { - cout.write('\r'); - cout.write('\n'); - } - } - /** Sign data and write the digital signature to 'out'. */ - private static void writeSignatureBlock( - CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey, - OutputStream out) - throws IOException, - CertificateEncodingException, - OperatorCreationException, - CMSException { - ArrayList certList = new ArrayList<>(1); - certList.add(publicKey); - JcaCertStore certs = new JcaCertStore(certList); - CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); - ContentSigner signer = new JcaContentSignerBuilder(getSignatureAlgorithm(publicKey)) - .setProvider(sBouncyCastleProvider) - .build(privateKey); - gen.addSignerInfoGenerator( - new JcaSignerInfoGeneratorBuilder( - new JcaDigestCalculatorProviderBuilder() - .setProvider(sBouncyCastleProvider) - .build()) - .setDirectSignature(true) - .build(signer, publicKey)); - gen.addCertificates(certs); - CMSSignedData sigData = gen.generate(data, false); - ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded()); - DEROutputStream dos = new DEROutputStream(out); - dos.writeObject(asn1.readObject()); - } - /** - * Copy all the files in a manifest from input to output. We set - * the modification times in the output to a fixed time, so as to - * reduce variation in the output file and make incremental OTAs - * more efficient. - */ - private static void copyFiles(Manifest manifest, JarMap in, JarOutputStream out, - long timestamp, int alignment) throws IOException { - byte[] buffer = new byte[4096]; - int num; - Map entries = manifest.getEntries(); - ArrayList names = new ArrayList<>(entries.keySet()); - Collections.sort(names); - boolean firstEntry = true; - long offset = 0L; - // We do the copy in two passes -- first copying all the - // entries that are STORED, then copying all the entries that - // have any other compression flag (which in practice means - // DEFLATED). This groups all the stored entries together at - // the start of the file and makes it easier to do alignment - // on them (since only stored entries are aligned). - for (String name : names) { - JarEntry inEntry = in.getJarEntry(name); - JarEntry outEntry = null; - if (inEntry.getMethod() != JarEntry.STORED) continue; - // Preserve the STORED method of the input entry. - outEntry = new JarEntry(inEntry); - outEntry.setTime(timestamp); - // 'offset' is the offset into the file at which we expect - // the file data to begin. This is the value we need to - // make a multiple of 'alignement'. - offset += JarFile.LOCHDR + outEntry.getName().length(); - if (firstEntry) { - // The first entry in a jar file has an extra field of - // four bytes that you can't get rid of; any extra - // data you specify in the JarEntry is appended to - // these forced four bytes. This is JAR_MAGIC in - // JarOutputStream; the bytes are 0xfeca0000. - offset += 4; - firstEntry = false; - } - if (alignment > 0 && (offset % alignment != 0)) { - // Set the "extra data" of the entry to between 1 and - // alignment-1 bytes, to make the file data begin at - // an aligned offset. - int needed = alignment - (int)(offset % alignment); - outEntry.setExtra(new byte[needed]); - offset += needed; - } - out.putNextEntry(outEntry); - InputStream data = in.getInputStream(inEntry); - while ((num = data.read(buffer)) > 0) { - out.write(buffer, 0, num); - offset += num; - } - out.flush(); - } - // Copy all the non-STORED entries. We don't attempt to - // maintain the 'offset' variable past this point; we don't do - // alignment on these entries. - for (String name : names) { - JarEntry inEntry = in.getJarEntry(name); - JarEntry outEntry = null; - if (inEntry.getMethod() == JarEntry.STORED) continue; - // Create a new entry so that the compressed len is recomputed. - outEntry = new JarEntry(name); - outEntry.setTime(timestamp); - out.putNextEntry(outEntry); - InputStream data = in.getInputStream(inEntry); - while ((num = data.read(buffer)) > 0) { - out.write(buffer, 0, num); - } - out.flush(); - } - } - - // This class is to provide a file's content, but trimming out the last two bytes - // Used for signWholeFile - private static class CMSProcessableFile implements CMSTypedData { - - private ASN1ObjectIdentifier type; - private RandomAccessFile file; - - CMSProcessableFile(File file) throws FileNotFoundException { - this.file = new RandomAccessFile(file, "r"); - type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()); - } - - @Override - public ASN1ObjectIdentifier getContentType() { - return type; - } - - @Override - public void write(OutputStream out) throws IOException, CMSException { - file.seek(0); - int read; - byte buffer[] = new byte[4096]; - int len = (int) file.length() - 2; - while ((read = file.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) { - out.write(buffer, 0, read); - len -= read; - } - } - - @Override - public Object getContent() { - return file; - } - - byte[] getTail() throws IOException { - byte tail[] = new byte[22]; - file.seek(file.length() - 22); - file.readFully(tail); - return tail; - } - } - - private static void signWholeFile(File input, X509Certificate publicKey, - PrivateKey privateKey, OutputStream outputStream) - throws Exception { - ByteArrayOutputStream temp = new ByteArrayOutputStream(); - // put a readable message and a null char at the start of the - // archive comment, so that tools that display the comment - // (hopefully) show something sensible. - // TODO: anything more useful we can put in this message? - byte[] message = "signed by SignApk".getBytes("UTF-8"); - temp.write(message); - temp.write(0); - - CMSProcessableFile cmsFile = new CMSProcessableFile(input); - writeSignatureBlock(cmsFile, publicKey, privateKey, temp); - - // For a zip with no archive comment, the - // end-of-central-directory record will be 22 bytes long, so - // we expect to find the EOCD marker 22 bytes from the end. - byte[] zipData = cmsFile.getTail(); - if (zipData[zipData.length-22] != 0x50 || - zipData[zipData.length-21] != 0x4b || - zipData[zipData.length-20] != 0x05 || - zipData[zipData.length-19] != 0x06) { - throw new IllegalArgumentException("zip data already has an archive comment"); - } - int total_size = temp.size() + 6; - if (total_size > 0xffff) { - throw new IllegalArgumentException("signature is too big for ZIP file comment"); - } - // signature starts this many bytes from the end of the file - int signature_start = total_size - message.length - 1; - temp.write(signature_start & 0xff); - temp.write((signature_start >> 8) & 0xff); - // Why the 0xff bytes? In a zip file with no archive comment, - // bytes [-6:-2] of the file are the little-endian offset from - // the start of the file to the central directory. So for the - // two high bytes to be 0xff 0xff, the archive would have to - // be nearly 4GB in size. So it's unlikely that a real - // commentless archive would have 0xffs here, and lets us tell - // an old signed archive from a new one. - temp.write(0xff); - temp.write(0xff); - temp.write(total_size & 0xff); - temp.write((total_size >> 8) & 0xff); - temp.flush(); - // Signature verification checks that the EOCD header is the - // last such sequence in the file (to avoid minzip finding a - // fake EOCD appended after the signature in its scan). The - // odds of producing this sequence by chance are very low, but - // let's catch it here if it does. - byte[] b = temp.toByteArray(); - for (int i = 0; i < b.length-3; ++i) { - if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) { - throw new IllegalArgumentException("found spurious EOCD header at " + i); - } - } - cmsFile.write(outputStream); - outputStream.write(total_size & 0xff); - outputStream.write((total_size >> 8) & 0xff); - temp.writeTo(outputStream); - } - private static void signFile(Manifest manifest, JarMap inputJar, - X509Certificate publicKey, PrivateKey privateKey, - JarOutputStream outputJar) - throws Exception { - // Assume the certificate is valid for at least an hour. - long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000; - // MANIFEST.MF - JarEntry je = new JarEntry(JarFile.MANIFEST_NAME); - je.setTime(timestamp); - outputJar.putNextEntry(je); - manifest.write(outputJar); - je = new JarEntry(CERT_SF_NAME); - je.setTime(timestamp); - outputJar.putNextEntry(je); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - writeSignatureFile(manifest, baos, getDigestAlgorithm(publicKey)); - byte[] signedData = baos.toByteArray(); - outputJar.write(signedData); - // CERT.{EC,RSA} / CERT#.{EC,RSA} - final String keyType = publicKey.getPublicKey().getAlgorithm(); - je = new JarEntry(String.format(CERT_SIG_NAME, keyType)); - je.setTime(timestamp); - outputJar.putNextEntry(je); - writeSignatureBlock(new CMSProcessableByteArray(signedData), - publicKey, privateKey, outputJar); - } -} diff --git a/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java b/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java deleted file mode 100644 index beb434274..000000000 --- a/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java +++ /dev/null @@ -1,231 +0,0 @@ -package com.topjohnwu.crypto; - -import org.bouncycastle.asn1.ASN1Encodable; -import org.bouncycastle.asn1.ASN1EncodableVector; -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1Integer; -import org.bouncycastle.asn1.ASN1Object; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.DERPrintableString; -import org.bouncycastle.asn1.DERSequence; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.Security; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Arrays; - -public class SignBoot { - - static { - Security.addProvider(new BouncyCastleProvider()); - } - - public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut, - InputStream keyIn, InputStream certIn) { - try { - ByteArrayStream bas = new ByteArrayStream(); - bas.readFrom(imgIn); - byte[] image = bas.toByteArray(); - bas.close(); - int signableSize = getSignableImageSize(image); - if (signableSize < image.length) { - System.err.println("NOTE: truncating input from " + - image.length + " to " + signableSize + " bytes"); - image = Arrays.copyOf(image, signableSize); - } else if (signableSize > image.length) { - throw new IllegalArgumentException("Invalid image: too short, expected " + - signableSize + " bytes"); - } - BootSignature bootsig = new BootSignature(target, image.length); - X509Certificate cert = CryptoUtils.readPublicKey(certIn); - bootsig.setCertificate(cert); - PrivateKey key = CryptoUtils.readPrivateKey(keyIn); - bootsig.setSignature(bootsig.sign(image, key), - CryptoUtils.getSignatureAlgorithmIdentifier(key)); - byte[] encoded_bootsig = bootsig.getEncoded(); - imgOut.write(image); - imgOut.write(encoded_bootsig); - imgOut.flush(); - return true; - } catch (Exception e) { - e.printStackTrace(System.err); - return false; - } - } - - public static boolean verifySignature(InputStream imgIn, InputStream certPath) { - try { - ByteArrayStream bas = new ByteArrayStream(); - bas.readFrom(imgIn); - byte[] image = bas.toByteArray(); - bas.close(); - int signableSize = getSignableImageSize(image); - if (signableSize >= image.length) { - System.err.println("Invalid image: not signed"); - return false; - } - byte[] signature = Arrays.copyOfRange(image, signableSize, image.length); - BootSignature bootsig = new BootSignature(signature); - if (certPath != null) { - bootsig.setCertificate(CryptoUtils.readPublicKey(certPath)); - } - if (bootsig.verify(Arrays.copyOf(image, signableSize))) { - System.err.println("Signature is VALID"); - return true; - } else { - System.err.println("Signature is INVALID"); - } - } catch (Exception e) { - e.printStackTrace(System.err); - System.err.println("Invalid image: not signed"); - } - return false; - } - - public static int getSignableImageSize(byte[] data) throws Exception { - if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8), - "ANDROID!".getBytes("US-ASCII"))) { - throw new IllegalArgumentException("Invalid image header: missing magic"); - } - ByteBuffer image = ByteBuffer.wrap(data); - image.order(ByteOrder.LITTLE_ENDIAN); - image.getLong(); // magic - int kernelSize = image.getInt(); - image.getInt(); // kernel_addr - int ramdskSize = image.getInt(); - image.getInt(); // ramdisk_addr - int secondSize = image.getInt(); - image.getLong(); // second_addr + tags_addr - int pageSize = image.getInt(); - int length = pageSize // include the page aligned image header - + ((kernelSize + pageSize - 1) / pageSize) * pageSize - + ((ramdskSize + pageSize - 1) / pageSize) * pageSize - + ((secondSize + pageSize - 1) / pageSize) * pageSize; - length = ((length + pageSize - 1) / pageSize) * pageSize; - if (length <= 0) { - throw new IllegalArgumentException("Invalid image header: invalid length"); - } - return length; - } - - static class BootSignature extends ASN1Object { - private ASN1Integer formatVersion; - private ASN1Encodable certificate; - private AlgorithmIdentifier algorithmIdentifier; - private DERPrintableString target; - private ASN1Integer length; - private DEROctetString signature; - private PublicKey publicKey; - private static final int FORMAT_VERSION = 1; - - /** - * Initializes the object for signing an image file - * @param target Target name, included in the signed data - * @param length Length of the image, included in the signed data - */ - public BootSignature(String target, int length) { - this.formatVersion = new ASN1Integer(FORMAT_VERSION); - this.target = new DERPrintableString(target); - this.length = new ASN1Integer(length); - } - - /** - * Initializes the object for verifying a signed image file - * @param signature Signature footer - */ - public BootSignature(byte[] signature) - throws Exception { - ASN1InputStream stream = new ASN1InputStream(signature); - ASN1Sequence sequence = (ASN1Sequence) stream.readObject(); - formatVersion = (ASN1Integer) sequence.getObjectAt(0); - if (formatVersion.getValue().intValue() != FORMAT_VERSION) { - throw new IllegalArgumentException("Unsupported format version"); - } - certificate = sequence.getObjectAt(1); - byte[] encoded = ((ASN1Object) certificate).getEncoded(); - ByteArrayInputStream bis = new ByteArrayInputStream(encoded); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate c = (X509Certificate) cf.generateCertificate(bis); - publicKey = c.getPublicKey(); - ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2); - algorithmIdentifier = new AlgorithmIdentifier( - (ASN1ObjectIdentifier) algId.getObjectAt(0)); - ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3); - target = (DERPrintableString) attrs.getObjectAt(0); - length = (ASN1Integer) attrs.getObjectAt(1); - this.signature = (DEROctetString) sequence.getObjectAt(4); - } - - public ASN1Object getAuthenticatedAttributes() { - ASN1EncodableVector attrs = new ASN1EncodableVector(); - attrs.add(target); - attrs.add(length); - return new DERSequence(attrs); - } - - public byte[] getEncodedAuthenticatedAttributes() throws IOException { - return getAuthenticatedAttributes().getEncoded(); - } - - public void setSignature(byte[] sig, AlgorithmIdentifier algId) { - algorithmIdentifier = algId; - signature = new DEROctetString(sig); - } - - public void setCertificate(X509Certificate cert) - throws Exception, IOException, CertificateEncodingException { - ASN1InputStream s = new ASN1InputStream(cert.getEncoded()); - certificate = s.readObject(); - publicKey = cert.getPublicKey(); - } - - public byte[] generateSignableImage(byte[] image) throws IOException { - byte[] attrs = getEncodedAuthenticatedAttributes(); - byte[] signable = Arrays.copyOf(image, image.length + attrs.length); - for (int i=0; i < attrs.length; i++) { - signable[i+image.length] = attrs[i]; - } - return signable; - } - - public byte[] sign(byte[] image, PrivateKey key) throws Exception { - byte[] signable = generateSignableImage(image); - return CryptoUtils.sign(key, signable); - } - - public boolean verify(byte[] image) throws Exception { - if (length.getValue().intValue() != image.length) { - throw new IllegalArgumentException("Invalid image length"); - } - byte[] signable = generateSignableImage(image); - return CryptoUtils.verify(publicKey, signable, signature.getOctets(), - algorithmIdentifier); - } - - @Override - public ASN1Primitive toASN1Primitive() { - ASN1EncodableVector v = new ASN1EncodableVector(); - v.add(formatVersion); - v.add(certificate); - v.add(algorithmIdentifier); - v.add(getAuthenticatedAttributes()); - v.add(signature); - return new DERSequence(v); - } - - } -} diff --git a/crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java b/crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java deleted file mode 100644 index 6b4599dbf..000000000 --- a/crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.topjohnwu.crypto; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.security.Security; - -public class ZipSigner { - public static void main(String[] args) { - boolean minSign = false; - int argStart = 0; - - if (args.length < 4) { - System.err.println("Usage: zipsigner [-m] publickey.x509[.pem] privatekey.pk8 input.jar output.jar"); - System.exit(2); - } - - if (args[0].equals("-m")) { - minSign = true; - argStart = 1; - } - - SignAPK.sBouncyCastleProvider = new BouncyCastleProvider(); - Security.insertProviderAt(SignAPK.sBouncyCastleProvider, 1); - - File pubKey = new File(args[argStart]); - File privKey = new File(args[argStart + 1]); - File input = new File(args[argStart + 2]); - File output = new File(args[argStart + 3]); - - try (InputStream pub = new FileInputStream(pubKey); - InputStream priv = new FileInputStream(privKey); - JarMap jar = new JarMap(input, false)) { - SignAPK.signZip(pub, priv, jar, output, minSign); - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - } -} diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index d13e92712..000000000 --- a/gradle.properties +++ /dev/null @@ -1,22 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -org.gradle.jvmargs=-Xmx2560m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -org.gradle.parallel=true - -# When set to true the Gradle daemon is used to run the build. For local developer builds this is our favorite property. -# The developer environment is optimized for speed and feedback so we nearly always run Gradle jobs with the daemon. -org.gradle.daemon=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ed88a042a..000000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index c583957d2..000000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-all.zip diff --git a/gradlew b/gradlew deleted file mode 100755 index cccdd3d51..000000000 --- a/gradlew +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index e95643d6a..000000000 --- a/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/app/proguard-rules.pro b/proguard-rules.pro similarity index 100% rename from app/proguard-rules.pro rename to proguard-rules.pro diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 9f2cf784e..000000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app', ':snet', ':crypto' diff --git a/snet/.gitignore b/snet/.gitignore deleted file mode 100644 index 796b96d1c..000000000 --- a/snet/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/snet/build.gradle b/snet/build.gradle deleted file mode 100644 index 5267cb9ae..000000000 --- a/snet/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 27 - buildToolsVersion "27.0.1" - - defaultConfig { - applicationId "com.topjohnwu.snet" - minSdkVersion 21 - targetSdkVersion 27 - versionCode 1 - versionName "1.0" - } - - buildTypes { - release { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -repositories { - google() -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.google.android.gms:play-services-safetynet:11.6.0' -} diff --git a/snet/proguard-rules.pro b/snet/proguard-rules.pro deleted file mode 100644 index d6e59f6f5..000000000 --- a/snet/proguard-rules.pro +++ /dev/null @@ -1,24 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile - --keep class com.topjohnwu.snet.SafetyNet* { *; } --dontwarn java.lang.invoke** diff --git a/snet/src/main/AndroidManifest.xml b/snet/src/main/AndroidManifest.xml deleted file mode 100644 index 4fa18362e..000000000 --- a/snet/src/main/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/snet/src/main/java/com/topjohnwu/snet/SafetyNetCallback.java b/snet/src/main/java/com/topjohnwu/snet/SafetyNetCallback.java deleted file mode 100644 index fa680422d..000000000 --- a/snet/src/main/java/com/topjohnwu/snet/SafetyNetCallback.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.topjohnwu.snet; - -public interface SafetyNetCallback { - void onResponse(int responseCode); -} diff --git a/snet/src/main/java/com/topjohnwu/snet/SafetyNetHelper.java b/snet/src/main/java/com/topjohnwu/snet/SafetyNetHelper.java deleted file mode 100644 index a66634160..000000000 --- a/snet/src/main/java/com/topjohnwu/snet/SafetyNetHelper.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.topjohnwu.snet; - -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Base64; - -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; -import com.google.android.gms.safetynet.SafetyNet; -import com.google.android.gms.safetynet.SafetyNetApi; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.lang.reflect.Field; -import java.security.SecureRandom; - -public class SafetyNetHelper - implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { - - public static final int CAUSE_SERVICE_DISCONNECTED = 0x01; - public static final int CAUSE_NETWORK_LOST = 0x02; - public static final int RESPONSE_ERR = 0x04; - public static final int CONNECTION_FAIL = 0x08; - - public static final int BASIC_PASS = 0x10; - public static final int CTS_PASS = 0x20; - - private GoogleApiClient mGoogleApiClient; - private Activity mActivity; - private int responseCode; - private SafetyNetCallback cb; - private String dexPath; - private boolean isDarkTheme; - - public SafetyNetHelper(Activity activity, String dexPath, SafetyNetCallback cb) { - mActivity = activity; - this.cb = cb; - this.dexPath = dexPath; - responseCode = 0; - - // Get theme - try { - Context context = activity.getApplicationContext(); - Field theme = context.getClass().getField("isDarkTheme"); - isDarkTheme = (boolean) theme.get(context); - } catch (Exception e) { - e.printStackTrace(); - } - } - - // Entry point to start test - public void attest() { - // Connect Google Service - mGoogleApiClient = new GoogleApiClient.Builder(mActivity) - .addApi(SafetyNet.API) - .addOnConnectionFailedListener(this) - .addConnectionCallbacks(this) - .build(); - mGoogleApiClient.connect(); - } - - @Override - public void onConnectionSuspended(int i) { - cb.onResponse(i); - } - - @Override - public void onConnectionFailed(@NonNull ConnectionResult result) { - Class clazz = mActivity.getClass(); - try { - // Use external resources - clazz.getMethod("swapResources", String.class, int.class).invoke(mActivity, dexPath, - isDarkTheme ? android.R.style.Theme_Material : android.R.style.Theme_Material_Light); - try { - GoogleApiAvailability.getInstance().getErrorDialog(mActivity, result.getErrorCode(), 0).show(); - } catch (Exception e) { - e.printStackTrace(); - } - clazz.getMethod("restoreResources").invoke(mActivity); - } catch (Exception e) { - e.printStackTrace(); - } - cb.onResponse(CONNECTION_FAIL); - } - - @Override - public void onConnected(@Nullable Bundle bundle) { - // Create nonce - byte[] nonce = new byte[24]; - new SecureRandom().nextBytes(nonce); - - // Call SafetyNet - SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce) - .setResultCallback(new ResultCallback() { - @Override - public void onResult(@NonNull SafetyNetApi.AttestationResult result) { - Status status = result.getStatus(); - try { - if (!status.isSuccess()) throw new JSONException(""); - String json = new String(Base64.decode( - result.getJwsResult().split("\\.")[1], Base64.DEFAULT)); - JSONObject decoded = new JSONObject(json); - responseCode |= decoded.getBoolean("ctsProfileMatch") ? CTS_PASS : 0; - responseCode |= decoded.getBoolean("basicIntegrity") ? BASIC_PASS : 0; - } catch (JSONException e) { - responseCode = RESPONSE_ERR; - } - - // Disconnect - mGoogleApiClient.disconnect(); - - // Return results - cb.onResponse(responseCode); - } - }); - } -} diff --git a/app/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml similarity index 100% rename from app/src/main/AndroidManifest.xml rename to src/main/AndroidManifest.xml diff --git a/app/src/main/assets/changelog.md b/src/main/assets/changelog.md similarity index 100% rename from app/src/main/assets/changelog.md rename to src/main/assets/changelog.md diff --git a/app/src/main/assets/dark.css b/src/main/assets/dark.css similarity index 100% rename from app/src/main/assets/dark.css rename to src/main/assets/dark.css diff --git a/app/src/main/assets/light.css b/src/main/assets/light.css similarity index 100% rename from app/src/main/assets/light.css rename to src/main/assets/light.css diff --git a/app/src/main/ic_launcher-web.png b/src/main/ic_launcher-web.png similarity index 100% rename from app/src/main/ic_launcher-web.png rename to src/main/ic_launcher-web.png diff --git a/app/src/main/java/com/topjohnwu/magisk/AboutActivity.java b/src/main/java/com/topjohnwu/magisk/AboutActivity.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/AboutActivity.java rename to src/main/java/com/topjohnwu/magisk/AboutActivity.java diff --git a/app/src/main/java/com/topjohnwu/magisk/FlashActivity.java b/src/main/java/com/topjohnwu/magisk/FlashActivity.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/FlashActivity.java rename to src/main/java/com/topjohnwu/magisk/FlashActivity.java diff --git a/app/src/main/java/com/topjohnwu/magisk/LogFragment.java b/src/main/java/com/topjohnwu/magisk/LogFragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/LogFragment.java rename to src/main/java/com/topjohnwu/magisk/LogFragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java b/src/main/java/com/topjohnwu/magisk/MagiskFragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java rename to src/main/java/com/topjohnwu/magisk/MagiskFragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskHideFragment.java b/src/main/java/com/topjohnwu/magisk/MagiskHideFragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/MagiskHideFragment.java rename to src/main/java/com/topjohnwu/magisk/MagiskHideFragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java b/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java rename to src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java b/src/main/java/com/topjohnwu/magisk/MagiskManager.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/MagiskManager.java rename to src/main/java/com/topjohnwu/magisk/MagiskManager.java diff --git a/app/src/main/java/com/topjohnwu/magisk/MainActivity.java b/src/main/java/com/topjohnwu/magisk/MainActivity.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/MainActivity.java rename to src/main/java/com/topjohnwu/magisk/MainActivity.java diff --git a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java b/src/main/java/com/topjohnwu/magisk/ModulesFragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java rename to src/main/java/com/topjohnwu/magisk/ModulesFragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java b/src/main/java/com/topjohnwu/magisk/ReposFragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/ReposFragment.java rename to src/main/java/com/topjohnwu/magisk/ReposFragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java b/src/main/java/com/topjohnwu/magisk/SettingsActivity.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java rename to src/main/java/com/topjohnwu/magisk/SettingsActivity.java diff --git a/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java b/src/main/java/com/topjohnwu/magisk/SplashActivity.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/SplashActivity.java rename to src/main/java/com/topjohnwu/magisk/SplashActivity.java diff --git a/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java b/src/main/java/com/topjohnwu/magisk/SuLogFragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java rename to src/main/java/com/topjohnwu/magisk/SuLogFragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java b/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java rename to src/main/java/com/topjohnwu/magisk/SuperuserFragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/ApplicationAdapter.java b/src/main/java/com/topjohnwu/magisk/adapters/ApplicationAdapter.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/adapters/ApplicationAdapter.java rename to src/main/java/com/topjohnwu/magisk/adapters/ApplicationAdapter.java diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/ModulesAdapter.java b/src/main/java/com/topjohnwu/magisk/adapters/ModulesAdapter.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/adapters/ModulesAdapter.java rename to src/main/java/com/topjohnwu/magisk/adapters/ModulesAdapter.java diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java b/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java rename to src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/ReposAdapter.java b/src/main/java/com/topjohnwu/magisk/adapters/ReposAdapter.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/adapters/ReposAdapter.java rename to src/main/java/com/topjohnwu/magisk/adapters/ReposAdapter.java diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/SectionedAdapter.java b/src/main/java/com/topjohnwu/magisk/adapters/SectionedAdapter.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/adapters/SectionedAdapter.java rename to src/main/java/com/topjohnwu/magisk/adapters/SectionedAdapter.java diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java b/src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java rename to src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/TabFragmentAdapter.java b/src/main/java/com/topjohnwu/magisk/adapters/TabFragmentAdapter.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/adapters/TabFragmentAdapter.java rename to src/main/java/com/topjohnwu/magisk/adapters/TabFragmentAdapter.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/CheckSafetyNet.java b/src/main/java/com/topjohnwu/magisk/asyncs/CheckSafetyNet.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/CheckSafetyNet.java rename to src/main/java/com/topjohnwu/magisk/asyncs/CheckSafetyNet.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/CheckUpdates.java b/src/main/java/com/topjohnwu/magisk/asyncs/CheckUpdates.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/CheckUpdates.java rename to src/main/java/com/topjohnwu/magisk/asyncs/CheckUpdates.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java b/src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java rename to src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/HideManager.java b/src/main/java/com/topjohnwu/magisk/asyncs/HideManager.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/HideManager.java rename to src/main/java/com/topjohnwu/magisk/asyncs/HideManager.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java b/src/main/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java rename to src/main/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java b/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java rename to src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/MarkDownWindow.java b/src/main/java/com/topjohnwu/magisk/asyncs/MarkDownWindow.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/MarkDownWindow.java rename to src/main/java/com/topjohnwu/magisk/asyncs/MarkDownWindow.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/ParallelTask.java b/src/main/java/com/topjohnwu/magisk/asyncs/ParallelTask.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/ParallelTask.java rename to src/main/java/com/topjohnwu/magisk/asyncs/ParallelTask.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/ProcessRepoZip.java b/src/main/java/com/topjohnwu/magisk/asyncs/ProcessRepoZip.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/ProcessRepoZip.java rename to src/main/java/com/topjohnwu/magisk/asyncs/ProcessRepoZip.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/RestoreStockBoot.java b/src/main/java/com/topjohnwu/magisk/asyncs/RestoreStockBoot.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/RestoreStockBoot.java rename to src/main/java/com/topjohnwu/magisk/asyncs/RestoreStockBoot.java diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java b/src/main/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java rename to src/main/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java diff --git a/app/src/main/java/com/topjohnwu/magisk/components/AboutCardRow.java b/src/main/java/com/topjohnwu/magisk/components/AboutCardRow.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/components/AboutCardRow.java rename to src/main/java/com/topjohnwu/magisk/components/AboutCardRow.java diff --git a/app/src/main/java/com/topjohnwu/magisk/components/Activity.java b/src/main/java/com/topjohnwu/magisk/components/Activity.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/components/Activity.java rename to src/main/java/com/topjohnwu/magisk/components/Activity.java diff --git a/app/src/main/java/com/topjohnwu/magisk/components/AlertDialogBuilder.java b/src/main/java/com/topjohnwu/magisk/components/AlertDialogBuilder.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/components/AlertDialogBuilder.java rename to src/main/java/com/topjohnwu/magisk/components/AlertDialogBuilder.java diff --git a/app/src/main/java/com/topjohnwu/magisk/components/ExpandableView.java b/src/main/java/com/topjohnwu/magisk/components/ExpandableView.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/components/ExpandableView.java rename to src/main/java/com/topjohnwu/magisk/components/ExpandableView.java diff --git a/app/src/main/java/com/topjohnwu/magisk/components/Fragment.java b/src/main/java/com/topjohnwu/magisk/components/Fragment.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/components/Fragment.java rename to src/main/java/com/topjohnwu/magisk/components/Fragment.java diff --git a/app/src/main/java/com/topjohnwu/magisk/components/SnackbarMaker.java b/src/main/java/com/topjohnwu/magisk/components/SnackbarMaker.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/components/SnackbarMaker.java rename to src/main/java/com/topjohnwu/magisk/components/SnackbarMaker.java diff --git a/app/src/main/java/com/topjohnwu/magisk/container/BaseModule.java b/src/main/java/com/topjohnwu/magisk/container/BaseModule.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/container/BaseModule.java rename to src/main/java/com/topjohnwu/magisk/container/BaseModule.java diff --git a/app/src/main/java/com/topjohnwu/magisk/container/CallbackList.java b/src/main/java/com/topjohnwu/magisk/container/CallbackList.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/container/CallbackList.java rename to src/main/java/com/topjohnwu/magisk/container/CallbackList.java diff --git a/app/src/main/java/com/topjohnwu/magisk/container/Module.java b/src/main/java/com/topjohnwu/magisk/container/Module.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/container/Module.java rename to src/main/java/com/topjohnwu/magisk/container/Module.java diff --git a/app/src/main/java/com/topjohnwu/magisk/container/Policy.java b/src/main/java/com/topjohnwu/magisk/container/Policy.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/container/Policy.java rename to src/main/java/com/topjohnwu/magisk/container/Policy.java diff --git a/app/src/main/java/com/topjohnwu/magisk/container/Repo.java b/src/main/java/com/topjohnwu/magisk/container/Repo.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/container/Repo.java rename to src/main/java/com/topjohnwu/magisk/container/Repo.java diff --git a/app/src/main/java/com/topjohnwu/magisk/container/SuLogEntry.java b/src/main/java/com/topjohnwu/magisk/container/SuLogEntry.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/container/SuLogEntry.java rename to src/main/java/com/topjohnwu/magisk/container/SuLogEntry.java diff --git a/app/src/main/java/com/topjohnwu/magisk/container/TarEntry.java b/src/main/java/com/topjohnwu/magisk/container/TarEntry.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/container/TarEntry.java rename to src/main/java/com/topjohnwu/magisk/container/TarEntry.java diff --git a/app/src/main/java/com/topjohnwu/magisk/container/ValueSortedMap.java b/src/main/java/com/topjohnwu/magisk/container/ValueSortedMap.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/container/ValueSortedMap.java rename to src/main/java/com/topjohnwu/magisk/container/ValueSortedMap.java diff --git a/app/src/main/java/com/topjohnwu/magisk/database/RepoDatabaseHelper.java b/src/main/java/com/topjohnwu/magisk/database/RepoDatabaseHelper.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/database/RepoDatabaseHelper.java rename to src/main/java/com/topjohnwu/magisk/database/RepoDatabaseHelper.java diff --git a/app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java b/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java rename to src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java diff --git a/app/src/main/java/com/topjohnwu/magisk/receivers/BootReceiver.java b/src/main/java/com/topjohnwu/magisk/receivers/BootReceiver.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/receivers/BootReceiver.java rename to src/main/java/com/topjohnwu/magisk/receivers/BootReceiver.java diff --git a/app/src/main/java/com/topjohnwu/magisk/receivers/DownloadReceiver.java b/src/main/java/com/topjohnwu/magisk/receivers/DownloadReceiver.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/receivers/DownloadReceiver.java rename to src/main/java/com/topjohnwu/magisk/receivers/DownloadReceiver.java diff --git a/app/src/main/java/com/topjohnwu/magisk/receivers/ManagerUpdate.java b/src/main/java/com/topjohnwu/magisk/receivers/ManagerUpdate.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/receivers/ManagerUpdate.java rename to src/main/java/com/topjohnwu/magisk/receivers/ManagerUpdate.java diff --git a/app/src/main/java/com/topjohnwu/magisk/receivers/PackageReceiver.java b/src/main/java/com/topjohnwu/magisk/receivers/PackageReceiver.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/receivers/PackageReceiver.java rename to src/main/java/com/topjohnwu/magisk/receivers/PackageReceiver.java diff --git a/app/src/main/java/com/topjohnwu/magisk/receivers/RebootReceiver.java b/src/main/java/com/topjohnwu/magisk/receivers/RebootReceiver.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/receivers/RebootReceiver.java rename to src/main/java/com/topjohnwu/magisk/receivers/RebootReceiver.java diff --git a/app/src/main/java/com/topjohnwu/magisk/services/OnBootIntentService.java b/src/main/java/com/topjohnwu/magisk/services/OnBootIntentService.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/services/OnBootIntentService.java rename to src/main/java/com/topjohnwu/magisk/services/OnBootIntentService.java diff --git a/app/src/main/java/com/topjohnwu/magisk/services/UpdateCheckService.java b/src/main/java/com/topjohnwu/magisk/services/UpdateCheckService.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/services/UpdateCheckService.java rename to src/main/java/com/topjohnwu/magisk/services/UpdateCheckService.java diff --git a/app/src/main/java/com/topjohnwu/magisk/superuser/RequestActivity.java b/src/main/java/com/topjohnwu/magisk/superuser/RequestActivity.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/superuser/RequestActivity.java rename to src/main/java/com/topjohnwu/magisk/superuser/RequestActivity.java diff --git a/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java b/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java rename to src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/BootSigner.java b/src/main/java/com/topjohnwu/magisk/utils/BootSigner.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/BootSigner.java rename to src/main/java/com/topjohnwu/magisk/utils/BootSigner.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Const.java b/src/main/java/com/topjohnwu/magisk/utils/Const.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/Const.java rename to src/main/java/com/topjohnwu/magisk/utils/Const.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java b/src/main/java/com/topjohnwu/magisk/utils/Logger.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/Logger.java rename to src/main/java/com/topjohnwu/magisk/utils/Logger.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Shell.java b/src/main/java/com/topjohnwu/magisk/utils/Shell.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/Shell.java rename to src/main/java/com/topjohnwu/magisk/utils/Shell.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/ShowUI.java b/src/main/java/com/topjohnwu/magisk/utils/ShowUI.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/ShowUI.java rename to src/main/java/com/topjohnwu/magisk/utils/ShowUI.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java b/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java rename to src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Topic.java b/src/main/java/com/topjohnwu/magisk/utils/Topic.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/Topic.java rename to src/main/java/com/topjohnwu/magisk/utils/Topic.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/src/main/java/com/topjohnwu/magisk/utils/Utils.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/Utils.java rename to src/main/java/com/topjohnwu/magisk/utils/Utils.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/WebService.java b/src/main/java/com/topjohnwu/magisk/utils/WebService.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/WebService.java rename to src/main/java/com/topjohnwu/magisk/utils/WebService.java diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java b/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java similarity index 100% rename from app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java rename to src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java diff --git a/app/src/main/jni/CMakeLists.txt b/src/main/jni/CMakeLists.txt similarity index 100% rename from app/src/main/jni/CMakeLists.txt rename to src/main/jni/CMakeLists.txt diff --git a/app/src/main/jni/jni_glue.c b/src/main/jni/jni_glue.c similarity index 100% rename from app/src/main/jni/jni_glue.c rename to src/main/jni/jni_glue.c diff --git a/app/src/main/jni/zipadjust.c b/src/main/jni/zipadjust.c similarity index 100% rename from app/src/main/jni/zipadjust.c rename to src/main/jni/zipadjust.c diff --git a/app/src/main/jni/zipadjust.h b/src/main/jni/zipadjust.h similarity index 100% rename from app/src/main/jni/zipadjust.h rename to src/main/jni/zipadjust.h diff --git a/app/src/main/res/drawable-nodpi/logo.png b/src/main/res/drawable-nodpi/logo.png similarity index 100% rename from app/src/main/res/drawable-nodpi/logo.png rename to src/main/res/drawable-nodpi/logo.png diff --git a/app/src/main/res/drawable/ic_add.xml b/src/main/res/drawable/ic_add.xml similarity index 100% rename from app/src/main/res/drawable/ic_add.xml rename to src/main/res/drawable/ic_add.xml diff --git a/app/src/main/res/drawable/ic_archive.xml b/src/main/res/drawable/ic_archive.xml similarity index 100% rename from app/src/main/res/drawable/ic_archive.xml rename to src/main/res/drawable/ic_archive.xml diff --git a/app/src/main/res/drawable/ic_arrow.xml b/src/main/res/drawable/ic_arrow.xml similarity index 100% rename from app/src/main/res/drawable/ic_arrow.xml rename to src/main/res/drawable/ic_arrow.xml diff --git a/app/src/main/res/drawable/ic_attach_money.xml b/src/main/res/drawable/ic_attach_money.xml similarity index 100% rename from app/src/main/res/drawable/ic_attach_money.xml rename to src/main/res/drawable/ic_attach_money.xml diff --git a/app/src/main/res/drawable/ic_autoroot.xml b/src/main/res/drawable/ic_autoroot.xml similarity index 100% rename from app/src/main/res/drawable/ic_autoroot.xml rename to src/main/res/drawable/ic_autoroot.xml diff --git a/app/src/main/res/drawable/ic_bug_report.xml b/src/main/res/drawable/ic_bug_report.xml similarity index 100% rename from app/src/main/res/drawable/ic_bug_report.xml rename to src/main/res/drawable/ic_bug_report.xml diff --git a/app/src/main/res/drawable/ic_cancel.xml b/src/main/res/drawable/ic_cancel.xml similarity index 100% rename from app/src/main/res/drawable/ic_cancel.xml rename to src/main/res/drawable/ic_cancel.xml diff --git a/app/src/main/res/drawable/ic_check_circle.xml b/src/main/res/drawable/ic_check_circle.xml similarity index 100% rename from app/src/main/res/drawable/ic_check_circle.xml rename to src/main/res/drawable/ic_check_circle.xml diff --git a/app/src/main/res/drawable/ic_cloud_download.xml b/src/main/res/drawable/ic_cloud_download.xml similarity index 100% rename from app/src/main/res/drawable/ic_cloud_download.xml rename to src/main/res/drawable/ic_cloud_download.xml diff --git a/app/src/main/res/drawable/ic_delete.xml b/src/main/res/drawable/ic_delete.xml similarity index 100% rename from app/src/main/res/drawable/ic_delete.xml rename to src/main/res/drawable/ic_delete.xml diff --git a/app/src/main/res/drawable/ic_device_information.xml b/src/main/res/drawable/ic_device_information.xml similarity index 100% rename from app/src/main/res/drawable/ic_device_information.xml rename to src/main/res/drawable/ic_device_information.xml diff --git a/app/src/main/res/drawable/ic_extension.xml b/src/main/res/drawable/ic_extension.xml similarity index 100% rename from app/src/main/res/drawable/ic_extension.xml rename to src/main/res/drawable/ic_extension.xml diff --git a/app/src/main/res/drawable/ic_file_download_black.xml b/src/main/res/drawable/ic_file_download_black.xml similarity index 100% rename from app/src/main/res/drawable/ic_file_download_black.xml rename to src/main/res/drawable/ic_file_download_black.xml diff --git a/app/src/main/res/drawable/ic_github.xml b/src/main/res/drawable/ic_github.xml similarity index 100% rename from app/src/main/res/drawable/ic_github.xml rename to src/main/res/drawable/ic_github.xml diff --git a/app/src/main/res/drawable/ic_help.xml b/src/main/res/drawable/ic_help.xml similarity index 100% rename from app/src/main/res/drawable/ic_help.xml rename to src/main/res/drawable/ic_help.xml diff --git a/app/src/main/res/drawable/ic_history.xml b/src/main/res/drawable/ic_history.xml similarity index 100% rename from app/src/main/res/drawable/ic_history.xml rename to src/main/res/drawable/ic_history.xml diff --git a/app/src/main/res/drawable/ic_info_outline.xml b/src/main/res/drawable/ic_info_outline.xml similarity index 100% rename from app/src/main/res/drawable/ic_info_outline.xml rename to src/main/res/drawable/ic_info_outline.xml diff --git a/app/src/main/res/drawable/ic_language.xml b/src/main/res/drawable/ic_language.xml similarity index 100% rename from app/src/main/res/drawable/ic_language.xml rename to src/main/res/drawable/ic_language.xml diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/src/main/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from app/src/main/res/drawable/ic_launcher_foreground.xml rename to src/main/res/drawable/ic_launcher_foreground.xml diff --git a/app/src/main/res/drawable/ic_magisk.xml b/src/main/res/drawable/ic_magisk.xml similarity index 100% rename from app/src/main/res/drawable/ic_magisk.xml rename to src/main/res/drawable/ic_magisk.xml diff --git a/app/src/main/res/drawable/ic_menu_overflow_material.xml b/src/main/res/drawable/ic_menu_overflow_material.xml similarity index 100% rename from app/src/main/res/drawable/ic_menu_overflow_material.xml rename to src/main/res/drawable/ic_menu_overflow_material.xml diff --git a/app/src/main/res/drawable/ic_more.xml b/src/main/res/drawable/ic_more.xml similarity index 100% rename from app/src/main/res/drawable/ic_more.xml rename to src/main/res/drawable/ic_more.xml diff --git a/app/src/main/res/drawable/ic_notifications.xml b/src/main/res/drawable/ic_notifications.xml similarity index 100% rename from app/src/main/res/drawable/ic_notifications.xml rename to src/main/res/drawable/ic_notifications.xml diff --git a/app/src/main/res/drawable/ic_person.xml b/src/main/res/drawable/ic_person.xml similarity index 100% rename from app/src/main/res/drawable/ic_person.xml rename to src/main/res/drawable/ic_person.xml diff --git a/app/src/main/res/drawable/ic_refresh.xml b/src/main/res/drawable/ic_refresh.xml similarity index 100% rename from app/src/main/res/drawable/ic_refresh.xml rename to src/main/res/drawable/ic_refresh.xml diff --git a/app/src/main/res/drawable/ic_safetynet.xml b/src/main/res/drawable/ic_safetynet.xml similarity index 100% rename from app/src/main/res/drawable/ic_safetynet.xml rename to src/main/res/drawable/ic_safetynet.xml diff --git a/app/src/main/res/drawable/ic_save.xml b/src/main/res/drawable/ic_save.xml similarity index 100% rename from app/src/main/res/drawable/ic_save.xml rename to src/main/res/drawable/ic_save.xml diff --git a/app/src/main/res/drawable/ic_settings.xml b/src/main/res/drawable/ic_settings.xml similarity index 100% rename from app/src/main/res/drawable/ic_settings.xml rename to src/main/res/drawable/ic_settings.xml diff --git a/app/src/main/res/drawable/ic_splash_activity.xml b/src/main/res/drawable/ic_splash_activity.xml similarity index 100% rename from app/src/main/res/drawable/ic_splash_activity.xml rename to src/main/res/drawable/ic_splash_activity.xml diff --git a/app/src/main/res/drawable/ic_su_warning.xml b/src/main/res/drawable/ic_su_warning.xml similarity index 100% rename from app/src/main/res/drawable/ic_su_warning.xml rename to src/main/res/drawable/ic_su_warning.xml diff --git a/app/src/main/res/drawable/ic_superuser.xml b/src/main/res/drawable/ic_superuser.xml similarity index 100% rename from app/src/main/res/drawable/ic_superuser.xml rename to src/main/res/drawable/ic_superuser.xml diff --git a/app/src/main/res/drawable/ic_undelete.xml b/src/main/res/drawable/ic_undelete.xml similarity index 100% rename from app/src/main/res/drawable/ic_undelete.xml rename to src/main/res/drawable/ic_undelete.xml diff --git a/app/src/main/res/drawable/ic_update.xml b/src/main/res/drawable/ic_update.xml similarity index 100% rename from app/src/main/res/drawable/ic_update.xml rename to src/main/res/drawable/ic_update.xml diff --git a/app/src/main/res/drawable/ic_xda.xml b/src/main/res/drawable/ic_xda.xml similarity index 100% rename from app/src/main/res/drawable/ic_xda.xml rename to src/main/res/drawable/ic_xda.xml diff --git a/app/src/main/res/layout/activity_about.xml b/src/main/res/layout/activity_about.xml similarity index 100% rename from app/src/main/res/layout/activity_about.xml rename to src/main/res/layout/activity_about.xml diff --git a/app/src/main/res/layout/activity_flash.xml b/src/main/res/layout/activity_flash.xml similarity index 100% rename from app/src/main/res/layout/activity_flash.xml rename to src/main/res/layout/activity_flash.xml diff --git a/app/src/main/res/layout/activity_main.xml b/src/main/res/layout/activity_main.xml similarity index 100% rename from app/src/main/res/layout/activity_main.xml rename to src/main/res/layout/activity_main.xml diff --git a/app/src/main/res/layout/activity_request.xml b/src/main/res/layout/activity_request.xml similarity index 100% rename from app/src/main/res/layout/activity_request.xml rename to src/main/res/layout/activity_request.xml diff --git a/app/src/main/res/layout/activity_settings.xml b/src/main/res/layout/activity_settings.xml similarity index 100% rename from app/src/main/res/layout/activity_settings.xml rename to src/main/res/layout/activity_settings.xml diff --git a/app/src/main/res/layout/alert_dialog.xml b/src/main/res/layout/alert_dialog.xml similarity index 100% rename from app/src/main/res/layout/alert_dialog.xml rename to src/main/res/layout/alert_dialog.xml diff --git a/app/src/main/res/layout/custom_channel_dialog.xml b/src/main/res/layout/custom_channel_dialog.xml similarity index 100% rename from app/src/main/res/layout/custom_channel_dialog.xml rename to src/main/res/layout/custom_channel_dialog.xml diff --git a/app/src/main/res/layout/fragment_log.xml b/src/main/res/layout/fragment_log.xml similarity index 100% rename from app/src/main/res/layout/fragment_log.xml rename to src/main/res/layout/fragment_log.xml diff --git a/app/src/main/res/layout/fragment_magisk.xml b/src/main/res/layout/fragment_magisk.xml similarity index 100% rename from app/src/main/res/layout/fragment_magisk.xml rename to src/main/res/layout/fragment_magisk.xml diff --git a/app/src/main/res/layout/fragment_magisk_hide.xml b/src/main/res/layout/fragment_magisk_hide.xml similarity index 100% rename from app/src/main/res/layout/fragment_magisk_hide.xml rename to src/main/res/layout/fragment_magisk_hide.xml diff --git a/app/src/main/res/layout/fragment_magisk_log.xml b/src/main/res/layout/fragment_magisk_log.xml similarity index 100% rename from app/src/main/res/layout/fragment_magisk_log.xml rename to src/main/res/layout/fragment_magisk_log.xml diff --git a/app/src/main/res/layout/fragment_modules.xml b/src/main/res/layout/fragment_modules.xml similarity index 100% rename from app/src/main/res/layout/fragment_modules.xml rename to src/main/res/layout/fragment_modules.xml diff --git a/app/src/main/res/layout/fragment_repos.xml b/src/main/res/layout/fragment_repos.xml similarity index 100% rename from app/src/main/res/layout/fragment_repos.xml rename to src/main/res/layout/fragment_repos.xml diff --git a/app/src/main/res/layout/fragment_su_log.xml b/src/main/res/layout/fragment_su_log.xml similarity index 100% rename from app/src/main/res/layout/fragment_su_log.xml rename to src/main/res/layout/fragment_su_log.xml diff --git a/app/src/main/res/layout/fragment_superuser.xml b/src/main/res/layout/fragment_superuser.xml similarity index 100% rename from app/src/main/res/layout/fragment_superuser.xml rename to src/main/res/layout/fragment_superuser.xml diff --git a/app/src/main/res/layout/info_item_row.xml b/src/main/res/layout/info_item_row.xml similarity index 100% rename from app/src/main/res/layout/info_item_row.xml rename to src/main/res/layout/info_item_row.xml diff --git a/app/src/main/res/layout/list_item_app.xml b/src/main/res/layout/list_item_app.xml similarity index 100% rename from app/src/main/res/layout/list_item_app.xml rename to src/main/res/layout/list_item_app.xml diff --git a/app/src/main/res/layout/list_item_module.xml b/src/main/res/layout/list_item_module.xml similarity index 100% rename from app/src/main/res/layout/list_item_module.xml rename to src/main/res/layout/list_item_module.xml diff --git a/app/src/main/res/layout/list_item_policy.xml b/src/main/res/layout/list_item_policy.xml similarity index 100% rename from app/src/main/res/layout/list_item_policy.xml rename to src/main/res/layout/list_item_policy.xml diff --git a/app/src/main/res/layout/list_item_repo.xml b/src/main/res/layout/list_item_repo.xml similarity index 100% rename from app/src/main/res/layout/list_item_repo.xml rename to src/main/res/layout/list_item_repo.xml diff --git a/app/src/main/res/layout/list_item_sulog.xml b/src/main/res/layout/list_item_sulog.xml similarity index 100% rename from app/src/main/res/layout/list_item_sulog.xml rename to src/main/res/layout/list_item_sulog.xml diff --git a/app/src/main/res/layout/list_item_sulog_group.xml b/src/main/res/layout/list_item_sulog_group.xml similarity index 100% rename from app/src/main/res/layout/list_item_sulog_group.xml rename to src/main/res/layout/list_item_sulog_group.xml diff --git a/app/src/main/res/layout/section.xml b/src/main/res/layout/section.xml similarity index 100% rename from app/src/main/res/layout/section.xml rename to src/main/res/layout/section.xml diff --git a/app/src/main/res/layout/toolbar.xml b/src/main/res/layout/toolbar.xml similarity index 100% rename from app/src/main/res/layout/toolbar.xml rename to src/main/res/layout/toolbar.xml diff --git a/app/src/main/res/menu/drawer.xml b/src/main/res/menu/drawer.xml similarity index 100% rename from app/src/main/res/menu/drawer.xml rename to src/main/res/menu/drawer.xml diff --git a/app/src/main/res/menu/menu_log.xml b/src/main/res/menu/menu_log.xml similarity index 100% rename from app/src/main/res/menu/menu_log.xml rename to src/main/res/menu/menu_log.xml diff --git a/app/src/main/res/menu/menu_magiskhide.xml b/src/main/res/menu/menu_magiskhide.xml similarity index 100% rename from app/src/main/res/menu/menu_magiskhide.xml rename to src/main/res/menu/menu_magiskhide.xml diff --git a/app/src/main/res/menu/menu_reboot.xml b/src/main/res/menu/menu_reboot.xml similarity index 100% rename from app/src/main/res/menu/menu_reboot.xml rename to src/main/res/menu/menu_reboot.xml diff --git a/app/src/main/res/menu/menu_repo.xml b/src/main/res/menu/menu_repo.xml similarity index 100% rename from app/src/main/res/menu/menu_repo.xml rename to src/main/res/menu/menu_repo.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher.png rename to src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher.png rename to src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml similarity index 100% rename from app/src/main/res/values-ar/strings.xml rename to src/main/res/values-ar/strings.xml diff --git a/app/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml similarity index 100% rename from app/src/main/res/values-cs/strings.xml rename to src/main/res/values-cs/strings.xml diff --git a/app/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml similarity index 100% rename from app/src/main/res/values-de/strings.xml rename to src/main/res/values-de/strings.xml diff --git a/app/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml similarity index 100% rename from app/src/main/res/values-el/strings.xml rename to src/main/res/values-el/strings.xml diff --git a/app/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml similarity index 100% rename from app/src/main/res/values-es/strings.xml rename to src/main/res/values-es/strings.xml diff --git a/app/src/main/res/values-et/strings.xml b/src/main/res/values-et/strings.xml similarity index 100% rename from app/src/main/res/values-et/strings.xml rename to src/main/res/values-et/strings.xml diff --git a/app/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml similarity index 100% rename from app/src/main/res/values-fr/strings.xml rename to src/main/res/values-fr/strings.xml diff --git a/app/src/main/res/values-hr/strings.xml b/src/main/res/values-hr/strings.xml similarity index 100% rename from app/src/main/res/values-hr/strings.xml rename to src/main/res/values-hr/strings.xml diff --git a/app/src/main/res/values-in/strings.xml b/src/main/res/values-in/strings.xml similarity index 100% rename from app/src/main/res/values-in/strings.xml rename to src/main/res/values-in/strings.xml diff --git a/app/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml similarity index 100% rename from app/src/main/res/values-it/strings.xml rename to src/main/res/values-it/strings.xml diff --git a/app/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml similarity index 100% rename from app/src/main/res/values-ja/strings.xml rename to src/main/res/values-ja/strings.xml diff --git a/app/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml similarity index 100% rename from app/src/main/res/values-ko/strings.xml rename to src/main/res/values-ko/strings.xml diff --git a/app/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml similarity index 100% rename from app/src/main/res/values-nl/strings.xml rename to src/main/res/values-nl/strings.xml diff --git a/app/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml similarity index 100% rename from app/src/main/res/values-pl/strings.xml rename to src/main/res/values-pl/strings.xml diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml similarity index 100% rename from app/src/main/res/values-pt-rBR/strings.xml rename to src/main/res/values-pt-rBR/strings.xml diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/src/main/res/values-pt-rPT/strings.xml similarity index 100% rename from app/src/main/res/values-pt-rPT/strings.xml rename to src/main/res/values-pt-rPT/strings.xml diff --git a/app/src/main/res/values-ro/strings.xml b/src/main/res/values-ro/strings.xml similarity index 100% rename from app/src/main/res/values-ro/strings.xml rename to src/main/res/values-ro/strings.xml diff --git a/app/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml similarity index 100% rename from app/src/main/res/values-ru/strings.xml rename to src/main/res/values-ru/strings.xml diff --git a/app/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml similarity index 100% rename from app/src/main/res/values-sr/strings.xml rename to src/main/res/values-sr/strings.xml diff --git a/app/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml similarity index 100% rename from app/src/main/res/values-sv/strings.xml rename to src/main/res/values-sv/strings.xml diff --git a/app/src/main/res/values-sw600dp/bools.xml b/src/main/res/values-sw600dp/bools.xml similarity index 100% rename from app/src/main/res/values-sw600dp/bools.xml rename to src/main/res/values-sw600dp/bools.xml diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/src/main/res/values-sw600dp/dimens.xml similarity index 100% rename from app/src/main/res/values-sw600dp/dimens.xml rename to src/main/res/values-sw600dp/dimens.xml diff --git a/app/src/main/res/values-sw600dp/styles.xml b/src/main/res/values-sw600dp/styles.xml similarity index 100% rename from app/src/main/res/values-sw600dp/styles.xml rename to src/main/res/values-sw600dp/styles.xml diff --git a/app/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml similarity index 100% rename from app/src/main/res/values-tr/strings.xml rename to src/main/res/values-tr/strings.xml diff --git a/app/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml similarity index 100% rename from app/src/main/res/values-uk/strings.xml rename to src/main/res/values-uk/strings.xml diff --git a/app/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml similarity index 100% rename from app/src/main/res/values-vi/strings.xml rename to src/main/res/values-vi/strings.xml diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml similarity index 100% rename from app/src/main/res/values-zh-rCN/strings.xml rename to src/main/res/values-zh-rCN/strings.xml diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml similarity index 100% rename from app/src/main/res/values-zh-rTW/strings.xml rename to src/main/res/values-zh-rTW/strings.xml diff --git a/app/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml similarity index 100% rename from app/src/main/res/values/arrays.xml rename to src/main/res/values/arrays.xml diff --git a/app/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml similarity index 100% rename from app/src/main/res/values/attrs.xml rename to src/main/res/values/attrs.xml diff --git a/app/src/main/res/values/bools.xml b/src/main/res/values/bools.xml similarity index 100% rename from app/src/main/res/values/bools.xml rename to src/main/res/values/bools.xml diff --git a/app/src/main/res/values/colors.xml b/src/main/res/values/colors.xml similarity index 100% rename from app/src/main/res/values/colors.xml rename to src/main/res/values/colors.xml diff --git a/app/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml similarity index 100% rename from app/src/main/res/values/dimens.xml rename to src/main/res/values/dimens.xml diff --git a/app/src/main/res/values/ic_launcher_background.xml b/src/main/res/values/ic_launcher_background.xml similarity index 100% rename from app/src/main/res/values/ic_launcher_background.xml rename to src/main/res/values/ic_launcher_background.xml diff --git a/app/src/main/res/values/strings.xml b/src/main/res/values/strings.xml similarity index 100% rename from app/src/main/res/values/strings.xml rename to src/main/res/values/strings.xml diff --git a/app/src/main/res/values/styles.xml b/src/main/res/values/styles.xml similarity index 100% rename from app/src/main/res/values/styles.xml rename to src/main/res/values/styles.xml diff --git a/app/src/main/res/xml/app_settings.xml b/src/main/res/xml/app_settings.xml similarity index 100% rename from app/src/main/res/xml/app_settings.xml rename to src/main/res/xml/app_settings.xml diff --git a/app/src/main/res/xml/file_paths.xml b/src/main/res/xml/file_paths.xml similarity index 100% rename from app/src/main/res/xml/file_paths.xml rename to src/main/res/xml/file_paths.xml