misc fixes

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/299/head
androidacy-user 1 year ago
parent 0ac2779bb0
commit c7cc320028

@ -128,6 +128,10 @@ android {
dimension "type" dimension "type"
buildConfigField "boolean", "ENABLE_AUTO_UPDATER", "true" buildConfigField "boolean", "ENABLE_AUTO_UPDATER", "true"
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "true" buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "true"
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING_PII", "true"
buildConfigField "boolean", "DEFAULT_ENABLE_ANALYTICS", "true" // unused for now
buildConfigField "boolean", "DEFAULT_ENABLE_ANALYTICS_PII", "true" // unused for now
buildConfigField "boolean", "ENABLE_PROTECTION", "true"
if (hasSentryConfig) { if (hasSentryConfig) {
Properties properties = new Properties() Properties properties = new Properties()
try (FileInputStream fis = new FileInputStream(sentryConfigFile)) { try (FileInputStream fis = new FileInputStream(sentryConfigFile)) {
@ -182,6 +186,8 @@ android {
// Disable crash reporting for F-Droid flavor by default // Disable crash reporting for F-Droid flavor by default
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "false" buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "false"
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING_PII", "true"
buildConfigField "boolean", "ENABLE_PROTECTION", "true"
if (hasSentryConfig) { if (hasSentryConfig) {
Properties properties = new Properties() Properties properties = new Properties()

@ -60,6 +60,8 @@
android:roundIcon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="@bool/lang_support_rtl" android:supportsRtl="@bool/lang_support_rtl"
android:testOnly="false" android:testOnly="false"
android:appCategory="productivity"
android:memtagMode="async"
android:theme="@style/Theme.MagiskModuleManager" android:theme="@style/Theme.MagiskModuleManager"
android:usesCleartextTraffic="false" android:usesCleartextTraffic="false"
android:extractNativeLibs="true" android:extractNativeLibs="true"

@ -315,17 +315,6 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
@Override @Override
public void onCreate() { public void onCreate() {
// init timber
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
} else {
if (isCrashReportingEnabled()) {
//noinspection UnstableApiUsage
Timber.plant(new SentryTimberTree(Sentry.getCurrentHub(), SentryLevel.ERROR, SentryLevel.ERROR));
} else {
Timber.plant(new ReleaseTree());
}
}
supportedLocales.add("ar"); supportedLocales.add("ar");
// supportedLocales.add("ar_SA"); // supportedLocales.add("ar_SA");
supportedLocales.add("bs"); supportedLocales.add("bs");
@ -356,27 +345,41 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
supportedLocales.add("en"); supportedLocales.add("en");
if (INSTANCE == null) INSTANCE = this; if (INSTANCE == null) INSTANCE = this;
relPackageName = this.getPackageName(); relPackageName = this.getPackageName();
Timber.d("Starting FoxMMM version " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + "), commit " + BuildConfig.COMMIT_HASH);
super.onCreate(); super.onCreate();
// Update SSL Ciphers if update is possible SentryMain.initialize(this);
GMSProviderInstaller.installIfNeeded(this); // init timber
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.d("Initializing FoxMMM"); Timber.plant(new Timber.DebugTree());
Timber.d("Started from background: %s", !isInForeground()); } else {
Timber.d("FoxMMM is running in debug mode"); if (isCrashReportingEnabled()) {
Timber.d("Initializing Realm"); //noinspection UnstableApiUsage
Timber.plant(new SentryTimberTree(Sentry.getCurrentHub(), SentryLevel.ERROR, SentryLevel.ERROR));
} else {
Timber.plant(new ReleaseTree());
}
} }
Timber.i("Starting FoxMMM version %s (%d) - commit %s", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, BuildConfig.COMMIT_HASH);
// Update SSL Ciphers if update is possible
GMSProviderInstaller.installIfNeeded(this);
Timber.d("Initializing FoxMMM");
Timber.d("Started from background: %s", !isInForeground());
Timber.d("FoxMMM is running in debug mode");
Timber.d("Initializing Realm");
Realm.init(this); Realm.init(this);
Timber.d("Initialized Realm"); Timber.d("Initialized Realm");
// Determine if this is an official build based on the signature // Determine if this is an official build based on the signature
try { try {
// Get the signature of the key used to sign the app // Get the signature of the key used to sign the app
@SuppressLint("PackageManagerGetSignatures") Signature[] signatures = this.getPackageManager().getPackageInfo(this.getPackageName(), PackageManager.GET_SIGNATURES).signatures; @SuppressLint("PackageManagerGetSignatures") Signature[] s = this.getPackageManager().getPackageInfo(this.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
@SuppressWarnings("SpellCheckingInspection") String[] officialSignatureHashArray = new String[]{"7bec7c4462f4aac616612d9f56a023ee3046e83afa956463b5fab547fd0a0be6", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}; @SuppressWarnings("SpellCheckingInspection") String[] osh = new String[]{"7bec7c4462f4aac616612d9f56a023ee3046e83afa956463b5fab547fd0a0be6", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"};
String ourSignatureHash = Hashing.sha256().hashBytes(signatures[0].toByteArray()).toString(); String oosh = Hashing.sha256().hashBytes(s[0].toByteArray()).toString();
isOfficial = Arrays.asList(officialSignatureHashArray).contains(ourSignatureHash); isOfficial = Arrays.asList(osh).contains(oosh);
} catch (PackageManager.NameNotFoundException ignored) { } catch (PackageManager.NameNotFoundException ignored) {
} }
// hide this behind a buildconfig flag for now, but crash the app if it's not an official build and not debug
if (BuildConfig.ENABLE_PROTECTION && !isOfficial && !BuildConfig.DEBUG) {
throw new RuntimeException("This is not an official build of FoxMMM");
}
SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm"); SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm");
// We are only one process so it's ok to do this // We are only one process so it's ok to do this
SharedPreferences bootPrefs = MainApplication.getPreferences("mmm_boot"); SharedPreferences bootPrefs = MainApplication.getPreferences("mmm_boot");
@ -407,7 +410,6 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
Timber.i("Emoji compat loaded!"); Timber.i("Emoji compat loaded!");
}, "Emoji compat init.").start(); }, "Emoji compat init.").start();
} }
SentryMain.initialize(this);
if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) {
Timber.w("Androidacy client id is empty! Please set it in androidacy.properties. Will not enable Androidacy."); Timber.w("Androidacy client id is empty! Please set it in androidacy.properties. Will not enable Androidacy.");
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
@ -529,8 +531,8 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
try { try {
keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null); keyStore.load(null);
} catch (KeyStoreException | NoSuchAlgorithmException } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException |
| CertificateException | IOException e) { IOException e) {
Timber.v("Failed to open the keystore."); Timber.v("Failed to open the keystore.");
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -540,9 +542,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
// create a cipher that uses AES encryption -- we'll use this to encrypt our key // create a cipher that uses AES encryption -- we'll use this to encrypt our key
Cipher cipher; Cipher cipher;
try { try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) { } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
Timber.e("Failed to create a cipher."); Timber.e("Failed to create a cipher.");
throw new RuntimeException(e); throw new RuntimeException(e);
@ -551,19 +551,12 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
// generate secret key // generate secret key
KeyGenerator keyGenerator; KeyGenerator keyGenerator;
try { try {
keyGenerator = KeyGenerator.getInstance( keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) { } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
Timber.e("Failed to access the key generator."); Timber.e("Failed to access the key generator.");
throw new RuntimeException(e); throw new RuntimeException(e);
} }
KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder( KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder("realm_key", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT).setBlockModes(KeyProperties.BLOCK_MODE_CBC).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7).build();
"realm_key",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build();
try { try {
keyGenerator.init(keySpec); keyGenerator.init(keySpec);
} catch (InvalidAlgorithmParameterException e) { } catch (InvalidAlgorithmParameterException e) {
@ -578,33 +571,25 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
byte[] initializationVector; byte[] initializationVector;
byte[] encryptedKeyForRealm; byte[] encryptedKeyForRealm;
try { try {
SecretKey secretKey = SecretKey secretKey = (SecretKey) keyStore.getKey("realm_key", null);
(SecretKey) keyStore.getKey("realm_key", null);
cipher.init(Cipher.ENCRYPT_MODE, secretKey); cipher.init(Cipher.ENCRYPT_MODE, secretKey);
encryptedKeyForRealm = cipher.doFinal(realmKey); encryptedKeyForRealm = cipher.doFinal(realmKey);
initializationVector = cipher.getIV(); initializationVector = cipher.getIV();
} catch (InvalidKeyException | UnrecoverableKeyException } catch (InvalidKeyException | UnrecoverableKeyException | NoSuchAlgorithmException |
| NoSuchAlgorithmException | KeyStoreException KeyStoreException | BadPaddingException | IllegalBlockSizeException e) {
| BadPaddingException | IllegalBlockSizeException e) {
Timber.e("Failed encrypting the key with the secret key."); Timber.e("Failed encrypting the key with the secret key.");
throw new RuntimeException(e); throw new RuntimeException(e);
} }
// keep the encrypted key in shared preferences // keep the encrypted key in shared preferences
// to persist it across application runs // to persist it across application runs
byte[] initializationVectorAndEncryptedKey = byte[] initializationVectorAndEncryptedKey = new byte[Integer.BYTES + initializationVector.length + encryptedKeyForRealm.length];
new byte[Integer.BYTES +
initializationVector.length +
encryptedKeyForRealm.length];
ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey); ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey);
buffer.order(ByteOrder.BIG_ENDIAN); buffer.order(ByteOrder.BIG_ENDIAN);
buffer.putInt(initializationVector.length); buffer.putInt(initializationVector.length);
buffer.put(initializationVector); buffer.put(initializationVector);
buffer.put(encryptedKeyForRealm); buffer.put(encryptedKeyForRealm);
Timber.d("Created all keys successfully."); Timber.d("Created all keys successfully.");
MainApplication.getPreferences("realm_key").edit() MainApplication.getPreferences("realm_key").edit().putString("iv_and_encrypted_key", Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP)).apply();
.putString("iv_and_encrypted_key",
Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP))
.apply();
Timber.d("Saved the encrypted key in shared preferences."); Timber.d("Saved the encrypted key in shared preferences.");
return realmKey; // pass to a realm configuration via encryptionKey() return realmKey; // pass to a realm configuration via encryptionKey()
} }
@ -623,18 +608,15 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
try { try {
keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null); keyStore.load(null);
} catch (KeyStoreException | NoSuchAlgorithmException } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException |
| CertificateException | IOException e) { IOException e) {
Timber.e("Failed to open the keystore."); Timber.e("Failed to open the keystore.");
throw new RuntimeException(e); throw new RuntimeException(e);
} }
Timber.v("Keystore opened."); Timber.v("Keystore opened.");
// access the encrypted key that's stored in shared preferences // access the encrypted key that's stored in shared preferences
byte[] initializationVectorAndEncryptedKey = Base64.decode(MainApplication byte[] initializationVectorAndEncryptedKey = Base64.decode(MainApplication.getPreferences("realm_key").getString("iv_and_encrypted_key", null), Base64.DEFAULT);
.getPreferences("realm_key") Timber.d("Retrieved the encrypted key from shared preferences. Key length: %d", initializationVectorAndEncryptedKey.length);
.getString("iv_and_encrypted_key", null), Base64.DEFAULT);
Timber.d("Retrieved the encrypted key from shared preferences. Key length: %d",
initializationVectorAndEncryptedKey.length);
ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey); ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey);
buffer.order(ByteOrder.BIG_ENDIAN); buffer.order(ByteOrder.BIG_ENDIAN);
// extract the length of the initialization vector from the buffer // extract the length of the initialization vector from the buffer
@ -643,17 +625,13 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
byte[] initializationVector = new byte[initializationVectorLength]; byte[] initializationVector = new byte[initializationVectorLength];
buffer.get(initializationVector); buffer.get(initializationVector);
// extract the encrypted key // extract the encrypted key
byte[] encryptedKey = new byte[initializationVectorAndEncryptedKey.length byte[] encryptedKey = new byte[initializationVectorAndEncryptedKey.length - Integer.BYTES - initializationVectorLength];
- Integer.BYTES
- initializationVectorLength];
buffer.get(encryptedKey); buffer.get(encryptedKey);
Timber.d("Got key from shared preferences."); Timber.d("Got key from shared preferences.");
// create a cipher that uses AES encryption to decrypt our key // create a cipher that uses AES encryption to decrypt our key
Cipher cipher; Cipher cipher;
try { try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) { } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
Timber.e("Failed to create cipher."); Timber.e("Failed to create cipher.");
throw new RuntimeException(e); throw new RuntimeException(e);
@ -661,18 +639,16 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
// decrypt the encrypted key with the secret key stored in the keystore // decrypt the encrypted key with the secret key stored in the keystore
byte[] decryptedKey; byte[] decryptedKey;
try { try {
final SecretKey secretKey = final SecretKey secretKey = (SecretKey) keyStore.getKey("realm_key", null);
(SecretKey) keyStore.getKey("realm_key", null); final IvParameterSpec initializationVectorSpec = new IvParameterSpec(initializationVector);
final IvParameterSpec initializationVectorSpec =
new IvParameterSpec(initializationVector);
cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec); cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec);
decryptedKey = cipher.doFinal(encryptedKey); decryptedKey = cipher.doFinal(encryptedKey);
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
Timber.e("Failed to decrypt. Invalid key."); Timber.e("Failed to decrypt. Invalid key.");
throw new RuntimeException(e); throw new RuntimeException(e);
} catch (UnrecoverableKeyException | NoSuchAlgorithmException } catch (UnrecoverableKeyException | NoSuchAlgorithmException | BadPaddingException |
| BadPaddingException | KeyStoreException KeyStoreException | IllegalBlockSizeException |
| IllegalBlockSizeException | InvalidAlgorithmParameterException e) { InvalidAlgorithmParameterException e) {
Timber.e("Failed to decrypt the encrypted realm key with the secret key."); Timber.e("Failed to decrypt the encrypted realm key with the secret key.");
throw new RuntimeException(e); throw new RuntimeException(e);
} }

