Database downloader changes

- Store the new database separately and move into place
- Check for network connection before trying to download
- Don't immediately delete the database on download start
- Only delete new database on download failure
- Wait until all downloads complete before attempting reload

Signed-off-by: Tad <tad@spotco.us>
This commit is contained in:
Tad 2023-12-22 15:30:53 -05:00
parent 55305f88f1
commit fa25395a08
No known key found for this signature in database
GPG key ID: B286E9F57A07424B
6 changed files with 70 additions and 11 deletions

View file

@ -6,8 +6,8 @@ android {
applicationId "us.spotco.malwarescanner"
minSdkVersion 19
targetSdkVersion 32
versionCode 112
versionName "2.35"
versionCode 113
versionName "2.36"
resConfigs 'en', 'af', 'de', 'el', 'es', 'fi', 'fr', 'it', 'pl', 'pt', 'ru', 'tr', 'zh-rCN'
}
buildTypes {

View file

@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="us.spotco.malwarescanner">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

View file

@ -53,6 +53,8 @@ class Database {
private static final DateFormat dateFormat = DateFormat.getDateInstance();
private static final ConcurrentLinkedQueue<AsyncTask<?, ?, ?>> downloadFutures = new ConcurrentLinkedQueue<>();
public Database(TextView log) {
Database.log = log;
}
@ -62,7 +64,15 @@ class Database {
}
public static boolean isDatabaseLoaded() {
return areDatabasesAvailable() && databaseFullyLoaded;
return areDatabasesAvailable() && !isDatabaseLoading();
}
public static boolean isDatabaseLoading() {
return !databaseFullyLoaded && databaseCurrentlyLoading;
}
public static boolean hasDownloadsRunning() {
return downloadFutures.size() > 0;
}
public static void updateDatabase(Context context, ConcurrentLinkedQueue<SignatureDatabase> signatureDatabases) {
@ -106,7 +116,7 @@ class Database {
try {
boolean validated = verifier.verify(databaseLocation, databaseSigLocation, publicKey);
if (validated) {
Log.d("Hypatia", "Successfully validated database");
Log.d("Hypatia", "Successfully validated database: " + databaseLocation.getName());
FileInputStream databaseLoading = new FileInputStream(databaseLocation);
switch (databaseLocation.getName()) {
case "hypatia-md5-bloom.bin":
@ -139,13 +149,29 @@ class Database {
}
public static class Downloader extends AsyncTask<Object, String, String> {
@Override
protected void onPreExecute() {
Database.downloadFutures.add(this);
super.onPreExecute();
}
@Override
protected void onPostExecute(String s) {
Database.downloadFutures.remove(this);
super.onPostExecute(s);
}
@Override
protected String doInBackground(Object... objects) {
boolean onionRouting = (boolean) objects[0];
String url = (String) objects[1];
File out = new File((String) objects[2]);
File outNew = new File((String) objects[2] + ".new");
String baseURL = (String) objects[3];
try {
if(outNew.exists()) {
outNew.delete();
}
HttpURLConnection connection;
if (onionRouting) {
Utils.waitUntilOrbotIsAvailable();
@ -167,10 +193,7 @@ class Database {
int res = connection.getResponseCode();
if (res != 304) {
if (res == 200) {
if (out.exists()) {
out.delete();
}
FileOutputStream fileOutputStream = new FileOutputStream(out);
FileOutputStream fileOutputStream = new FileOutputStream(outNew);
final byte[] data = new byte[1024];
int count;
@ -179,6 +202,8 @@ class Database {
}
fileOutputStream.close();
outNew.renameTo(out); //Move the new file into place
publishProgress(url.replaceAll(baseURL, "")
+ "\n\t" + Utils.getContext().getString(R.string.main_database_download_success)
+ "\n\t" + Utils.getContext().getString(R.string.main_database_released_on, lastModifiedServer) + "\n");
@ -193,7 +218,9 @@ class Database {
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
out.delete();
if(outNew.exists()) {
outNew.delete();
}
publishProgress(url.replaceAll(baseURL, "")
+ "\n" + Utils.getContext().getString(R.string.main_database_download_error_logcat) + "\n");
}

View file

@ -35,6 +35,7 @@ import android.os.Environment;
import android.provider.Settings;
import android.text.InputType;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.WindowManager;
@ -152,6 +153,8 @@ public class MainActivity extends Activity {
case R.id.mnuUpdateDatabase:
if (malwareScanner.running) {
logView.append(getString(R.string.lblScanRunning) + "\n");
} else if (!Utils.isNetworkAvailable(this)) {
logView.append(getString(R.string.lblNoNetwork) + "\n");
} else {
if (prefs.getBoolean("ONION_ROUTING", false)) {
Utils.requestStartOrbot(this);
@ -276,9 +279,27 @@ public class MainActivity extends Activity {
private void updateDatabase() {
new Database(findViewById(R.id.txtLogOutput));
Database.updateDatabase(this, Database.signatureDatabases);
if(!Database.isDatabaseLoading()) {
Database.updateDatabase(this, Database.signatureDatabases);
} else {
Log.w("Hypatia", "Database is loading, not downloading!");
}
if (Database.isDatabaseLoaded()) {
Utils.getThreadPoolExecutor().execute(() -> Database.loadDatabase(getApplicationContext(), true, Database.signatureDatabases));
Utils.getThreadPoolExecutor().execute(() -> {
try {
Thread.sleep(500);
Log.w("Hypatia", "Invoking database reload!");
while (Database.hasDownloadsRunning()) {
Thread.sleep(500);
Log.w("Hypatia", "Download in progress, waiting!");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Log.w("Hypatia", "Really reloading database!");
Database.loadDatabase(getApplicationContext(), true, Database.signatureDatabases);
});
}
}

View file

@ -22,6 +22,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import java.io.File;
@ -169,6 +171,13 @@ class Utils {
}
}
//Credit: https://stackoverflow.com/a/4239019
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null;
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
public static Context getContext() {
return context;
}

View file

@ -62,4 +62,5 @@
<string name="scan_control">Scan Control</string>
<string name="lblScanRunning">Skipping action, a scan is running!</string>
<string name="lblSigningKey">Database signing key</string>
<string name="lblNoNetwork">No network connected!</string>
</resources>