From dac0c5dddec283820ca3b6bce0e2fda65c27c8c8 Mon Sep 17 00:00:00 2001 From: androidacy-user Date: Sat, 25 Feb 2023 21:29:49 -0500 Subject: [PATCH] encrypt realms still a lil crashy but works Signed-off-by: androidacy-user --- .../java/com/fox2code/mmm/MainActivity.java | 2 +- .../com/fox2code/mmm/MainApplication.java | 71 +++++++++ .../java/com/fox2code/mmm/SetupActivity.java | 138 ++++++++++++++---- .../background/BackgroundUpdateChecker.java | 4 +- .../fox2code/mmm/manager/ModuleManager.java | 6 +- .../java/com/fox2code/mmm/repo/RepoData.java | 24 +-- .../com/fox2code/mmm/repo/RepoManager.java | 4 + .../com/fox2code/mmm/repo/RepoUpdater.java | 8 +- .../mmm/settings/SettingsActivity.java | 10 +- .../fox2code/mmm/utils/sentry/SentryMain.java | 2 +- 10 files changed, 213 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 92b3377..46270bc 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -697,7 +697,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe Timber.i("Checking if we need to run setup"); // Check if this is the first launch SharedPreferences prefs = MainApplication.getSharedPreferences("mmm"); - boolean firstLaunch = prefs.getBoolean("first_time_setup_done", true); + boolean firstLaunch = prefs.getString("last_shown_setup", null) == null; if (BuildConfig.DEBUG) Timber.i("First launch: %s", firstLaunch); if (firstLaunch) { diff --git a/app/src/main/java/com/fox2code/mmm/MainApplication.java b/app/src/main/java/com/fox2code/mmm/MainApplication.java index 59243f7..851be53 100644 --- a/app/src/main/java/com/fox2code/mmm/MainApplication.java +++ b/app/src/main/java/com/fox2code/mmm/MainApplication.java @@ -12,6 +12,8 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.os.SystemClock; +import android.security.keystore.KeyProperties; +import android.util.Base64; import android.util.Log; import androidx.annotation.NonNull; @@ -37,7 +39,16 @@ import com.topjohnwu.superuser.Shell; import java.io.File; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; @@ -47,6 +58,13 @@ import java.util.Locale; import java.util.Objects; import java.util.Random; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + import io.noties.markwon.Markwon; import io.noties.markwon.html.HtmlPlugin; import io.noties.markwon.image.ImagesPlugin; @@ -513,4 +531,57 @@ public class MainApplication extends FoxApplication implements androidx.work.Con } } } + + // Access the encrypted key in the keystore, decrypt it with the secret, + // and use it to open and read from the realm again + public byte[] getExistingKey() { + // open a connection to the android keystore + KeyStore keyStore; + try { + keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + } catch (KeyStoreException | NoSuchAlgorithmException + | CertificateException | IOException e) { + throw new RuntimeException(e); + } + // access the encrypted key that's stored in shared preferences + byte[] initializationVectorAndEncryptedKey = Base64.decode(getSharedPreferences("realm_key") + .getString("iv_and_encrypted_key", null), Base64.DEFAULT); + ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey); + buffer.order(ByteOrder.BIG_ENDIAN); + // extract the length of the initialization vector from the buffer + int initializationVectorLength = buffer.getInt(); + // extract the initialization vector based on that length + byte[] initializationVector = new byte[initializationVectorLength]; + buffer.get(initializationVector); + // extract the encrypted key + byte[] encryptedKey = new byte[initializationVectorAndEncryptedKey.length + - Integer.BYTES + - initializationVectorLength]; + buffer.get(encryptedKey); + // create a cipher that uses AES encryption to decrypt our key + Cipher cipher; + try { + cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + + "/" + KeyProperties.BLOCK_MODE_CBC + + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException(e); + } + // decrypt the encrypted key with the secret key stored in the keystore + byte[] decryptedKey; + try { + final SecretKey secretKey = + (SecretKey) keyStore.getKey("realm_key", null); + final IvParameterSpec initializationVectorSpec = + new IvParameterSpec(initializationVector); + cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec); + decryptedKey = cipher.doFinal(encryptedKey); + } catch (InvalidKeyException | UnrecoverableKeyException | NoSuchAlgorithmException | + BadPaddingException | KeyStoreException | IllegalBlockSizeException | + InvalidAlgorithmParameterException e) { + throw new RuntimeException(e); + } + return decryptedKey; // pass to a realm configuration via encryptionKey() + } } diff --git a/app/src/main/java/com/fox2code/mmm/SetupActivity.java b/app/src/main/java/com/fox2code/mmm/SetupActivity.java index 4ae7f24..77a3c7e 100644 --- a/app/src/main/java/com/fox2code/mmm/SetupActivity.java +++ b/app/src/main/java/com/fox2code/mmm/SetupActivity.java @@ -10,6 +10,9 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Bundle; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.util.Base64; import android.view.View; import android.view.WindowManager; @@ -36,15 +39,35 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.Objects; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; + import io.realm.Realm; import io.realm.RealmConfiguration; import timber.log.Timber; public class SetupActivity extends FoxActivity implements LanguageActivity { + MasterKey mainKeyAlias; + @SuppressLint({"ApplySharedPref", "RestrictedApi"}) @Override protected void onCreate(Bundle savedInstanceState) { @@ -61,7 +84,6 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { actionBar.show(); } this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 0); - createRealmDatabase(); createFiles(); disableUpdateActivityForFdroidFlavor(); // Set theme @@ -165,42 +187,27 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // Set first launch to false // get instance of editor SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean("first_time_setup_done", false); + editor.putString("last_shown_setup", "v1"); // Set the Automatic update check pref editor.putBoolean("pref_background_update_check", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).isChecked()); // Set the crash reporting pref editor.putBoolean("pref_crash_reporting", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).isChecked()); // Set the repos in the ReposList realm db - RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).build(); + RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).build(); boolean androidacyRepo = ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_androidacy_repo))).isChecked(); boolean magiskAltRepo = ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).isChecked(); - Realm.getInstanceAsync(realmConfig, new Realm.Callback() { - @Override - public void onSuccess(@NonNull Realm realm) { - realm.executeTransaction(realm1 -> { - ReposList androidacyRepoDB = realm1.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); - if (androidacyRepoDB != null) { - androidacyRepoDB.setEnabled(androidacyRepo); - } - ReposList magiskAltRepoDB = realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst(); - if (magiskAltRepoDB != null) { - magiskAltRepoDB.setEnabled(magiskAltRepo); - } - // commit the changes - realm1.commitTransaction(); - realm1.close(); - }); - realm.commitTransaction(); - realm.close(); - } - }); + Realm realm = Realm.getInstance(realmConfig); + Objects.requireNonNull(realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst()).setEnabled(androidacyRepo); + Objects.requireNonNull(realm.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst()).setEnabled(magiskAltRepo); + // commit the changes + realm.commitTransaction(); + realm.close(); // Commit the changes editor.commit(); // Sleep for 1 second to allow the user to see the changes try { Thread.sleep(500); - } catch ( - InterruptedException e) { + } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // Log the changes if debug @@ -219,7 +226,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { cancelButton.setText(R.string.cancel); cancelButton.setOnClickListener(v -> { // Set first launch to false and restart the activity - prefs.edit().putBoolean("first_time_setup_done", false).commit(); + prefs.edit().putString("last_shown_setup", "v1").commit(); MainActivity.doSetupRestarting = true; Intent intent = new Intent(this, MainActivity.class); startActivity(intent); @@ -258,9 +265,13 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // creates the realm database private void createRealmDatabase() { Timber.d("Creating Realm databases"); + // create encryption key + Timber.d("Creating encryption key"); + // generate the encryption key and store it in the prefs + byte[] encryptionKey = getNewKey(); // create the realm database for ReposList // next, create the realm database for ReposList - RealmConfiguration config2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration config2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).encryptionKey(encryptionKey).build(); // get the instance Realm.getInstanceAsync(config2, new Realm.Callback() { @Override @@ -308,14 +319,12 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // initial set of cookies, only really used to create the keypair and encrypted file String initialCookie = "is_foxmmm=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=production-api.androidacy.com; SameSite=None; Secure;|foxmmm_version=" + BuildConfig.VERSION_CODE + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=production-api.androidacy.com; SameSite=None; Secure;"; Context context = getApplicationContext(); - MasterKey mainKeyAlias; mainKeyAlias = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(); EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName), mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build(); InputStream inputStream; try { inputStream = encryptedFile.openFileInput(); - } catch ( - FileNotFoundException e) { + } catch (FileNotFoundException e) { Timber.d("Cookie file not found, creating new file"); OutputStream outputStream = encryptedFile.openFileOutput(); outputStream.write(initialCookie.getBytes()); @@ -337,8 +346,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { outputStream.close(); outputStream.flush(); } - } catch (GeneralSecurityException | - IOException e) { + } catch (GeneralSecurityException | IOException e) { Timber.e(e); } // we literally only use these to create the http cache folders @@ -354,6 +362,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { Timber.d("Created http cache dir"); } } + createRealmDatabase(); } @SuppressWarnings("ConstantConditions") @@ -366,4 +375,69 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { pm.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } } + + @SuppressLint("NewApi") + public byte[] getNewKey() { + if (MainApplication.getSharedPreferences("mmm").getBoolean("keygen", false)) { + Timber.d("Key already generated, returning"); + return MainApplication.getINSTANCE().getExistingKey(); + } + // open a connection to the android keystore + KeyStore keyStore; + try { + keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + } catch (java.security.KeyStoreException | NoSuchAlgorithmException | CertificateException | + IOException e) { + throw new RuntimeException(e); + } + // create a securely generated random asymmetric RSA key + byte[] realmKey = new byte[Realm.ENCRYPTION_KEY_LENGTH]; + new SecureRandom().nextBytes(realmKey); + // create a cipher that uses AES encryption -- we'll use this to encrypt our key + Cipher cipher; + try { + cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException(e); + } + // generate secret key + KeyGenerator keyGenerator; + try { + keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); + } catch (NoSuchAlgorithmException | NoSuchProviderException e) { + throw new RuntimeException(e); + } + KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder("realm_key", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT).setBlockModes(KeyProperties.BLOCK_MODE_CBC).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7).setUserAuthenticationRequired(false).build(); + try { + keyGenerator.init(keySpec); + } catch (InvalidAlgorithmParameterException e) { + throw new RuntimeException(e); + } + keyGenerator.generateKey(); + // access the generated key in the android keystore, then + // use the cipher to create an encrypted version of the key + byte[] initializationVector; + byte[] encryptedKeyForRealm; + try { + SecretKey secretKey = (SecretKey) keyStore.getKey("realm_key", null); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + encryptedKeyForRealm = cipher.doFinal(realmKey); + initializationVector = cipher.getIV(); + } catch (InvalidKeyException | UnrecoverableKeyException | NoSuchAlgorithmException | + KeyStoreException | BadPaddingException | IllegalBlockSizeException e) { + throw new RuntimeException(e); + } + // keep the encrypted key in shared preferences + // to persist it across application runs + byte[] initializationVectorAndEncryptedKey = new byte[Integer.BYTES + initializationVector.length + encryptedKeyForRealm.length]; + ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey); + buffer.order(ByteOrder.BIG_ENDIAN); + buffer.putInt(initializationVector.length); + buffer.put(initializationVector); + buffer.put(encryptedKeyForRealm); + MainApplication.getSharedPreferences("realm_key").edit().putString("iv_and_encrypted_key", Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP)).apply(); + MainApplication.getSharedPreferences("mmm").edit().putBoolean("keygen", true).apply(); + return realmKey; // pass to a realm configuration via encryptionKey() + } } \ No newline at end of file diff --git a/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java b/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java index 91871f6..cf24466 100644 --- a/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java +++ b/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java @@ -208,7 +208,7 @@ public class BackgroundUpdateChecker extends Worker { public static void onMainActivityCreate(Context context) { // Refuse to run if first_launch pref is not false - if (MainApplication.getSharedPreferences("mmm").getBoolean("first_time_setup_done", true)) + if (!Objects.equals(MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", null), "v1")) return; // create notification channel group if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -219,7 +219,7 @@ public class BackgroundUpdateChecker extends Worker { NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); notificationManagerCompat.createNotificationChannel(new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_HIGH).setShowBadge(true).setName(context.getString(R.string.notification_update_pref)).setDescription(context.getString(R.string.auto_updates_notifs)).setGroup(NOTFIICATION_GROUP).build()); notificationManagerCompat.cancel(BackgroundUpdateChecker.NOTIFICATION_ID); - WorkManager.getInstance(context).enqueueUniquePeriodicWork("background_checker", ExistingPeriodicWorkPolicy.REPLACE, new PeriodicWorkRequest.Builder(BackgroundUpdateChecker.class, 6, TimeUnit.HOURS).setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()).build()); + WorkManager.getInstance(context).enqueueUniquePeriodicWork("background_checker", ExistingPeriodicWorkPolicy.UPDATE, new PeriodicWorkRequest.Builder(BackgroundUpdateChecker.class, 6, TimeUnit.HOURS).setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()).build()); } public static void onMainActivityResume(Context context) { diff --git a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java index 976e989..0da695a 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -54,6 +54,10 @@ public final class ModuleManager extends SyncManager { } protected void scanInternal(@NonNull UpdateListener updateListener) { + // if last_shown_setup is not "v1", them=n refuse to continue + if (!MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", "").equals("v1")) { + return; + } boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true); SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null; for (ModuleInfo v : this.moduleInfos.values()) { @@ -90,7 +94,7 @@ public final class ModuleManager extends SyncManager { // if the dir name matches the module name, use it as the cache dir File tempCacheRoot = new File(dir.toString()); Timber.d("Looking for cache in %s", tempCacheRoot); - realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(tempCacheRoot).build(); + realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(tempCacheRoot).build(); Realm realm = Realm.getInstance(realmConfiguration); Timber.d("Looking for cache for %s out of %d", module, realm.where(ModuleListCache.class).count()); moduleListCache = realm.where(ModuleListCache.class).equalTo("codename", module).findFirst(); diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java index 431b15d..dabea3d 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -36,11 +36,11 @@ import io.realm.RealmResults; import timber.log.Timber; public class RepoData extends XRepo { - public final String url; - public final String id; - public final File cacheRoot; - public final SharedPreferences cachedPreferences; - public final HashMap moduleHashMap; + public String url; + public String id; + public File cacheRoot; + public SharedPreferences cachedPreferences; + public HashMap moduleHashMap; public final JSONObject supportedProperties = new JSONObject(); private final Object populateLock = new Object(); public JSONObject metaDataCache; @@ -72,6 +72,10 @@ public class RepoData extends XRepo { private boolean forceHide, enabled; // Cache for speed public RepoData(String url, File cacheRoot, SharedPreferences cachedPreferences) { + // if last_shown_setup is not "v1", them=n refuse to continue + if (!cachedPreferences.getString("last_shown_setup", "").equals("v1")) { + return; + } // setup supportedProperties try { supportedProperties.put("id", ""); @@ -106,7 +110,7 @@ public class RepoData extends XRepo { this.defaultName = url; // Set url as default name this.forceHide = AppUpdateManager.shouldForceHide(this.id); // this.enable is set from the database - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); if (BuildConfig.DEBUG) { @@ -292,7 +296,7 @@ public class RepoData extends XRepo { public void setEnabled(boolean enabled) { this.enabled = enabled && !this.forceHide; // reposlist realm - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); realm2.executeTransaction(realm -> { ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); @@ -310,7 +314,7 @@ public class RepoData extends XRepo { } this.forceHide = AppUpdateManager.shouldForceHide(this.id); // reposlist realm - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); boolean dbEnabled; try { @@ -369,12 +373,12 @@ public class RepoData extends XRepo { // should update (lastUpdate > 15 minutes) public boolean shouldUpdate() { Timber.d("Repo " + this.id + " should update check called"); - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); ReposList repo = realm2.where(ReposList.class).equalTo("id", this.id).findFirst(); // Make sure ModuleListCache for repoId is not null File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.id); - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); Realm realm = Realm.getInstance(realmConfiguration); RealmResults moduleListCache = realm.where(ModuleListCache.class).equalTo("repoId", this.id).findAll(); if (repo != null) { diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java index fb2c207..4027604 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -147,6 +147,10 @@ public final class RepoManager extends SyncManager { @SuppressWarnings("StatementWithEmptyBody") private void populateDefaultCache(RepoData repoData) { + // if last_shown_setup is not "v1", them=n refuse to continue + if (!MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", "").equals("v1")) { + return; + } for (RepoModule repoModule : repoData.moduleHashMap.values()) { if (!repoModule.moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)) { RepoModule registeredRepoModule = this.modules.get(repoModule.id); diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java index 877fb77..36ba289 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java @@ -49,11 +49,11 @@ public class RepoUpdater { if (!this.repoData.shouldUpdate()) { Timber.d("Fetching index from cache for %s", this.repoData.id); File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.repoData.id); - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); Realm realm = Realm.getInstance(realmConfiguration); RealmResults results = realm.where(ModuleListCache.class).equalTo("repoId", this.repoData.id).findAll(); // reposlist realm - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); this.toUpdate = Collections.emptyList(); this.toApply = new HashSet<>(); @@ -122,7 +122,7 @@ public class RepoUpdater { // use realm to insert to // props avail: File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.repoData.id); - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); // array with module info default values // supported properties for a module //id= @@ -333,7 +333,7 @@ public class RepoUpdater { Timber.w("Failed to get module info from %s with error %s", this.repoData.id, e.getMessage()); } this.indexRaw = null; - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); if (realm2.isInTransaction()) { realm2.cancelTransaction(); diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java index e02d636..967b619 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -850,7 +850,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { }); } // Get magisk_alt_repo enabled state from realm db - RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm1 = Realm.getInstance(realmConfig); ReposList reposList = realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst(); if (reposList != null) { @@ -885,7 +885,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { SwitchPreferenceCompat switchPreferenceCompat = (SwitchPreferenceCompat) androidacyRepoEnabled; switchPreferenceCompat.setChecked(false); // Disable in realm db - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); realm.executeTransaction(realm2 -> { ReposList repoRealmResults = realm2.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); @@ -898,7 +898,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { }); } // get if androidacy repo is enabled from realm db - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); ReposList repoRealmResults = realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); if (repoRealmResults == null) { @@ -1058,7 +1058,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { @SuppressLint("RestrictedApi") public void updateCustomRepoList(boolean initial) { - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); // get all repos that are not built-in int CUSTOM_REPO_ENTRIES = 0; @@ -1184,7 +1184,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { return; if (!preferenceName.contains("androidacy") && !preferenceName.contains("magisk_alt_repo")) { if (repoData != null) { - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); RealmResults repoDataRealmResults = realm.where(ReposList.class).equalTo("id", repoData.id).findAll(); Timber.d("Setting preference " + preferenceName + " because it is not the Androidacy repo or the Magisk Alt Repo"); diff --git a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java index 1c37b45..f8afe9c 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java +++ b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java @@ -29,7 +29,7 @@ public class SentryMain { public static void initialize(final MainApplication mainApplication) { // If first_launch pref is not false, refuse to initialize Sentry SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm"); - if (sharedPreferences.getBoolean("first_time_setup_done", true)) { + if (!Objects.equals(sharedPreferences.getString("last_shown_setup", null), "v1")) { return; } Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {