add frequent restart detection

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/Config.java                         |  3 
src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java    | 24 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 14 
3 files changed, 38 insertions(+), 3 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/Config.java 🔗

@@ -101,6 +101,9 @@ public final class Config {
 	public static final long MAM_MAX_CATCHUP =  MILLISECONDS_IN_DAY / 2;
 	public static final int MAM_MAX_MESSAGES = 500;
 
+	public static final long FREQUENT_RESTARTS_DETECTION_WINDOW = 10 * 60 * 60 * 1000; // 10 hours
+	public static final long FREQUENT_RESTARTS_THRESHOLD = 10;
+
 	public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE;
 	public static final int TYPING_TIMEOUT = 8;
 

src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java 🔗

@@ -54,7 +54,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 	private static DatabaseBackend instance = null;
 
 	private static final String DATABASE_NAME = "history";
-	private static final int DATABASE_VERSION = 29;
+	private static final int DATABASE_VERSION = 30;
 
 	private static String CREATE_CONTATCS_STATEMENT = "create table "
 			+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -139,6 +139,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 			+ ") ON CONFLICT IGNORE"
 			+ ");";
 
+	private static String CREATE_START_TIMES_TABLE = "create table start_times (timestamp NUMBER);";
+
 	private DatabaseBackend(Context context) {
 		super(context, DATABASE_NAME, null, DATABASE_VERSION);
 	}
@@ -194,6 +196,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 		db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
 		db.execSQL(CREATE_IDENTITIES_STATEMENT);
 		db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+		db.execSQL(CREATE_START_TIMES_TABLE);
 	}
 
 	@Override
@@ -338,6 +341,9 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 		if (oldVersion < 29 && newVersion >= 29) {
 			db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.ERROR_MESSAGE + " TEXT");
 		}
+		if (oldVersion < 30 && newVersion >= 30) {
+			db.execSQL(CREATE_START_TIMES_TABLE);
+		}
 	}
 
 	private void canonicalizeJids(SQLiteDatabase db) {
@@ -1222,4 +1228,20 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 				SQLiteAxolotlStore.ACCOUNT + " = ?",
 				deleteArgs);
 	}
+
+	public boolean startTimeCountExceedsThreshold() {
+		SQLiteDatabase db = this.getWritableDatabase();
+		long cleanBeforeTimestamp = System.currentTimeMillis() - Config.FREQUENT_RESTARTS_DETECTION_WINDOW;
+		db.execSQL("delete from start_times where timestamp < "+cleanBeforeTimestamp);
+		ContentValues values = new ContentValues();
+		values.put("timestamp",System.currentTimeMillis());
+		db.insert("start_times",null,values);
+		String[] columns = new String[]{"count(timestamp)"};
+		Cursor cursor = db.query("start_times",columns,null,null,null,null,null);
+		if (cursor.moveToFirst()) {
+			return cursor.getInt(0) >= Config.FREQUENT_RESTARTS_THRESHOLD;
+		} else {
+			return false;
+		}
+	}
 }

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -864,6 +864,11 @@ public class XmppConnectionService extends Service {
 		this.databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
 		this.accounts = databaseBackend.getAccounts();
 
+		if (!keepForegroundService() && databaseBackend.startTimeCountExceedsThreshold()) {
+			getPreferences().edit().putBoolean("keep_foreground_service",true).commit();
+			Log.d(Config.LOGTAG,"number of restarts exceeds threshold. enabling foreground service");
+		}
+
 		restoreFromDatabase();
 
 		getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
@@ -894,6 +899,7 @@ public class XmppConnectionService extends Service {
 
 		this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 		this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService");
+
 		toggleForegroundService();
 		updateUnreadCountBadge();
 		toggleScreenEventReceiver();
@@ -940,17 +946,21 @@ public class XmppConnectionService extends Service {
 	}
 
 	public void toggleForegroundService() {
-		if (getPreferences().getBoolean("keep_foreground_service", false)) {
+		if (keepForegroundService()) {
 			startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification());
 		} else {
 			stopForeground(true);
 		}
 	}
 
+	private boolean keepForegroundService() {
+		return getPreferences().getBoolean("keep_foreground_service",false);
+	}
+
 	@Override
 	public void onTaskRemoved(final Intent rootIntent) {
 		super.onTaskRemoved(rootIntent);
-		if (!getPreferences().getBoolean("keep_foreground_service", false)) {
+		if (!keepForegroundService()) {
 			this.logoutAndSave(false);
 		} else {
 			Log.d(Config.LOGTAG,"ignoring onTaskRemoved because foreground service is activated");