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")
public enum Constants {
@Suppress("unused")
enum class Constants {
;
public static final int MAGISK_VER_CODE_FLAT_MODULES = 19000;
public static final int MAGISK_VER_CODE_UTIL_INSTALL = 20400;
public static final int MAGISK_VER_CODE_PATH_SUPPORT = 21000;
public static final int MAGISK_VER_CODE_INSTALL_COMMAND = 21200;
public static final int MAGISK_VER_CODE_MAGISK_ZYGOTE = 24000;
public static final String INTENT_INSTALL_INTERNAL =
BuildConfig.APPLICATION_ID + ".intent.action.INSTALL_MODULE_INTERNAL";
public static final String INTENT_ANDROIDACY_INTERNAL =
BuildConfig.APPLICATION_ID + ".intent.action.OPEN_ANDROIDACY_INTERNAL";
public static final String EXTRA_INSTALL_PATH = "extra_install_path";
public static final String EXTRA_INSTALL_NAME = "extra_install_name";
public static final String EXTRA_INSTALL_CONFIG = "extra_install_config";
public static final String EXTRA_INSTALL_CHECKSUM = "extra_install_checksum";
public static final String EXTRA_INSTALL_MMT_REBORN = "extra_install_mmt_reborn";
public static final String EXTRA_INSTALL_NO_EXTENSIONS = "extra_install_no_extensions";
public static final String EXTRA_INSTALL_TEST_ROOTLESS = "extra_install_test_rootless";
public static final String EXTRA_ANDROIDACY_COMPAT_LEVEL = "extra_androidacy_compat_level";
public static final String EXTRA_ANDROIDACY_ALLOW_INSTALL = "extra_androidacy_allow_install";
public static final String EXTRA_ANDROIDACY_ACTIONBAR_TITLE = "extra_androidacy_actionbar_title";
public static final String EXTRA_ANDROIDACY_ACTIONBAR_CONFIG = "extra_androidacy_actionbar_config";
public static final String EXTRA_MARKDOWN_URL = "extra_markdown_url";
public static final String EXTRA_MARKDOWN_TITLE = "extra_markdown_title";
public static final String EXTRA_MARKDOWN_CONFIG = "extra_markdown_config";
public static final String EXTRA_MARKDOWN_CHANGE_BOOT = "extra_markdown_change_boot";
public static final String EXTRA_MARKDOWN_NEEDS_RAMDISK = "extra_markdown_needs_ramdisk";
public static final String EXTRA_MARKDOWN_MIN_MAGISK = "extra_markdown_min_magisk";
public static final String EXTRA_MARKDOWN_MIN_API = "extra_markdown_min_api";
public static final String EXTRA_MARKDOWN_MAX_API = "extra_markdown_max_api";
public static final String EXTRA_FADE_OUT = "extra_fade_out";
public static final String EXTRA_FROM_MANAGER = "extra_from_manager";
}
companion object {
const val MAGISK_VER_CODE_FLAT_MODULES = 19000
const val MAGISK_VER_CODE_UTIL_INSTALL = 20400
const val MAGISK_VER_CODE_PATH_SUPPORT = 21000
const val MAGISK_VER_CODE_INSTALL_COMMAND = 21200
const val MAGISK_VER_CODE_MAGISK_ZYGOTE = 24000
const val INTENT_INSTALL_INTERNAL =
BuildConfig.APPLICATION_ID + ".intent.action.INSTALL_MODULE_INTERNAL"
const val INTENT_ANDROIDACY_INTERNAL =
BuildConfig.APPLICATION_ID + ".intent.action.OPEN_ANDROIDACY_INTERNAL"
const val EXTRA_INSTALL_PATH = "extra_install_path"
const val EXTRA_INSTALL_NAME = "extra_install_name"
const val EXTRA_INSTALL_CONFIG = "extra_install_config"
const val EXTRA_INSTALL_CHECKSUM = "extra_install_checksum"
const val EXTRA_INSTALL_MMT_REBORN = "extra_install_mmt_reborn"
const val EXTRA_INSTALL_NO_EXTENSIONS = "extra_install_no_extensions"
const val EXTRA_INSTALL_TEST_ROOTLESS = "extra_install_test_rootless"
const val EXTRA_ANDROIDACY_COMPAT_LEVEL = "extra_androidacy_compat_level"
const val EXTRA_ANDROIDACY_ALLOW_INSTALL = "extra_androidacy_allow_install"
const val EXTRA_ANDROIDACY_ACTIONBAR_TITLE = "extra_androidacy_actionbar_title"
const val EXTRA_ANDROIDACY_ACTIONBAR_CONFIG = "extra_androidacy_actionbar_config"
const val EXTRA_MARKDOWN_URL = "extra_markdown_url"
const val EXTRA_MARKDOWN_TITLE = "extra_markdown_title"
const val EXTRA_MARKDOWN_CONFIG = "extra_markdown_config"
const val EXTRA_MARKDOWN_CHANGE_BOOT = "extra_markdown_change_boot"
const val EXTRA_MARKDOWN_NEEDS_RAMDISK = "extra_markdown_needs_ramdisk"
const val EXTRA_MARKDOWN_MIN_MAGISK = "extra_markdown_min_magisk"
const val EXTRA_MARKDOWN_MIN_API = "extra_markdown_min_api"
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.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.OpenableColumns;
import android.util.Log;
import android.content.Context
import android.net.Uri
import android.os.Build
import android.provider.OpenableColumns
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;
import androidx.annotation.Nullable;
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 {
/** @noinspection ResultOfMethodCallIgnored
*/
enum class Files {
;
private static final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
// stolen from https://stackoverflow.com/a/25005243
public static @NonNull String getFileName(Context context, Uri uri) {
String result = null;
if (Objects.equals(uri.getScheme(), "content")) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (index != -1) {
result = cursor.getString(index);
companion object {
private val is64bit = Build.SUPPORTED_64_BIT_ABIS.isNotEmpty()
// stolen from https://stackoverflow.com/a/25005243
@JvmStatic
fun getFileName(context: Context, uri: Uri): String {
var result: String? = null
if (uri.scheme == "content") {
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) {
result = uri.getPath();
int cut = Objects.requireNonNull(result).lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
if (result == null) {
result = uri.path
val cut = Objects.requireNonNull<String?>(result).lastIndexOf('/')
if (cut != -1) {
result = result!!.substring(cut + 1)
}
}
return result!!
}
return result;
}
// based on https://stackoverflow.com/a/63018108
public static @Nullable Long getFileSize(Context context, Uri uri) {
Long result = null;
try {
String scheme = uri.getScheme();
if (Objects.equals(scheme, "content")) {
Cursor returnCursor = context.getContentResolver().
query(uri, null, null, null, null);
int sizeIndex = Objects.requireNonNull(returnCursor).getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
long size = returnCursor.getLong(sizeIndex);
returnCursor.close();
result = size;
}
if (Objects.equals(scheme, "file")) {
result = new File(Objects.requireNonNull(uri.getPath())).length();
// based on https://stackoverflow.com/a/63018108
@JvmStatic
fun getFileSize(context: Context, uri: Uri): Long? {
var result: Long? = null
try {
val scheme = uri.scheme
if (scheme == "content") {
val returnCursor = context.contentResolver.query(uri, null, null, null, null)
val sizeIndex =
returnCursor?.getColumnIndex(OpenableColumns.SIZE)
returnCursor!!.moveToFirst()
val size = sizeIndex.let { it?.let { it1 -> returnCursor.getLong(it1) } }
returnCursor.close()
result = size
}
if (scheme == "file") {
result = File(Objects.requireNonNull(uri.path)).length()
}
} catch (e: Exception) {
Timber.e(Log.getStackTraceString(e))
return null
}
} catch (Exception e) {
Timber.e(Log.getStackTraceString(e));
return result;
return result
}
return result;
}
public static void write(File file, byte[] bytes) throws IOException {
// make the dir if necessary
Objects.requireNonNull(file.getParentFile()).mkdirs();
try (OutputStream outputStream = new FileOutputStream(file)) {
outputStream.write(bytes);
outputStream.flush();
@JvmStatic
@Throws(IOException::class)
fun write(file: File, bytes: ByteArray?) {
// make the dir if necessary
Objects.requireNonNull(file.parentFile).mkdirs()
FileOutputStream(file).use { outputStream ->
outputStream.write(bytes)
outputStream.flush()
}
}
}
public static byte[] read(File file) throws IOException {
try (InputStream inputStream = new FileInputStream(file)) {
return readAllBytes(inputStream);
@JvmStatic
@Throws(IOException::class)
fun read(file: File?): ByteArray {
FileInputStream(file).use { inputStream -> return readAllBytes(inputStream) }
}
}
public static void writeSU(File file, byte[] bytes) throws IOException {
// make the dir if necessary
Objects.requireNonNull(file.getParentFile()).mkdirs();
try (OutputStream outputStream = SuFileOutputStream.open(file)) {
outputStream.write(bytes);
outputStream.flush();
@JvmStatic
@Throws(IOException::class)
fun writeSU(file: File, bytes: ByteArray?) {
// make the dir if necessary
Objects.requireNonNull(file.parentFile).mkdirs()
SuFileOutputStream.open(file).use { outputStream ->
outputStream.write(bytes)
outputStream.flush()
}
}
}
public static byte[] readSU(File file) throws IOException {
if (file.isFile() && file.canRead()) {
try { // Read as app if su not required
return read(file);
} catch (IOException ignored) {
@JvmStatic
@Throws(IOException::class)
fun readSU(file: File): ByteArray {
if (file.isFile && file.canRead()) {
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) {
return file.exists() || new SuFile(file.getAbsolutePath()).exists();
}
@JvmStatic
fun existsSU(file: File): Boolean {
return file.exists() || SuFile(file.absolutePath).exists()
}
public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException {
int nRead;
byte[] data = new byte[16384];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, nRead);
@JvmStatic
@Throws(IOException::class)
fun copy(inputStream: InputStream, outputStream: OutputStream) {
var nRead: Int
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) {
try {
if (closeable != null) closeable.close();
} catch (IOException ignored) {
@JvmStatic
fun closeSilently(closeable: Closeable?) {
try {
closeable?.close()
} catch (ignored: IOException) {
}
}
}
public static ByteArrayOutputStream makeBuffer(long capacity) {
// Cap buffer to 1 Gib (or 512 Mib for 32bit) to avoid memory errors
return Files.makeBuffer((int) Math.min(capacity, is64bit ? 0x40000000 : 0x20000000));
}
@JvmStatic
fun makeBuffer(capacity: Long): ByteArrayOutputStream {
// 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) {
return new ByteArrayOutputStream(Math.max(0x20, capacity)) {
@NonNull
@Override
public byte[] toByteArray() {
return this.buf.length == this.count ?
this.buf : super.toByteArray();
private fun makeBuffer(capacity: Int): ByteArrayOutputStream {
return object : ByteArrayOutputStream(0x20.coerceAtLeast(capacity)) {
override fun toByteArray(): ByteArray {
return if (buf.size == count) buf else super.toByteArray()
}
}
};
}
}
public static byte[] readAllBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream buffer = Files.makeBuffer(inputStream.available());
copy(inputStream, buffer);
return buffer.toByteArray();
}
@JvmStatic
@Throws(IOException::class)
fun readAllBytes(inputStream: InputStream): ByteArray {
val buffer = makeBuffer(inputStream.available())
copy(inputStream, buffer)
return buffer.toByteArray()
}
public static void fixJavaZipHax(byte[] bytes) {
if (bytes.length > 8 && bytes[0x6] == 0x0 && bytes[0x7] == 0x0 && bytes[0x8] == 0x8)
bytes[0x7] = 0x8; // Known hax to prevent java zip file read
}
@JvmStatic
fun fixJavaZipHax(bytes: ByteArray) {
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 {
fixJavaZipHax(bytes);
patchModuleSimple(new ByteArrayInputStream(bytes), outputStream);
}
@JvmStatic
@Throws(IOException::class)
fun patchModuleSimple(bytes: ByteArray, outputStream: OutputStream?) {
fixJavaZipHax(bytes)
patchModuleSimple(ByteArrayInputStream(bytes), outputStream)
}
public static void patchModuleSimple(InputStream inputStream, OutputStream outputStream) throws IOException {
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
int nRead;
byte[] data = new byte[16384];
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String name = zipEntry.getName();
int i = name.indexOf('/', 1);
if (i == -1) continue;
String newName = name.substring(i + 1);
if (newName.startsWith(".git")) continue; // Skip metadata
zipOutputStream.putNextEntry(new ZipEntry(newName));
while ((nRead = zipInputStream.read(data, 0, data.length)) != -1) {
zipOutputStream.write(data, 0, nRead);
@Throws(IOException::class)
fun patchModuleSimple(inputStream: InputStream?, outputStream: OutputStream?) {
val zipInputStream = ZipInputStream(inputStream)
val zipOutputStream = ZipOutputStream(outputStream)
var nRead: Int
val data = ByteArray(16384)
var zipEntry: ZipEntry
while (zipInputStream.nextEntry.also { zipEntry = it } != null) {
val name = zipEntry.name
val i = name.indexOf('/', 1)
if (i == -1) continue
val newName = name.substring(i + 1)
if (newName.startsWith(".git")) continue // Skip metadata
zipOutputStream.putNextEntry(ZipEntry(newName))
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.closeEntry();
zipInputStream.closeEntry();
zipOutputStream.finish()
zipOutputStream.flush()
zipOutputStream.close()
zipInputStream.close()
}
zipOutputStream.finish();
zipOutputStream.flush();
zipOutputStream.close();
zipInputStream.close();
}
public static void fixSourceArchiveShit(byte[] rawModule) {
// 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
try {
File tempDir = new File(MainApplication.getINSTANCE().getCacheDir(), "temp");
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++;
}
@JvmStatic
fun fixSourceArchiveShit(rawModule: ByteArray?) {
// 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
try {
val tempDir = File(MainApplication.getINSTANCE().cacheDir, "temp")
if (tempDir.exists()) {
FileUtils.deleteDirectory(tempDir)
}
if (folderCount == 1) {
files = zipFile.getEntries();
while (files.hasMoreElements()) {
ZipArchiveEntry entry = files.nextElement();
if (entry.isDirectory()) {
continue;
}
File file = new File(tempUnzipDir, entry.getName());
if (!Objects.requireNonNull(file.getParentFile()).exists()) {
if (!file.getParentFile().mkdirs()) {
throw new IOException("Unable to create parent dir");
if (!tempDir.mkdirs()) {
throw IOException("Unable to create temp dir")
}
val tempFile = File(tempDir, "module.zip")
write(tempFile, rawModule)
val tempUnzipDir = File(tempDir, "unzip")
if (!tempUnzipDir.mkdirs()) {
throw IOException("Unable to create temp unzip dir")
}
// unzip
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)) {
zipArchiveOutputStream.putArchiveEntry(entry);
try (InputStream inputStream = zipFile.getInputStream(entry)) {
copy(inputStream, zipArchiveOutputStream);
if (folderCount == 1) {
files = zipFile.entries
while (files.hasMoreElements()) {
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
File[] filesInFolder = Objects.requireNonNull(tempUnzipDir.listFiles());
// create a new zip file
try (ZipArchiveOutputStream archive = new ZipArchiveOutputStream(new FileOutputStream("new.zip"))) {
for (File files2 : filesInFolder) {
// create a new ZipArchiveEntry and add it to the ZipArchiveOutputStream
ZipArchiveEntry entry = new ZipArchiveEntry(files2, files2.getName());
archive.putArchiveEntry(entry);
try (InputStream input = new FileInputStream(files2)) {
copy(input, archive);
// zip up the contents of the folder but not the folder itself
val filesInFolder = Objects.requireNonNull(tempUnzipDir.listFiles())
// create a new zip file
try {
ZipArchiveOutputStream(FileOutputStream("new.zip")).use { archive ->
for (files2 in filesInFolder) {
// create a new ZipArchiveEntry and add it to the ZipArchiveOutputStream
val entry = ZipArchiveEntry(files2, files2.name)
archive.putArchiveEntry(entry)
FileInputStream(files2).use { input ->
copy(
input,
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 {
Timber.d("Module does not have a single folder in the top level, skipping");
} catch (e: IOException) {
Timber.e(e, "Unable to unzip module")
}
} catch (IOException e) {
Timber.e(e, "Unable to unzip module");
} catch (e: IOException) {
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.
*
* */
package com.fox2code.mmm.utils.io
package com.fox2code.mmm.utils.io;
import android.content.Context;
import android.content.pm.PackageManager;
import timber.log.Timber;
import android.content.Context
import android.content.pm.PackageManager
import timber.log.Timber
/**
* 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
// 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) {
if (context == null) {
throw new NullPointerException("Context must not be null");
}
if (called)
return;
called = true;
try {
// Trust default GMS implementation
Context remote = context.createPackageContext("com.google.android.gms", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
Class<?> cl = remote.getClassLoader().loadClass("com.google.android.gms.common.security.ProviderInstallerImpl");
cl.getDeclaredMethod("insertProvider", Context.class).invoke(null, remote);
Timber.i("Installed GMS security providers!");
} catch (
PackageManager.NameNotFoundException e) {
Timber.w("No GMS Implementation are installed on this device");
} catch (
Exception e) {
Timber.w(e);
companion object {
private var called = false
@JvmStatic
fun installIfNeeded(context: Context?) {
if (context == null) {
throw NullPointerException("Context must not be null")
}
if (called) return
called = true
try {
// Trust default GMS implementation
val remote = context.createPackageContext(
"com.google.android.gms",
Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY
)
val cl =
remote.classLoader.loadClass("com.google.android.gms.common.security.ProviderInstallerImpl")
cl.getDeclaredMethod("insertProvider", Context::class.java).invoke(null, remote)
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;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
import java.util.regex.Pattern;
package com.fox2code.mmm.utils.io
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) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
companion object {
private val HEX_ARRAY = "0123456789abcdef".toCharArray()
private val nonAlphaNum = Pattern.compile("[^a-zA-Z0-9]")
@JvmStatic
fun bytesToHex(bytes: ByteArray): String {
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) {
throw new SecurityException("MD5 is not secure");
}
@JvmStatic
fun hashMd5(ignoredInput: ByteArray?): String {
throw SecurityException("MD5 is not secure")
}
public static String hashSha1(byte[] input) {
throw new SecurityException("SHA-1 is not secure");
}
@JvmStatic
fun hashSha1(ignoredInput: ByteArray?): String {
throw SecurityException("SHA-1 is not secure")
}
public static String hashSha256(byte[] input) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
@JvmStatic
fun hashSha256(input: ByteArray?): String {
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));
} catch (
NoSuchAlgorithmException e) {
throw new RuntimeException(e);
@JvmStatic
fun hashSha512(input: ByteArray?): String {
input ?: return ""
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 {
MessageDigest md = MessageDigest.getInstance("SHA-512");
/**
* Check if the checksum match a file by picking the correct
* 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));
} catch (
NoSuchAlgorithmException e) {
throw new RuntimeException(e);
32 -> hashMd5(data)
40 -> hashSha1(data)
64 -> hashSha256(data)
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()
}
}
/**
* Check if the checksum match a file by picking the correct
* hashing algorithm depending on the length of the checksum
*/
public static boolean checkSumMatch(byte[] data, String checksum) {
String hash;
if (checksum == null)
return false;
switch (checksum.length()) {
case 0:
return true; // No checksum
case 32:
hash = Hashes.hashMd5(data);
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));
}
@JvmStatic
fun checkSumValid(checksum: String?): Boolean {
return if (checksum == null) false else when (checksum.length) {
32, 40, 64, 128 -> {
val len = checksum.length
for (i in 0 until len) {
val c = checksum[i]
if (c < '0' || c > 'f') return false
if (c > '9' && // Easier working with bits
c.code and 95 < 'A'.code
) return false
}
true
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public static boolean checkSumValid(String checksum) {
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;
else -> {
false
}
return true;
}
}
}
public static String checkSumName(String checksum) {
if (checksum == null)
return null;
return switch (checksum.length()) {
default -> null;
case 32 -> "MD5";
case 40 -> "SHA-1";
case 64 -> "SHA-256";
case 128 -> "SHA-512";
};
}
@JvmStatic
fun checkSumName(checksum: String?): String? {
return if (checksum == null) null else when (checksum.length) {
32 -> "MD5"
40 -> "SHA-1"
64 -> "SHA-256"
128 -> "SHA-512"
else -> {
null
"MD5"
"SHA-1"
"SHA-256"
"SHA-512"
}
}
}
public static String checkSumFormat(String checksum) {
if (checksum == null)
return null;
// Remove all non-alphanumeric characters
return nonAlphaNum.matcher(checksum.trim()).replaceAll("");
@JvmStatic
fun checkSumFormat(checksum: String?): String? {
return if (checksum == null) null else nonAlphaNum.matcher(checksum.trim { it <= ' ' })
.replaceAll("")
// Remove all non-alphanumeric characters
}
}
}
}
Loading…
Cancel
Save