MOOOOAAAAAAR KOTLIN

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/27/head
androidacy-user 2 years ago
parent 3096b89e28
commit f3bd95b251

@ -1,36 +1,39 @@
package com.fox2code.mmm; package com.fox2code.mmm
@SuppressWarnings("unused") @Suppress("unused")
public enum Constants { enum class Constants {
; ;
public static final int MAGISK_VER_CODE_FLAT_MODULES = 19000;
public static final int MAGISK_VER_CODE_UTIL_INSTALL = 20400; companion object {
public static final int MAGISK_VER_CODE_PATH_SUPPORT = 21000; const val MAGISK_VER_CODE_FLAT_MODULES = 19000
public static final int MAGISK_VER_CODE_INSTALL_COMMAND = 21200; const val MAGISK_VER_CODE_UTIL_INSTALL = 20400
public static final int MAGISK_VER_CODE_MAGISK_ZYGOTE = 24000; const val MAGISK_VER_CODE_PATH_SUPPORT = 21000
public static final String INTENT_INSTALL_INTERNAL = const val MAGISK_VER_CODE_INSTALL_COMMAND = 21200
BuildConfig.APPLICATION_ID + ".intent.action.INSTALL_MODULE_INTERNAL"; const val MAGISK_VER_CODE_MAGISK_ZYGOTE = 24000
public static final String INTENT_ANDROIDACY_INTERNAL = const val INTENT_INSTALL_INTERNAL =
BuildConfig.APPLICATION_ID + ".intent.action.OPEN_ANDROIDACY_INTERNAL"; BuildConfig.APPLICATION_ID + ".intent.action.INSTALL_MODULE_INTERNAL"
public static final String EXTRA_INSTALL_PATH = "extra_install_path"; const val INTENT_ANDROIDACY_INTERNAL =
public static final String EXTRA_INSTALL_NAME = "extra_install_name"; BuildConfig.APPLICATION_ID + ".intent.action.OPEN_ANDROIDACY_INTERNAL"
public static final String EXTRA_INSTALL_CONFIG = "extra_install_config"; const val EXTRA_INSTALL_PATH = "extra_install_path"
public static final String EXTRA_INSTALL_CHECKSUM = "extra_install_checksum"; const val EXTRA_INSTALL_NAME = "extra_install_name"
public static final String EXTRA_INSTALL_MMT_REBORN = "extra_install_mmt_reborn"; const val EXTRA_INSTALL_CONFIG = "extra_install_config"
public static final String EXTRA_INSTALL_NO_EXTENSIONS = "extra_install_no_extensions"; const val EXTRA_INSTALL_CHECKSUM = "extra_install_checksum"
public static final String EXTRA_INSTALL_TEST_ROOTLESS = "extra_install_test_rootless"; const val EXTRA_INSTALL_MMT_REBORN = "extra_install_mmt_reborn"
public static final String EXTRA_ANDROIDACY_COMPAT_LEVEL = "extra_androidacy_compat_level"; const val EXTRA_INSTALL_NO_EXTENSIONS = "extra_install_no_extensions"
public static final String EXTRA_ANDROIDACY_ALLOW_INSTALL = "extra_androidacy_allow_install"; const val EXTRA_INSTALL_TEST_ROOTLESS = "extra_install_test_rootless"
public static final String EXTRA_ANDROIDACY_ACTIONBAR_TITLE = "extra_androidacy_actionbar_title"; const val EXTRA_ANDROIDACY_COMPAT_LEVEL = "extra_androidacy_compat_level"
public static final String EXTRA_ANDROIDACY_ACTIONBAR_CONFIG = "extra_androidacy_actionbar_config"; const val EXTRA_ANDROIDACY_ALLOW_INSTALL = "extra_androidacy_allow_install"
public static final String EXTRA_MARKDOWN_URL = "extra_markdown_url"; const val EXTRA_ANDROIDACY_ACTIONBAR_TITLE = "extra_androidacy_actionbar_title"
public static final String EXTRA_MARKDOWN_TITLE = "extra_markdown_title"; const val EXTRA_ANDROIDACY_ACTIONBAR_CONFIG = "extra_androidacy_actionbar_config"
public static final String EXTRA_MARKDOWN_CONFIG = "extra_markdown_config"; const val EXTRA_MARKDOWN_URL = "extra_markdown_url"
public static final String EXTRA_MARKDOWN_CHANGE_BOOT = "extra_markdown_change_boot"; const val EXTRA_MARKDOWN_TITLE = "extra_markdown_title"
public static final String EXTRA_MARKDOWN_NEEDS_RAMDISK = "extra_markdown_needs_ramdisk"; const val EXTRA_MARKDOWN_CONFIG = "extra_markdown_config"
public static final String EXTRA_MARKDOWN_MIN_MAGISK = "extra_markdown_min_magisk"; const val EXTRA_MARKDOWN_CHANGE_BOOT = "extra_markdown_change_boot"
public static final String EXTRA_MARKDOWN_MIN_API = "extra_markdown_min_api"; const val EXTRA_MARKDOWN_NEEDS_RAMDISK = "extra_markdown_needs_ramdisk"
public static final String EXTRA_MARKDOWN_MAX_API = "extra_markdown_max_api"; const val EXTRA_MARKDOWN_MIN_MAGISK = "extra_markdown_min_magisk"
public static final String EXTRA_FADE_OUT = "extra_fade_out"; const val EXTRA_MARKDOWN_MIN_API = "extra_markdown_min_api"
public static final String EXTRA_FROM_MANAGER = "extra_from_manager"; const val EXTRA_MARKDOWN_MAX_API = "extra_markdown_max_api"
const val EXTRA_FADE_OUT = "extra_fade_out"
const val EXTRA_FROM_MANAGER = "extra_from_manager"
}
} }

