Fix reaction notification data inconsistencies.

Co-authored-by: Greyson Parrelli <greyson@signal.org>
This commit is contained in:
Cody Henthorne 2021-04-26 15:51:23 -04:00
parent 006eebb09e
commit c9d1fb8533
6 changed files with 82 additions and 4 deletions

View file

@ -319,6 +319,8 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
setReactions(db, messageId, updatedList);
db.setTransactionSuccessful();
} catch (NoSuchMessageException e) {
Log.w(TAG, "No message for provided id", e);
} finally {
db.endTransaction();
}
@ -338,6 +340,8 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
setReactions(db, messageId, updatedList);
db.setTransactionSuccessful();
} catch (NoSuchMessageException e) {
Log.w(TAG, "No message for provided id", e);
} finally {
db.endTransaction();
}
@ -543,14 +547,15 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
return Optional.absent();
}
private void setReactions(@NonNull SQLiteDatabase db, long messageId, @NonNull ReactionList reactionList) {
ContentValues values = new ContentValues(1);
private void setReactions(@NonNull SQLiteDatabase db, long messageId, @NonNull ReactionList reactionList) throws NoSuchMessageException {
ContentValues values = new ContentValues();
boolean isOutgoing = getMessageRecord(messageId).isOutgoing();
boolean hasReactions = reactionList.getReactionsCount() != 0;
values.put(REACTIONS, reactionList.getReactionsList().isEmpty() ? null : reactionList.toByteArray());
values.put(REACTIONS_UNREAD, hasReactions ? 1 : 0);
if (hasReactions) {
if (isOutgoing && hasReactions) {
values.put(NOTIFIED, 0);
}

View file

@ -889,6 +889,7 @@ public class MmsDatabase extends MessageDatabase {
ContentValues contentValues = new ContentValues();
contentValues.put(NOTIFIED, 1);
contentValues.put(REACTIONS_LAST_SEEN, System.currentTimeMillis());
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
}

View file

@ -465,6 +465,7 @@ public class SmsDatabase extends MessageDatabase {
ContentValues contentValues = new ContentValues();
contentValues.put(NOTIFIED, 1);
contentValues.put(REACTIONS_LAST_SEEN, System.currentTimeMillis());
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
}

View file

@ -16,6 +16,7 @@ import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import com.bumptech.glide.Glide;
import com.google.protobuf.InvalidProtocolBufferException;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;
@ -45,6 +46,7 @@ import org.thoughtcrime.securesms.database.SqlCipherDatabaseHook;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
@ -69,6 +71,7 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@ -174,8 +177,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
private static final int MP4_GIF_SUPPORT = 93;
private static final int BLUR_AVATARS = 94;
private static final int CLEAN_STORAGE_IDS_WITHOUT_INFO = 95;
private static final int CLEAN_REACTION_NOTIFICATIONS = 96;
private static final int DATABASE_VERSION = 95;
private static final int DATABASE_VERSION = 96;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@ -1330,6 +1334,68 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
Log.i(TAG, "There were " + count + " bad rows that had their storageID removed due to not having any other identifier.");
}
if (oldVersion < CLEAN_REACTION_NOTIFICATIONS) {
ContentValues values = new ContentValues(1);
values.put("notified", 1);
int count = 0;
count += db.update("sms", values, "notified = 0 AND read = 1 AND reactions_unread = 1 AND NOT ((type & 31) = 23 AND (type & 10485760) AND (type & 131072 = 0))", null);
count += db.update("mms", values, "notified = 0 AND read = 1 AND reactions_unread = 1 AND NOT ((msg_box & 31) = 23 AND (msg_box & 10485760) AND (msg_box & 131072 = 0))", null);
Log.d(TAG, "Resetting notified for " + count + " read incoming messages that were incorrectly flipped when receiving reactions");
List<Long> smsIds = new ArrayList<>();
try (Cursor cursor = db.query("sms", new String[]{"_id", "reactions", "notified_timestamp"}, "notified = 0 AND reactions_unread = 1", null, null, null, null)) {
while (cursor.moveToNext()) {
byte[] reactions = cursor.getBlob(cursor.getColumnIndexOrThrow("reactions"));
long notifiedTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow("notified_timestamp"));
try {
boolean hasReceiveLaterThanNotified = ReactionList.parseFrom(reactions)
.getReactionsList()
.stream()
.anyMatch(r -> r.getReceivedTime() > notifiedTimestamp);
if (!hasReceiveLaterThanNotified) {
smsIds.add(cursor.getLong(cursor.getColumnIndexOrThrow("_id")));
}
} catch (InvalidProtocolBufferException e) {
Log.e(TAG, e);
}
}
}
if (smsIds.size() > 0) {
Log.d(TAG, "Updating " + smsIds.size() + " records in sms");
db.execSQL("UPDATE sms SET reactions_last_seen = notified_timestamp WHERE _id in (" + Util.join(smsIds, ",") + ")");
}
List<Long> mmsIds = new ArrayList<>();
try (Cursor cursor = db.query("mms", new String[]{"_id", "reactions", "notified_timestamp"}, "notified = 0 AND reactions_unread = 1", null, null, null, null)) {
while (cursor.moveToNext()) {
byte[] reactions = cursor.getBlob(cursor.getColumnIndexOrThrow("reactions"));
long notifiedTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow("notified_timestamp"));
try {
boolean hasReceiveLaterThanNotified = ReactionList.parseFrom(reactions)
.getReactionsList()
.stream()
.anyMatch(r -> r.getReceivedTime() > notifiedTimestamp);
if (!hasReceiveLaterThanNotified) {
mmsIds.add(cursor.getLong(cursor.getColumnIndexOrThrow("_id")));
}
} catch (InvalidProtocolBufferException e) {
Log.e(TAG, e);
}
}
}
if (mmsIds.size() > 0) {
Log.d(TAG, "Updating " + mmsIds.size() + " records in mms");
db.execSQL("UPDATE mms SET reactions_last_seen = notified_timestamp WHERE _id in (" + Util.join(mmsIds, ",") + ")");
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View file

@ -35,6 +35,8 @@ public class DeleteNotificationReceiver extends BroadcastReceiver {
if (ids == null || mms == null || ids.length != mms.length) return;
PendingResult finisher = goAsync();
SignalExecutors.BOUNDED.execute(() -> {
for (int i = 0; i < ids.length; i++) {
if (!mms[i]) {
@ -43,6 +45,7 @@ public class DeleteNotificationReceiver extends BroadcastReceiver {
DatabaseFactory.getMmsDatabase(context).markAsNotified(ids[i]);
}
}
finisher.finish();
});
}
}

View file

@ -49,6 +49,7 @@ public class MarkReadReceiver extends BroadcastReceiver {
NotificationCancellationHelper.cancelLegacy(context, intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1));
PendingResult finisher = goAsync();
SignalExecutors.BOUNDED.execute(() -> {
List<MarkedMessageInfo> messageIdsCollection = new LinkedList<>();
@ -61,6 +62,7 @@ public class MarkReadReceiver extends BroadcastReceiver {
process(context, messageIdsCollection);
ApplicationDependencies.getMessageNotifier().updateNotification(context);
finisher.finish();
});
}
}