Improve constraints on thread and message tables.

main
Greyson Parrelli 2022-12-03 12:38:25 -05:00 zatwierdzone przez Cody Henthorne
rodzic 5d9f00b268
commit 95eba78d9c
29 zmienionych plików z 789 dodań i 1158 usunięć

Wyświetl plik

@ -332,11 +332,6 @@
android:theme="@style/Signal.DayNight.NoActionBar"
android:windowSoftInputMode="adjustResize"/>
<activity android:name=".DatabaseMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".migrations.ApplicationMigrationActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:launchMode="singleTask"
@ -702,7 +697,6 @@
<service android:enabled="true" android:name=".exporter.SignalSmsExportService" android:foregroundServiceType="dataSync" />
<service android:enabled="true" android:name=".service.webrtc.WebRtcCallService" android:foregroundServiceType="camera|microphone"/>
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
<service android:enabled="true" android:exported="false" android:name=".service.KeyCachingService"/>
<service android:enabled="true" android:name=".messages.IncomingMessageObserver$ForegroundService"/>
<service android:name=".service.webrtc.AndroidCallConnectionService"

Wyświetl plik

@ -146,7 +146,6 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
SignalDatabase.init(this,
DatabaseSecretProvider.getOrCreateDatabaseSecret(this),
AttachmentSecretProvider.getInstance(this).getOrCreateAttachmentSecret());
SignalDatabase.triggerDatabaseAccess();
})
.addBlocking("logging", () -> {
initializeLogging();

Wyświetl plik

@ -1,201 +0,0 @@
package org.thoughtcrime.securesms;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.thoughtcrime.securesms.database.SmsMigrator.ProgressDescription;
import org.thoughtcrime.securesms.service.ApplicationMigrationService;
import org.thoughtcrime.securesms.service.ApplicationMigrationService.ImportState;
public class DatabaseMigrationActivity extends PassphraseRequiredActivity {
private final ImportServiceConnection serviceConnection = new ImportServiceConnection();
private final ImportStateHandler importStateHandler = new ImportStateHandler();
private final BroadcastReceiver completedReceiver = new NullReceiver();
private LinearLayout promptLayout;
private LinearLayout progressLayout;
private Button skipButton;
private Button importButton;
private ProgressBar progress;
private TextView progressLabel;
private ApplicationMigrationService importService;
private boolean isVisible = false;
@Override
protected void onCreate(Bundle bundle, boolean ready) {
setContentView(R.layout.database_migration_activity);
initializeResources();
initializeServiceBinding();
}
@Override
public void onResume() {
super.onResume();
isVisible = true;
registerForCompletedNotification();
}
@Override
public void onPause() {
super.onPause();
isVisible = false;
unregisterForCompletedNotification();
}
@Override
public void onDestroy() {
super.onDestroy();
shutdownServiceBinding();
}
@Override
public void onBackPressed() {
}
private void initializeServiceBinding() {
Intent intent = new Intent(this, ApplicationMigrationService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void initializeResources() {
this.promptLayout = (LinearLayout)findViewById(R.id.prompt_layout);
this.progressLayout = (LinearLayout)findViewById(R.id.progress_layout);
this.skipButton = (Button) findViewById(R.id.skip_button);
this.importButton = (Button) findViewById(R.id.import_button);
this.progress = (ProgressBar) findViewById(R.id.import_progress);
this.progressLabel = (TextView) findViewById(R.id.import_status);
this.progressLayout.setVisibility(View.GONE);
this.promptLayout.setVisibility(View.GONE);
this.importButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(DatabaseMigrationActivity.this, ApplicationMigrationService.class);
intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE);
intent.putExtra("master_secret", (Parcelable)getIntent().getParcelableExtra("master_secret"));
startService(intent);
promptLayout.setVisibility(View.GONE);
progressLayout.setVisibility(View.VISIBLE);
}
});
this.skipButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ApplicationMigrationService.setDatabaseImported(DatabaseMigrationActivity.this);
handleImportComplete();
}
});
}
private void registerForCompletedNotification() {
IntentFilter filter = new IntentFilter();
filter.addAction(ApplicationMigrationService.COMPLETED_ACTION);
filter.setPriority(1000);
registerReceiver(completedReceiver, filter);
}
private void unregisterForCompletedNotification() {
unregisterReceiver(completedReceiver);
}
private void shutdownServiceBinding() {
unbindService(serviceConnection);
}
private void handleStateIdle() {
this.promptLayout.setVisibility(View.VISIBLE);
this.progressLayout.setVisibility(View.GONE);
}
private void handleStateProgress(ProgressDescription update) {
this.promptLayout.setVisibility(View.GONE);
this.progressLayout.setVisibility(View.VISIBLE);
this.progressLabel.setText(update.primaryComplete + "/" + update.primaryTotal);
double max = this.progress.getMax();
double primaryTotal = update.primaryTotal;
double primaryComplete = update.primaryComplete;
double secondaryTotal = update.secondaryTotal;
double secondaryComplete = update.secondaryComplete;
this.progress.setProgress((int)Math.round((primaryComplete / primaryTotal) * max));
this.progress.setSecondaryProgress((int)Math.round((secondaryComplete / secondaryTotal) * max));
}
private void handleImportComplete() {
if (isVisible) {
if (getIntent().hasExtra("next_intent")) {
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
} else {
// TODO [greyson] Navigation
startActivity(MainActivity.clearTop(this));
}
}
finish();
}
private class ImportStateHandler extends Handler {
public ImportStateHandler() {
super(Looper.getMainLooper());
}
@Override
public void handleMessage(Message message) {
switch (message.what) {
case ImportState.STATE_IDLE: handleStateIdle(); break;
case ImportState.STATE_MIGRATING_IN_PROGRESS: handleStateProgress((ProgressDescription)message.obj); break;
case ImportState.STATE_MIGRATING_COMPLETE: handleImportComplete(); break;
}
}
}
private class ImportServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
importService = ((ApplicationMigrationService.ApplicationMigrationBinder)service).getService();
importService.setImportStateHandler(importStateHandler);
ImportState state = importService.getState();
importStateHandler.obtainMessage(state.state, state.progress).sendToTarget();
}
@Override
public void onServiceDisconnected(ComponentName name) {
importService.setImportStateHandler(null);
}
}
private static class NullReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
abortBroadcast();
}
}
}

Wyświetl plik

@ -1,43 +0,0 @@
package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import org.thoughtcrime.securesms.DatabaseMigrationActivity;
import org.thoughtcrime.securesms.MainActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.service.ApplicationMigrationService;
public class SystemSmsImportReminder extends Reminder {
public SystemSmsImportReminder(final Context context) {
super(context.getString(R.string.reminder_header_sms_import_title),
context.getString(R.string.reminder_header_sms_import_text));
final OnClickListener okListener = v -> {
Intent intent = new Intent(context, ApplicationMigrationService.class);
intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE);
context.startService(intent);
// TODO [greyson] Navigation
Intent nextIntent = MainActivity.clearTop(context);
Intent activityIntent = new Intent(context, DatabaseMigrationActivity.class);
activityIntent.putExtra("next_intent", nextIntent);
context.startActivity(activityIntent);
};
final OnClickListener cancelListener = new OnClickListener() {
@Override
public void onClick(View v) {
ApplicationMigrationService.setDatabaseImported(context);
}
};
setOkListener(okListener);
setDismissListener(cancelListener);
}
public static boolean isEligible(Context context) {
return !ApplicationMigrationService.isDatabaseImported(context);
}
}

Wyświetl plik

@ -137,7 +137,7 @@ public class GroupTable extends DatabaseTable implements RecipientIdDatabaseRefe
TIMESTAMP, ACTIVE, MMS, V2_MASTER_KEY, V2_REVISION, V2_DECRYPTED_GROUP, LAST_FORCE_UPDATE_TIMESTAMP
};
static final List<String> TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList();
static final List<String> TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).filterNot(it -> it.equals(RECIPIENT_ID)).map(columnName -> TABLE_NAME + "." + columnName).toList();
public GroupTable(Context context, SignalDatabase databaseHelper) {
super(context, databaseHelper);

Wyświetl plik

@ -48,7 +48,7 @@ public class MediaTable extends DatabaseTable {
+ AttachmentTable.TABLE_NAME + "." + AttachmentTable.CAPTION + ", "
+ AttachmentTable.TABLE_NAME + "." + AttachmentTable.NAME + ", "
+ AttachmentTable.TABLE_NAME + "." + AttachmentTable.UPLOAD_TIMESTAMP + ", "
+ MmsTable.TABLE_NAME + "." + MmsTable.MESSAGE_BOX + ", "
+ MmsTable.TABLE_NAME + "." + MmsTable.TYPE + ", "
+ MmsTable.TABLE_NAME + "." + MmsTable.DATE_SENT + ", "
+ MmsTable.TABLE_NAME + "." + MmsTable.DATE_RECEIVED + ", "
+ MmsTable.TABLE_NAME + "." + MmsTable.DATE_SERVER + ", "
@ -67,7 +67,7 @@ public class MediaTable extends DatabaseTable {
+ AttachmentTable.DATA + " IS NOT NULL AND "
+ "(" + AttachmentTable.QUOTE + " = 0 OR (" + AttachmentTable.QUOTE + " = 1 AND " + AttachmentTable.DATA_HASH + " IS NULL)) AND "
+ AttachmentTable.STICKER_PACK_ID + " IS NULL AND "
+ MmsTable.RECIPIENT_ID + " > 0 AND "
+ MmsTable.TABLE_NAME + "." + MmsTable.RECIPIENT_ID + " > 0 AND "
+ THREAD_RECIPIENT_ID + " > 0";
private static final String UNIQUE_MEDIA_QUERY = "SELECT "
@ -194,11 +194,11 @@ public class MediaTable extends DatabaseTable {
List<DatabaseAttachment> attachments = attachmentDatabase.getAttachments(cursor);
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.RECIPIENT_ID)));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.THREAD_ID));
boolean outgoing = MessageTable.Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MESSAGE_BOX)));
boolean outgoing = MessageTable.Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.TYPE)));
long date;
if (MmsTable.Types.isPushType(cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MESSAGE_BOX)))) {
if (MmsTable.Types.isPushType(cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.TYPE)))) {
date = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.DATE_SENT));
} else {
date = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.DATE_RECEIVED));

Wyświetl plik