@ -1,283 +1,303 @@
package com.fox2code.mmm.utils.io; package com.fox2code.mmm.utils.io
import android.content.Context; import android.content.Context
import android.database.Cursor; import android.net.Uri
import android.net.Uri; import android.os.Build
import android.os.Build; import android.provider.OpenableColumns
import android.provider.OpenableColumns; import android.util.Log
import android.util.Log; import com.fox2code.mmm.MainApplication
import com.topjohnwu.superuser.io.SuFile
import com.topjohnwu.superuser.io.SuFileInputStream
import com.topjohnwu.superuser.io.SuFileOutputStream
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
import org.apache.commons.compress.archivers.zip.ZipFile
import org.apache.commons.io.FileUtils
import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.Closeable
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.Objects
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
import androidx.annotation.NonNull; /** @noinspection ResultOfMethodCallIgnored
import androidx.annotation.Nullable; */
enum class Files {
import com.fox2code.mmm.MainApplication;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
import com.topjohnwu.superuser.io.SuFileOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import timber.log.Timber;
/** @noinspection ResultOfMethodCallIgnored*/
public enum Files {
; ;
private static final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
// stolen from https://stackoverflow.com/a/25005243 companion object {
public static @NonNull String getFileName(Context context, Uri uri) { private val is64bit = Build.SUPPORTED_64_BIT_ABIS.isNotEmpty()
String result = null;
if (Objects.equals(uri.getScheme(), "content")) { // stolen from https://stackoverflow.com/a/25005243
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { @JvmStatic
if (cursor != null && cursor.moveToFirst()) { fun getFileName(context: Context, uri: Uri): String {
int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); var result: String? = null
if (index != -1) { if (uri.scheme == "content") {
result = cursor.getString(index); context.contentResolver.query(uri, null, null, null, null).use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (index != -1) {
result = cursor.getString(index)
}
} }
} }
} }
} if (result == null) {
if (result == null) { result = uri.path
result = uri.getPath(); val cut = Objects.requireNonNull<String?>(result).lastIndexOf('/')
int cut = Objects.requireNonNull(result).lastIndexOf('/'); if (cut != -1) {
if (cut != -1) { result = result!!.substring(cut + 1)
result = result.substring(cut + 1); }
} }
return result!!
} }
return result;
}
// based on https://stackoverflow.com/a/63018108 // based on https://stackoverflow.com/a/63018108
public static @Nullable Long getFileSize(Context context, Uri uri) { @JvmStatic
Long result = null; fun getFileSize(context: Context, uri: Uri): Long? {
try { var result: Long? = null
String scheme = uri.getScheme(); try {
if (Objects.equals(scheme, "content")) { val scheme = uri.scheme
Cursor returnCursor = context.getContentResolver(). if (scheme == "content") {
query(uri, null, null, null, null); val returnCursor = context.contentResolver.query(uri, null, null, null, null)
int sizeIndex = Objects.requireNonNull(returnCursor).getColumnIndex(OpenableColumns.SIZE); val sizeIndex =
returnCursor.moveToFirst(); returnCursor?.getColumnIndex(OpenableColumns.SIZE)
returnCursor!!.moveToFirst()
long size = returnCursor.getLong(sizeIndex); val size = sizeIndex.let { it?.let { it1 -> returnCursor.getLong(it1) } }
returnCursor.close(); returnCursor.close()
result = size
result = size; }
} if (scheme == "file") {
if (Objects.equals(scheme, "file")) { result = File(Objects.requireNonNull(uri.path)).length()
result = new File(Objects.requireNonNull(uri.getPath())).length(); }
} catch (e: Exception) {
Timber.e(Log.getStackTraceString(e))
return null
} }
} catch (Exception e) { return result
Timber.e(Log.getStackTraceString(e));
return result;
} }
return result;
}
public static void write(File file, byte[] bytes) throws IOException { @JvmStatic
// make the dir if necessary @Throws(IOException::class)
Objects.requireNonNull(file.getParentFile()).mkdirs(); fun write(file: File, bytes: ByteArray?) {
try (OutputStream outputStream = new FileOutputStream(file)) { // make the dir if necessary
outputStream.write(bytes); Objects.requireNonNull(file.parentFile).mkdirs()
outputStream.flush(); FileOutputStream(file).use { outputStream ->
outputStream.write(bytes)
outputStream.flush()
}
} }
}
public static byte[] read(File file) throws IOException { @JvmStatic
try (InputStream inputStream = new FileInputStream(file)) { @Throws(IOException::class)
return readAllBytes(inputStream); fun read(file: File?): ByteArray {
FileInputStream(file).use { inputStream -> return readAllBytes(inputStream) }
} }
}
public static void writeSU(File file, byte[] bytes) throws IOException { @JvmStatic
// make the dir if necessary @Throws(IOException::class)
Objects.requireNonNull(file.getParentFile()).mkdirs(); fun writeSU(file: File, bytes: ByteArray?) {
try (OutputStream outputStream = SuFileOutputStream.open(file)) { // make the dir if necessary
outputStream.write(bytes); Objects.requireNonNull(file.parentFile).mkdirs()
outputStream.flush(); SuFileOutputStream.open(file).use { outputStream ->
outputStream.write(bytes)
outputStream.flush()
}
} }
}
public static byte[] readSU(File file) throws IOException { @JvmStatic
if (file.isFile() && file.canRead()) { @Throws(IOException::class)
try { // Read as app if su not required fun readSU(file: File): ByteArray {
return read(file); if (file.isFile && file.canRead()) {
} catch (IOException ignored) { try { // Read as app if su not required
return read(file)
} catch (ignored: IOException) {
}
} }
SuFileInputStream.open(file).use { inputStream -> return readAllBytes(inputStream) }
} }
try (InputStream inputStream = SuFileInputStream.open(file)) {
return readAllBytes(inputStream);
}
}
public static boolean existsSU(File file) { @JvmStatic
return file.exists() || new SuFile(file.getAbsolutePath()).exists(); fun existsSU(file: File): Boolean {
} return file.exists() || SuFile(file.absolutePath).exists()
}
public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException { @JvmStatic
int nRead; @Throws(IOException::class)
byte[] data = new byte[16384]; fun copy(inputStream: InputStream, outputStream: OutputStream) {
while ((nRead = inputStream.read(data, 0, data.length)) != -1) { var nRead: Int
outputStream.write(data, 0, nRead); val data = ByteArray(16384)
while (inputStream.read(data, 0, data.size).also { nRead = it } != -1) {
outputStream.write(data, 0, nRead)
}
outputStream.flush()
} }
outputStream.flush();
}
public static void closeSilently(Closeable closeable) { @JvmStatic
try { fun closeSilently(closeable: Closeable?) {
if (closeable != null) closeable.close(); try {
} catch (IOException ignored) { closeable?.close()
} catch (ignored: IOException) {
}
} }
}
public static ByteArrayOutputStream makeBuffer(long capacity) { @JvmStatic
// Cap buffer to 1 Gib (or 512 Mib for 32bit) to avoid memory errors fun makeBuffer(capacity: Long): ByteArrayOutputStream {
return Files.makeBuffer((int) Math.min(capacity, is64bit ? 0x40000000 : 0x20000000)); // Cap buffer to 1 Gib (or 512 Mib for 32bit) to avoid memory errors
} return makeBuffer(
capacity.coerceAtMost((if (is64bit) 0x40000000 else 0x20000000).toLong()).toInt()
)
}
public static ByteArrayOutputStream makeBuffer(int capacity) { private fun makeBuffer(capacity: Int): ByteArrayOutputStream {
return new ByteArrayOutputStream(Math.max(0x20, capacity)) { return object : ByteArrayOutputStream(0x20.coerceAtLeast(capacity)) {
@NonNull override fun toByteArray(): ByteArray {
@Override return if (buf.size == count) buf else super.toByteArray()
public byte[] toByteArray() { }
return this.buf.length == this.count ?
this.buf : super.toByteArray();
} }
}; }
}
public static byte[] readAllBytes(InputStream inputStream) throws IOException { @JvmStatic
ByteArrayOutputStream buffer = Files.makeBuffer(inputStream.available()); @Throws(IOException::class)
copy(inputStream, buffer); fun readAllBytes(inputStream: InputStream): ByteArray {
return buffer.toByteArray(); val buffer = makeBuffer(inputStream.available())
} copy(inputStream, buffer)
return buffer.toByteArray()
}
public static void fixJavaZipHax(byte[] bytes) { @JvmStatic
if (bytes.length > 8 && bytes[0x6] == 0x0 && bytes[0x7] == 0x0 && bytes[0x8] == 0x8) fun fixJavaZipHax(bytes: ByteArray) {
bytes[0x7] = 0x8; // Known hax to prevent java zip file read if (bytes.size > 8 && bytes[0x6].toInt() == 0x0 && bytes[0x7].toInt() == 0x0 && bytes[0x8].toInt() == 0x8) bytes[0x7] =
} 0x8 // Known hax to prevent java zip file read
}
public static void patchModuleSimple(byte[] bytes, OutputStream outputStream) throws IOException { @JvmStatic
fixJavaZipHax(bytes); @Throws(IOException::class)
patchModuleSimple(new ByteArrayInputStream(bytes), outputStream); fun patchModuleSimple(bytes: ByteArray, outputStream: OutputStream?) {
} fixJavaZipHax(bytes)
patchModuleSimple(ByteArrayInputStream(bytes), outputStream)
}
public static void patchModuleSimple(InputStream inputStream, OutputStream outputStream) throws IOException { @Throws(IOException::class)
ZipInputStream zipInputStream = new ZipInputStream(inputStream); fun patchModuleSimple(inputStream: InputStream?, outputStream: OutputStream?) {
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream); val zipInputStream = ZipInputStream(inputStream)
int nRead; val zipOutputStream = ZipOutputStream(outputStream)
byte[] data = new byte[16384]; var nRead: Int
ZipEntry zipEntry; val data = ByteArray(16384)
while ((zipEntry = zipInputStream.getNextEntry()) != null) { var zipEntry: ZipEntry
String name = zipEntry.getName(); while (zipInputStream.nextEntry.also { zipEntry = it } != null) {
int i = name.indexOf('/', 1); val name = zipEntry.name
if (i == -1) continue; val i = name.indexOf('/', 1)
String newName = name.substring(i + 1); if (i == -1) continue
if (newName.startsWith(".git")) continue; // Skip metadata val newName = name.substring(i + 1)
zipOutputStream.putNextEntry(new ZipEntry(newName)); if (newName.startsWith(".git")) continue // Skip metadata
while ((nRead = zipInputStream.read(data, 0, data.length)) != -1) { zipOutputStream.putNextEntry(ZipEntry(newName))
zipOutputStream.write(data, 0, nRead); while (zipInputStream.read(data, 0, data.size).also { nRead = it } != -1) {
zipOutputStream.write(data, 0, nRead)
}
zipOutputStream.flush()
zipOutputStream.closeEntry()
zipInputStream.closeEntry()
} }
zipOutputStream.flush(); zipOutputStream.finish()
zipOutputStream.closeEntry(); zipOutputStream.flush()
zipInputStream.closeEntry(); zipOutputStream.close()
zipInputStream.close()
} }
zipOutputStream.finish();
zipOutputStream.flush();
zipOutputStream.close();
zipInputStream.close();
}
public static void fixSourceArchiveShit(byte[] rawModule) { @JvmStatic
// unzip the module, check if it has just one folder within. if so, switch to the folder and zip up contents, and replace the original file with that fun fixSourceArchiveShit(rawModule: ByteArray?) {
try { // unzip the module, check if it has just one folder within. if so, switch to the folder and zip up contents, and replace the original file with that
File tempDir = new File(MainApplication.getINSTANCE().getCacheDir(), "temp"); try {
if (tempDir.exists()) { val tempDir = File(MainApplication.getINSTANCE().cacheDir, "temp")
FileUtils.deleteDirectory(tempDir); if (tempDir.exists()) {
} FileUtils.deleteDirectory(tempDir)
if (!tempDir.mkdirs()) {
throw new IOException("Unable to create temp dir");
}
File tempFile = new File(tempDir, "module.zip");
Files.write(tempFile, rawModule);
File tempUnzipDir = new File(tempDir, "unzip");
if (!tempUnzipDir.mkdirs()) {
throw new IOException("Unable to create temp unzip dir");
}
// unzip
Timber.d("Unzipping module to %s", tempUnzipDir.getAbsolutePath());
try (ZipFile zipFile = new ZipFile(tempFile)) {
Enumeration<ZipArchiveEntry> files = zipFile.getEntries();
// check if there is only one folder in the top level
int folderCount = 0;
while (files.hasMoreElements()) {
ZipArchiveEntry entry = files.nextElement();
if (entry.isDirectory()) {
folderCount++;
}
} }
if (folderCount == 1) { if (!tempDir.mkdirs()) {
files = zipFile.getEntries(); throw IOException("Unable to create temp dir")
while (files.hasMoreElements()) { }
ZipArchiveEntry entry = files.nextElement(); val tempFile = File(tempDir, "module.zip")
if (entry.isDirectory()) { write(tempFile, rawModule)
continue; val tempUnzipDir = File(tempDir, "unzip")
} if (!tempUnzipDir.mkdirs()) {
File file = new File(tempUnzipDir, entry.getName()); throw IOException("Unable to create temp unzip dir")
if (!Objects.requireNonNull(file.getParentFile()).exists()) { }
if (!file.getParentFile().mkdirs()) { // unzip
throw new IOException("Unable to create parent dir"); Timber.d("Unzipping module to %s", tempUnzipDir.absolutePath)
try {
ZipFile(tempFile).use { zipFile ->
var files = zipFile.entries
// check if there is only one folder in the top level
var folderCount = 0
while (files.hasMoreElements()) {
val entry = files.nextElement()
if (entry.isDirectory) {
folderCount++
} }
} }
try (ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(file)) { if (folderCount == 1) {
zipArchiveOutputStream.putArchiveEntry(entry); files = zipFile.entries
try (InputStream inputStream = zipFile.getInputStream(entry)) { while (files.hasMoreElements()) {
copy(inputStream, zipArchiveOutputStream); val entry = files.nextElement()
if (entry.isDirectory) {
continue
}
val file = File(tempUnzipDir, entry.name)
if (!Objects.requireNonNull(file.parentFile).exists()) {
if (!file.parentFile?.mkdirs()!!) {
throw IOException("Unable to create parent dir")
}
}
ZipArchiveOutputStream(file).use { zipArchiveOutputStream ->
zipArchiveOutputStream.putArchiveEntry(entry)
zipFile.getInputStream(entry).use { inputStream ->
copy(
inputStream,
zipArchiveOutputStream
)
}
zipArchiveOutputStream.closeArchiveEntry()
}
} }
zipArchiveOutputStream.closeArchiveEntry(); // zip up the contents of the folder but not the folder itself
} val filesInFolder = Objects.requireNonNull(tempUnzipDir.listFiles())
} // create a new zip file
// zip up the contents of the folder but not the folder itself try {
File[] filesInFolder = Objects.requireNonNull(tempUnzipDir.listFiles()); ZipArchiveOutputStream(FileOutputStream("new.zip")).use { archive ->
// create a new zip file for (files2 in filesInFolder) {
try (ZipArchiveOutputStream archive = new ZipArchiveOutputStream(new FileOutputStream("new.zip"))) { // create a new ZipArchiveEntry and add it to the ZipArchiveOutputStream
for (File files2 : filesInFolder) { val entry = ZipArchiveEntry(files2, files2.name)
// create a new ZipArchiveEntry and add it to the ZipArchiveOutputStream archive.putArchiveEntry(entry)
ZipArchiveEntry entry = new ZipArchiveEntry(files2, files2.getName()); FileInputStream(files2).use { input ->
archive.putArchiveEntry(entry); copy(
try (InputStream input = new FileInputStream(files2)) { input,
copy(input, archive); archive
)
}
archive.closeArchiveEntry()
}
}
} catch (e: IOException) {
Timber.e(e, "Unable to zip up module")
} }
archive.closeArchiveEntry(); } else {
Timber.d("Module does not have a single folder in the top level, skipping")
} }
} catch (IOException e) {
Timber.e(e, "Unable to zip up module");
} }
} else { } catch (e: IOException) {
Timber.d("Module does not have a single folder in the top level, skipping"); Timber.e(e, "Unable to unzip module")
} }
} catch (IOException e) { } catch (e: IOException) {
Timber.e(e, "Unable to unzip module"); Timber.e(e, "Unable to create temp dir")
} }
} catch (IOException e) {
Timber.e(e, "Unable to create temp dir");
} }
} }
} }