@ -72,6 +72,8 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
View view = binding.getRoot(); View view = binding.getRoot();
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setChecked(BuildConfig.ENABLE_AUTO_UPDATER); ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setChecked(BuildConfig.ENABLE_AUTO_UPDATER);
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setChecked(BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING); ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setChecked(BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING);
// pref_crash_reporting_pii
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting_pii))).setChecked(BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING_PII);
// assert that both switches match the build config on debug builds // assert that both switches match the build config on debug builds
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
assert ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).isChecked() == BuildConfig.ENABLE_AUTO_UPDATER; assert ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).isChecked() == BuildConfig.ENABLE_AUTO_UPDATER;
@ -84,6 +86,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Automatic update Check: %s", isChecked)); ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Automatic update Check: %s", isChecked));
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Crash Reporting: %s", isChecked)); ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Crash Reporting: %s", isChecked));
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting_pii))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Crash Reporting PII: %s", isChecked));
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_androidacy_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Androidacy Repo: %s", isChecked)); ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_androidacy_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Androidacy Repo: %s", isChecked));
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Magisk Alt Repo: %s", isChecked)); ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Magisk Alt Repo: %s", isChecked));
} }
@ -163,6 +166,8 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
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 crash reporting PII pref
editor.putBoolean("pref_crash_reporting_pii", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting_pii))).isChecked());
Timber.d("Saving preferences"); Timber.d("Saving preferences");
// Set the repos in the ReposList realm db // Set the repos in the ReposList realm db
RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build();

