diff --git a/app/build.gradle b/app/build.gradle index 049f33b..e76de54 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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 { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index db89b3f..35ad236 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + diff --git a/app/src/main/java/us/spotco/malwarescanner/Database.java b/app/src/main/java/us/spotco/malwarescanner/Database.java index 6e3c341..7c6ebec 100644 --- a/app/src/main/java/us/spotco/malwarescanner/Database.java +++ b/app/src/main/java/us/spotco/malwarescanner/Database.java @@ -53,6 +53,8 @@ class Database { private static final DateFormat dateFormat = DateFormat.getDateInstance(); + private static final ConcurrentLinkedQueue> 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 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 { + @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"); } diff --git a/app/src/main/java/us/spotco/malwarescanner/MainActivity.java b/app/src/main/java/us/spotco/malwarescanner/MainActivity.java index da33133..a3b46e4 100644 --- a/app/src/main/java/us/spotco/malwarescanner/MainActivity.java +++ b/app/src/main/java/us/spotco/malwarescanner/MainActivity.java @@ -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); + }); } } diff --git a/app/src/main/java/us/spotco/malwarescanner/Utils.java b/app/src/main/java/us/spotco/malwarescanner/Utils.java index 9919993..ee71bd9 100644 --- a/app/src/main/java/us/spotco/malwarescanner/Utils.java +++ b/app/src/main/java/us/spotco/malwarescanner/Utils.java @@ -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; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index de5b1ef..7c25794 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -62,4 +62,5 @@ Scan Control Skipping action, a scan is running! Database signing key + No network connected!