@ -21,13 +21,11 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* *
* */ * */
package com.fox2code.mmm.utils.io
package com.fox2code.mmm.utils.io; import android.content.Context
import android.content.pm.PackageManager
import android.content.Context; import timber.log.Timber
import android.content.pm.PackageManager;
import timber.log.Timber;
/** /**
* Open implementation of ProviderInstaller.installIfNeeded * Open implementation of ProviderInstaller.installIfNeeded
@ -35,29 +33,33 @@ import timber.log.Timber;
*/ */
// Note: This code is MIT because I took it from another unpublished project I had // Note: This code is MIT because I took it from another unpublished project I had
// I might upstream this to MicroG at some point // I might upstream this to MicroG at some point
public enum GMSProviderInstaller { enum class GMSProviderInstaller {
; ;
private static boolean called = false;
public static void installIfNeeded(final Context context) { companion object {
if (context == null) { private var called = false
throw new NullPointerException("Context must not be null"); @JvmStatic
} fun installIfNeeded(context: Context?) {
if (called) if (context == null) {
return; throw NullPointerException("Context must not be null")
called = true; }
try { if (called) return
// Trust default GMS implementation called = true
Context remote = context.createPackageContext("com.google.android.gms", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); try {
Class<?> cl = remote.getClassLoader().loadClass("com.google.android.gms.common.security.ProviderInstallerImpl"); // Trust default GMS implementation
cl.getDeclaredMethod("insertProvider", Context.class).invoke(null, remote); val remote = context.createPackageContext(
Timber.i("Installed GMS security providers!"); "com.google.android.gms",
} catch ( Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY
PackageManager.NameNotFoundException e) { )
Timber.w("No GMS Implementation are installed on this device"); val cl =
} catch ( remote.classLoader.loadClass("com.google.android.gms.common.security.ProviderInstallerImpl")
Exception e) { cl.getDeclaredMethod("insertProvider", Context::class.java).invoke(null, remote)
Timber.w(e); Timber.i("Installed GMS security providers!")
} catch (e: PackageManager.NameNotFoundException) {
Timber.w("No GMS Implementation are installed on this device")
} catch (e: Exception) {
Timber.w(e)
}
} }
} }
} }