@ -197,7 +197,6 @@ public abstract class MessageTable extends DatabaseTable implements MmsSmsColumn
public abstract void endTransaction(SQLiteDatabase database);
public abstract void setTransactionSuccessful();
public abstract void endTransaction();
public abstract SQLiteStatement createInsertStatement(SQLiteDatabase database);
public abstract void ensureMigration();
@ -233,7 +232,7 @@ public abstract class MessageTable extends DatabaseTable implements MmsSmsColumn
final @NonNull String getOutgoingTypeClause() {
List<String> segments = new ArrayList<>(Types.OUTGOING_MESSAGE_TYPES.length);
for (long outgoingMessageType : Types.OUTGOING_MESSAGE_TYPES) {
segments.add("(" + getTypeField() + " & " + Types.BASE_TYPE_MASK + " = " + outgoingMessageType + ")");
segments.add("(" + getTableName() + "." + getTypeField() + " & " + Types.BASE_TYPE_MASK + " = " + outgoingMessageType + ")");
}
return Util.join(segments, " OR ");

Wyświetl plik

@ -4,21 +4,21 @@ package org.thoughtcrime.securesms.database;
public interface MmsSmsColumns {
public static final String ID = "_id";
public static final String NORMALIZED_DATE_SENT = "date_sent";
public static final String NORMALIZED_DATE_RECEIVED = "date_received";
public static final String NORMALIZED_TYPE = "type";
public static final String DATE_SENT = "date_sent";
public static final String DATE_RECEIVED = "date_received";
public static final String TYPE = "type";
public static final String DATE_SERVER = "date_server";
public static final String THREAD_ID = "thread_id";
public static final String READ = "read";
public static final String BODY = "body";
public static final String RECIPIENT_ID = "address";
public static final String ADDRESS_DEVICE_ID = "address_device_id";
public static final String RECIPIENT_ID = "recipient_id";
public static final String RECIPIENT_DEVICE_ID = "recipient_device_id";
public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
public static final String READ_RECEIPT_COUNT = "read_receipt_count";
public static final String VIEWED_RECEIPT_COUNT = "viewed_receipt_count";
public static final String MISMATCHED_IDENTITIES = "mismatched_identities";
public static final String UNIQUE_ROW_ID = "unique_row_id";
public static final String SUBSCRIPTION_ID = "subscription_id";
public static final String SMS_SUBSCRIPTION_ID = "subscription_id";
public static final String EXPIRES_IN = "expires_in";
public static final String EXPIRE_STARTED = "expire_started";
public static final String NOTIFIED = "notified";

Wyświetl plik

@ -70,30 +70,27 @@ public class MmsSmsTable extends DatabaseTable {
private static final String[] PROJECTION = { MmsSmsColumns.ID,
MmsSmsColumns.UNIQUE_ROW_ID,
SmsTable.BODY,
SmsTable.TYPE,
MmsSmsColumns.BODY,
MmsSmsColumns.TYPE,
MmsSmsColumns.THREAD_ID,
SmsTable.RECIPIENT_ID,
SmsTable.ADDRESS_DEVICE_ID,
SmsTable.SUBJECT,
MmsSmsColumns.NORMALIZED_DATE_SENT,
MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
SmsTable.RECIPIENT_DEVICE_ID,
MmsSmsColumns.DATE_SENT,
MmsSmsColumns.DATE_RECEIVED,
MmsSmsColumns.DATE_SERVER,
MmsTable.MESSAGE_TYPE,
MmsTable.MESSAGE_BOX,
SmsTable.STATUS,
MmsTable.MMS_MESSAGE_TYPE,
SmsTable.SMS_STATUS,
MmsSmsColumns.UNIDENTIFIED,
MmsTable.PART_COUNT,
MmsTable.CONTENT_LOCATION,
MmsTable.TRANSACTION_ID,
MmsTable.MESSAGE_SIZE,
MmsTable.EXPIRY,
MmsTable.STATUS,
MmsTable.MMS_CONTENT_LOCATION,
MmsTable.MMS_TRANSACTION_ID,
MmsTable.MMS_MESSAGE_SIZE,
MmsTable.MMS_EXPIRY,
MmsTable.MMS_STATUS,
MmsSmsColumns.DELIVERY_RECEIPT_COUNT,
MmsSmsColumns.READ_RECEIPT_COUNT,
MmsSmsColumns.MISMATCHED_IDENTITIES,
MmsTable.NETWORK_FAILURE,
MmsSmsColumns.SUBSCRIPTION_ID,
MmsTable.NETWORK_FAILURES,
MmsSmsColumns.SMS_SUBSCRIPTION_ID,
MmsSmsColumns.EXPIRES_IN,
MmsSmsColumns.EXPIRE_STARTED,
MmsSmsColumns.NOTIFIED,
@ -103,7 +100,6 @@ public class MmsSmsTable extends DatabaseTable {
MmsTable.QUOTE_AUTHOR,
MmsTable.QUOTE_BODY,
MmsTable.QUOTE_MISSING,
MmsTable.QUOTE_ATTACHMENT,
MmsTable.QUOTE_TYPE,
MmsTable.QUOTE_MENTIONS,
MmsTable.SHARED_CONTACTS,
@ -121,12 +117,12 @@ public class MmsSmsTable extends DatabaseTable {
MmsTable.STORY_TYPE,
MmsTable.PARENT_STORY_ID};
private static final String SNIPPET_QUERY = "SELECT " + MmsSmsColumns.ID + ", 0 AS " + TRANSPORT + ", " + SmsTable.TYPE + " AS " + MmsSmsColumns.NORMALIZED_TYPE + ", " + SmsTable.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " FROM " + SmsTable.TABLE_NAME + " " +
"WHERE " + MmsSmsColumns.THREAD_ID + " = ? AND " + SmsTable.TYPE + " NOT IN (" + SmsTable.Types.PROFILE_CHANGE_TYPE + ", " + SmsTable.Types.GV1_MIGRATION_TYPE + ", " + SmsTable.Types.CHANGE_NUMBER_TYPE + ", " + SmsTable.Types.BOOST_REQUEST_TYPE + ", " + SmsTable.Types.SMS_EXPORT_TYPE + ") AND " + SmsTable.TYPE + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + " " +
private static final String SNIPPET_QUERY = "SELECT " + MmsSmsColumns.ID + ", 0 AS " + TRANSPORT + ", " + MmsSmsColumns.TYPE + ", " + MmsSmsColumns.DATE_RECEIVED + " FROM " + SmsTable.TABLE_NAME + " " +
"WHERE " + MmsSmsColumns.THREAD_ID + " = ? AND " + MmsSmsColumns.TYPE + " NOT IN (" + SmsTable.Types.PROFILE_CHANGE_TYPE + ", " + SmsTable.Types.GV1_MIGRATION_TYPE + ", " + SmsTable.Types.CHANGE_NUMBER_TYPE + ", " + SmsTable.Types.BOOST_REQUEST_TYPE + ", " + SmsTable.Types.SMS_EXPORT_TYPE + ") AND " + SmsTable.TYPE + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + " " +
"UNION ALL " +
"SELECT " + MmsSmsColumns.ID + ", 1 AS " + TRANSPORT + ", " + MmsTable.MESSAGE_BOX + " AS " + MmsSmsColumns.NORMALIZED_TYPE + ", " + MmsTable.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " FROM " + MmsTable.TABLE_NAME + " " +
"WHERE " + MmsSmsColumns.THREAD_ID + " = ? AND " + MmsTable.MESSAGE_BOX + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + " AND " + MmsTable.STORY_TYPE + " = 0 AND " + MmsTable.PARENT_STORY_ID + " <= 0 " +
"ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC " +
"SELECT " + MmsSmsColumns.ID + ", 1 AS " + TRANSPORT + ", " + MmsSmsColumns.TYPE + ", " + MmsTable.DATE_RECEIVED + " FROM " + MmsTable.TABLE_NAME + " " +
"WHERE " + MmsSmsColumns.THREAD_ID + " = ? AND " + MmsTable.TYPE + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + " AND " + MmsTable.STORY_TYPE + " = 0 AND " + MmsTable.PARENT_STORY_ID + " <= 0 " +
"ORDER BY " + MmsSmsColumns.DATE_RECEIVED + " DESC " +
"LIMIT 1";
public MmsSmsTable(Context context, SignalDatabase databaseHelper) {
@ -165,7 +161,7 @@ public class MmsSmsTable extends DatabaseTable {
public int getMessagePositionOnOrAfterTimestamp(long threadId, long timestamp) {
String[] projection = new String[] { "COUNT(*)" };
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " +
MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " >= " + timestamp + " AND " +
MmsSmsColumns.DATE_RECEIVED + " >= " + timestamp + " AND " +
MmsTable.STORY_TYPE + " = 0 AND " + MmsTable.PARENT_STORY_ID + " <= 0";
try (Cursor cursor = queryTables(projection, selection, null, null, false)) {
@ -179,7 +175,7 @@ public class MmsSmsTable extends DatabaseTable {
public @Nullable MessageRecord getMessageFor(long timestamp, RecipientId authorId) {
Recipient author = Recipient.resolved(authorId);
try (Cursor cursor = queryTables(PROJECTION, MmsSmsColumns.NORMALIZED_DATE_SENT + " = " + timestamp, null, null, true)) {
try (Cursor cursor = queryTables(PROJECTION, MmsSmsColumns.DATE_SENT + " = " + timestamp, null, null, true)) {
MmsSmsTable.Reader reader = readerFor(cursor);
MessageRecord messageRecord;
@ -210,7 +206,7 @@ public class MmsSmsTable extends DatabaseTable {
public Cursor getConversation(long threadId, long offset, long limit) {
SQLiteDatabase db = databaseHelper.getSignalReadableDatabase();
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
String order = MmsSmsColumns.DATE_RECEIVED + " DESC";
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsTable.STORY_TYPE + " = 0 AND " + MmsTable.PARENT_STORY_ID + " <= 0";
String limitStr = limit > 0 || offset > 0 ? offset + ", " + limit : null;
String query = buildQuery(PROJECTION, selection, order, limitStr, false);
@ -249,7 +245,7 @@ public class MmsSmsTable extends DatabaseTable {
SQLiteDatabase db = databaseHelper.getSignalReadableDatabase();
try (Cursor cursor = db.rawQuery(SNIPPET_QUERY, SqlUtil.buildArgs(threadId, threadId))) {
if (cursor.moveToFirst()) {
return CursorUtil.requireLong(cursor, MmsSmsColumns.NORMALIZED_TYPE);
return CursorUtil.requireLong(cursor, MmsSmsColumns.TYPE);
} else {
throw new NoSuchMessageException("no message");
}
@ -266,14 +262,14 @@ public class MmsSmsTable extends DatabaseTable {
.append(MmsSmsColumns.THREAD_ID + " = ")
.append(stickyThread.getConversationId().getThreadId())
.append(" AND ")
.append(MmsSmsColumns.NORMALIZED_DATE_RECEIVED)
.append(MmsSmsColumns.DATE_RECEIVED)
.append(" >= ")
.append(stickyThread.getEarliestTimestamp())
.append(getStickyWherePartForParentStoryId(stickyThread.getConversationId().getGroupStoryId()))
.append(")");
}
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
String order = MmsSmsColumns.DATE_RECEIVED + " ASC";
String selection = MmsSmsColumns.NOTIFIED + " = 0 AND " + MmsTable.STORY_TYPE + " = 0 AND (" + MmsSmsColumns.READ + " = 0 OR " + MmsSmsColumns.REACTIONS_UNREAD + " = 1" + (stickyQuery.length() > 0 ? " OR (" + stickyQuery + ")" : "") + ")";
return queryTables(PROJECTION, selection, order, null, true);
@ -304,7 +300,7 @@ public class MmsSmsTable extends DatabaseTable {
RecipientId author = targetMessage.isOutgoing() ? Recipient.self().getId() : targetMessage.getRecipient().getId();
String query = MmsTable.QUOTE_ID + " = " + targetMessage.getDateSent() + " AND " + MmsTable.QUOTE_AUTHOR + " = " + author.serialize();
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
String order = MmsSmsColumns.DATE_RECEIVED + " DESC";
List<MessageRecord> records = new ArrayList<>();
@ -421,7 +417,7 @@ public class MmsSmsTable extends DatabaseTable {
}
public int getMessageCountBeforeDate(long date) {
String selection = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " < " + date;
String selection = MmsSmsColumns.DATE_RECEIVED + " < " + date;
try (Cursor cursor = queryTables(new String[] { "COUNT(*)" }, selection, null, null, false)) {
if (cursor != null && cursor.moveToFirst()) {
@ -761,10 +757,10 @@ public class MmsSmsTable extends DatabaseTable {
}
public int getQuotedMessagePosition(long threadId, long quoteId, @NonNull RecipientId recipientId) {
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
String order = MmsSmsColumns.DATE_RECEIVED + " DESC";
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsTable.STORY_TYPE + " = 0" + " AND " + MmsTable.PARENT_STORY_ID + " <= 0";
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.NORMALIZED_DATE_SENT, MmsSmsColumns.RECIPIENT_ID, MmsSmsColumns.REMOTE_DELETED}, selection, order, null, false)) {
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.DATE_SENT, MmsSmsColumns.RECIPIENT_ID, MmsSmsColumns.REMOTE_DELETED}, selection, order, null, false)) {
boolean isOwnNumber = Recipient.resolved(recipientId).isSelf();
while (cursor != null && cursor.moveToNext()) {
@ -784,10 +780,10 @@ public class MmsSmsTable extends DatabaseTable {
}
public int getMessagePositionInConversation(long threadId, long receivedTimestamp, @NonNull RecipientId recipientId) {
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
String order = MmsSmsColumns.DATE_RECEIVED + " DESC";
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsTable.STORY_TYPE + " = 0" + " AND " + MmsTable.PARENT_STORY_ID + " <= 0";
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.NORMALIZED_DATE_RECEIVED, MmsSmsColumns.RECIPIENT_ID, MmsSmsColumns.REMOTE_DELETED}, selection, order, null, false)) {
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.DATE_RECEIVED, MmsSmsColumns.RECIPIENT_ID, MmsSmsColumns.REMOTE_DELETED}, selection, order, null, false)) {
boolean isOwnNumber = Recipient.resolved(recipientId).isSelf();
while (cursor != null && cursor.moveToNext()) {
@ -831,14 +827,14 @@ public class MmsSmsTable extends DatabaseTable {
final String selection;
if (groupStoryId > 0) {
order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
order = MmsSmsColumns.DATE_RECEIVED + " ASC";
selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " +
MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " < " + receivedTimestamp + " AND " +
MmsSmsColumns.DATE_RECEIVED + " < " + receivedTimestamp + " AND " +
MmsTable.STORY_TYPE + " = 0 AND " + MmsTable.PARENT_STORY_ID + " = " + groupStoryId;
} else {
order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
order = MmsSmsColumns.DATE_RECEIVED + " DESC";
selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " +
MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " > " + receivedTimestamp + " AND " +
MmsSmsColumns.DATE_RECEIVED + " > " + receivedTimestamp + " AND " +
MmsTable.STORY_TYPE + " = 0 AND " + MmsTable.PARENT_STORY_ID + " <= 0";
}
@ -851,10 +847,10 @@ public class MmsSmsTable extends DatabaseTable {
}
public long getTimestampForFirstMessageAfterDate(long date) {
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
String selection = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " > " + date;
String order = MmsSmsColumns.DATE_RECEIVED + " ASC";
String selection = MmsSmsColumns.DATE_RECEIVED + " > " + date;
try (Cursor cursor = queryTables(new String[] { MmsSmsColumns.NORMALIZED_DATE_RECEIVED }, selection, order, "1", false)) {
try (Cursor cursor = queryTables(new String[] { MmsSmsColumns.DATE_RECEIVED }, selection, order, "1", false)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(0);
}
@ -926,41 +922,37 @@ public class MmsSmsTable extends DatabaseTable {
attachmentJsonJoin = "NULL";
}
String[] mmsProjection = { MmsTable.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
MmsTable.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
String[] mmsProjection = { MmsSmsColumns.DATE_SENT,
MmsSmsColumns.DATE_RECEIVED,
MmsTable.TABLE_NAME + "." + MmsTable.ID + " AS " + MmsSmsColumns.ID,
"'MMS::' || " + MmsTable.TABLE_NAME + "." + MmsTable.ID + " || '::' || " + MmsTable.DATE_SENT + " AS " + MmsSmsColumns.UNIQUE_ROW_ID,
attachmentJsonJoin + " AS " + AttachmentTable.ATTACHMENT_JSON_ALIAS,
attachmentJsonJoin + " AS " + AttachmentTable.ATTACHMENT_JSON_ALIAS,
SmsTable.BODY,
MmsSmsColumns.READ,
MmsSmsColumns.THREAD_ID,
SmsTable.TYPE,
SmsTable.RECIPIENT_ID,
SmsTable.ADDRESS_DEVICE_ID,
SmsTable.SUBJECT,
MmsTable.MESSAGE_TYPE,
MmsTable.MESSAGE_BOX,
SmsTable.STATUS,
MmsTable.PART_COUNT,
MmsTable.CONTENT_LOCATION,
MmsTable.TRANSACTION_ID,
MmsTable.MESSAGE_SIZE,
MmsTable.EXPIRY,
MmsTable.STATUS,
MmsSmsColumns.TYPE,
MmsSmsColumns.RECIPIENT_ID,
MmsSmsColumns.RECIPIENT_DEVICE_ID,
MmsTable.MMS_MESSAGE_TYPE,
SmsTable.SMS_STATUS,
MmsTable.MMS_CONTENT_LOCATION,
MmsTable.MMS_TRANSACTION_ID,
MmsTable.MMS_MESSAGE_SIZE,
MmsTable.MMS_EXPIRY,
MmsTable.MMS_STATUS,
MmsTable.UNIDENTIFIED,
MmsSmsColumns.DELIVERY_RECEIPT_COUNT,
MmsSmsColumns.READ_RECEIPT_COUNT,
MmsSmsColumns.MISMATCHED_IDENTITIES,
MmsSmsColumns.SUBSCRIPTION_ID,
MmsSmsColumns.SMS_SUBSCRIPTION_ID,
MmsSmsColumns.EXPIRES_IN,
MmsSmsColumns.EXPIRE_STARTED,
MmsSmsColumns.NOTIFIED,
MmsTable.NETWORK_FAILURE, TRANSPORT,
MmsTable.NETWORK_FAILURES, TRANSPORT,
MmsTable.QUOTE_ID,
MmsTable.QUOTE_AUTHOR,
MmsTable.QUOTE_BODY,
MmsTable.QUOTE_MISSING,
MmsTable.QUOTE_ATTACHMENT,
MmsTable.QUOTE_TYPE,
MmsTable.QUOTE_MENTIONS,
MmsTable.SHARED_CONTACTS,
@ -978,26 +970,36 @@ public class MmsSmsTable extends DatabaseTable {
MmsTable.STORY_TYPE,
MmsTable.PARENT_STORY_ID};
String[] smsProjection = { SmsTable.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
SmsTable.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
String[] smsProjection = { MmsSmsColumns.DATE_SENT,
MmsSmsColumns.DATE_RECEIVED,
MmsSmsColumns.ID, "'SMS::' || " + MmsSmsColumns.ID + " || '::' || " + SmsTable.DATE_SENT + " AS " + MmsSmsColumns.UNIQUE_ROW_ID,
"NULL AS " + AttachmentTable.ATTACHMENT_JSON_ALIAS,
SmsTable.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID,
SmsTable.TYPE, SmsTable.RECIPIENT_ID, SmsTable.ADDRESS_DEVICE_ID, SmsTable.SUBJECT, MmsTable.MESSAGE_TYPE,
MmsTable.MESSAGE_BOX, SmsTable.STATUS, MmsTable.PART_COUNT,
MmsTable.CONTENT_LOCATION, MmsTable.TRANSACTION_ID,
MmsTable.MESSAGE_SIZE, MmsTable.EXPIRY, MmsTable.STATUS,
"NULL AS " + AttachmentTable.ATTACHMENT_JSON_ALIAS,
SmsTable.BODY,
MmsSmsColumns.READ,
MmsSmsColumns.THREAD_ID,
MmsSmsColumns.TYPE,
SmsTable.RECIPIENT_ID,
SmsTable.RECIPIENT_DEVICE_ID,
MmsTable.MMS_MESSAGE_TYPE,
SmsTable.SMS_STATUS,
MmsTable.MMS_CONTENT_LOCATION,
MmsTable.MMS_TRANSACTION_ID,
MmsTable.MMS_MESSAGE_SIZE,
MmsTable.MMS_EXPIRY,
MmsTable.MMS_STATUS,
MmsTable.UNIDENTIFIED,
MmsSmsColumns.DELIVERY_RECEIPT_COUNT, MmsSmsColumns.READ_RECEIPT_COUNT,
MmsSmsColumns.DELIVERY_RECEIPT_COUNT,
MmsSmsColumns.READ_RECEIPT_COUNT,
MmsSmsColumns.MISMATCHED_IDENTITIES,
MmsSmsColumns.SUBSCRIPTION_ID, MmsSmsColumns.EXPIRES_IN, MmsSmsColumns.EXPIRE_STARTED,
MmsSmsColumns.SMS_SUBSCRIPTION_ID,
MmsSmsColumns.EXPIRES_IN,
MmsSmsColumns.EXPIRE_STARTED,
MmsSmsColumns.NOTIFIED,
MmsTable.NETWORK_FAILURE, TRANSPORT,
MmsTable.NETWORK_FAILURES, TRANSPORT,
MmsTable.QUOTE_ID,
MmsTable.QUOTE_AUTHOR,
MmsTable.QUOTE_BODY,
MmsTable.QUOTE_MISSING,
MmsTable.QUOTE_ATTACHMENT,
MmsTable.QUOTE_TYPE,
MmsTable.QUOTE_MENTIONS,
MmsTable.SHARED_CONTACTS,
@ -1038,32 +1040,30 @@ public class MmsSmsTable extends DatabaseTable {
mmsColumnsPresent.add(MmsSmsColumns.THREAD_ID);
mmsColumnsPresent.add(MmsSmsColumns.BODY);
mmsColumnsPresent.add(MmsSmsColumns.RECIPIENT_ID);
mmsColumnsPresent.add(MmsSmsColumns.ADDRESS_DEVICE_ID);
mmsColumnsPresent.add(MmsSmsColumns.RECIPIENT_DEVICE_ID);
mmsColumnsPresent.add(MmsSmsColumns.DELIVERY_RECEIPT_COUNT);
mmsColumnsPresent.add(MmsSmsColumns.READ_RECEIPT_COUNT);
mmsColumnsPresent.add(MmsSmsColumns.MISMATCHED_IDENTITIES);
mmsColumnsPresent.add(MmsSmsColumns.SUBSCRIPTION_ID);
mmsColumnsPresent.add(MmsSmsColumns.SMS_SUBSCRIPTION_ID);
mmsColumnsPresent.add(MmsSmsColumns.EXPIRES_IN);
mmsColumnsPresent.add(MmsSmsColumns.EXPIRE_STARTED);
mmsColumnsPresent.add(MmsTable.MESSAGE_TYPE);
mmsColumnsPresent.add(MmsTable.MESSAGE_BOX);
mmsColumnsPresent.add(MmsTable.MMS_MESSAGE_TYPE);
mmsColumnsPresent.add(MmsTable.TYPE);
mmsColumnsPresent.add(MmsTable.DATE_SENT);
mmsColumnsPresent.add(MmsTable.DATE_RECEIVED);
mmsColumnsPresent.add(MmsTable.DATE_SERVER);
mmsColumnsPresent.add(MmsTable.PART_COUNT);
mmsColumnsPresent.add(MmsTable.CONTENT_LOCATION);
mmsColumnsPresent.add(MmsTable.TRANSACTION_ID);
mmsColumnsPresent.add(MmsTable.MESSAGE_SIZE);
mmsColumnsPresent.add(MmsTable.EXPIRY);
mmsColumnsPresent.add(MmsTable.MMS_CONTENT_LOCATION);
mmsColumnsPresent.add(MmsTable.MMS_TRANSACTION_ID);
mmsColumnsPresent.add(MmsTable.MMS_MESSAGE_SIZE);
mmsColumnsPresent.add(MmsTable.MMS_EXPIRY);
mmsColumnsPresent.add(MmsTable.NOTIFIED);
mmsColumnsPresent.add(MmsTable.STATUS);
mmsColumnsPresent.add(MmsTable.MMS_STATUS);
mmsColumnsPresent.add(MmsTable.UNIDENTIFIED);
mmsColumnsPresent.add(MmsTable.NETWORK_FAILURE);
mmsColumnsPresent.add(MmsTable.NETWORK_FAILURES);
mmsColumnsPresent.add(MmsTable.QUOTE_ID);
mmsColumnsPresent.add(MmsTable.QUOTE_AUTHOR);
mmsColumnsPresent.add(MmsTable.QUOTE_BODY);
mmsColumnsPresent.add(MmsTable.QUOTE_MISSING);
mmsColumnsPresent.add(MmsTable.QUOTE_ATTACHMENT);
mmsColumnsPresent.add(MmsTable.QUOTE_TYPE);
mmsColumnsPresent.add(MmsTable.QUOTE_MENTIONS);
mmsColumnsPresent.add(MmsTable.SHARED_CONTACTS);
@ -1084,22 +1084,21 @@ public class MmsSmsTable extends DatabaseTable {
smsColumnsPresent.add(MmsSmsColumns.ID);
smsColumnsPresent.add(MmsSmsColumns.BODY);
smsColumnsPresent.add(MmsSmsColumns.RECIPIENT_ID);
smsColumnsPresent.add(MmsSmsColumns.ADDRESS_DEVICE_ID);
smsColumnsPresent.add(MmsSmsColumns.RECIPIENT_DEVICE_ID);
smsColumnsPresent.add(MmsSmsColumns.READ);
smsColumnsPresent.add(MmsSmsColumns.THREAD_ID);
smsColumnsPresent.add(MmsSmsColumns.DELIVERY_RECEIPT_COUNT);
smsColumnsPresent.add(MmsSmsColumns.READ_RECEIPT_COUNT);
smsColumnsPresent.add(MmsSmsColumns.MISMATCHED_IDENTITIES);
smsColumnsPresent.add(MmsSmsColumns.SUBSCRIPTION_ID);
smsColumnsPresent.add(MmsSmsColumns.SMS_SUBSCRIPTION_ID);
smsColumnsPresent.add(MmsSmsColumns.EXPIRES_IN);
smsColumnsPresent.add(MmsSmsColumns.EXPIRE_STARTED);
smsColumnsPresent.add(MmsSmsColumns.NOTIFIED);
smsColumnsPresent.add(SmsTable.TYPE);
smsColumnsPresent.add(SmsTable.SUBJECT);
smsColumnsPresent.add(SmsTable.DATE_SENT);
smsColumnsPresent.add(SmsTable.DATE_RECEIVED);
smsColumnsPresent.add(SmsTable.DATE_SERVER);
smsColumnsPresent.add(SmsTable.STATUS);
smsColumnsPresent.add(SmsTable.SMS_STATUS);
smsColumnsPresent.add(SmsTable.UNIDENTIFIED);
smsColumnsPresent.add(SmsTable.REACTIONS_UNREAD);
smsColumnsPresent.add(SmsTable.REACTIONS_LAST_SEEN);

Wyświetl plik

@ -31,8 +31,6 @@ import com.google.android.mms.pdu_alt.NotificationInd;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.protobuf.InvalidProtocolBufferException;
import net.zetetic.database.sqlcipher.SQLiteStatement;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -121,80 +119,73 @@ public class MmsTable extends MessageTable {
private static final String TAG = Log.tag(MmsTable.class);
public static final String TABLE_NAME = "mms";
static final String DATE_SENT = "date";
static final String DATE_RECEIVED = "date_received";
public static final String MESSAGE_BOX = "msg_box";
static final String CONTENT_LOCATION = "ct_l";
static final String EXPIRY = "exp";
public static final String MESSAGE_TYPE = "m_type";
static final String MESSAGE_SIZE = "m_size";
static final String STATUS = "st";
static final String TRANSACTION_ID = "tr_id";
static final String PART_COUNT = "part_count";
static final String NETWORK_FAILURE = "network_failures";
public static final String TABLE_NAME = "mms";
static final String MMS_CONTENT_LOCATION = "ct_l";
static final String MMS_EXPIRY = "exp";
public static final String MMS_MESSAGE_TYPE = "m_type";
static final String MMS_MESSAGE_SIZE = "m_size";
static final String MMS_STATUS = "st";
static final String MMS_TRANSACTION_ID = "tr_id";
static final String NETWORK_FAILURES = "network_failures";
static final String QUOTE_ID = "quote_id";
static final String QUOTE_AUTHOR = "quote_author";
static final String QUOTE_BODY = "quote_body";
static final String QUOTE_ATTACHMENT = "quote_attachment";
static final String QUOTE_MISSING = "quote_missing";
static final String QUOTE_MENTIONS = "quote_mentions";
static final String QUOTE_TYPE = "quote_type";
static final String SHARED_CONTACTS = "shared_contacts";
static final String LINK_PREVIEWS = "previews";
static final String LINK_PREVIEWS = "link_previews";
static final String MENTIONS_SELF = "mentions_self";
static final String MESSAGE_RANGES = "ranges";
static final String MESSAGE_RANGES = "message_ranges";
public static final String VIEW_ONCE = "reveal_duration";
public static final String STORY_TYPE = "is_story";
public static final String VIEW_ONCE = "view_once";
public static final String STORY_TYPE = "story_type";
static final String PARENT_STORY_ID = "parent_story_id";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
THREAD_ID + " INTEGER, " +
DATE_SENT + " INTEGER, " +
DATE_RECEIVED + " INTEGER, " +
DATE_SENT + " INTEGER NOT NULL, " +
DATE_RECEIVED + " INTEGER NOT NULL, " +
DATE_SERVER + " INTEGER DEFAULT -1, " +
MESSAGE_BOX + " INTEGER, " +
READ + " INTEGER DEFAULT 0, " +
THREAD_ID + " INTEGER NOT NULL REFERENCES " + ThreadTable.TABLE_NAME + " (" + ThreadTable.ID + ") ON DELETE CASCADE, " +
RECIPIENT_ID + " INTEGER NOT NULL REFERENCES " + RecipientTable.TABLE_NAME + " (" + RecipientTable.ID + ") ON DELETE CASCADE, " +
RECIPIENT_DEVICE_ID + " INTEGER, " +
TYPE + " INTEGER NOT NULL, " +
BODY + " TEXT, " +
PART_COUNT + " INTEGER, " +
CONTENT_LOCATION + " TEXT, " +
RECIPIENT_ID + " INTEGER, " +
ADDRESS_DEVICE_ID + " INTEGER, " +
EXPIRY + " INTEGER, " +
MESSAGE_TYPE + " INTEGER, " +
MESSAGE_SIZE + " INTEGER, " +
STATUS + " INTEGER, " +
TRANSACTION_ID + " TEXT, " +
READ + " INTEGER DEFAULT 0, " +
MMS_CONTENT_LOCATION + " TEXT, " +
MMS_EXPIRY + " INTEGER, " +
MMS_MESSAGE_TYPE + " INTEGER, " +
MMS_MESSAGE_SIZE + " INTEGER, " +
MMS_STATUS + " INTEGER, " +
MMS_TRANSACTION_ID + " TEXT, " +
SMS_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1, " +
DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
VIEWED_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " +
NETWORK_FAILURE + " TEXT DEFAULT NULL," +
SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
NETWORK_FAILURES + " TEXT DEFAULT NULL," +
EXPIRES_IN + " INTEGER DEFAULT 0, " +
EXPIRE_STARTED + " INTEGER DEFAULT 0, " +
NOTIFIED + " INTEGER DEFAULT 0, " +
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
QUOTE_ID + " INTEGER DEFAULT 0, " +
QUOTE_AUTHOR + " TEXT, " +
QUOTE_BODY + " TEXT, " +
QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " +
QUOTE_AUTHOR + " INTEGER DEFAULT 0, " +
QUOTE_BODY + " TEXT DEFAULT NULL, " +
QUOTE_MISSING + " INTEGER DEFAULT 0, " +
QUOTE_MENTIONS + " BLOB DEFAULT NULL," +
QUOTE_TYPE + " INTEGER DEFAULT 0," +
SHARED_CONTACTS + " TEXT, " +
SHARED_CONTACTS + " TEXT DEFAULT NULL, " +
UNIDENTIFIED + " INTEGER DEFAULT 0, " +
LINK_PREVIEWS + " TEXT, " +
LINK_PREVIEWS + " TEXT DEFAULT NULL, " +
VIEW_ONCE + " INTEGER DEFAULT 0, " +
REACTIONS_UNREAD + " INTEGER DEFAULT 0, " +
REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " +
REMOTE_DELETED + " INTEGER DEFAULT 0, " +
MENTIONS_SELF + " INTEGER DEFAULT 0, " +
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " +
VIEWED_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
SERVER_GUID + " TEXT DEFAULT NULL, " +
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1, " +
MESSAGE_RANGES + " BLOB DEFAULT NULL, " +
STORY_TYPE + " INTEGER DEFAULT 0, " +
PARENT_STORY_ID + " INTEGER DEFAULT 0, " +
@ -203,33 +194,64 @@ public class MmsTable extends MessageTable {
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");",
"CREATE INDEX IF NOT EXISTS mms_message_box_index ON " + TABLE_NAME + " (" + MESSAGE_BOX + ");",
"CREATE INDEX IF NOT EXISTS mms_type_index ON " + TABLE_NAME + " (" + TYPE + ");",
"CREATE INDEX IF NOT EXISTS mms_date_sent_index ON " + TABLE_NAME + " (" + DATE_SENT + ", " + RECIPIENT_ID + ", " + THREAD_ID + ");",
"CREATE INDEX IF NOT EXISTS mms_date_server_index ON " + TABLE_NAME + " (" + DATE_SERVER + ");",
"CREATE INDEX IF NOT EXISTS mms_thread_date_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");",
"CREATE INDEX IF NOT EXISTS mms_reactions_unread_index ON " + TABLE_NAME + " (" + REACTIONS_UNREAD + ");",
"CREATE INDEX IF NOT EXISTS mms_is_story_index ON " + TABLE_NAME + " (" + STORY_TYPE + ");",
"CREATE INDEX IF NOT EXISTS mms_story_type_index ON " + TABLE_NAME + " (" + STORY_TYPE + ");",
"CREATE INDEX IF NOT EXISTS mms_parent_story_id_index ON " + TABLE_NAME + " (" + PARENT_STORY_ID + ");",
"CREATE INDEX IF NOT EXISTS mms_thread_story_parent_story_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + "," + STORY_TYPE + "," + PARENT_STORY_ID + ");",
"CREATE INDEX IF NOT EXISTS mms_quote_id_quote_author_index ON " + TABLE_NAME + "(" + QUOTE_ID + ", " + QUOTE_AUTHOR + ");",
"CREATE INDEX IF NOT EXISTS mms_exported_index ON " + TABLE_NAME + " (" + EXPORTED + ");",
"CREATE INDEX IF NOT EXISTS mms_id_msg_box_payment_transactions_index ON " + TABLE_NAME + " (" + ID + "," + MESSAGE_BOX + ") WHERE " + MESSAGE_BOX + " & " + Types.SPECIAL_TYPE_PAYMENTS_NOTIFICATION + " != 0;"
"CREATE INDEX IF NOT EXISTS mms_id_type_payment_transactions_index ON " + TABLE_NAME + " (" + ID + "," + TYPE + ") WHERE " + TYPE + " & " + Types.SPECIAL_TYPE_PAYMENTS_NOTIFICATION + " != 0;"
};
private static final String[] MMS_PROJECTION = new String[] {
MmsTable.TABLE_NAME + "." + ID + " AS " + ID,
THREAD_ID, DATE_SENT + " AS " + NORMALIZED_DATE_SENT,
DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED,
THREAD_ID,
DATE_SENT,
DATE_RECEIVED,
DATE_SERVER,
MESSAGE_BOX, READ,
CONTENT_LOCATION, EXPIRY, MESSAGE_TYPE,
MESSAGE_SIZE, STATUS, TRANSACTION_ID,
BODY, PART_COUNT, RECIPIENT_ID, ADDRESS_DEVICE_ID,
DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_TYPE, QUOTE_MISSING, QUOTE_MENTIONS,
SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, VIEW_ONCE, REACTIONS_UNREAD, REACTIONS_LAST_SEEN,
REMOTE_DELETED, MENTIONS_SELF, NOTIFIED_TIMESTAMP, VIEWED_RECEIPT_COUNT, RECEIPT_TIMESTAMP, MESSAGE_RANGES,
STORY_TYPE, PARENT_STORY_ID,
TYPE,
READ,
MMS_CONTENT_LOCATION,
MMS_EXPIRY,
MMS_MESSAGE_TYPE,
MMS_MESSAGE_SIZE,
MMS_STATUS,
MMS_TRANSACTION_ID,
BODY,
RECIPIENT_ID,
RECIPIENT_DEVICE_ID,
DELIVERY_RECEIPT_COUNT,
READ_RECEIPT_COUNT,
MISMATCHED_IDENTITIES,
NETWORK_FAILURES,
SMS_SUBSCRIPTION_ID,
EXPIRES_IN,
EXPIRE_STARTED,
NOTIFIED,
QUOTE_ID,
QUOTE_AUTHOR,
QUOTE_BODY,
QUOTE_TYPE,
QUOTE_MISSING,
QUOTE_MENTIONS,
SHARED_CONTACTS,
LINK_PREVIEWS,
UNIDENTIFIED,
VIEW_ONCE,
REACTIONS_UNREAD,
REACTIONS_LAST_SEEN,
REMOTE_DELETED,
MENTIONS_SELF,
NOTIFIED_TIMESTAMP,
VIEWED_RECEIPT_COUNT,
RECEIPT_TIMESTAMP,
MESSAGE_RANGES,
STORY_TYPE,
PARENT_STORY_ID,
"json_group_array(json_object(" +
"'" + AttachmentTable.ROW_ID + "', " + AttachmentTable.TABLE_NAME + "." + AttachmentTable.ROW_ID + ", " +
"'" + AttachmentTable.UNIQUE_ID + "', " + AttachmentTable.TABLE_NAME + "." + AttachmentTable.UNIQUE_ID + ", " +
@ -266,9 +288,6 @@ public class MmsTable extends MessageTable {
private static final String RAW_ID_WHERE = TABLE_NAME + "._id = ?";
private static final String OUTGOING_INSECURE_MESSAGES_CLAUSE = "(" + MESSAGE_BOX + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_SENT_TYPE + " AND NOT (" + MESSAGE_BOX + " & " + Types.SECURE_MESSAGE_BIT + ")";
private static final String OUTGOING_SECURE_MESSAGES_CLAUSE = "(" + MESSAGE_BOX + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_SENT_TYPE + " AND (" + MESSAGE_BOX + " & " + (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT) + ")";
private final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache("MmsDelivery");
public MmsTable(Context context, SignalDatabase databaseHelper) {
@ -292,7 +311,7 @@ public class MmsTable extends MessageTable {
@Override
protected String getTypeField() {
return MESSAGE_BOX;
return TYPE;
}
@Override
@ -384,8 +403,8 @@ public class MmsTable extends MessageTable {
@Override
public @NonNull List<MarkedMessageInfo> getViewedIncomingMessages(long threadId) {
SQLiteDatabase db = databaseHelper.getSignalReadableDatabase();
String[] columns = new String[]{ID, RECIPIENT_ID, DATE_SENT, MESSAGE_BOX, THREAD_ID, STORY_TYPE};
String where = THREAD_ID + " = ? AND " + VIEWED_RECEIPT_COUNT + " > 0 AND " + MESSAGE_BOX + " & " + Types.BASE_INBOX_TYPE + " = " + Types.BASE_INBOX_TYPE;
String[] columns = new String[]{ ID, RECIPIENT_ID, DATE_SENT, TYPE, THREAD_ID, STORY_TYPE};
String where = THREAD_ID + " = ? AND " + VIEWED_RECEIPT_COUNT + " > 0 AND " + TYPE + " & " + Types.BASE_INBOX_TYPE + " = " + Types.BASE_INBOX_TYPE;
String[] args = SqlUtil.buildArgs(threadId);
@ -427,21 +446,20 @@ public class MmsTable extends MessageTable {
}
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
String[] columns = new String[]{ID, RECIPIENT_ID, DATE_SENT, MESSAGE_BOX, THREAD_ID, STORY_TYPE};
String[] columns = new String[]{ ID, RECIPIENT_ID, DATE_SENT, TYPE, THREAD_ID, STORY_TYPE};
String where = ID + " IN (" + Util.join(messageIds, ",") + ") AND " + VIEWED_RECEIPT_COUNT + " = 0";
List<MarkedMessageInfo> results = new LinkedList<>();
database.beginTransaction();
try (Cursor cursor = database.query(TABLE_NAME, columns, where, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
long type = CursorUtil.requireLong(cursor, MESSAGE_BOX);
long type = CursorUtil.requireLong(cursor, TYPE);
if (Types.isSecureType(type) && Types.isInboxType(type)) {
long messageId = CursorUtil.requireLong(cursor, ID);
long threadId = CursorUtil.requireLong(cursor, THREAD_ID);
RecipientId recipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID));
long dateSent = CursorUtil.requireLong(cursor, DATE_SENT);
SyncMessageId syncMessageId = new SyncMessageId(recipientId, dateSent);
StoryType storyType = StoryType.fromCode(CursorUtil.requireInt(cursor, STORY_TYPE));
results.add(new MarkedMessageInfo(threadId, syncMessageId, new MessageId(messageId, true), null));
@ -599,11 +617,6 @@ public class MmsTable extends MessageTable {
database.endTransaction();
}
@Override
public SQLiteStatement createInsertStatement(SQLiteDatabase database) {
throw new UnsupportedOperationException();
}
@Override
public void ensureMigration() {
databaseHelper.getSignalWritableDatabase();
@ -827,7 +840,7 @@ public class MmsTable extends MessageTable {
@Override
public @NonNull List<RecipientId> getUnreadStoryThreadRecipientIds() {
SQLiteDatabase db = getReadableDatabase();
String query = "SELECT DISTINCT " + ThreadTable.RECIPIENT_ID + "\n"
String query = "SELECT DISTINCT " + ThreadTable.TABLE_NAME + "." + ThreadTable.RECIPIENT_ID + "\n"
+ "FROM " + TABLE_NAME + "\n"
+ "JOIN " + ThreadTable.TABLE_NAME + "\n"
+ "ON " + TABLE_NAME + "." + THREAD_ID + " = " + ThreadTable.TABLE_NAME + "." + ThreadTable.ID + "\n"
@ -849,27 +862,27 @@ public class MmsTable extends MessageTable {
@Override
public @NonNull List<StoryResult> getOrderedStoryRecipientsAndIds(boolean isOutgoingOnly) {
String where = "WHERE is_story > 0 AND remote_deleted = 0" + (isOutgoingOnly ? " AND is_outgoing != 0" : "") + "\n";
String where = "WHERE " + STORY_TYPE + " > 0 AND " + REMOTE_DELETED + " = 0" + (isOutgoingOnly ? " AND is_outgoing != 0" : "") + "\n";
SQLiteDatabase db = getReadableDatabase();
String query = "SELECT\n"
+ " mms.date AS sent_timestamp,\n"
+ " mms._id AS mms_id,\n"
+ " thread_recipient_id,\n"
+ " " + TABLE_NAME + "." + DATE_SENT + " AS sent_timestamp,\n"
+ " " + TABLE_NAME + "." + ID + " AS mms_id,\n"
+ " " + ThreadTable.TABLE_NAME + "." + ThreadTable.RECIPIENT_ID + ",\n"
+ " (" + getOutgoingTypeClause() + ") AS is_outgoing,\n"
+ " viewed_receipt_count,\n"
+ " mms.date,\n"
+ " receipt_timestamp,\n"
+ " (" + getOutgoingTypeClause() + ") = 0 AND viewed_receipt_count = 0 AS is_unread\n"
+ "FROM mms\n"
+ "JOIN thread\n"
+ "ON mms.thread_id = thread._id\n"
+ " " + VIEWED_RECEIPT_COUNT + ",\n"
+ " " + TABLE_NAME + "." + DATE_SENT + ",\n"
+ " " + RECEIPT_TIMESTAMP + ",\n"
+ " (" + getOutgoingTypeClause() + ") = 0 AND " + VIEWED_RECEIPT_COUNT + " = 0 AS is_unread\n"
+ "FROM " + TABLE_NAME + "\n"
+ "JOIN " + ThreadTable.TABLE_NAME + "\n"
+ "ON " + TABLE_NAME + "." + THREAD_ID + " = " + ThreadTable.TABLE_NAME + "." + ThreadTable.ID + "\n"
+ where
+ "ORDER BY\n"
+ "is_unread DESC,\n"
+ "CASE\n"
+ "WHEN is_outgoing = 0 AND viewed_receipt_count = 0 THEN mms.date\n"
+ "WHEN is_outgoing = 0 AND viewed_receipt_count > 0 THEN receipt_timestamp\n"
+ "WHEN is_outgoing = 1 THEN mms.date\n"
+ "WHEN is_outgoing = 0 AND " + VIEWED_RECEIPT_COUNT + " = 0 THEN " + MmsTable.TABLE_NAME + "." + MmsTable.DATE_SENT + "\n"
+ "WHEN is_outgoing = 0 AND viewed_receipt_count > 0 THEN " + MmsTable.RECEIPT_TIMESTAMP + "\n"
+ "WHEN is_outgoing = 1 THEN " + MmsTable.TABLE_NAME + "." + MmsTable.DATE_SENT + "\n"
+ "END DESC";
List<StoryResult> results;
@ -1047,7 +1060,7 @@ public class MmsTable extends MessageTable {
String[] columns = new String[]{ID};
long type = Types.getOutgoingEncryptedMessageType() | Types.GROUP_LEAVE_BIT;
String query = ID + " = ? AND " + MESSAGE_BOX + " & " + type + " = " + type + " AND " + MESSAGE_BOX + " & " + Types.GROUP_V2_BIT + " = 0";
String query = ID + " = ? AND " + TYPE + " & " + type + " = " + type + " AND " + TYPE + " & " + Types.GROUP_V2_BIT + " = 0";
String[] args = SqlUtil.buildArgs(messageId);
try (Cursor cursor = db.query(TABLE_NAME, columns, query, args, null, null, null, null)) {
@ -1065,7 +1078,7 @@ public class MmsTable extends MessageTable {
String[] columns = new String[]{DATE_SENT};
long type = Types.getOutgoingEncryptedMessageType() | Types.GROUP_LEAVE_BIT;
String query = THREAD_ID + " = ? AND " + MESSAGE_BOX + " & " + type + " = " + type + " AND " + MESSAGE_BOX + " & " + Types.GROUP_V2_BIT + " = 0 AND " + DATE_SENT + " < ?";
String query = THREAD_ID + " = ? AND " + TYPE + " & " + type + " = " + type + " AND " + TYPE + " & " + Types.GROUP_V2_BIT + " = 0 AND " + DATE_SENT + " < ?";
String[] args = new String[]{String.valueOf(threadId), String.valueOf(quitTimeBarrier)};
String orderBy = DATE_SENT + " DESC";
String limit = "1";
@ -1139,7 +1152,7 @@ public class MmsTable extends MessageTable {
@Override
public void addFailures(long messageId, List<NetworkFailure> failure) {
try {
addToDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureSet.class);
addToDocument(messageId, NETWORK_FAILURES, failure, NetworkFailureSet.class);
} catch (IOException e) {
Log.w(TAG, e);
}
@ -1148,7 +1161,7 @@ public class MmsTable extends MessageTable {
@Override
public void setNetworkFailures(long messageId, Set<NetworkFailure> failures) {
try {
setDocument(databaseHelper.getSignalWritableDatabase(), messageId, NETWORK_FAILURE, new NetworkFailureSet(failures));
setDocument(databaseHelper.getSignalWritableDatabase(), messageId, NETWORK_FAILURES, new NetworkFailureSet(failures));
} catch (IOException e) {
Log.w(TAG, e);
}
@ -1174,13 +1187,13 @@ public class MmsTable extends MessageTable {
throw new IllegalArgumentException("Unsupported qualifier: " + messageQualifier);
}
try (Cursor cursor = SQLiteDatabaseExtensionsKt.select(database, ID, THREAD_ID, MESSAGE_BOX, RECIPIENT_ID, receiptType.getColumnName(), RECEIPT_TIMESTAMP)
try (Cursor cursor = SQLiteDatabaseExtensionsKt.select(database, ID, THREAD_ID, TYPE, RECIPIENT_ID, receiptType.getColumnName(), RECEIPT_TIMESTAMP)
.from(TABLE_NAME)
.where(DATE_SENT + " = ?" + qualifierWhere, messageId.getTimetamp())
.run())
{
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(CursorUtil.requireLong(cursor, MESSAGE_BOX))) {
if (Types.isOutgoingMessageType(CursorUtil.requireLong(cursor, TYPE))) {
RecipientId theirRecipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID));
RecipientId ourRecipientId = messageId.getRecipientId();
String columnName = receiptType.getColumnName();
@ -1340,7 +1353,7 @@ public class MmsTable extends MessageTable {
db.beginTransaction();
try {
db.execSQL("UPDATE " + TABLE_NAME +
" SET " + MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" +
" SET " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" +
" WHERE " + ID + " = ?", new String[] { id + "" });
if (threadId.isPresent()) {
@ -1435,7 +1448,6 @@ public class MmsTable extends MessageTable {
values.putNull(BODY);
values.putNull(QUOTE_BODY);
values.putNull(QUOTE_AUTHOR);
values.putNull(QUOTE_ATTACHMENT);
values.putNull(QUOTE_TYPE);
values.putNull(QUOTE_ID);
values.putNull(LINK_PREVIEWS);
@ -1469,7 +1481,7 @@ public class MmsTable extends MessageTable {
public void markDownloadState(long messageId, long state) {
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(STATUS, state);
contentValues.put(MMS_STATUS, state);
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {messageId + ""});
ApplicationDependencies.getDatabaseObserver().notifyMessageUpdateObservers(new MessageId(messageId, true));
@ -1608,10 +1620,10 @@ public class MmsTable extends MessageTable {
database.beginTransaction();
try {
cursor = database.query(TABLE_NAME, new String[] {ID, RECIPIENT_ID, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, THREAD_ID, STORY_TYPE }, where, arguments, null, null, null);
cursor = database.query(TABLE_NAME, new String[] { ID, RECIPIENT_ID, DATE_SENT, TYPE, EXPIRES_IN, EXPIRE_STARTED, THREAD_ID, STORY_TYPE }, where, arguments, null, null, null);
while(cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(CursorUtil.requireLong(cursor, MESSAGE_BOX))) {
if (Types.isSecureType(CursorUtil.requireLong(cursor, TYPE))) {
long threadId = CursorUtil.requireLong(cursor, THREAD_ID);
RecipientId recipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID));
long dateSent = CursorUtil.requireLong(cursor, DATE_SENT);
@ -1712,9 +1724,9 @@ public class MmsTable extends MessageTable {
if (cursor != null && cursor.moveToNext()) {
return Optional.of(new MmsNotificationInfo(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)),
cursor.getString(cursor.getColumnIndexOrThrow(TRANSACTION_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID))));
cursor.getString(cursor.getColumnIndexOrThrow(MMS_CONTENT_LOCATION)),
cursor.getString(cursor.getColumnIndexOrThrow(MMS_TRANSACTION_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(SMS_SUBSCRIPTION_ID))));
} else {
return Optional.empty();
}
@ -1739,17 +1751,17 @@ public class MmsTable extends MessageTable {
List<DatabaseAttachment> associatedAttachments = attachmentDatabase.getAttachmentsForMessage(messageId);
List<Mention> mentions = mentionDatabase.getMentionsForMessage(messageId);
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE));
String body = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SMS_SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
boolean viewOnce = cursor.getLong(cursor.getColumnIndexOrThrow(VIEW_ONCE)) == 1;
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int distributionType = SignalDatabase.threads().getDistributionType(threadId);
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.MISMATCHED_IDENTITIES));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.NETWORK_FAILURE));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.NETWORK_FAILURES));
StoryType storyType = StoryType.fromCode(CursorUtil.requireInt(cursor, STORY_TYPE));
ParentStoryId parentStoryId = ParentStoryId.deserialize(CursorUtil.requireLong(cursor, PARENT_STORY_ID));
@ -1942,14 +1954,13 @@ public class MmsTable extends MessageTable {
contentValues.put(DATE_SERVER, retrieved.getServerTimeMillis());
contentValues.put(RECIPIENT_ID, retrieved.getFrom().serialize());
contentValues.put(MESSAGE_BOX, mailbox);
contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
contentValues.put(TYPE, mailbox);
contentValues.put(MMS_MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
contentValues.put(THREAD_ID, threadId);
contentValues.put(CONTENT_LOCATION, contentLocation);
contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED);
contentValues.put(MMS_CONTENT_LOCATION, contentLocation);
contentValues.put(MMS_STATUS, Status.DOWNLOAD_INITIALIZED);
contentValues.put(DATE_RECEIVED, retrieved.isPushMessage() ? retrieved.getReceivedTimeMillis() : generatePduCompatTimestamp(retrieved.getReceivedTimeMillis()));
contentValues.put(PART_COUNT, retrieved.getAttachments().size());
contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId());
contentValues.put(SMS_SUBSCRIPTION_ID, retrieved.getSubscriptionId());
contentValues.put(EXPIRES_IN, retrieved.getExpiresIn());
contentValues.put(VIEW_ONCE, retrieved.isViewOnce() ? 1 : 0);
contentValues.put(STORY_TYPE, retrieved.getStoryType().getCode());
@ -2101,12 +2112,12 @@ public class MmsTable extends MessageTable {
Log.i(TAG, "Message received type: " + notification.getMessageType());
contentBuilder.add(CONTENT_LOCATION, notification.getContentLocation());
contentBuilder.add(MMS_CONTENT_LOCATION, notification.getContentLocation());
contentBuilder.add(DATE_SENT, System.currentTimeMillis());
contentBuilder.add(EXPIRY, notification.getExpiry());
contentBuilder.add(MESSAGE_SIZE, notification.getMessageSize());
contentBuilder.add(TRANSACTION_ID, notification.getTransactionId());
contentBuilder.add(MESSAGE_TYPE, notification.getMessageType());
contentBuilder.add(MMS_EXPIRY, notification.getExpiry());
contentBuilder.add(MMS_MESSAGE_SIZE, notification.getMessageSize());
contentBuilder.add(MMS_TRANSACTION_ID, notification.getTransactionId());
contentBuilder.add(MMS_MESSAGE_TYPE, notification.getMessageType());
if (notification.getFrom() != null) {
Recipient recipient = Recipient.external(context, Util.toIsoString(notification.getFrom().getTextString()));
@ -2115,12 +2126,12 @@ public class MmsTable extends MessageTable {
contentValues.put(RECIPIENT_ID, RecipientId.UNKNOWN.serialize());
}
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE);
contentValues.put(TYPE, Types.BASE_INBOX_TYPE);
contentValues.put(THREAD_ID, threadId);
contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED);
contentValues.put(MMS_STATUS, Status.DOWNLOAD_INITIALIZED);
contentValues.put(DATE_RECEIVED, generatePduCompatTimestamp(System.currentTimeMillis()));
contentValues.put(READ, Util.isDefaultSmsProvider(context) ? 0 : 1);
contentValues.put(SUBSCRIPTION_ID, subscriptionId);
contentValues.put(SMS_SUBSCRIPTION_ID, subscriptionId);
if (!contentValues.containsKey(DATE_SENT))
contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));
@ -2170,7 +2181,7 @@ public class MmsTable extends MessageTable {
private void markGiftRedemptionState(long messageId, @NonNull GiftBadge.RedemptionState redemptionState) {
String[] projection = SqlUtil.buildArgs(BODY, THREAD_ID);
String where = "(" + MESSAGE_BOX + " & " + Types.SPECIAL_TYPES_MASK + " = " + Types.SPECIAL_TYPE_GIFT_BADGE + ") AND " +
String where = "(" + TYPE + " & " + Types.SPECIAL_TYPES_MASK + " = " + Types.SPECIAL_TYPE_GIFT_BADGE + ") AND " +
ID + " = ?";
String[] args = SqlUtil.buildArgs(messageId);
boolean updated = false;
@ -2282,13 +2293,13 @@ public class MmsTable extends MessageTable {
ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, message.getSentTimeMillis());
contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);
contentValues.put(MMS_MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);
contentValues.put(MESSAGE_BOX, type);
contentValues.put(TYPE, type);
contentValues.put(THREAD_ID, threadId);
contentValues.put(READ, 1);
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(SMS_SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn());
contentValues.put(VIEW_ONCE, message.isViewOnce());
contentValues.put(RECIPIENT_ID, message.getRecipient().getId().serialize());
@ -2415,7 +2426,6 @@ public class MmsTable extends MessageTable {
allAttachments.addAll(previewAttachments);
contentValues.put(BODY, body);
contentValues.put(PART_COUNT, allAttachments.size());
contentValues.put(MENTIONS_SELF, mentionsSelf ? 1 : 0);
if (messageRanges != null) {
@ -2568,9 +2578,9 @@ public class MmsTable extends MessageTable {
@Override
public boolean isSent(long messageId) {
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
try (Cursor cursor = database.query(TABLE_NAME, new String[] { MESSAGE_BOX }, ID + " = ?", new String[] { String.valueOf(messageId)}, null, null, null)) {
try (Cursor cursor = database.query(TABLE_NAME, new String[] { TYPE }, ID + " = ?", new String[] { String.valueOf(messageId)}, null, null, null)) {
if (cursor != null && cursor.moveToNext()) {
long type = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE));
return Types.isSentType(type);
}
}
@ -2585,7 +2595,7 @@ public class MmsTable extends MessageTable {
@Override
public Set<Long> getAllRateLimitedMessageIds() {
SQLiteDatabase db = databaseHelper.getSignalReadableDatabase();
String where = "(" + MESSAGE_BOX + " & " + Types.TOTAL_MASK + " & " + Types.MESSAGE_RATE_LIMITED_BIT + ") > 0";
String where = "(" + TYPE + " & " + Types.TOTAL_MASK + " & " + Types.MESSAGE_RATE_LIMITED_BIT + ") > 0";
Set<Long> ids = new HashSet<>();
@ -2851,7 +2861,6 @@ public class MmsTable extends MessageTable {
0,
threadId, message.getBody(),
slideDeck,
slideDeck.getSlides().size(),
message.isSecure() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
Collections.emptySet(),
Collections.emptySet(),
@ -2915,7 +2924,7 @@ public class MmsTable extends MessageTable {
@Override
public MessageRecord getCurrent() {
long mmsType = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MESSAGE_TYPE));
long mmsType = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MMS_MESSAGE_TYPE));
if (mmsType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) {
return getNotificationMmsMessageRecord(cursor);
@ -2945,22 +2954,22 @@ public class MmsTable extends MessageTable {
private NotificationMmsMessageRecord getNotificationMmsMessageRecord(Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.ID));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.NORMALIZED_DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.NORMALIZED_DATE_RECEIVED));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.DATE_RECEIVED));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.THREAD_ID));
long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MESSAGE_BOX));
long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.TYPE));
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.RECIPIENT_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.ADDRESS_DEVICE_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.RECIPIENT_DEVICE_ID));
Recipient recipient = Recipient.live(RecipientId.from(recipientId)).get();
String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.CONTENT_LOCATION));
String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.TRANSACTION_ID));
long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MESSAGE_SIZE));
long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.EXPIRY));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.STATUS));
String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.MMS_CONTENT_LOCATION));
String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.MMS_TRANSACTION_ID));
long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MMS_MESSAGE_SIZE));
long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MMS_EXPIRY));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.MMS_STATUS));
int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.DELIVERY_RECEIPT_COUNT));
int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.READ_RECEIPT_COUNT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.SUBSCRIPTION_ID));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.SMS_SUBSCRIPTION_ID));
int viewedReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.VIEWED_RECEIPT_COUNT));
long receiptTimestamp = CursorUtil.requireLong(cursor, MmsSmsColumns.RECEIPT_TIMESTAMP);
StoryType storyType = StoryType.fromCode(CursorUtil.requireInt(cursor, STORY_TYPE));
@ -3001,20 +3010,19 @@ public class MmsTable extends MessageTable {
private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.ID));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.NORMALIZED_DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.NORMALIZED_DATE_RECEIVED));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.DATE_RECEIVED));
long dateServer = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.DATE_SERVER));
long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.MESSAGE_BOX));
long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.TYPE));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.THREAD_ID));
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.RECIPIENT_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.ADDRESS_DEVICE_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.RECIPIENT_DEVICE_ID));
int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.DELIVERY_RECEIPT_COUNT));
int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.READ_RECEIPT_COUNT));
String body = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.BODY));
int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.PART_COUNT));
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.MISMATCHED_IDENTITIES));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.NETWORK_FAILURE));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.SUBSCRIPTION_ID));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsTable.NETWORK_FAILURES));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.SMS_SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsTable.EXPIRE_STARTED));
boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsTable.UNIDENTIFIED)) == 1;
@ -3067,7 +3075,7 @@ public class MmsTable extends MessageTable {
return new MediaMmsMessageRecord(id, recipient, recipient,
addressDeviceId, dateSent, dateReceived, dateServer, deliveryReceiptCount,
threadId, body, slideDeck, partCount, box, mismatches,
threadId, body, slideDeck, box, mismatches,
networkFailures, subscriptionId, expiresIn, expireStarted,
isViewOnce, readReceiptCount, quote, contacts, previews, unidentified, Collections.emptyList(),
remoteDelete, mentionsSelf, notifiedTimestamp, viewedReceiptCount, receiptTimestamp, messageRanges,

Wyświetl plik

@ -61,9 +61,9 @@ public class SearchTable extends DatabaseTable {
private static final String MESSAGES_QUERY =
"SELECT " +
ThreadTable.TABLE_NAME + "." + ThreadTable.RECIPIENT_ID + " AS " + CONVERSATION_RECIPIENT + ", " +
MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
SmsTable.TABLE_NAME + "." + MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
"snippet(" + SMS_FTS_TABLE_NAME + ", -1, '', '', '" + SNIPPET_WRAP + "', 7) AS " + SNIPPET + ", " +
SmsTable.TABLE_NAME + "." + SmsTable.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
SmsTable.TABLE_NAME + "." + MmsSmsColumns.DATE_RECEIVED + ", " +
SMS_FTS_TABLE_NAME + "." + THREAD_ID + ", " +
SMS_FTS_TABLE_NAME + "." + BODY + ", " +
SMS_FTS_TABLE_NAME + "." + ID + " AS " + MESSAGE_ID + ", " +
@ -78,9 +78,9 @@ public class SearchTable extends DatabaseTable {
"UNION ALL " +
"SELECT " +
ThreadTable.TABLE_NAME + "." + ThreadTable.RECIPIENT_ID + " AS " + CONVERSATION_RECIPIENT + ", " +
MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
MmsTable.TABLE_NAME + "." + MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
"snippet(" + MMS_FTS_TABLE_NAME + ", -1, '', '', '" + SNIPPET_WRAP + "', 7) AS " + SNIPPET + ", " +
MmsTable.TABLE_NAME + "." + MmsTable.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
MmsTable.TABLE_NAME + "." + MmsSmsColumns.DATE_RECEIVED + ", " +
MMS_FTS_TABLE_NAME + "." + THREAD_ID + ", " +
MMS_FTS_TABLE_NAME + "." + BODY + ", " +
MMS_FTS_TABLE_NAME + "." + ID + " AS " + MESSAGE_ID + ", " +
@ -89,17 +89,17 @@ public class SearchTable extends DatabaseTable {
"INNER JOIN " + MMS_FTS_TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + ID + " = " + MmsTable.TABLE_NAME + "." + MmsTable.ID + " " +
"INNER JOIN " + ThreadTable.TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + THREAD_ID + " = " + ThreadTable.TABLE_NAME + "." + ThreadTable.ID + " " +
"WHERE " + MMS_FTS_TABLE_NAME + " MATCH ? " +
"AND " + MmsTable.TABLE_NAME + "." + MmsTable.MESSAGE_BOX + " & " + MmsSmsColumns.Types.GROUP_V2_BIT + " = 0 " +
"AND " + MmsTable.TABLE_NAME + "." + MmsTable.MESSAGE_BOX + " & " + MmsSmsColumns.Types.SPECIAL_TYPE_PAYMENTS_NOTIFICATION + " = 0 " +
"ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC " +
"AND " + MmsTable.TABLE_NAME + "." + MmsTable.TYPE + " & " + MmsSmsColumns.Types.GROUP_V2_BIT + " = 0 " +
"AND " + MmsTable.TABLE_NAME + "." + MmsTable.TYPE + " & " + MmsSmsColumns.Types.SPECIAL_TYPE_PAYMENTS_NOTIFICATION + " = 0 " +
"ORDER BY " + MmsSmsColumns.DATE_RECEIVED + " DESC " +
"LIMIT 500";
private static final String MESSAGES_FOR_THREAD_QUERY =
"SELECT " +
ThreadTable.TABLE_NAME + "." + ThreadTable.RECIPIENT_ID + " AS " + CONVERSATION_RECIPIENT + ", " +
MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
SmsTable.TABLE_NAME + "." + MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
"snippet(" + SMS_FTS_TABLE_NAME + ", -1, '', '', '" + SNIPPET_WRAP + "', 7) AS " + SNIPPET + ", " +
SmsTable.TABLE_NAME + "." + SmsTable.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
SmsTable.TABLE_NAME + "." + MmsSmsColumns.DATE_RECEIVED + ", " +
SMS_FTS_TABLE_NAME + "." + THREAD_ID + ", " +
SMS_FTS_TABLE_NAME + "." + BODY + ", " +
SMS_FTS_TABLE_NAME + "." + ID + " AS " + MESSAGE_ID + ", " +
@ -111,9 +111,9 @@ public class SearchTable extends DatabaseTable {
"UNION ALL " +
"SELECT " +
ThreadTable.TABLE_NAME + "." + ThreadTable.RECIPIENT_ID + " AS " + CONVERSATION_RECIPIENT + ", " +
MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
MmsTable.TABLE_NAME + "." + MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
"snippet(" + MMS_FTS_TABLE_NAME + ", -1, '', '', '" + SNIPPET_WRAP + "', 7) AS " + SNIPPET + ", " +
MmsTable.TABLE_NAME + "." + MmsTable.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
MmsTable.TABLE_NAME + "." + MmsSmsColumns.DATE_RECEIVED + ", " +
MMS_FTS_TABLE_NAME + "." + THREAD_ID + ", " +
MMS_FTS_TABLE_NAME + "." + BODY + ", " +
MMS_FTS_TABLE_NAME + "." + ID + " AS " + MESSAGE_ID + ", " +
@ -122,7 +122,7 @@ public class SearchTable extends DatabaseTable {
"INNER JOIN " + MMS_FTS_TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + ID + " = " + MmsTable.TABLE_NAME + "." + MmsTable.ID + " " +
"INNER JOIN " + ThreadTable.TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + THREAD_ID + " = " + ThreadTable.TABLE_NAME + "." + ThreadTable.ID + " " +
"WHERE " + MMS_FTS_TABLE_NAME + " MATCH ? AND " + MmsTable.TABLE_NAME + "." + MmsSmsColumns.THREAD_ID + " = ? " +
"ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC " +
"ORDER BY " + MmsSmsColumns.DATE_RECEIVED + " DESC " +
"LIMIT 500";
public SearchTable(@NonNull Context context, @NonNull SignalDatabase databaseHelper) {

Wyświetl plik

@ -156,15 +156,25 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
}
override fun onUpgrade(db: net.zetetic.database.sqlcipher.SQLiteDatabase, oldVersion: Int, newVersion: Int) {
// The caller of onUpgrade starts a transaction, which prevents us from turning off foreign keys.
// At this point it hasn't done anything, so we can just end it and then start it again ourselves.
db.endTransaction()
Log.i(TAG, "Upgrading database: $oldVersion, $newVersion")
val startTime = System.currentTimeMillis()
db.setForeignKeyConstraintsEnabled(false)
db.beginTransaction()
try {
migrate(context, db, oldVersion, newVersion)
db.setTransactionSuccessful()
} finally {
db.endTransaction()
db.setForeignKeyConstraintsEnabled(true)
// We have to re-begin the transaction for the calling code (see comment at start of method)
db.beginTransaction()
}
migratePostTransaction(context, oldVersion)
Log.i(TAG, "Upgrade complete. Took " + (System.currentTimeMillis() - startTime) + " ms.")
}

Wyświetl plik

@ -1,288 +0,0 @@
/*
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import net.zetetic.database.sqlcipher.SQLiteStatement;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
public class SmsMigrator {
private static final String TAG = Log.tag(SmsMigrator.class);
private static class SystemColumns {
private static final String ADDRESS = "address";
private static final String PERSON = "person";
private static final String DATE_RECEIVED = "date";
private static final String PROTOCOL = "protocol";
private static final String READ = "read";
private static final String STATUS = "status";
private static final String TYPE = "type";
private static final String SUBJECT = "subject";
private static final String REPLY_PATH_PRESENT = "reply_path_present";
private static final String BODY = "body";
private static final String SERVICE_CENTER = "service_center";
}
private static void addStringToStatement(SQLiteStatement statement, Cursor cursor,
int index, String key)
{
int columnIndex = cursor.getColumnIndexOrThrow(key);
if (cursor.isNull(columnIndex)) {
statement.bindNull(index);
} else {
statement.bindString(index, cursor.getString(columnIndex));
}
}
private static void addIntToStatement(SQLiteStatement statement, Cursor cursor,
int index, String key)
{
int columnIndex = cursor.getColumnIndexOrThrow(key);
if (cursor.isNull(columnIndex)) {
statement.bindNull(index);
} else {
statement.bindLong(index, cursor.getLong(columnIndex));
}
}
@SuppressWarnings("SameParameterValue")
private static void addTranslatedTypeToStatement(SQLiteStatement statement, Cursor cursor, int index, String key)
{
int columnIndex = cursor.getColumnIndexOrThrow(key);
if (cursor.isNull(columnIndex)) {
statement.bindLong(index, SmsTable.Types.BASE_INBOX_TYPE);
} else {
long theirType = cursor.getLong(columnIndex);
statement.bindLong(index, SmsTable.Types.translateFromSystemBaseType(theirType));
}
}
private static boolean isAppropriateTypeForMigration(Cursor cursor, int columnIndex) {
long systemType = cursor.getLong(columnIndex);
long ourType = SmsTable.Types.translateFromSystemBaseType(systemType);
return ourType == MmsSmsColumns.Types.BASE_INBOX_TYPE ||
ourType == MmsSmsColumns.Types.BASE_SENT_TYPE ||
ourType == MmsSmsColumns.Types.BASE_SENT_FAILED_TYPE;
}
private static void getContentValuesForRow(Context context, Cursor cursor, long threadId, SQLiteStatement statement) {
String address = cursor.getString(cursor.getColumnIndexOrThrow(SystemColumns.ADDRESS));
RecipientId id = Recipient.external(context, address).getId();
statement.bindString(1, id.serialize());
addIntToStatement(statement, cursor, 2, SystemColumns.PERSON);
addIntToStatement(statement, cursor, 3, SystemColumns.DATE_RECEIVED);
addIntToStatement(statement, cursor, 4, SystemColumns.DATE_RECEIVED);
addIntToStatement(statement, cursor, 5, SystemColumns.PROTOCOL);
addIntToStatement(statement, cursor, 6, SystemColumns.READ);
addIntToStatement(statement, cursor, 7, SystemColumns.STATUS);
addTranslatedTypeToStatement(statement, cursor, 8, SystemColumns.TYPE);
addIntToStatement(statement, cursor, 9, SystemColumns.REPLY_PATH_PRESENT);
addStringToStatement(statement, cursor, 10, SystemColumns.SUBJECT);
addStringToStatement(statement, cursor, 11, SystemColumns.BODY);
addStringToStatement(statement, cursor, 12, SystemColumns.SERVICE_CENTER);
statement.bindLong(13, threadId);
}
private static String getTheirCanonicalAddress(Context context, String theirRecipientId) {
Uri uri = Uri.parse("content://mms-sms/canonical-address/" + theirRecipientId);
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(0);
} else {
return null;
}
} catch (IllegalStateException iae) {
Log.w(TAG, iae);
return null;
} finally {
if (cursor != null)
cursor.close();
}
}
private static @Nullable Set<Recipient> getOurRecipients(Context context, String theirRecipients) {
StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " ");
Set<Recipient> recipientList = new HashSet<>();
while (tokenizer.hasMoreTokens()) {
String theirRecipientId = tokenizer.nextToken();
String address = getTheirCanonicalAddress(context, theirRecipientId);
if (address != null) {
recipientList.add(Recipient.external(context, address));
}
}
if (recipientList.isEmpty()) return null;
else return recipientList;
}
private static void migrateConversation(Context context, SmsMigrationProgressListener listener,
ProgressDescription progress,
long theirThreadId, long ourThreadId)
{
MessageTable ourSmsDatabase = SignalDatabase.sms();
Cursor cursor = null;
SQLiteStatement statement = null;
try {
Uri uri = Uri.parse("content://sms/conversations/" + theirThreadId);
try {
cursor = context.getContentResolver().query(uri, null, null, null, null);
} catch (SQLiteException e) {
/// Work around for weird sony-specific (?) bug: #4309
Log.w(TAG, e);
return;
}
SQLiteDatabase transaction = ourSmsDatabase.beginTransaction();
statement = ourSmsDatabase.createInsertStatement(transaction);
while (cursor != null && cursor.moveToNext()) {
int addressColumn = cursor.getColumnIndexOrThrow(SystemColumns.ADDRESS);
int typeColumn = cursor.getColumnIndex(SmsTable.TYPE);
if (!cursor.isNull(addressColumn) && (cursor.isNull(typeColumn) || isAppropriateTypeForMigration(cursor, typeColumn))) {
getContentValuesForRow(context, cursor, ourThreadId, statement);
statement.execute();
}
listener.progressUpdate(new ProgressDescription(progress, cursor.getCount(), cursor.getPosition()));
}
ourSmsDatabase.endTransaction(transaction);
SignalDatabase.threads().update(ourThreadId, true);
SignalDatabase.threads().setLastScrolled(ourThreadId, 0);
SignalDatabase.threads().notifyConversationListeners(ourThreadId);
} finally {
if (statement != null)
statement.close();
if (cursor != null)
cursor.close();
}
}
public static void migrateDatabase(Context context, SmsMigrationProgressListener listener)
{
// if (context.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE).getBoolean("migrated", false))
// return;
ThreadTable threadTable = SignalDatabase.threads();
Cursor cursor = null;
try {
Uri threadListUri = Uri.parse("content://mms-sms/conversations?simple=true");
cursor = context.getContentResolver().query(threadListUri, null, null, null, "date ASC");
while (cursor != null && cursor.moveToNext()) {
long theirThreadId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
String theirRecipients = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
Set<Recipient> ourRecipients = getOurRecipients(context, theirRecipients);
ProgressDescription progress = new ProgressDescription(cursor.getCount(), cursor.getPosition(), 100, 0);
if (ourRecipients != null) {
if (ourRecipients.size() == 1) {
long ourThreadId = threadTable.getOrCreateThreadIdFor(ourRecipients.iterator().next());
migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
} else if (ourRecipients.size() > 1) {
ourRecipients.add(Recipient.self());
List<RecipientId> recipientIds = Stream.of(ourRecipients).map(Recipient::getId).toList();
GroupId.Mms ourGroupId = SignalDatabase.groups().getOrCreateMmsGroupForMembers(recipientIds);
RecipientId ourGroupRecipientId = SignalDatabase.recipients().getOrInsertFromGroupId(ourGroupId);
Recipient ourGroupRecipient = Recipient.resolved(ourGroupRecipientId);
long ourThreadId = threadTable.getOrCreateThreadIdFor(ourGroupRecipient, ThreadTable.DistributionTypes.CONVERSATION);
migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
}
}
progress.incrementPrimaryComplete();
listener.progressUpdate(progress);
}
} finally {
if (cursor != null)
cursor.close();
}
context.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE).edit()
.putBoolean("migrated", true).apply();
}
public interface SmsMigrationProgressListener {
void progressUpdate(ProgressDescription description);
}
public static class ProgressDescription {
public final int primaryTotal;
public int primaryComplete;
public final int secondaryTotal;
public final int secondaryComplete;
ProgressDescription(int primaryTotal, int primaryComplete,
int secondaryTotal, int secondaryComplete)
{
this.primaryTotal = primaryTotal;
this.primaryComplete = primaryComplete;
this.secondaryTotal = secondaryTotal;
this.secondaryComplete = secondaryComplete;
}
ProgressDescription(ProgressDescription that, int secondaryTotal, int secondaryComplete) {
this.primaryComplete = that.primaryComplete;
this.primaryTotal = that.primaryTotal;
this.secondaryComplete = secondaryComplete;
this.secondaryTotal = secondaryTotal;
}
void incrementPrimaryComplete() {
primaryComplete += 1;
}
}
}

Wyświetl plik

@ -31,8 +31,6 @@ import com.google.android.mms.pdu_alt.NotificationInd;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import net.zetetic.database.sqlcipher.SQLiteStatement;
import org.signal.core.util.CursorExtensionsKt;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.SQLiteDatabaseExtensionsKt;
@ -100,36 +98,23 @@ public class SmsTable extends MessageTable {
private static final String TAG = Log.tag(SmsTable.class);
public static final String TABLE_NAME = "sms";
public static final String PERSON = "person";
static final String DATE_RECEIVED = "date";
static final String DATE_SENT = "date_sent";
public static final String PROTOCOL = "protocol";
public static final String STATUS = "status";
public static final String TYPE = "type";
public static final String REPLY_PATH_PRESENT = "reply_path_present";
public static final String SUBJECT = "subject";
public static final String SERVICE_CENTER = "service_center";
public static final String TABLE_NAME = "sms";
public static final String SMS_STATUS = "status";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
THREAD_ID + " INTEGER, " +
RECIPIENT_ID + " INTEGER, " +
ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " +
PERSON + " INTEGER, " +
DATE_RECEIVED + " INTEGER, " +
DATE_SENT + " INTEGER, " +
DATE_SENT + " INTEGER NOT NULL, " +
DATE_RECEIVED + " INTEGER NOT NULL, " +
DATE_SERVER + " INTEGER DEFAULT -1, " +
PROTOCOL + " INTEGER, " +
READ + " INTEGER DEFAULT 0, " +
STATUS + " INTEGER DEFAULT -1," +
THREAD_ID + " INTEGER NOT NULL REFERENCES " + ThreadTable.TABLE_NAME + " (" + ThreadTable.ID + ") ON DELETE CASCADE, " +
RECIPIENT_ID + " INTEGER NOT NULL REFERENCES " + RecipientTable.TABLE_NAME + " (" + RecipientTable.ID + ") ON DELETE CASCADE, " +
RECIPIENT_DEVICE_ID + " INTEGER DEFAULT 1, " +
TYPE + " INTEGER, " +
REPLY_PATH_PRESENT + " INTEGER, " +
DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," +
SUBJECT + " TEXT, " +
BODY + " TEXT, " +
READ + " INTEGER DEFAULT 0, " +
SMS_STATUS + " INTEGER DEFAULT -1," +
DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," +
MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " +
SERVICE_CENTER + " TEXT, " +
SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
SMS_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
EXPIRES_IN + " INTEGER DEFAULT 0, " +
EXPIRE_STARTED + " INTEGER DEFAULT 0, " +
NOTIFIED + " DEFAULT 0, " +
@ -155,15 +140,30 @@ public class SmsTable extends MessageTable {
};
private static final String[] MESSAGE_PROJECTION = new String[] {
ID, THREAD_ID, RECIPIENT_ID, ADDRESS_DEVICE_ID, PERSON,
DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED,
DATE_SENT + " AS " + NORMALIZED_DATE_SENT,
ID,
THREAD_ID,
RECIPIENT_ID,
RECIPIENT_DEVICE_ID,
DATE_RECEIVED,
DATE_SENT,
DATE_SERVER,
PROTOCOL, READ, STATUS, TYPE,
REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, DELIVERY_RECEIPT_COUNT,
MISMATCHED_IDENTITIES, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED,
NOTIFIED, READ_RECEIPT_COUNT, UNIDENTIFIED, REACTIONS_UNREAD, REACTIONS_LAST_SEEN,
REMOTE_DELETED, NOTIFIED_TIMESTAMP, RECEIPT_TIMESTAMP
READ,
SMS_STATUS,
TYPE,
BODY,
DELIVERY_RECEIPT_COUNT,
MISMATCHED_IDENTITIES,
SMS_SUBSCRIPTION_ID,
EXPIRES_IN,
EXPIRE_STARTED,
NOTIFIED,
READ_RECEIPT_COUNT,
UNIDENTIFIED,
REACTIONS_UNREAD,
REACTIONS_LAST_SEEN,
REMOTE_DELETED,
NOTIFIED_TIMESTAMP,
RECEIPT_TIMESTAMP
};
@VisibleForTesting
@ -489,7 +489,7 @@ public class SmsTable extends MessageTable {
public void markSmsStatus(long id, int status) {
Log.i(TAG, "Updating ID: " + id + " to status: " + status);
ContentValues contentValues = new ContentValues();
contentValues.put(STATUS, status);
contentValues.put(SMS_STATUS, status);
SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {id+""});
@ -731,7 +731,7 @@ public class SmsTable extends MessageTable {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, sender.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, timestamp);
values.put(DATE_SENT, timestamp);
values.put(READ, markRead ? 1 : 0);
@ -808,7 +808,7 @@ public class SmsTable extends MessageTable {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, sender.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, timestamp);
values.put(DATE_SENT, timestamp);
values.put(READ, 0);
@ -880,7 +880,7 @@ public class SmsTable extends MessageTable {
ContentValues values = new ContentValues(6);
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, timestamp);
values.put(READ, unread ? 0 : 1);
@ -1008,7 +1008,7 @@ public class SmsTable extends MessageTable {
.forEach(threadId -> {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipient.getId().serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
values.put(READ, 1);
@ -1056,7 +1056,7 @@ public class SmsTable extends MessageTable {
{
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
values.put(READ, 1);
@ -1092,7 +1092,7 @@ public class SmsTable extends MessageTable {
.forEach(threadId -> {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
values.put(READ, 1);
@ -1121,7 +1121,7 @@ public class SmsTable extends MessageTable {
public void insertBoostRequestMessage(@NonNull RecipientId recipientId, long threadId) {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
values.put(READ, 1);
@ -1136,7 +1136,7 @@ public class SmsTable extends MessageTable {
public void insertThreadMergeEvent(@NonNull RecipientId recipientId, long threadId, @NonNull ThreadMergeEvent event) {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
values.put(READ, 1);
@ -1153,7 +1153,7 @@ public class SmsTable extends MessageTable {
public void insertSmsExportMessage(@NonNull RecipientId recipientId, long threadId) {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
values.put(READ, 1);
@ -1251,21 +1251,14 @@ public class SmsTable extends MessageTable {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, message.getSender().serialize());
values.put(ADDRESS_DEVICE_ID, message.getSenderDeviceId());
values.put(RECIPIENT_DEVICE_ID, message.getSenderDeviceId());
values.put(DATE_RECEIVED, message.getReceivedTimestampMillis());
values.put(DATE_SENT, message.getSentTimestampMillis());
values.put(DATE_SERVER, message.getServerTimestampMillis());
values.put(PROTOCOL, message.getProtocol());
values.put(READ, unread ? 0 : 1);
values.put(SUBSCRIPTION_ID, message.getSubscriptionId());
values.put(SMS_SUBSCRIPTION_ID, message.getSubscriptionId());
values.put(EXPIRES_IN, message.getExpiresIn());
values.put(UNIDENTIFIED, message.isUnidentified());
if (!TextUtils.isEmpty(message.getPseudoSubject()))
values.put(SUBJECT, message.getPseudoSubject());
values.put(REPLY_PATH_PRESENT, message.isReplyPathPresent());
values.put(SERVICE_CENTER, message.getServiceCenterAddress());
values.put(BODY, message.getMessageBody());
values.put(TYPE, type);
values.put(THREAD_ID, threadId);
@ -1316,7 +1309,7 @@ public class SmsTable extends MessageTable {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, senderDeviceId);
values.put(RECIPIENT_DEVICE_ID, senderDeviceId);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, sentTimestamp);
values.put(DATE_SERVER, -1);
@ -1341,7 +1334,7 @@ public class SmsTable extends MessageTable {
public void insertBadDecryptMessage(@NonNull RecipientId recipientId, int senderDevice, long sentTimestamp, long receivedTimestamp, long threadId) {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, senderDevice);
values.put(RECIPIENT_DEVICE_ID, senderDevice);
values.put(DATE_SENT, sentTimestamp);
values.put(DATE_RECEIVED, receivedTimestamp);
values.put(DATE_SERVER, -1);
@ -1387,7 +1380,7 @@ public class SmsTable extends MessageTable {
contentValues.put(DATE_SENT, date);
contentValues.put(READ, 1);
contentValues.put(TYPE, type);
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(SMS_SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn());
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getCount).sum());
contentValues.put(RECEIPT_TIMESTAMP, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getTimestamp).max().orElse(-1));
@ -1732,24 +1725,6 @@ public class SmsTable extends MessageTable {
databaseHelper.getSignalWritableDatabase().endTransaction();
}
@Override
public SQLiteStatement createInsertStatement(SQLiteDatabase database) {
return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + RECIPIENT_ID + ", " +
PERSON + ", " +
DATE_SENT + ", " +
DATE_RECEIVED + ", " +
PROTOCOL + ", " +
READ + ", " +
STATUS + ", " +
TYPE + ", " +
REPLY_PATH_PRESENT + ", " +
SUBJECT + ", " +
BODY + ", " +
SERVICE_CENTER +
", " + THREAD_ID + ") " +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
}
@Override
public @Nullable ViewOnceExpirationInfo getNearestExpiringViewOnceMessage() {
throw new UnsupportedOperationException();
@ -1948,17 +1923,17 @@ public class SmsTable extends MessageTable {
public SmsMessageRecord getCurrent() {
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.ID));
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.RECIPIENT_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsTable.ADDRESS_DEVICE_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsTable.RECIPIENT_DEVICE_ID));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.TYPE));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.NORMALIZED_DATE_RECEIVED));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.NORMALIZED_DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.DATE_RECEIVED));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.DATE_SENT));
long dateServer = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.DATE_SERVER));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.THREAD_ID));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsTable.STATUS));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsTable.SMS_STATUS));
int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsTable.DELIVERY_RECEIPT_COUNT));
int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsTable.READ_RECEIPT_COUNT));
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsTable.MISMATCHED_IDENTITIES));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsTable.SUBSCRIPTION_ID));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsTable.SMS_SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsTable.EXPIRE_STARTED));
String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsTable.BODY));

Wyświetl plik

@ -326,7 +326,7 @@ class StorySendTable(context: Context, databaseHelper: SignalDatabase) : Databas
// language=sql
"""
SELECT
$RECIPIENT_ID,
$TABLE_NAME.$RECIPIENT_ID,
$ALLOWS_REPLIES,
$DISTRIBUTION_ID,
${MmsTable.REMOTE_DELETED}

Wyświetl plik

@ -76,14 +76,13 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
const val TABLE_NAME = "thread"
const val ID = "_id"
const val DATE = "date"
const val MEANINGFUL_MESSAGES = "message_count"
const val RECIPIENT_ID = "thread_recipient_id"
const val SNIPPET = "snippet"
const val SNIPPET_CHARSET = "snippet_charset"
const val MEANINGFUL_MESSAGES = "meaningful_messages"
const val RECIPIENT_ID = "recipient_id"
const val READ = "read"
const val UNREAD_COUNT = "unread_count"
const val TYPE = "type"
const val ERROR = "error"
const val SNIPPET = "snippet"
const val SNIPPET_TYPE = "snippet_type"
const val SNIPPET_URI = "snippet_uri"
const val SNIPPET_CONTENT_TYPE = "snippet_content_type"
@ -106,11 +105,10 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
$DATE INTEGER DEFAULT 0,
$MEANINGFUL_MESSAGES INTEGER DEFAULT 0,
$RECIPIENT_ID INTEGER,
$SNIPPET TEXT,
$SNIPPET_CHARSET INTEGER DEFAULT 0,
$READ INTEGER DEFAULT ${ReadStatus.READ.serialize()},
$TYPE INTEGER DEFAULT 0,
$ERROR INTEGER DEFAULT 0,
$SNIPPET TEXT,
$SNIPPET_TYPE INTEGER DEFAULT 0,
$SNIPPET_URI TEXT DEFAULT NULL,
$SNIPPET_CONTENT_TYPE TEXT DEFAULT NULL,
@ -143,7 +141,6 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
MEANINGFUL_MESSAGES,
RECIPIENT_ID,
SNIPPET,
SNIPPET_CHARSET,
READ,
UNREAD_COUNT,
TYPE,
@ -343,7 +340,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
mmsSms.getConversation(threadId).use { cursor ->
if (cursor.count > length) {
cursor.moveToPosition(length - 1)
max(trimBeforeDate, cursor.requireLong(MmsSmsColumns.NORMALIZED_DATE_RECEIVED))
max(trimBeforeDate, cursor.requireLong(MmsSmsColumns.DATE_RECEIVED))
} else {
trimBeforeDate
}
@ -702,14 +699,14 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
}
if (hideSelf) {
where += " AND $RECIPIENT_ID != ${Recipient.self().id.toLong()}"
where += " AND $TABLE_NAME.$RECIPIENT_ID != ${Recipient.self().id.toLong()}"
}
where += " AND $ARCHIVED = 0"
where += " AND ${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED} = 0"
if (SignalStore.releaseChannelValues().releaseChannelRecipientId != null) {
where += " AND $RECIPIENT_ID != ${SignalStore.releaseChannelValues().releaseChannelRecipientId!!.toLong()}"
where += " AND $TABLE_NAME.$RECIPIENT_ID != ${SignalStore.releaseChannelValues().releaseChannelRecipientId!!.toLong()}"
}
val query = createQuery(

Wyświetl plik

@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V162_ThreadUnreadSe
import org.thoughtcrime.securesms.database.helpers.migration.V163_RemoteMegaphoneSnoozeSupportMigration
import org.thoughtcrime.securesms.database.helpers.migration.V164_ThreadDatabaseReadIndexMigration
import org.thoughtcrime.securesms.database.helpers.migration.V165_MmsMessageBoxPaymentTransactionIndexMigration
import org.thoughtcrime.securesms.database.helpers.migration.V166_ThreadAndMessageForeignKeys
/**
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
@ -29,7 +30,7 @@ object SignalDatabaseMigrations {
val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass)
const val DATABASE_VERSION = 165
const val DATABASE_VERSION = 166
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@ -100,6 +101,10 @@ object SignalDatabaseMigrations {
if (oldVersion < 165) {
V165_MmsMessageBoxPaymentTransactionIndexMigration.migrate(context, db, oldVersion, newVersion)
}
if (oldVersion < 166) {
V166_ThreadAndMessageForeignKeys.migrate(context, db, oldVersion, newVersion)
}
}
@JvmStatic

Wyświetl plik

@ -0,0 +1,424 @@
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import net.zetetic.database.sqlcipher.SQLiteDatabase
import org.signal.core.util.Stopwatch
import org.signal.core.util.delete
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
import org.signal.core.util.requireLong
import org.signal.core.util.update
/**
* This one's a doozy. We want to add additional foreign key constraints between the thread, recipient, and message tables. This will let us know for sure
* that there aren't threads with invalid recipients, or messages with invalid threads, or multiple threads for the same recipient.
*/
object V166_ThreadAndMessageForeignKeys : SignalDatabaseMigration {
private val TAG = Log.tag(V166_ThreadAndMessageForeignKeys::class.java)
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
val stopwatch = Stopwatch("migration")
removeDuplicateThreadEntries(db)
stopwatch.split("thread-dupes")
updateThreadTableSchema(db)
stopwatch.split("thread-schema")
fixDanglingSmsMessages(db)
stopwatch.split("sms-dangling")
fixDanglingMmsMessages(db)
stopwatch.split("mms-dangling")
updateSmsTableSchema(db)
stopwatch.split("sms-schema")
updateMmsTableSchema(db)
stopwatch.split("mms-schema")
stopwatch.stop(TAG)
}
private fun removeDuplicateThreadEntries(db: SQLiteDatabase) {
db.rawQuery(
"""
SELECT
thread_recipient_id,
COUNT(*) AS thread_count
FROM thread
GROUP BY thread_recipient_id HAVING thread_count > 1
""".trimMargin()
).use { cursor ->
while (cursor.moveToNext()) {
val recipientId = cursor.requireLong("thread_recipient_id")
val count = cursor.requireLong("thread_count")
Log.w(TAG, "There were $count threads for RecipientId::$recipientId. Merging.")
val threads: List<ThreadInfo> = getThreadsByRecipientId(db, cursor.requireLong("thread_recipient_id"))
mergeThreads(db, threads)
}
}
}
private fun getThreadsByRecipientId(db: SQLiteDatabase, recipientId: Long): List<ThreadInfo> {
return db.rawQuery("SELECT _id, date FROM thread WHERE thread_recipient_id = ?".trimIndent(), recipientId).readToList { cursor ->
ThreadInfo(cursor.requireLong("_id"), cursor.requireLong("date"))
}
}
private fun mergeThreads(db: SQLiteDatabase, threads: List<ThreadInfo>) {
val primaryThread: ThreadInfo = threads.maxByOrNull { it.date }!!
val secondaryThreads: List<ThreadInfo> = threads.filterNot { it.id == primaryThread.id }
secondaryThreads.forEach { secondaryThread ->
remapThread(db, primaryThread.id, secondaryThread.id)
}
}
private fun remapThread(db: SQLiteDatabase, primaryId: Long, secondaryId: Long) {
db.update("drafts")
.values("thread_id" to primaryId)
.where("thread_id = ?", secondaryId)
.run()
db.update("mention")
.values("thread_id" to primaryId)
.where("thread_id = ?", secondaryId)
.run()
db.update("mms")
.values("thread_id" to primaryId)
.where("thread_id = ?", secondaryId)
.run()
db.update("sms")
.values("thread_id" to primaryId)
.where("thread_id = ?", secondaryId)
.run()
db.update("pending_retry_receipts")
.values("thread_id" to primaryId)
.where("thread_id = ?", secondaryId)
.run()
db.delete("thread")
.where("_id = ?", secondaryId)
.run()
}
private fun updateThreadTableSchema(db: SQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE thread_tmp (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
date INTEGER DEFAULT 0,
meaningful_messages INTEGER DEFAULT 0,
recipient_id INTEGER NOT NULL UNIQUE REFERENCES recipient (_id) ON DELETE CASCADE,
read INTEGER DEFAULT 1,
type INTEGER DEFAULT 0,
error INTEGER DEFAULT 0,
snippet TEXT,
snippet_type INTEGER DEFAULT 0,
snippet_uri TEXT DEFAULT NULL,
snippet_content_type TEXT DEFAULT NULL,
snippet_extras TEXT DEFAULT NULL,
unread_count INTEGER DEFAULT 0,
archived INTEGER DEFAULT 0,
status INTEGER DEFAULT 0,
delivery_receipt_count INTEGER DEFAULT 0,
read_receipt_count INTEGER DEFAULT 0,
expires_in INTEGER DEFAULT 0,
last_seen INTEGER DEFAULT 0,
has_sent INTEGER DEFAULT 0,
last_scrolled INTEGER DEFAULT 0,
pinned INTEGER DEFAULT 0,
unread_self_mention_count INTEGER DEFAULT 0
)
""".trimIndent()
)
db.execSQL(
"""
INSERT INTO thread_tmp
SELECT
_id,
date,
message_count,
thread_recipient_id,
read,
type,
error,
snippet,
snippet_type,
snippet_uri,
snippet_content_type,
snippet_extras,
unread_count,
archived,
status,
delivery_receipt_count,
read_receipt_count,
expires_in,
last_seen,
has_sent,
last_scrolled,
pinned,
unread_self_mention_count
FROM thread
""".trimMargin()
)
db.execSQL("DROP TABLE thread")
db.execSQL("ALTER TABLE thread_tmp RENAME TO thread")
db.execSQL("CREATE INDEX thread_recipient_id_index ON thread (recipient_id)")
db.execSQL("CREATE INDEX archived_count_index ON thread (archived, meaningful_messages)")
db.execSQL("CREATE INDEX thread_pinned_index ON thread (pinned)")
db.execSQL("CREATE INDEX thread_read ON thread (read)")
}
private fun fixDanglingSmsMessages(db: SQLiteDatabase) {
db.delete("sms")
.where("address NOT IN (SELECT _id FROM recipient)")
.run()
// Can't even attempt to "fix" these because without the threadId we don't know if it's a 1:1 or group message
db.delete("sms")
.where("thread_id NOT IN (SELECT _id FROM thread)")
.run()
}
private fun fixDanglingMmsMessages(db: SQLiteDatabase) {
db.delete("mms")
.where("address NOT IN (SELECT _id FROM recipient)")
.run()
// Can't even attempt to "fix" these because without the threadId we don't know if it's a 1:1 or group message
db.delete("mms")
.where("thread_id NOT IN (SELECT _id FROM thread)")
.run()
}
private fun updateSmsTableSchema(db: SQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE sms_tmp (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
date_sent INTEGER NOT NULL,
date_received INTEGER NOT NULL,
date_server INTEGER DEFAULT -1,
thread_id INTEGER NOT NULL REFERENCES thread (_id) ON DELETE CASCADE,
recipient_id NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
recipient_device_id INTEGER DEFAULT 1,
type INTEGER,
body TEXT,
read INTEGER DEFAULT 0,
status INTEGER DEFAULT -1,
delivery_receipt_count INTEGER DEFAULT 0,
mismatched_identities TEXT DEFAULT NULL,
subscription_id INTEGER DEFAULT -1,
expires_in INTEGER DEFAULT 0,
expire_started INTEGER DEFAULT 0,
notified INTEGER DEFAULT 0,
read_receipt_count INTEGER DEFAULT 0,
unidentified INTEGER DEFAULT 0,
reactions_unread INTEGER DEFAULT 0,
reactions_last_seen INTEGER DEFAULT -1,
remote_deleted INTEGER DEFAULT 0,
notified_timestamp INTEGER DEFAULT 0,
server_guid TEXT DEFAULT NULL,
receipt_timestamp INTEGER DEFAULT -1,
export_state BLOB DEFAULT NULL,
exported INTEGER DEFAULT 0
)
""".trimIndent()
)
db.execSQL(
"""
INSERT INTO sms_tmp
SELECT
_id,
date_sent,
date,
date_server,
thread_id,
address,
address_device_id,
type,
body,
read,
status,
delivery_receipt_count,
mismatched_identities,
subscription_id,
expires_in,
expire_started,
notified,
read_receipt_count,
unidentified,
reactions_unread,
reactions_last_seen,
remote_deleted,
notified_timestamp,
server_guid,
receipt_timestamp,
export_state,
exported
FROM sms
""".trimIndent()
)
db.execSQL("DROP TABLE sms")
db.execSQL("ALTER TABLE sms_tmp RENAME TO sms")
db.execSQL("CREATE INDEX sms_read_and_notified_and_thread_id_index ON sms(read, notified, thread_id)")
db.execSQL("CREATE INDEX sms_type_index ON sms (type)")
db.execSQL("CREATE INDEX sms_date_sent_index ON sms (date_sent, recipient_id, thread_id)")
db.execSQL("CREATE INDEX sms_date_server_index ON sms (date_server)")
db.execSQL("CREATE INDEX sms_thread_date_index ON sms (thread_id, date_received)")
db.execSQL("CREATE INDEX sms_reactions_unread_index ON sms (reactions_unread)")
db.execSQL("CREATE INDEX sms_exported_index ON sms (exported)")
db.execSQL("CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN INSERT INTO sms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id); END;")
db.execSQL("CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id); END;")
db.execSQL("CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id); INSERT INTO sms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id); END;")
db.execSQL("CREATE TRIGGER msl_sms_delete AFTER DELETE ON sms BEGIN DELETE FROM msl_payload WHERE _id IN (SELECT payload_id FROM msl_message WHERE message_id = old._id AND is_mms = 0); END")
}
private fun updateMmsTableSchema(db: SQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE mms_tmp (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
date_sent INTEGER NOT NULL,
date_received INTEGER NOT NULL,
date_server INTEGER DEFAULT -1,
thread_id INTEGER NOT NULL REFERENCES thread (_id) ON DELETE CASCADE,
recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
recipient_device_id INTEGER,
type INTEGER NOT NULL,
body TEXT,
read INTEGER DEFAULT 0,
ct_l TEXT,
exp INTEGER,
m_type INTEGER,
m_size INTEGER,
st INTEGER,
tr_id TEXT,
subscription_id INTEGER DEFAULT -1,
receipt_timestamp INTEGER DEFAULT -1,
delivery_receipt_count INTEGER DEFAULT 0,
read_receipt_count INTEGER DEFAULT 0,
viewed_receipt_count INTEGER DEFAULT 0,
mismatched_identities TEXT DEFAULT NULL,
network_failures TEXT DEFAULT NULL,
expires_in INTEGER DEFAULT 0,
expire_started INTEGER DEFAULT 0,
notified INTEGER DEFAULT 0,
quote_id INTEGER DEFAULT 0,
quote_author INTEGER DEFAULT 0,
quote_body TEXT DEFAULT NULL,
quote_missing INTEGER DEFAULT 0,
quote_mentions BLOB DEFAULT NULL,
quote_type INTEGER DEFAULT 0,
shared_contacts TEXT DEFAULT NULL,
unidentified INTEGER DEFAULT 0,
link_previews TEXT DEFAULT NULL,
view_once INTEGER DEFAULT 0,
reactions_unread INTEGER DEFAULT 0,
reactions_last_seen INTEGER DEFAULT -1,
remote_deleted INTEGER DEFAULT 0,
mentions_self INTEGER DEFAULT 0,
notified_timestamp INTEGER DEFAULT 0,
server_guid TEXT DEFAULT NULL,
message_ranges BLOB DEFAULT NULL,
story_type INTEGER DEFAULT 0,
parent_story_id INTEGER DEFAULT 0,
export_state BLOB DEFAULT NULL,
exported INTEGER DEFAULT 0
)
""".trimIndent()
)
db.execSQL(
"""
INSERT INTO mms_tmp
SELECT
_id,
date,
date_received,
date_server,
thread_id,
address,
address_device_id,
msg_box,
body,
read,
ct_l,
exp,
m_type,
m_size,
st,
tr_id,
subscription_id,
receipt_timestamp,
delivery_receipt_count,
read_receipt_count,
viewed_receipt_count,
mismatched_identities,
network_failures,
expires_in,
expire_started,
notified,
quote_id,
quote_author,
quote_body,
quote_missing,
quote_mentions,
quote_type,
shared_contacts,
unidentified,
previews,
reveal_duration,
reactions_unread,
reactions_last_seen,
remote_deleted,
mentions_self,
notified_timestamp,
server_guid,
ranges,
is_story,
parent_story_id,
export_state,
exported
FROM mms
""".trimIndent()
)
db.execSQL("DROP TABLE mms")
db.execSQL("ALTER TABLE mms_tmp RENAME TO mms")
db.execSQL("CREATE INDEX mms_read_and_notified_and_thread_id_index ON mms(read, notified, thread_id)")
db.execSQL("CREATE INDEX mms_type_index ON mms (type)")
db.execSQL("CREATE INDEX mms_date_sent_index ON mms (date_sent, recipient_id, thread_id)")
db.execSQL("CREATE INDEX mms_date_server_index ON mms (date_server)")
db.execSQL("CREATE INDEX mms_thread_date_index ON mms (thread_id, date_received)")
db.execSQL("CREATE INDEX mms_reactions_unread_index ON mms (reactions_unread)")
db.execSQL("CREATE INDEX IF NOT EXISTS mms_story_type_index ON mms (story_type)")
db.execSQL("CREATE INDEX IF NOT EXISTS mms_parent_story_id_index ON mms (parent_story_id)")
db.execSQL("CREATE INDEX IF NOT EXISTS mms_thread_story_parent_story_index ON mms (thread_id, date_received, story_type, parent_story_id)")
db.execSQL("CREATE INDEX IF NOT EXISTS mms_quote_id_quote_author_index ON mms (quote_id, quote_author)")
db.execSQL("CREATE INDEX IF NOT EXISTS mms_exported_index ON mms (exported)")
db.execSQL("CREATE INDEX IF NOT EXISTS mms_id_type_payment_transactions_index ON mms (_id, type) WHERE type & ${0x300000000L} != 0")
db.execSQL("CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN INSERT INTO mms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id); END")
db.execSQL("CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id); END")
db.execSQL("CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id); INSERT INTO mms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id); END")
db.execSQL("CREATE TRIGGER msl_mms_delete AFTER DELETE ON mms BEGIN DELETE FROM msl_payload WHERE _id IN (SELECT payload_id FROM msl_message WHERE message_id = old._id AND is_mms = 1); END")
}
data class ThreadInfo(val id: Long, val date: Long)
}

Wyświetl plik

@ -60,7 +60,6 @@ import java.util.stream.Collectors;
public class MediaMmsMessageRecord extends MmsMessageRecord {
private final static String TAG = Log.tag(MediaMmsMessageRecord.class);
private final int partCount;
private final boolean mentionsSelf;
private final BodyRangeList messageRanges;
private final Payment payment;
@ -76,7 +75,6 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
long threadId,
String body,
@NonNull SlideDeck slideDeck,
int partCount,
long mailbox,
Set<IdentityKeyMismatch> mismatches,
Set<NetworkFailure> failures,
@ -106,7 +104,6 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
subscriptionId, expiresIn, expireStarted, viewOnce, slideDeck,
readReceiptCount, quote, contacts, linkPreviews, unidentified, reactions, remoteDelete, notifiedTimestamp, viewedReceiptCount, receiptTimestamp,
storyType, parentStoryId, giftBadge);
this.partCount = partCount;
this.mentionsSelf = mentionsSelf;
this.messageRanges = messageRanges;
this.payment = payment;
@ -140,10 +137,6 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
return super.getDisplayBody(context);
}
public int getPartCount() {
return partCount;
}
public @Nullable BodyRangeList getMessageRanges() {
return messageRanges;
}
@ -164,14 +157,14 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
public @NonNull MediaMmsMessageRecord withReactions(@NonNull List<ReactionRecord> reactions) {
return new MediaMmsMessageRecord(getId(), getRecipient(), getIndividualRecipient(), getRecipientDeviceId(), getDateSent(), getDateReceived(), getServerTimestamp(), getDeliveryReceiptCount(), getThreadId(), getBody(), getSlideDeck(),
getPartCount(), getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
getReadReceiptCount(), getQuote(), getSharedContacts(), getLinkPreviews(), isUnidentified(), reactions, isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), getViewedReceiptCount(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment());
}
public @NonNull MediaMmsMessageRecord withoutQuote() {
return new MediaMmsMessageRecord(getId(), getRecipient(), getIndividualRecipient(), getRecipientDeviceId(), getDateSent(), getDateReceived(), getServerTimestamp(), getDeliveryReceiptCount(), getThreadId(), getBody(), getSlideDeck(),
getPartCount(), getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
getReadReceiptCount(), null, getSharedContacts(), getLinkPreviews(), isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), getViewedReceiptCount(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment());
}
@ -192,14 +185,14 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
SlideDeck slideDeck = MmsTable.Reader.buildSlideDeck(context, slideAttachments);
return new MediaMmsMessageRecord(getId(), getRecipient(), getIndividualRecipient(), getRecipientDeviceId(), getDateSent(), getDateReceived(), getServerTimestamp(), getDeliveryReceiptCount(), getThreadId(), getBody(), slideDeck,
getPartCount(), getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
getReadReceiptCount(), quote, contacts, linkPreviews, isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), getViewedReceiptCount(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment());
}
public @NonNull MediaMmsMessageRecord withPayment(@NonNull Payment payment) {
return new MediaMmsMessageRecord(getId(), getRecipient(), getIndividualRecipient(), getRecipientDeviceId(), getDateSent(), getDateReceived(), getServerTimestamp(), getDeliveryReceiptCount(), getThreadId(), getBody(), getSlideDeck(),
getPartCount(), getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
getReadReceiptCount(), getQuote(), getSharedContacts(), getLinkPreviews(), isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), getViewedReceiptCount(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), payment);
}

Wyświetl plik

@ -111,9 +111,10 @@ public class ApplicationMigrations {
static final int SYSTEM_NAME_SYNC = 67;
static final int STORY_VIEWED_STATE = 68;
static final int STORY_READ_STATE = 69;
static final int THREAD_MESSAGE_SCHEMA_CHANGE = 70;
}
public static final int CURRENT_VERSION = 69;
public static final int CURRENT_VERSION = 70;
/**
* This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
@ -491,6 +492,10 @@ public class ApplicationMigrations {
jobs.put(Version.STORY_READ_STATE, new StoryReadStateMigrationJob());
}
if (lastSeenVersion < Version.THREAD_MESSAGE_SCHEMA_CHANGE) {
jobs.put(Version.THREAD_MESSAGE_SCHEMA_CHANGE, new DatabaseMigrationJob());
}
return jobs;
}

Wyświetl plik

@ -451,7 +451,7 @@ public class SearchRepository {
Recipient messageRecipient = Recipient.live(messageRecipientId).get();
String body = CursorUtil.requireString(cursor, SearchTable.BODY);
String bodySnippet = CursorUtil.requireString(cursor, SearchTable.SNIPPET);
long receivedMs = CursorUtil.requireLong(cursor, MmsSmsColumns.NORMALIZED_DATE_RECEIVED);
long receivedMs = CursorUtil.requireLong(cursor, MmsSmsColumns.DATE_RECEIVED);
long threadId = CursorUtil.requireLong(cursor, MmsSmsColumns.THREAD_ID);
int messageId = CursorUtil.requireInt(cursor, SearchTable.MESSAGE_ID);
boolean isMms = CursorUtil.requireInt(cursor, SearchTable.IS_MMS) == 1;

Wyświetl plik

@ -1,226 +0,0 @@
package org.thoughtcrime.securesms.service;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import androidx.core.app.NotificationCompat;
import org.signal.core.util.PendingIntentFlags;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.MainActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.SmsMigrator;
import org.thoughtcrime.securesms.database.SmsMigrator.ProgressDescription;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.notifications.NotificationIds;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
// FIXME: This class is nuts.
public class ApplicationMigrationService extends Service
implements SmsMigrator.SmsMigrationProgressListener
{
private static final String TAG = Log.tag(ApplicationMigrationService.class);
public static final String MIGRATE_DATABASE = "org.thoughtcrime.securesms.ApplicationMigration.MIGRATE_DATABSE";
public static final String COMPLETED_ACTION = "org.thoughtcrime.securesms.ApplicationMigrationService.COMPLETED";
private static final String PREFERENCES_NAME = "SecureSMS";
private static final String DATABASE_MIGRATED = "migrated";
private final BroadcastReceiver completedReceiver = new CompletedReceiver();
private final Binder binder = new ApplicationMigrationBinder();
private final Executor executor = Executors.newSingleThreadExecutor();
private WeakReference<Handler> handler = null;
private NotificationCompat.Builder notification = null;
private ImportState state = new ImportState(ImportState.STATE_IDLE, null);
@Override
public void onCreate() {
registerCompletedReceiver();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) return START_NOT_STICKY;
if (intent.getAction() != null && intent.getAction().equals(MIGRATE_DATABASE)) {
executor.execute(new ImportRunnable());
}
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
unregisterCompletedReceiver();
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public void setImportStateHandler(Handler handler) {
this.handler = new WeakReference<>(handler);
}
private void registerCompletedReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(COMPLETED_ACTION);
registerReceiver(completedReceiver, filter);
}
private void unregisterCompletedReceiver() {
unregisterReceiver(completedReceiver);
}
private void notifyImportComplete() {
Intent intent = new Intent();
intent.setAction(COMPLETED_ACTION);
sendOrderedBroadcast(intent, null);
}
@Override
public void progressUpdate(ProgressDescription progress) {
setState(new ImportState(ImportState.STATE_MIGRATING_IN_PROGRESS, progress));
}
public ImportState getState() {
return state;
}
private void setState(ImportState state) {
this.state = state;
if (this.handler != null) {
Handler handler = this.handler.get();
if (handler != null) {
handler.obtainMessage(state.state, state.progress).sendToTarget();
}
}
if (state.progress != null && state.progress.secondaryComplete == 0) {
updateBackgroundNotification(state.progress.primaryTotal, state.progress.primaryComplete);
}
}
private void updateBackgroundNotification(int total, int complete) {
notification.setProgress(total, complete, false);
((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE))
.notify(NotificationIds.APPLICATION_MIGRATION, notification.build());
}
private NotificationCompat.Builder initializeBackgroundNotification() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NotificationChannels.OTHER);
builder.setSmallIcon(R.drawable.ic_notification);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_notification));
builder.setContentTitle(getString(R.string.ApplicationMigrationService_importing_text_messages));
builder.setContentText(getString(R.string.ApplicationMigrationService_import_in_progress));
builder.setOngoing(true);
builder.setProgress(100, 0, false);
// TODO [greyson] Navigation
builder.setContentIntent(PendingIntent.getActivity(this, 0, MainActivity.clearTop(this), PendingIntentFlags.mutable()));
stopForeground(true);
startForeground(NotificationIds.APPLICATION_MIGRATION, builder.build());
return builder;
}
private class ImportRunnable implements Runnable {
ImportRunnable() {}
@Override
public void run() {
notification = initializeBackgroundNotification();
PowerManager powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "signal:migration");
try {
wakeLock.acquire();
setState(new ImportState(ImportState.STATE_MIGRATING_BEGIN, null));
SmsMigrator.migrateDatabase(ApplicationMigrationService.this,
ApplicationMigrationService.this);
setState(new ImportState(ImportState.STATE_MIGRATING_COMPLETE, null));
setDatabaseImported(ApplicationMigrationService.this);
stopForeground(true);
notifyImportComplete();
stopSelf();
} finally {
wakeLock.release();
}
}
}
public class ApplicationMigrationBinder extends Binder {
public ApplicationMigrationService getService() {
return ApplicationMigrationService.this;
}
}
private static class CompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationChannels.OTHER);
builder.setSmallIcon(R.drawable.ic_notification);
builder.setContentTitle(context.getString(R.string.ApplicationMigrationService_import_complete));
builder.setContentText(context.getString(R.string.ApplicationMigrationService_system_database_import_is_complete));
// TODO [greyson] Navigation
builder.setContentIntent(PendingIntent.getActivity(context, 0, MainActivity.clearTop(context), PendingIntentFlags.mutable()));
builder.setWhen(System.currentTimeMillis());
builder.setDefaults(Notification.DEFAULT_VIBRATE);
builder.setAutoCancel(true);
Notification notification = builder.build();
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(NotificationIds.SMS_IMPORT_COMPLETE, notification);
}
}
public static class ImportState {
public static final int STATE_IDLE = 0;
public static final int STATE_MIGRATING_BEGIN = 1;
public static final int STATE_MIGRATING_IN_PROGRESS = 2;
public static final int STATE_MIGRATING_COMPLETE = 3;
public int state;
public ProgressDescription progress;
public ImportState(int state, ProgressDescription progress) {
this.state = state;
this.progress = progress;
}
}
public static boolean isDatabaseImported(Context context) {
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
.getBoolean(DATABASE_MIGRATED, false);
}
public static void setDatabaseImported(Context context) {
context.getSharedPreferences(PREFERENCES_NAME, 0).edit().putBoolean(DATABASE_MIGRATED, true).apply();
}
}

Wyświetl plik

@ -31,10 +31,6 @@ public class MmsListener extends BroadcastReceiver {
private static final String TAG = Log.tag(MmsListener.class);
private boolean isRelevant(Context context, Intent intent) {
if (!ApplicationMigrationService.isDatabaseImported(context)) {
return false;
}
if (Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION.equals(intent.getAction()) && Util.isDefaultSmsProvider(context)) {
return false;
}

Wyświetl plik

@ -86,9 +86,6 @@ public class SmsListener extends BroadcastReceiver {
if (isExemption(message, messageBody))
return false;
if (!ApplicationMigrationService.isDatabaseImported(context))
return false;
if (SMS_RECEIVED_ACTION.equals(intent.getAction()) && Util.isDefaultSmsProvider(context)) {
return false;
}

Wyświetl plik

@ -39,8 +39,7 @@ object GV2UpdateTransformer : ColumnTransformer {
private fun Cursor.getMessageType(): Long {
return when {
getColumnIndex(SmsTable.TYPE) != -1 -> requireLong(SmsTable.TYPE)
getColumnIndex(MmsTable.MESSAGE_BOX) != -1 -> requireLong(MmsTable.MESSAGE_BOX)
getColumnIndex(MmsSmsColumns.TYPE) != -1 -> requireLong(MmsSmsColumns.TYPE)
else -> -1
}
}

Wyświetl plik

@ -151,7 +151,6 @@ object FakeMessageRecords {
threadId,
body,
slideDeck,
partCount,
mailbox,
mismatches,
failures,

Wyświetl plik

@ -77,13 +77,13 @@ object TestMms {
): Long {
val contentValues = ContentValues().apply {
put(MmsTable.DATE_SENT, message.sentTimeMillis)
put(MmsTable.MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ)
put(MmsTable.MMS_MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ)
put(MmsTable.MESSAGE_BOX, type)
put(MmsTable.TYPE, type)
put(MmsSmsColumns.THREAD_ID, threadId)
put(MmsSmsColumns.READ, if (unread) 0 else 1)
put(MmsTable.DATE_RECEIVED, receivedTimestampMillis)
put(MmsSmsColumns.SUBSCRIPTION_ID, message.subscriptionId)
put(MmsSmsColumns.SMS_SUBSCRIPTION_ID, message.subscriptionId)
put(MmsSmsColumns.EXPIRES_IN, message.expiresIn)
put(MmsTable.VIEW_ONCE, message.isViewOnce)
put(MmsSmsColumns.RECIPIENT_ID, recipientId.serialize())
@ -93,7 +93,6 @@ object TestMms {
put(MmsTable.STORY_TYPE, message.storyType.code)
put(MmsSmsColumns.BODY, body)
put(MmsTable.PART_COUNT, 0)
put(MmsTable.MENTIONS_SELF, 0)
}
@ -106,7 +105,6 @@ object TestMms {
values.putNull(MmsSmsColumns.BODY)
values.putNull(MmsTable.QUOTE_BODY)
values.putNull(MmsTable.QUOTE_AUTHOR)
values.putNull(MmsTable.QUOTE_ATTACHMENT)
values.put(MmsTable.QUOTE_TYPE, -1)
values.putNull(MmsTable.QUOTE_ID)
values.putNull(MmsTable.LINK_PREVIEWS)

Wyświetl plik

@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.database
import android.content.ContentValues
import android.text.TextUtils
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingTextMessage
@ -61,22 +60,14 @@ object TestSms {
): Long {
val values = ContentValues().apply {
put(MmsSmsColumns.RECIPIENT_ID, message.sender.serialize())
put(MmsSmsColumns.ADDRESS_DEVICE_ID, message.senderDeviceId)
put(MmsSmsColumns.RECIPIENT_DEVICE_ID, message.senderDeviceId)
put(SmsTable.DATE_RECEIVED, message.receivedTimestampMillis)
put(SmsTable.DATE_SENT, message.sentTimestampMillis)
put(MmsSmsColumns.DATE_SERVER, message.serverTimestampMillis)
put(SmsTable.PROTOCOL, message.protocol)
put(MmsSmsColumns.READ, if (unread) 0 else 1)
put(MmsSmsColumns.SUBSCRIPTION_ID, message.subscriptionId)
put(MmsSmsColumns.SMS_SUBSCRIPTION_ID, message.subscriptionId)
put(MmsSmsColumns.EXPIRES_IN, message.expiresIn)
put(MmsSmsColumns.UNIDENTIFIED, message.isUnidentified)
if (!TextUtils.isEmpty(message.pseudoSubject)) {
put(SmsTable.SUBJECT, message.pseudoSubject)
}
put(SmsTable.REPLY_PATH_PRESENT, message.isReplyPathPresent)
put(SmsTable.SERVICE_CENTER, message.serviceCenterAddress)
put(MmsSmsColumns.BODY, message.messageBody)
put(SmsTable.TYPE, type)
put(MmsSmsColumns.THREAD_ID, threadId)

Wyświetl plik

@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase
import androidx.core.content.contentValuesOf
import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.sqlite.db.SupportSQLiteQueryBuilder
import org.intellij.lang.annotations.Language
/**
* Begins a transaction on the `this` database, runs the provided [block] providing the `this` value as it's argument
@ -85,7 +86,7 @@ class SelectBuilderPart2(
private val columns: Array<String>,
private val tableName: String
) {
fun where(where: String, vararg whereArgs: Any): SelectBuilderPart3 {
fun where(@Language("sql") where: String, vararg whereArgs: Any): SelectBuilderPart3 {
return SelectBuilderPart3(db, columns, tableName, where, SqlUtil.buildArgs(*whereArgs))
}
@ -213,7 +214,7 @@ class UpdateBuilderPart2(
private val tableName: String,
private val values: ContentValues
) {
fun where(where: String, vararg whereArgs: Any): UpdateBuilderPart3 {
fun where(@Language("sql") where: String, vararg whereArgs: Any): UpdateBuilderPart3 {
return UpdateBuilderPart3(db, tableName, values, where, SqlUtil.buildArgs(*whereArgs))
}
@ -239,7 +240,7 @@ class DeleteBuilderPart1(
private val db: SupportSQLiteDatabase,
private val tableName: String
) {
fun where(where: String, vararg whereArgs: Any): DeleteBuilderPart2 {
fun where(@Language("sql") where: String, vararg whereArgs: Any): DeleteBuilderPart2 {
return DeleteBuilderPart2(db, tableName, where, SqlUtil.buildArgs(*whereArgs))
}