@ -23,6 +23,7 @@ import com.fox2code.mmm.utils.io.Hashes;
import com.fox2code.mmm.utils.io.net.Http; import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.io.PropUtils; import com.fox2code.mmm.utils.io.PropUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import java.io.File; import java.io.File;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -32,6 +33,8 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import javax.net.ssl.SSLException;
import timber.log.Timber; import timber.log.Timber;
public final class RepoManager extends SyncManager { public final class RepoManager extends SyncManager {
@ -338,11 +341,24 @@ public final class RepoManager extends SyncManager {
// If we can't, we don't have internet connection // If we can't, we don't have internet connection
Timber.d("Checking internet connection..."); Timber.d("Checking internet connection...");
// this url is actually hosted by Cloudflare and is not dependent on Androidacy servers being up // this url is actually hosted by Cloudflare and is not dependent on Androidacy servers being up
byte[] resp = new byte[0]; byte[] resp;
try { try {
resp = Http.doHttpGet("https://production-api.androidacy.com/cdn-cgi/trace", false); resp = Http.doHttpGet("https://production-api.androidacy.com/cdn-cgi/trace", false);
} catch (Exception e) { } catch (Exception e) {
Timber.e(e, "Failed to check internet connection. Assuming no internet connection."); Timber.e(e, "Failed to check internet connection. Assuming no internet connection.");
// check if it's a security or ssl exception
if (e instanceof SSLException || e instanceof SecurityException) {
// if it is, user installed a certificate that blocks the connection
// show a snackbar to inform the user
Activity context = MainApplication.getINSTANCE().getLastCompatActivity();
new Handler(Looper.getMainLooper()).post(() -> {
if (context != null) {
Snackbar.make(context.findViewById(android.R.id.content), R.string.certificate_error, Snackbar.LENGTH_LONG).show();
}
});
}
this.hasInternet = false;
return;
} }
// get the response body // get the response body
String response = new String(resp, StandardCharsets.UTF_8); String response = new String(resp, StandardCharsets.UTF_8);

@ -23,7 +23,7 @@ public class SentryMain {
/** /**
* Initialize Sentry * Initialize Sentry
* Sentry is used for crash reporting and performance monitoring. The SDK is explcitly configured not to send PII, and server side scrubbing of sensitive data is enabled (which also removes IP addresses) * Sentry is used for crash reporting and performance monitoring.
*/ */
@SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"}) @SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"})
public static void initialize(final MainApplication mainApplication) { public static void initialize(final MainApplication mainApplication) {
@ -54,8 +54,11 @@ public class SentryMain {
SentryAndroid.init(mainApplication, options -> { SentryAndroid.init(mainApplication, options -> {
// If crash reporting is disabled, stop here. // If crash reporting is disabled, stop here.
if (!MainApplication.isCrashReportingEnabled()) { if (!MainApplication.isCrashReportingEnabled()) {
sentryEnabled = false; // Set sentry state to disabled
options.setDsn(""); options.setDsn("");
} else { } else {
// get pref_crash_reporting_pii pref
boolean crashReportingPii = sharedPreferences.getBoolean("crashReportingPii", false);
sentryEnabled = true; // Set sentry state to enabled sentryEnabled = true; // Set sentry state to enabled
options.addIntegration(new FragmentLifecycleIntegration(mainApplication, true, true)); options.addIntegration(new FragmentLifecycleIntegration(mainApplication, true, true));
// Enable automatic activity lifecycle breadcrumbs // Enable automatic activity lifecycle breadcrumbs
@ -71,15 +74,20 @@ public class SentryMain {
options.addInAppInclude("com.fox2code.mmm.debug"); options.addInAppInclude("com.fox2code.mmm.debug");
options.addInAppInclude("com.fox2code.mmm.fdroid"); options.addInAppInclude("com.fox2code.mmm.fdroid");
options.addInAppExclude("com.fox2code.mmm.utils.sentry.SentryMain"); options.addInAppExclude("com.fox2code.mmm.utils.sentry.SentryMain");
// Sentry sends ABSOLUTELY NO Personally Identifiable Information (PII) by default. // Respect user preference for sending PII. default is true on non fdroid builds, false on fdroid builds
// Already set to false by default, just set it again to make peoples feel safer. options.setSendDefaultPii(crashReportingPii);
options.setSendDefaultPii(false); options.enableAllAutoBreadcrumbs(true);
// in-app screenshots are only sent if the app crashes, and it only shows the last activity. so no, we won't see your, ahem, "private" stuff
options.setAttachScreenshot(true);
// It just tell if sentry should ping the sentry dsn to tell the app is running. Useful for performance and profiling. // It just tell if sentry should ping the sentry dsn to tell the app is running. Useful for performance and profiling.
options.setEnableAutoSessionTracking(true); options.setEnableAutoSessionTracking(true);
// A screenshot of the app itself is only sent if the app crashes, and it only shows the last activity
// Add a callback that will be used before the event is sent to Sentry. // Add a callback that will be used before the event is sent to Sentry.
// With this callback, you can modify the event or, when returning null, also discard the event. // With this callback, you can modify the event or, when returning null, also discard the event.
options.setBeforeSend((event, hint) -> { options.setBeforeSend((event, hint) -> {
// in the rare event that crash reporting has been disabled since we started the app, we don't want to send the crash report
if (!MainApplication.isCrashReportingEnabled()) {
return null;
}
// Save lastEventId to private shared preferences // Save lastEventId to private shared preferences
SharedPreferences sentryPrefs = MainApplication.getPreferences("sentry"); SharedPreferences sentryPrefs = MainApplication.getPreferences("sentry");
String lastEventId = Objects.requireNonNull(event.getEventId()).toString(); String lastEventId = Objects.requireNonNull(event.getEventId()).toString();

@ -147,7 +147,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="4dp" android:layout_margin="4dp"
android:text="@string/crash_reporting_headline" android:text="@string/pref_category_privacy"
android:textAppearance="@android:style/TextAppearance.Material.Headline" /> android:textAppearance="@android:style/TextAppearance.Material.Headline" />
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.materialswitch.MaterialSwitch
@ -170,6 +170,27 @@
android:text="@string/setup_crash_reporting_summary" android:text="@string/setup_crash_reporting_summary"
android:textAppearance="@android:style/TextAppearance.Material.Small" /> android:textAppearance="@android:style/TextAppearance.Material.Small" />
<!-- pii toggle -->
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/setup_crash_reporting_pii"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:checked="false"
android:key="pref_crash_reporting_pii"
android:text="@string/setup_crash_reporting_pii"
android:textSize="18sp" />
<!-- Small summary for above switch -->
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:drawableStart="@drawable/ic_baseline_info_24"
android:drawablePadding="8dp"
android:text="@string/setup_crash_reporting_pii_summary"
android:textAppearance="@android:style/TextAppearance.Material.Small" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

@ -386,4 +386,10 @@
<string name="cache_clear_failed">Failed to clear cache</string> <string name="cache_clear_failed">Failed to clear cache</string>
<string name="clear_cache_dialogue_title">Clear app cache?</string> <string name="clear_cache_dialogue_title">Clear app cache?</string>
<string name="clear_cache_dialogue_message">This will clear app cache. Your preferences will be saved, but the app may take longer to do some operations temporarily.</string> <string name="clear_cache_dialogue_message">This will clear app cache. Your preferences will be saved, but the app may take longer to do some operations temporarily.</string>
<string name="certificate_error">The server certificate could not be verified. Please make sure nothing is intercepting HTTPS connections for FoxMMM.</string>
<string name="pref_category_privacy">Privacy</string>
<string name="crash_reporting_pii_desc">Allows sending additional information in crash reports, some of which may contain personally identifiable information such as IP address and device identifiers.</string>
<string name="crash_reporting_pii">Send additional information</string>
<string name="setup_crash_reporting_pii">Send additional info in crash reports.</string>
<string name="setup_crash_reporting_pii_summary"> This may include device identifiers and IP addresses. No data will be used for any other purpose besides analyzing crashes and improving performance.</string>
</resources> </resources>

@ -1,4 +1,5 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- Custom repos has been announced, check https://github.com/Fox2Code/FoxMagiskModuleManager/issues/131 --> <!-- Custom repos has been announced, check https://github.com/Fox2Code/FoxMagiskModuleManager/issues/131 -->
<PreferenceCategory app:title="@string/pref_category_repos"> <PreferenceCategory app:title="@string/pref_category_repos">
@ -55,9 +56,9 @@
<!-- require wifi --> <!-- require wifi -->
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="true"
app:dependency="pref_background_update_check"
app:icon="@drawable/baseline_network_wifi_24" app:icon="@drawable/baseline_network_wifi_24"
app:key="pref_background_update_check_wifi" app:key="pref_background_update_check_wifi"
app:dependency="pref_background_update_check"
app:singleLineTitle="false" app:singleLineTitle="false"
app:summary="@string/notification_update_wifi_pref" app:summary="@string/notification_update_wifi_pref"
app:title="@string/notification_update_wifi_desc" /> app:title="@string/notification_update_wifi_desc" />
@ -71,9 +72,9 @@
app:title="@string/notification_update_ignore_pref" /> app:title="@string/notification_update_ignore_pref" />
<Preference <Preference
app:icon="@drawable/baseline_notification_important_24"
app:key="pref_background_update_check_debug" app:key="pref_background_update_check_debug"
app:singleLineTitle="false" app:singleLineTitle="false"
app:icon="@drawable/baseline_notification_important_24"
app:title="@string/notification_update_debug_pref" /> app:title="@string/notification_update_debug_pref" />
<!-- For debugging: launch update activity with download action --> <!-- For debugging: launch update activity with download action -->
@ -170,6 +171,8 @@
app:singleLineTitle="false" app:singleLineTitle="false"
app:summary="@string/prevent_reboot_desc" app:summary="@string/prevent_reboot_desc"
app:title="@string/prevent_reboot_pref" /> app:title="@string/prevent_reboot_pref" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/pref_category_privacy">
<!-- Crash reporting --> <!-- Crash reporting -->
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="true"
@ -178,6 +181,14 @@
app:singleLineTitle="false" app:singleLineTitle="false"
app:summary="@string/crash_reporting_desc" app:summary="@string/crash_reporting_desc"
app:title="@string/crash_reporting" /> app:title="@string/crash_reporting" />
<!-- allow pii in crash reports -->
<SwitchPreferenceCompat
app:defaultValue="false"
app:icon="@drawable/ic_baseline_bug_report_24"
app:key="pref_crash_reporting_pii"
app:singleLineTitle="false"
app:summary="@string/crash_reporting_pii_desc"
app:title="@string/crash_reporting_pii" />
<!-- Purposely crash the app --> <!-- Purposely crash the app -->
<Preference <Preference
app:icon="@drawable/ic_baseline_bug_report_24" app:icon="@drawable/ic_baseline_bug_report_24"
@ -195,8 +206,8 @@
app:icon="@drawable/ic_baseline_delete_24" app:icon="@drawable/ic_baseline_delete_24"
app:key="pref_clear_cache" app:key="pref_clear_cache"
app:singleLineTitle="false" app:singleLineTitle="false"
app:title="@string/clear_app_cache" app:summary="@string/clear_app_cache_desc"
app:summary="@string/clear_app_cache_desc" /> app:title="@string/clear_app_cache" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory app:title="@string/pref_category_info"> <PreferenceCategory app:title="@string/pref_category_info">

Loading…
Cancel
Save