@ -1,129 +1,130 @@
package com.fox2code.mmm.utils.io; @file:Suppress("UNUSED_PARAMETER")
import java.security.MessageDigest; package com.fox2code.mmm.utils.io
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
import java.util.regex.Pattern;
import timber.log.Timber; import timber.log.Timber
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.regex.Pattern
public enum Hashes { @Suppress("UNUSED_EXPRESSION")
enum class Hashes {
; ;
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
private static final Pattern nonAlphaNum = Pattern.compile("[^a-zA-Z0-9]");
public static String bytesToHex(byte[] bytes) { companion object {
char[] hexChars = new char[bytes.length * 2]; private val HEX_ARRAY = "0123456789abcdef".toCharArray()
for (int j = 0; j < bytes.length; j++) { private val nonAlphaNum = Pattern.compile("[^a-zA-Z0-9]")
int v = bytes[j] & 0xFF; @JvmStatic
hexChars[j * 2] = HEX_ARRAY[v >>> 4]; fun bytesToHex(bytes: ByteArray): String {
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; val hexChars = CharArray(bytes.size * 2)
for (j in bytes.indices) {
val v = bytes[j].toInt() and 0xFF
hexChars[j * 2] = HEX_ARRAY[v ushr 4]
hexChars[j * 2 + 1] = HEX_ARRAY[v and 0x0F]
}
return String(hexChars)
} }
return String.valueOf(hexChars);
}
public static String hashMd5(byte[] input) { @JvmStatic
throw new SecurityException("MD5 is not secure"); fun hashMd5(ignoredInput: ByteArray?): String {
} throw SecurityException("MD5 is not secure")
}
public static String hashSha1(byte[] input) { @JvmStatic
throw new SecurityException("SHA-1 is not secure"); fun hashSha1(ignoredInput: ByteArray?): String {
} throw SecurityException("SHA-1 is not secure")
}
public static String hashSha256(byte[] input) { @JvmStatic
try { fun hashSha256(input: ByteArray?): String {
MessageDigest md = MessageDigest.getInstance("SHA-256"); input ?: return ""
return try {
val md = MessageDigest.getInstance("SHA-256")
bytesToHex(md.digest(input))
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException(e)
}
}
return bytesToHex(md.digest(input)); @JvmStatic
} catch ( fun hashSha512(input: ByteArray?): String {
NoSuchAlgorithmException e) { input ?: return ""
throw new RuntimeException(e); return try {
val md = MessageDigest.getInstance("SHA-512")
bytesToHex(md.digest(input))
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException(e)
}
} }
}
public static String hashSha512(byte[] input) { /**
try { * Check if the checksum match a file by picking the correct
MessageDigest md = MessageDigest.getInstance("SHA-512"); * hashing algorithm depending on the length of the checksum
*/
@JvmStatic
fun checkSumMatch(data: ByteArray?, checksum: String?): Boolean {
if (checksum == null) return false
val hash: String = when (checksum.length) {
0 -> {
return true // No checksum
}
return bytesToHex(md.digest(input)); 32 -> hashMd5(data)
} catch ( 40 -> hashSha1(data)
NoSuchAlgorithmException e) { 64 -> hashSha256(data)
throw new RuntimeException(e); 128 -> hashSha512(data)
else -> {
Timber.e("No hash algorithm for " + checksum.length * 8 + "bit checksums")
return false
}
}
Timber.i("Checksum result (data: $hash,expected: $checksum)")
return hash == checksum.lowercase()
} }
}
/** @JvmStatic
* Check if the checksum match a file by picking the correct fun checkSumValid(checksum: String?): Boolean {
* hashing algorithm depending on the length of the checksum return if (checksum == null) false else when (checksum.length) {
*/ 32, 40, 64, 128 -> {
public static boolean checkSumMatch(byte[] data, String checksum) { val len = checksum.length
String hash; for (i in 0 until len) {
if (checksum == null) val c = checksum[i]
return false; if (c < '0' || c > 'f') return false
switch (checksum.length()) { if (c > '9' && // Easier working with bits
case 0: c.code and 95 < 'A'.code
return true; // No checksum ) return false
case 32: }
hash = Hashes.hashMd5(data); true
break; }
case 40:
hash = Hashes.hashSha1(data);
break;
case 64:
hash = Hashes.hashSha256(data);
break;
case 128:
hash = Hashes.hashSha512(data);
break;
default:
Timber.e("No hash algorithm for " + checksum.length() * 8 + "bit checksums");
return false;
}
Timber.i("Checksum result (data: " + hash + ",expected: " + checksum + ")");
return hash.equals(checksum.toLowerCase(Locale.ROOT));
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted") else -> {
public static boolean checkSumValid(String checksum) { false
if (checksum == null)
return false;
switch (checksum.length()) {
case 0:
default:
return false;
case 32:
case 40:
case 64:
case 128:
final int len = checksum.length();
for (int i = 0; i < len; i++) {
char c = checksum.charAt(i);
if (c < '0' || c > 'f')
return false;
if (c > '9' && // Easier working with bits
(c & 0b01011111) < 'A')
return false;
} }
return true; }
} }
}
public static String checkSumName(String checksum) { @JvmStatic
if (checksum == null) fun checkSumName(checksum: String?): String? {
return null; return if (checksum == null) null else when (checksum.length) {
return switch (checksum.length()) { 32 -> "MD5"
default -> null; 40 -> "SHA-1"
case 32 -> "MD5"; 64 -> "SHA-256"
case 40 -> "SHA-1"; 128 -> "SHA-512"
case 64 -> "SHA-256"; else -> {
case 128 -> "SHA-512"; null
}; "MD5"
} "SHA-1"
"SHA-256"
"SHA-512"
}
}
}
public static String checkSumFormat(String checksum) { @JvmStatic
if (checksum == null) fun checkSumFormat(checksum: String?): String? {
return null; return if (checksum == null) null else nonAlphaNum.matcher(checksum.trim { it <= ' ' })
// Remove all non-alphanumeric characters .replaceAll("")
return nonAlphaNum.matcher(checksum.trim()).replaceAll(""); // Remove all non-alphanumeric characters
}
} }
} }
Loading…
Cancel
Save