encrypt realms

still a lil crashy but works

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/287/head
androidacy-user 1 year ago
parent a247d801c5
commit 37c158b3b9

@ -697,7 +697,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
Timber.i("Checking if we need to run setup"); Timber.i("Checking if we need to run setup");
// Check if this is the first launch // Check if this is the first launch
SharedPreferences prefs = MainApplication.getSharedPreferences("mmm"); 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) if (BuildConfig.DEBUG)
Timber.i("First launch: %s", firstLaunch); Timber.i("First launch: %s", firstLaunch);
if (firstLaunch) { if (firstLaunch) {

@ -12,6 +12,8 @@ import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Build; import android.os.Build;
import android.os.SystemClock; import android.os.SystemClock;
import android.security.keystore.KeyProperties;
import android.util.Base64;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -37,7 +39,16 @@ import com.topjohnwu.superuser.Shell;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException; 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.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
@ -47,6 +58,13 @@ import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.Random; 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.Markwon;
import io.noties.markwon.html.HtmlPlugin; import io.noties.markwon.html.HtmlPlugin;
import io.noties.markwon.image.ImagesPlugin; 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()
}
} }

@ -10,6 +10,9 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; 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.View;
import android.view.WindowManager; import android.view.WindowManager;
@ -36,15 +39,35 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException; 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 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.Realm;
import io.realm.RealmConfiguration; import io.realm.RealmConfiguration;
import timber.log.Timber; import timber.log.Timber;
public class SetupActivity extends FoxActivity implements LanguageActivity { public class SetupActivity extends FoxActivity implements LanguageActivity {
MasterKey mainKeyAlias;
@SuppressLint({"ApplySharedPref", "RestrictedApi"}) @SuppressLint({"ApplySharedPref", "RestrictedApi"})
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -61,7 +84,6 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
actionBar.show(); actionBar.show();
} }
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 0); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 0);
createRealmDatabase();
createFiles(); createFiles();
disableUpdateActivityForFdroidFlavor(); disableUpdateActivityForFdroidFlavor();
// Set theme // Set theme
@ -165,42 +187,27 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
// Set first launch to false // Set first launch to false
// get instance of editor // get instance of editor
SharedPreferences.Editor editor = prefs.edit(); SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("first_time_setup_done", false); editor.putString("last_shown_setup", "v1");
// Set the Automatic update check pref // Set the Automatic update check pref
editor.putBoolean("pref_background_update_check", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).isChecked()); editor.putBoolean("pref_background_update_check", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).isChecked());
// Set the crash reporting pref // Set the crash reporting pref
editor.putBoolean("pref_crash_reporting", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).isChecked()); editor.putBoolean("pref_crash_reporting", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).isChecked());
// Set the repos in the ReposList realm db // 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 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(); boolean magiskAltRepo = ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).isChecked();
Realm.getInstanceAsync(realmConfig, new Realm.Callback() { Realm realm = Realm.getInstance(realmConfig);
@Override Objects.requireNonNull(realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst()).setEnabled(androidacyRepo);
public void onSuccess(@NonNull Realm realm) { Objects.requireNonNull(realm.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst()).setEnabled(magiskAltRepo);
realm.executeTransaction(realm1 -> { // commit the changes
ReposList androidacyRepoDB = realm1.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); realm.commitTransaction();
if (androidacyRepoDB != null) { realm.close();
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();
}
});
// Commit the changes // Commit the changes
editor.commit(); editor.commit();
// Sleep for 1 second to allow the user to see the changes // Sleep for 1 second to allow the user to see the changes
try { try {
Thread.sleep(500); Thread.sleep(500);
} catch ( } catch (InterruptedException e) {
InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
// Log the changes if debug // Log the changes if debug
@ -219,7 +226,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
cancelButton.setText(R.string.cancel); cancelButton.setText(R.string.cancel);
cancelButton.setOnClickListener(v -> { cancelButton.setOnClickListener(v -> {
// Set first launch to false and restart the activity // 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; MainActivity.doSetupRestarting = true;
Intent intent = new Intent(this, MainActivity.class); Intent intent = new Intent(this, MainActivity.class);
startActivity(intent); startActivity(intent);
@ -258,9 +265,13 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
// creates the realm database // creates the realm database
private void createRealmDatabase() { private void createRealmDatabase() {
Timber.d("Creating Realm databases"); 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 // create the realm database for ReposList
// next, 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 // get the instance
Realm.getInstanceAsync(config2, new Realm.Callback() { Realm.getInstanceAsync(config2, new Realm.Callback() {
@Override @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 // 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;"; 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(); Context context = getApplicationContext();
MasterKey mainKeyAlias;
mainKeyAlias = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(); 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(); EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName), mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build();
InputStream inputStream; InputStream inputStream;
try { try {
inputStream = encryptedFile.openFileInput(); inputStream = encryptedFile.openFileInput();
} catch ( } catch (FileNotFoundException e) {
FileNotFoundException e) {
Timber.d("Cookie file not found, creating new file"); Timber.d("Cookie file not found, creating new file");
OutputStream outputStream = encryptedFile.openFileOutput(); OutputStream outputStream = encryptedFile.openFileOutput();
outputStream.write(initialCookie.getBytes()); outputStream.write(initialCookie.getBytes());
@ -337,8 +346,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
outputStream.close(); outputStream.close();
outputStream.flush(); outputStream.flush();
} }
} catch (GeneralSecurityException | } catch (GeneralSecurityException | IOException e) {
IOException e) {
Timber.e(e); Timber.e(e);
} }
// we literally only use these to create the http cache folders // 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"); Timber.d("Created http cache dir");
} }
} }
createRealmDatabase();
} }
@SuppressWarnings("ConstantConditions") @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); 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()
}
} }

@ -208,7 +208,7 @@ public class BackgroundUpdateChecker extends Worker {
public static void onMainActivityCreate(Context context) { public static void onMainActivityCreate(Context context) {
// Refuse to run if first_launch pref is not false // 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; return;
// create notification channel group // create notification channel group
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -219,7 +219,7 @@ public class BackgroundUpdateChecker extends Worker {
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); 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.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); 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) { public static void onMainActivityResume(Context context) {

@ -54,6 +54,10 @@ public final class ModuleManager extends SyncManager {
} }
protected void scanInternal(@NonNull UpdateListener updateListener) { 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); boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true);
SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null; SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null;
for (ModuleInfo v : this.moduleInfos.values()) { 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 // if the dir name matches the module name, use it as the cache dir
File tempCacheRoot = new File(dir.toString()); File tempCacheRoot = new File(dir.toString());
Timber.d("Looking for cache in %s", tempCacheRoot); 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); Realm realm = Realm.getInstance(realmConfiguration);
Timber.d("Looking for cache for %s out of %d", module, realm.where(ModuleListCache.class).count()); 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(); moduleListCache = realm.where(ModuleListCache.class).equalTo("codename", module).findFirst();

@ -36,11 +36,11 @@ import io.realm.RealmResults;
import timber.log.Timber; import timber.log.Timber;
public class RepoData extends XRepo { public class RepoData extends XRepo {
public final String url; public String url;
public final String id; public String id;
public final File cacheRoot; public File cacheRoot;
public final SharedPreferences cachedPreferences; public SharedPreferences cachedPreferences;
public final HashMap<String, RepoModule> moduleHashMap; public HashMap<String, RepoModule> moduleHashMap;
public final JSONObject supportedProperties = new JSONObject(); public final JSONObject supportedProperties = new JSONObject();
private final Object populateLock = new Object(); private final Object populateLock = new Object();
public JSONObject metaDataCache; public JSONObject metaDataCache;
@ -72,6 +72,10 @@ public class RepoData extends XRepo {
private boolean forceHide, enabled; // Cache for speed private boolean forceHide, enabled; // Cache for speed
public RepoData(String url, File cacheRoot, SharedPreferences cachedPreferences) { 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 // setup supportedProperties
try { try {
supportedProperties.put("id", ""); supportedProperties.put("id", "");
@ -106,7 +110,7 @@ public class RepoData extends XRepo {
this.defaultName = url; // Set url as default name this.defaultName = url; // Set url as default name
this.forceHide = AppUpdateManager.shouldForceHide(this.id); this.forceHide = AppUpdateManager.shouldForceHide(this.id);
// this.enable is set from the database // 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); Realm realm = Realm.getInstance(realmConfiguration);
ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst();
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
@ -292,7 +296,7 @@ public class RepoData extends XRepo {
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
this.enabled = enabled && !this.forceHide; this.enabled = enabled && !this.forceHide;
// reposlist realm // 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); Realm realm2 = Realm.getInstance(realmConfiguration2);
realm2.executeTransaction(realm -> { realm2.executeTransaction(realm -> {
ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); 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); this.forceHide = AppUpdateManager.shouldForceHide(this.id);
// reposlist realm // 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); Realm realm2 = Realm.getInstance(realmConfiguration2);
boolean dbEnabled; boolean dbEnabled;
try { try {
@ -369,12 +373,12 @@ public class RepoData extends XRepo {
// should update (lastUpdate > 15 minutes) // should update (lastUpdate > 15 minutes)
public boolean shouldUpdate() { public boolean shouldUpdate() {
Timber.d("Repo " + this.id + " should update check called"); 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); Realm realm2 = Realm.getInstance(realmConfiguration2);
ReposList repo = realm2.where(ReposList.class).equalTo("id", this.id).findFirst(); ReposList repo = realm2.where(ReposList.class).equalTo("id", this.id).findFirst();
// Make sure ModuleListCache for repoId is not null // Make sure ModuleListCache for repoId is not null
File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.id); 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); Realm realm = Realm.getInstance(realmConfiguration);
RealmResults<ModuleListCache> moduleListCache = realm.where(ModuleListCache.class).equalTo("repoId", this.id).findAll(); RealmResults<ModuleListCache> moduleListCache = realm.where(ModuleListCache.class).equalTo("repoId", this.id).findAll();
if (repo != null) { if (repo != null) {

@ -147,6 +147,10 @@ public final class RepoManager extends SyncManager {
@SuppressWarnings("StatementWithEmptyBody") @SuppressWarnings("StatementWithEmptyBody")
private void populateDefaultCache(RepoData repoData) { 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()) { for (RepoModule repoModule : repoData.moduleHashMap.values()) {
if (!repoModule.moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)) { if (!repoModule.moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)) {
RepoModule registeredRepoModule = this.modules.get(repoModule.id); RepoModule registeredRepoModule = this.modules.get(repoModule.id);

@ -49,11 +49,11 @@ public class RepoUpdater {
if (!this.repoData.shouldUpdate()) { if (!this.repoData.shouldUpdate()) {
Timber.d("Fetching index from cache for %s", this.repoData.id); Timber.d("Fetching index from cache for %s", this.repoData.id);
File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + 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); Realm realm = Realm.getInstance(realmConfiguration);
RealmResults<ModuleListCache> results = realm.where(ModuleListCache.class).equalTo("repoId", this.repoData.id).findAll(); RealmResults<ModuleListCache> results = realm.where(ModuleListCache.class).equalTo("repoId", this.repoData.id).findAll();
// reposlist realm // 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); Realm realm2 = Realm.getInstance(realmConfiguration2);
this.toUpdate = Collections.emptyList(); this.toUpdate = Collections.emptyList();
this.toApply = new HashSet<>(); this.toApply = new HashSet<>();
@ -122,7 +122,7 @@ public class RepoUpdater {
// use realm to insert to // use realm to insert to
// props avail: // props avail:
File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + 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();
// array with module info default values // array with module info default values
// supported properties for a module // supported properties for a module
//id=<string> //id=<string>
@ -333,7 +333,7 @@ public class RepoUpdater {
Timber.w("Failed to get module info from %s with error %s", this.repoData.id, e.getMessage()); Timber.w("Failed to get module info from %s with error %s", this.repoData.id, e.getMessage());
} }
this.indexRaw = null; 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); Realm realm2 = Realm.getInstance(realmConfiguration2);
if (realm2.isInTransaction()) { if (realm2.isInTransaction()) {
realm2.cancelTransaction(); realm2.cancelTransaction();

@ -850,7 +850,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
}); });
} }
// Get magisk_alt_repo enabled state from realm db // 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); Realm realm1 = Realm.getInstance(realmConfig);
ReposList reposList = realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst(); ReposList reposList = realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst();
if (reposList != null) { if (reposList != null) {
@ -885,7 +885,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
SwitchPreferenceCompat switchPreferenceCompat = (SwitchPreferenceCompat) androidacyRepoEnabled; SwitchPreferenceCompat switchPreferenceCompat = (SwitchPreferenceCompat) androidacyRepoEnabled;
switchPreferenceCompat.setChecked(false); switchPreferenceCompat.setChecked(false);
// Disable in realm db // 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 realm = Realm.getInstance(realmConfiguration);
realm.executeTransaction(realm2 -> { realm.executeTransaction(realm2 -> {
ReposList repoRealmResults = realm2.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); 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 // 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); Realm realm = Realm.getInstance(realmConfiguration);
ReposList repoRealmResults = realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); ReposList repoRealmResults = realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst();
if (repoRealmResults == null) { if (repoRealmResults == null) {
@ -1058,7 +1058,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
public void updateCustomRepoList(boolean initial) { 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); Realm realm = Realm.getInstance(realmConfiguration);
// get all repos that are not built-in // get all repos that are not built-in
int CUSTOM_REPO_ENTRIES = 0; int CUSTOM_REPO_ENTRIES = 0;
@ -1184,7 +1184,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
return; return;
if (!preferenceName.contains("androidacy") && !preferenceName.contains("magisk_alt_repo")) { if (!preferenceName.contains("androidacy") && !preferenceName.contains("magisk_alt_repo")) {
if (repoData != null) { 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); Realm realm = Realm.getInstance(realmConfiguration);
RealmResults<ReposList> repoDataRealmResults = realm.where(ReposList.class).equalTo("id", repoData.id).findAll(); RealmResults<ReposList> 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"); Timber.d("Setting preference " + preferenceName + " because it is not the Androidacy repo or the Magisk Alt Repo");

@ -29,7 +29,7 @@ public class SentryMain {
public static void initialize(final MainApplication mainApplication) { public static void initialize(final MainApplication mainApplication) {
// If first_launch pref is not false, refuse to initialize Sentry // If first_launch pref is not false, refuse to initialize Sentry
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm"); SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm");
if (sharedPreferences.getBoolean("first_time_setup_done", true)) { if (!Objects.equals(sharedPreferences.getString("last_shown_setup", null), "v1")) {
return; return;
} }
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {

Loading…
Cancel
Save