mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-10 08:08:34 +00:00
Detect real age of call request by using server timestamps.
This commit is contained in:
parent
891a1af995
commit
629ba105cb
@ -175,7 +175,9 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
|||||||
messageRecord.getDateSent(),
|
messageRecord.getDateSent(),
|
||||||
legacy ? Base64.decode(messageRecord.getBody()) : null,
|
legacy ? Base64.decode(messageRecord.getBody()) : null,
|
||||||
!legacy ? Base64.decode(messageRecord.getBody()) : null,
|
!legacy ? Base64.decode(messageRecord.getBody()) : null,
|
||||||
0, null);
|
0,
|
||||||
|
0,
|
||||||
|
null);
|
||||||
|
|
||||||
long pushId = pushDatabase.insert(envelope);
|
long pushId = pushDatabase.insert(envelope);
|
||||||
|
|
||||||
|
@ -3,10 +3,8 @@ package org.thoughtcrime.securesms.database;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import net.sqlcipher.database.SQLiteDatabase;
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
@ -16,31 +14,38 @@ import org.thoughtcrime.securesms.util.Base64;
|
|||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
|
||||||
import org.whispersystems.signalservice.internal.util.Util;
|
import org.whispersystems.signalservice.internal.util.Util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PushDatabase extends Database {
|
public class PushDatabase extends Database {
|
||||||
|
|
||||||
private static final String TAG = PushDatabase.class.getSimpleName();
|
private static final String TAG = PushDatabase.class.getSimpleName();
|
||||||
|
|
||||||
private static final String TABLE_NAME = "push";
|
private static final String TABLE_NAME = "push";
|
||||||
public static final String ID = "_id";
|
public static final String ID = "_id";
|
||||||
public static final String TYPE = "type";
|
public static final String TYPE = "type";
|
||||||
public static final String SOURCE_E164 = "source";
|
public static final String SOURCE_E164 = "source";
|
||||||
public static final String SOURCE_UUID = "source_uuid";
|
public static final String SOURCE_UUID = "source_uuid";
|
||||||
public static final String DEVICE_ID = "device_id";
|
public static final String DEVICE_ID = "device_id";
|
||||||
public static final String LEGACY_MSG = "body";
|
public static final String LEGACY_MSG = "body";
|
||||||
public static final String CONTENT = "content";
|
public static final String CONTENT = "content";
|
||||||
public static final String TIMESTAMP = "timestamp";
|
public static final String TIMESTAMP = "timestamp";
|
||||||
public static final String SERVER_TIMESTAMP = "server_timestamp";
|
public static final String SERVER_RECEIVED_TIMESTAMP = "server_timestamp";
|
||||||
public static final String SERVER_GUID = "server_guid";
|
public static final String SERVER_DELIVERED_TIMESTAMP = "server_delivered_timestamp";
|
||||||
|
public static final String SERVER_GUID = "server_guid";
|
||||||
|
|
||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
TYPE + " INTEGER, " + SOURCE_E164 + " TEXT, " + SOURCE_UUID + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " +
|
TYPE + " INTEGER, " +
|
||||||
SERVER_TIMESTAMP + " INTEGER DEFAULT 0, " + SERVER_GUID + " TEXT DEFAULT NULL);";
|
SOURCE_E164 + " TEXT, " +
|
||||||
|
SOURCE_UUID + " TEXT, " +
|
||||||
|
DEVICE_ID + " INTEGER, " +
|
||||||
|
LEGACY_MSG + " TEXT, " +
|
||||||
|
CONTENT + " TEXT, " +
|
||||||
|
TIMESTAMP + " INTEGER, " +
|
||||||
|
SERVER_RECEIVED_TIMESTAMP + " INTEGER DEFAULT 0, " +
|
||||||
|
SERVER_DELIVERED_TIMESTAMP + " INTEGER DEFAULT 0, " +
|
||||||
|
SERVER_GUID + " TEXT DEFAULT NULL);";
|
||||||
|
|
||||||
public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
@ -60,7 +65,8 @@ public class PushDatabase extends Database {
|
|||||||
values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "");
|
values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "");
|
||||||
values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "");
|
values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "");
|
||||||
values.put(TIMESTAMP, envelope.getTimestamp());
|
values.put(TIMESTAMP, envelope.getTimestamp());
|
||||||
values.put(SERVER_TIMESTAMP, envelope.getServerTimestamp());
|
values.put(SERVER_RECEIVED_TIMESTAMP, envelope.getServerReceivedTimestamp());
|
||||||
|
values.put(SERVER_DELIVERED_TIMESTAMP, envelope.getServerDeliveredTimestamp());
|
||||||
values.put(SERVER_GUID, envelope.getUuid());
|
values.put(SERVER_GUID, envelope.getUuid());
|
||||||
|
|
||||||
return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
|
return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
|
||||||
@ -87,7 +93,8 @@ public class PushDatabase extends Database {
|
|||||||
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
|
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
|
||||||
Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage),
|
Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage),
|
||||||
Util.isEmpty(content) ? null : Base64.decode(content),
|
Util.isEmpty(content) ? null : Base64.decode(content),
|
||||||
cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP)),
|
cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_RECEIVED_TIMESTAMP)),
|
||||||
|
cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_DELIVERED_TIMESTAMP)),
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID)));
|
cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID)));
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -154,15 +161,16 @@ public class PushDatabase extends Database {
|
|||||||
if (cursor == null || !cursor.moveToNext())
|
if (cursor == null || !cursor.moveToNext())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
|
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
|
||||||
String sourceUuid = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_UUID));
|
String sourceUuid = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_UUID));
|
||||||
String sourceE164 = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_E164));
|
String sourceE164 = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_E164));
|
||||||
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
|
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
|
||||||
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
|
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
|
||||||
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
|
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
|
||||||
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
||||||
long serverTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP));
|
long serverReceivedTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_RECEIVED_TIMESTAMP));
|
||||||
String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID));
|
long serverDeliveredTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_DELIVERED_TIMESTAMP));
|
||||||
|
String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID));
|
||||||
|
|
||||||
return new SignalServiceEnvelope(type,
|
return new SignalServiceEnvelope(type,
|
||||||
SignalServiceAddress.fromRaw(sourceUuid, sourceE164),
|
SignalServiceAddress.fromRaw(sourceUuid, sourceE164),
|
||||||
@ -170,7 +178,8 @@ public class PushDatabase extends Database {
|
|||||||
timestamp,
|
timestamp,
|
||||||
legacyMessage != null ? Base64.decode(legacyMessage) : null,
|
legacyMessage != null ? Base64.decode(legacyMessage) : null,
|
||||||
content != null ? Base64.decode(content) : null,
|
content != null ? Base64.decode(content) : null,
|
||||||
serverTimestamp,
|
serverReceivedTimestamp,
|
||||||
|
serverDeliveredTimestamp,
|
||||||
serverGuid);
|
serverGuid);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
|
@ -136,8 +136,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
private static final int COLOR_MIGRATION = 61;
|
private static final int COLOR_MIGRATION = 61;
|
||||||
private static final int LAST_SCROLLED = 62;
|
private static final int LAST_SCROLLED = 62;
|
||||||
private static final int LAST_PROFILE_FETCH = 63;
|
private static final int LAST_PROFILE_FETCH = 63;
|
||||||
|
private static final int SERVER_DELIVERED_TIMESTAMP = 64;
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 63;
|
private static final int DATABASE_VERSION = 64;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
@ -916,6 +917,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
db.execSQL("ALTER TABLE recipient ADD COLUMN last_profile_fetch INTEGER DEFAULT 0");
|
db.execSQL("ALTER TABLE recipient ADD COLUMN last_profile_fetch INTEGER DEFAULT 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < SERVER_DELIVERED_TIMESTAMP) {
|
||||||
|
db.execSQL("ALTER TABLE push ADD COLUMN server_delivered_timestamp INTEGER DEFAULT 0");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
@ -245,7 +245,7 @@ public final class GroupV1MessageProcessor {
|
|||||||
} else {
|
} else {
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||||
String body = Base64.encodeBytes(storage.toByteArray());
|
String body = Base64.encodeBytes(storage.toByteArray());
|
||||||
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerTimestamp(), body, Optional.of(GroupId.v1orThrow(group.getGroupId())), 0, content.isNeedsReceipt());
|
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerReceivedTimestamp(), body, Optional.of(GroupId.v1orThrow(group.getGroupId())), 0, content.isNeedsReceipt());
|
||||||
IncomingGroupUpdateMessage groupMessage = new IncomingGroupUpdateMessage(incoming, storage, body);
|
IncomingGroupUpdateMessage groupMessage = new IncomingGroupUpdateMessage(incoming, storage, body);
|
||||||
|
|
||||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
||||||
|
@ -501,13 +501,14 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
RemotePeer remotePeer = new RemotePeer(Recipient.externalPush(context, content.getSender()).getId());
|
RemotePeer remotePeer = new RemotePeer(Recipient.externalPush(context, content.getSender()).getId());
|
||||||
|
|
||||||
intent.setAction(WebRtcCallService.ACTION_RECEIVE_OFFER)
|
intent.setAction(WebRtcCallService.ACTION_RECEIVE_OFFER)
|
||||||
.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId())
|
.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId())
|
||||||
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, remotePeer)
|
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, remotePeer)
|
||||||
.putExtra(WebRtcCallService.EXTRA_REMOTE_DEVICE, content.getSenderDevice())
|
.putExtra(WebRtcCallService.EXTRA_REMOTE_DEVICE, content.getSenderDevice())
|
||||||
.putExtra(WebRtcCallService.EXTRA_OFFER_DESCRIPTION, message.getDescription())
|
.putExtra(WebRtcCallService.EXTRA_OFFER_DESCRIPTION, message.getDescription())
|
||||||
.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp())
|
.putExtra(WebRtcCallService.EXTRA_SERVER_RECEIVED_TIMESTAMP, content.getServerReceivedTimestamp())
|
||||||
.putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, message.getType().getCode())
|
.putExtra(WebRtcCallService.EXTRA_SERVER_DELIVERED_TIMESTAMP, content.getServerDeliveredTimestamp())
|
||||||
.putExtra(WebRtcCallService.EXTRA_MULTI_RING, content.getCallMessage().get().isMultiRing());
|
.putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, message.getType().getCode())
|
||||||
|
.putExtra(WebRtcCallService.EXTRA_MULTI_RING, content.getCallMessage().get().isMultiRing());
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(intent);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(intent);
|
||||||
else context.startService(intent);
|
else context.startService(intent);
|
||||||
@ -601,7 +602,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
||||||
content.getSenderDevice(),
|
content.getSenderDevice(),
|
||||||
content.getTimestamp(),
|
content.getTimestamp(),
|
||||||
content.getServerTimestamp(),
|
content.getServerReceivedTimestamp(),
|
||||||
"", Optional.absent(), 0,
|
"", Optional.absent(), 0,
|
||||||
content.isNeedsReceipt());
|
content.isNeedsReceipt());
|
||||||
|
|
||||||
@ -710,7 +711,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
Recipient sender = Recipient.externalPush(context, content.getSender());
|
Recipient sender = Recipient.externalPush(context, content.getSender());
|
||||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(sender.getId(),
|
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(sender.getId(),
|
||||||
content.getTimestamp(),
|
content.getTimestamp(),
|
||||||
content.getServerTimestamp(),
|
content.getServerReceivedTimestamp(),
|
||||||
-1,
|
-1,
|
||||||
expiresInSeconds * 1000L,
|
expiresInSeconds * 1000L,
|
||||||
true,
|
true,
|
||||||
@ -768,7 +769,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
Recipient sender = Recipient.externalPush(context, content.getSender());
|
Recipient sender = Recipient.externalPush(context, content.getSender());
|
||||||
MessageRecord targetMessage = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(delete.getTargetSentTimestamp(), sender.getId());
|
MessageRecord targetMessage = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(delete.getTargetSentTimestamp(), sender.getId());
|
||||||
|
|
||||||
if (targetMessage != null && RemoteDeleteUtil.isValidReceive(targetMessage, sender, content.getServerTimestamp())) {
|
if (targetMessage != null && RemoteDeleteUtil.isValidReceive(targetMessage, sender, content.getServerReceivedTimestamp())) {
|
||||||
MessagingDatabase db = targetMessage.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context);
|
MessagingDatabase db = targetMessage.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context);
|
||||||
db.markAsRemoteDelete(targetMessage.getId());
|
db.markAsRemoteDelete(targetMessage.getId());
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false);
|
||||||
@ -777,7 +778,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
ApplicationDependencies.getEarlyMessageCache().store(sender.getId(), delete.getTargetSentTimestamp(), content);
|
ApplicationDependencies.getEarlyMessageCache().store(sender.getId(), delete.getTargetSentTimestamp(), content);
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, String.format(Locale.ENGLISH, "[handleRemoteDelete] Invalid remote delete! deleteTime: %d, targetTime: %d, deleteAuthor: %s, targetAuthor: %s",
|
Log.w(TAG, String.format(Locale.ENGLISH, "[handleRemoteDelete] Invalid remote delete! deleteTime: %d, targetTime: %d, deleteAuthor: %s, targetAuthor: %s",
|
||||||
content.getServerTimestamp(), targetMessage.getServerTimestamp(), sender.getId(), targetMessage.getRecipient().getId()));
|
content.getServerReceivedTimestamp(), targetMessage.getServerTimestamp(), sender.getId(), targetMessage.getRecipient().getId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1025,7 +1026,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
|
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
|
||||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
||||||
message.getTimestamp(),
|
message.getTimestamp(),
|
||||||
content.getServerTimestamp(),
|
content.getServerReceivedTimestamp(),
|
||||||
-1,
|
-1,
|
||||||
message.getExpiresInSeconds() * 1000L,
|
message.getExpiresInSeconds() * 1000L,
|
||||||
false,
|
false,
|
||||||
@ -1245,7 +1246,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
||||||
content.getSenderDevice(),
|
content.getSenderDevice(),
|
||||||
message.getTimestamp(),
|
message.getTimestamp(),
|
||||||
content.getServerTimestamp(),
|
content.getServerReceivedTimestamp(),
|
||||||
body,
|
body,
|
||||||
groupId,
|
groupId,
|
||||||
message.getExpiresInSeconds() * 1000L,
|
message.getExpiresInSeconds() * 1000L,
|
||||||
|
@ -95,26 +95,27 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
|
|||||||
|
|
||||||
private static final String TAG = WebRtcCallService.class.getSimpleName();
|
private static final String TAG = WebRtcCallService.class.getSimpleName();
|
||||||
|
|
||||||
public static final String EXTRA_MUTE = "mute_value";
|
public static final String EXTRA_MUTE = "mute_value";
|
||||||
public static final String EXTRA_AVAILABLE = "enabled_value";
|
public static final String EXTRA_AVAILABLE = "enabled_value";
|
||||||
public static final String EXTRA_TIMESTAMP = "timestamp";
|
public static final String EXTRA_SERVER_RECEIVED_TIMESTAMP = "server_received_timestamp";
|
||||||
public static final String EXTRA_CALL_ID = "call_id";
|
public static final String EXTRA_SERVER_DELIVERED_TIMESTAMP = "server_delivered_timestamp";
|
||||||
public static final String EXTRA_RESULT_RECEIVER = "result_receiver";
|
public static final String EXTRA_CALL_ID = "call_id";
|
||||||
public static final String EXTRA_SPEAKER = "audio_speaker";
|
public static final String EXTRA_RESULT_RECEIVER = "result_receiver";
|
||||||
public static final String EXTRA_BLUETOOTH = "audio_bluetooth";
|
public static final String EXTRA_SPEAKER = "audio_speaker";
|
||||||
public static final String EXTRA_REMOTE_PEER = "remote_peer";
|
public static final String EXTRA_BLUETOOTH = "audio_bluetooth";
|
||||||
public static final String EXTRA_REMOTE_DEVICE = "remote_device";
|
public static final String EXTRA_REMOTE_PEER = "remote_peer";
|
||||||
public static final String EXTRA_OFFER_DESCRIPTION = "offer_description";
|
public static final String EXTRA_REMOTE_DEVICE = "remote_device";
|
||||||
public static final String EXTRA_OFFER_TYPE = "offer_type";
|
public static final String EXTRA_OFFER_DESCRIPTION = "offer_description";
|
||||||
public static final String EXTRA_MULTI_RING = "multi_ring";
|
public static final String EXTRA_OFFER_TYPE = "offer_type";
|
||||||
public static final String EXTRA_HANGUP_TYPE = "hangup_type";
|
public static final String EXTRA_MULTI_RING = "multi_ring";
|
||||||
public static final String EXTRA_HANGUP_IS_LEGACY = "hangup_is_legacy";
|
public static final String EXTRA_HANGUP_TYPE = "hangup_type";
|
||||||
public static final String EXTRA_HANGUP_DEVICE_ID = "hangup_device_id";
|
public static final String EXTRA_HANGUP_IS_LEGACY = "hangup_is_legacy";
|
||||||
public static final String EXTRA_ANSWER_DESCRIPTION = "answer_description";
|
public static final String EXTRA_HANGUP_DEVICE_ID = "hangup_device_id";
|
||||||
public static final String EXTRA_ICE_CANDIDATES = "ice_candidates";
|
public static final String EXTRA_ANSWER_DESCRIPTION = "answer_description";
|
||||||
public static final String EXTRA_ENABLE = "enable_value";
|
public static final String EXTRA_ICE_CANDIDATES = "ice_candidates";
|
||||||
public static final String EXTRA_BROADCAST = "broadcast";
|
public static final String EXTRA_ENABLE = "enable_value";
|
||||||
public static final String EXTRA_ANSWER_WITH_VIDEO = "enable_video";
|
public static final String EXTRA_BROADCAST = "broadcast";
|
||||||
|
public static final String EXTRA_ANSWER_WITH_VIDEO = "enable_video";
|
||||||
|
|
||||||
public static final String ACTION_OUTGOING_CALL = "CALL_OUTGOING";
|
public static final String ACTION_OUTGOING_CALL = "CALL_OUTGOING";
|
||||||
public static final String ACTION_DENY_CALL = "DENY_CALL";
|
public static final String ACTION_DENY_CALL = "DENY_CALL";
|
||||||
@ -383,13 +384,14 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
|
|||||||
// Handlers
|
// Handlers
|
||||||
|
|
||||||
private void handleReceivedOffer(Intent intent) {
|
private void handleReceivedOffer(Intent intent) {
|
||||||
CallId callId = getCallId(intent);
|
CallId callId = getCallId(intent);
|
||||||
RemotePeer remotePeer = getRemotePeer(intent);
|
RemotePeer remotePeer = getRemotePeer(intent);
|
||||||
Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1);
|
Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1);
|
||||||
String offer = intent.getStringExtra(EXTRA_OFFER_DESCRIPTION);
|
String offer = intent.getStringExtra(EXTRA_OFFER_DESCRIPTION);
|
||||||
Long timeStamp = intent.getLongExtra(EXTRA_TIMESTAMP, -1);
|
long serverReceivedTimestamp = intent.getLongExtra(EXTRA_SERVER_RECEIVED_TIMESTAMP, -1);
|
||||||
OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE));
|
long serverDeliveredTimestamp = intent.getLongExtra(EXTRA_SERVER_DELIVERED_TIMESTAMP, -1);
|
||||||
boolean isMultiRing = intent.getBooleanExtra(EXTRA_MULTI_RING, false);
|
OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE));
|
||||||
|
boolean isMultiRing = intent.getBooleanExtra(EXTRA_MULTI_RING, false);
|
||||||
|
|
||||||
Log.i(TAG, "handleReceivedOffer(): id: " + callId.format(remoteDevice));
|
Log.i(TAG, "handleReceivedOffer(): id: " + callId.format(remoteDevice));
|
||||||
|
|
||||||
@ -413,12 +415,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
|
|||||||
|
|
||||||
CallManager.CallMediaType callType = getCallMediaTypeFromOfferType(offerType);
|
CallManager.CallMediaType callType = getCallMediaTypeFromOfferType(offerType);
|
||||||
|
|
||||||
long timeMilli = new Date().getTime();
|
long messageAgeSec = Math.max(serverDeliveredTimestamp - serverReceivedTimestamp, 0) / 1000;
|
||||||
long messageAgeSec = 0L;
|
Log.i(TAG, "handleReceivedOffer(): messageAgeSec: " + messageAgeSec + ", serverReceivedTimestamp: " + serverReceivedTimestamp + ", serverDeliveredTimestamp: " + serverDeliveredTimestamp);
|
||||||
if (timeMilli > timeStamp) {
|
|
||||||
messageAgeSec = (timeMilli - timeStamp) / 1000;
|
|
||||||
}
|
|
||||||
Log.i(TAG, "handleReceivedOffer(): messageAgeSec: " + messageAgeSec);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
callManager.receivedOffer(callId, remotePeer, remoteDevice, offer, messageAgeSec, callType, 1, isMultiRing, true);
|
callManager.receivedOffer(callId, remotePeer, remoteDevice, offer, messageAgeSec, callType, 1, isMultiRing, true);
|
||||||
|
@ -16,6 +16,7 @@ import org.signal.zkgroup.profiles.ProfileKeyCredentialRequest;
|
|||||||
import org.signal.zkgroup.profiles.ProfileKeyCredentialRequestContext;
|
import org.signal.zkgroup.profiles.ProfileKeyCredentialRequestContext;
|
||||||
import org.signal.zkgroup.profiles.ProfileKeyVersion;
|
import org.signal.zkgroup.profiles.ProfileKeyVersion;
|
||||||
import org.whispersystems.libsignal.InvalidVersionException;
|
import org.whispersystems.libsignal.InvalidVersionException;
|
||||||
|
import org.whispersystems.libsignal.logging.Log;
|
||||||
import org.whispersystems.libsignal.util.Hex;
|
import org.whispersystems.libsignal.util.Hex;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||||
@ -61,6 +62,8 @@ public class SignalServiceMessagePipe {
|
|||||||
|
|
||||||
private static final String TAG = SignalServiceMessagePipe.class.getName();
|
private static final String TAG = SignalServiceMessagePipe.class.getName();
|
||||||
|
|
||||||
|
private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp";
|
||||||
|
|
||||||
private final WebSocketConnection websocket;
|
private final WebSocketConnection websocket;
|
||||||
private final Optional<CredentialsProvider> credentialsProvider;
|
private final Optional<CredentialsProvider> credentialsProvider;
|
||||||
private final ClientZkProfileOperations clientZkProfile;
|
private final ClientZkProfileOperations clientZkProfile;
|
||||||
@ -146,9 +149,21 @@ public class SignalServiceMessagePipe {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (isSignalServiceEnvelope(request)) {
|
if (isSignalServiceEnvelope(request)) {
|
||||||
|
Optional<String> timestampHeader = findHeader(request, SERVER_DELIVERED_TIMESTAMP_HEADER);
|
||||||
|
long timestamp = 0;
|
||||||
|
|
||||||
|
if (timestampHeader.isPresent()) {
|
||||||
|
try {
|
||||||
|
timestamp = Long.parseLong(timestampHeader.get());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, "Failed to parse " + SERVER_DELIVERED_TIMESTAMP_HEADER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SignalServiceEnvelope envelope = new SignalServiceEnvelope(request.getBody().toByteArray(),
|
SignalServiceEnvelope envelope = new SignalServiceEnvelope(request.getBody().toByteArray(),
|
||||||
credentialsProvider.get().getSignalingKey(),
|
credentialsProvider.get().getSignalingKey(),
|
||||||
signalKeyEncrypted);
|
signalKeyEncrypted,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
callback.onMessage(envelope);
|
callback.onMessage(envelope);
|
||||||
return Optional.of(envelope);
|
return Optional.of(envelope);
|
||||||
@ -345,6 +360,23 @@ public class SignalServiceMessagePipe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Optional<String> findHeader(WebSocketRequestMessage message, String targetHeader) {
|
||||||
|
if (message.getHeadersCount() == 0) {
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String header : message.getHeadersList()) {
|
||||||
|
if (header.startsWith(targetHeader)) {
|
||||||
|
String[] split = header.split(":");
|
||||||
|
if (split.length == 2 && split[0].trim().toLowerCase().equals(targetHeader.toLowerCase())) {
|
||||||
|
return Optional.of(split[1].trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For receiving a callback when a new message has been
|
* For receiving a callback when a new message has been
|
||||||
* received.
|
* received.
|
||||||
|
@ -32,6 +32,7 @@ import org.whispersystems.signalservice.api.websocket.ConnectivityListener;
|
|||||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
||||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
|
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceEnvelopeEntity;
|
import org.whispersystems.signalservice.internal.push.SignalServiceEnvelopeEntity;
|
||||||
|
import org.whispersystems.signalservice.internal.push.SignalServiceMessagesResult;
|
||||||
import org.whispersystems.signalservice.internal.sticker.StickerProtos;
|
import org.whispersystems.signalservice.internal.sticker.StickerProtos;
|
||||||
import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider;
|
import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider;
|
||||||
import org.whispersystems.signalservice.internal.util.Util;
|
import org.whispersystems.signalservice.internal.util.Util;
|
||||||
@ -263,22 +264,31 @@ public class SignalServiceMessageReceiver {
|
|||||||
public List<SignalServiceEnvelope> retrieveMessages(MessageReceivedCallback callback)
|
public List<SignalServiceEnvelope> retrieveMessages(MessageReceivedCallback callback)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
List<SignalServiceEnvelope> results = new LinkedList<>();
|
List<SignalServiceEnvelope> results = new LinkedList<>();
|
||||||
List<SignalServiceEnvelopeEntity> entities = socket.getMessages();
|
SignalServiceMessagesResult messageResult = socket.getMessages();
|
||||||
|
|
||||||
for (SignalServiceEnvelopeEntity entity : entities) {
|
for (SignalServiceEnvelopeEntity entity : messageResult.getEnvelopes()) {
|
||||||
SignalServiceEnvelope envelope;
|
SignalServiceEnvelope envelope;
|
||||||
|
|
||||||
if (entity.hasSource() && entity.getSourceDevice() > 0) {
|
if (entity.hasSource() && entity.getSourceDevice() > 0) {
|
||||||
SignalServiceAddress address = new SignalServiceAddress(UuidUtil.parseOrNull(entity.getSourceUuid()), entity.getSourceE164());
|
SignalServiceAddress address = new SignalServiceAddress(UuidUtil.parseOrNull(entity.getSourceUuid()), entity.getSourceE164());
|
||||||
envelope = new SignalServiceEnvelope(entity.getType(), Optional.of(address),
|
envelope = new SignalServiceEnvelope(entity.getType(),
|
||||||
entity.getSourceDevice(), entity.getTimestamp(),
|
Optional.of(address),
|
||||||
entity.getMessage(), entity.getContent(),
|
entity.getSourceDevice(),
|
||||||
entity.getServerTimestamp(), entity.getServerUuid());
|
entity.getTimestamp(),
|
||||||
|
entity.getMessage(),
|
||||||
|
entity.getContent(),
|
||||||
|
entity.getServerTimestamp(),
|
||||||
|
messageResult.getServerDeliveredTimestamp(),
|
||||||
|
entity.getServerUuid());
|
||||||
} else {
|
} else {
|
||||||
envelope = new SignalServiceEnvelope(entity.getType(), entity.getTimestamp(),
|
envelope = new SignalServiceEnvelope(entity.getType(),
|
||||||
entity.getMessage(), entity.getContent(),
|
entity.getTimestamp(),
|
||||||
entity.getServerTimestamp(), entity.getServerUuid());
|
entity.getMessage(),
|
||||||
|
entity.getContent(),
|
||||||
|
entity.getServerTimestamp(),
|
||||||
|
messageResult.getServerDeliveredTimestamp(),
|
||||||
|
entity.getServerUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
callback.onMessage(envelope);
|
callback.onMessage(envelope);
|
||||||
|
@ -176,23 +176,23 @@ public class SignalServiceCipher {
|
|||||||
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
|
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
|
||||||
|
|
||||||
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext));
|
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext));
|
||||||
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerTimestamp(), false);
|
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), false);
|
||||||
sessionVersion = sessionCipher.getSessionVersion();
|
sessionVersion = sessionCipher.getSessionVersion();
|
||||||
} else if (envelope.isSignalMessage()) {
|
} else if (envelope.isSignalMessage()) {
|
||||||
SignalProtocolAddress sourceAddress = getPreferredProtocolAddress(signalProtocolStore, envelope.getSourceAddress(), envelope.getSourceDevice());
|
SignalProtocolAddress sourceAddress = getPreferredProtocolAddress(signalProtocolStore, envelope.getSourceAddress(), envelope.getSourceDevice());
|
||||||
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
|
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
|
||||||
|
|
||||||
paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext));
|
paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext));
|
||||||
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerTimestamp(), false);
|
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), false);
|
||||||
sessionVersion = sessionCipher.getSessionVersion();
|
sessionVersion = sessionCipher.getSessionVersion();
|
||||||
} else if (envelope.isUnidentifiedSender()) {
|
} else if (envelope.isUnidentifiedSender()) {
|
||||||
SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.getUuid().orNull(), localAddress.getNumber().orNull(), 1);
|
SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.getUuid().orNull(), localAddress.getNumber().orNull(), 1);
|
||||||
DecryptionResult result = sealedSessionCipher.decrypt(certificateValidator, ciphertext, envelope.getServerTimestamp());
|
DecryptionResult result = sealedSessionCipher.decrypt(certificateValidator, ciphertext, envelope.getServerReceivedTimestamp());
|
||||||
SignalServiceAddress resultAddress = new SignalServiceAddress(UuidUtil.parse(result.getSenderUuid().orNull()), result.getSenderE164());
|
SignalServiceAddress resultAddress = new SignalServiceAddress(UuidUtil.parse(result.getSenderUuid().orNull()), result.getSenderE164());
|
||||||
SignalProtocolAddress protocolAddress = getPreferredProtocolAddress(signalProtocolStore, resultAddress, result.getDeviceId());
|
SignalProtocolAddress protocolAddress = getPreferredProtocolAddress(signalProtocolStore, resultAddress, result.getDeviceId());
|
||||||
|
|
||||||
paddedMessage = result.getPaddedMessage();
|
paddedMessage = result.getPaddedMessage();
|
||||||
metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.getTimestamp(), envelope.getServerTimestamp(), true);
|
metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), true);
|
||||||
sessionVersion = sealedSessionCipher.getSessionVersion(protocolAddress);
|
sessionVersion = sealedSessionCipher.getSessionVersion(protocolAddress);
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType());
|
throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType());
|
||||||
|
@ -59,7 +59,8 @@ public final class SignalServiceContent {
|
|||||||
private final SignalServiceAddress sender;
|
private final SignalServiceAddress sender;
|
||||||
private final int senderDevice;
|
private final int senderDevice;
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
private final long serverTimestamp;
|
private final long serverReceivedTimestamp;
|
||||||
|
private final long serverDeliveredTimestamp;
|
||||||
private final boolean needsReceipt;
|
private final boolean needsReceipt;
|
||||||
private final SignalServiceContentProto serializedState;
|
private final SignalServiceContentProto serializedState;
|
||||||
|
|
||||||
@ -69,13 +70,22 @@ public final class SignalServiceContent {
|
|||||||
private final Optional<SignalServiceReceiptMessage> readMessage;
|
private final Optional<SignalServiceReceiptMessage> readMessage;
|
||||||
private final Optional<SignalServiceTypingMessage> typingMessage;
|
private final Optional<SignalServiceTypingMessage> typingMessage;
|
||||||
|
|
||||||
private SignalServiceContent(SignalServiceDataMessage message, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
private SignalServiceContent(SignalServiceDataMessage message,
|
||||||
this.sender = sender;
|
SignalServiceAddress sender,
|
||||||
this.senderDevice = senderDevice;
|
int senderDevice,
|
||||||
this.timestamp = timestamp;
|
long timestamp,
|
||||||
this.serverTimestamp = serverTimestamp;
|
long serverReceivedTimestamp,
|
||||||
this.needsReceipt = needsReceipt;
|
long serverDeliveredTimestamp,
|
||||||
this.serializedState = serializedState;
|
boolean needsReceipt,
|
||||||
|
SignalServiceContentProto serializedState)
|
||||||
|
{
|
||||||
|
this.sender = sender;
|
||||||
|
this.senderDevice = senderDevice;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
|
this.needsReceipt = needsReceipt;
|
||||||
|
this.serializedState = serializedState;
|
||||||
|
|
||||||
this.message = Optional.fromNullable(message);
|
this.message = Optional.fromNullable(message);
|
||||||
this.synchronizeMessage = Optional.absent();
|
this.synchronizeMessage = Optional.absent();
|
||||||
@ -84,13 +94,22 @@ public final class SignalServiceContent {
|
|||||||
this.typingMessage = Optional.absent();
|
this.typingMessage = Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
private SignalServiceContent(SignalServiceSyncMessage synchronizeMessage,
|
||||||
this.sender = sender;
|
SignalServiceAddress sender,
|
||||||
this.senderDevice = senderDevice;
|
int senderDevice,
|
||||||
this.timestamp = timestamp;
|
long timestamp,
|
||||||
this.serverTimestamp = serverTimestamp;
|
long serverReceivedTimestamp,
|
||||||
this.needsReceipt = needsReceipt;
|
long serverDeliveredTimestamp,
|
||||||
this.serializedState = serializedState;
|
boolean needsReceipt,
|
||||||
|
SignalServiceContentProto serializedState)
|
||||||
|
{
|
||||||
|
this.sender = sender;
|
||||||
|
this.senderDevice = senderDevice;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
|
this.needsReceipt = needsReceipt;
|
||||||
|
this.serializedState = serializedState;
|
||||||
|
|
||||||
this.message = Optional.absent();
|
this.message = Optional.absent();
|
||||||
this.synchronizeMessage = Optional.fromNullable(synchronizeMessage);
|
this.synchronizeMessage = Optional.fromNullable(synchronizeMessage);
|
||||||
@ -99,13 +118,22 @@ public final class SignalServiceContent {
|
|||||||
this.typingMessage = Optional.absent();
|
this.typingMessage = Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceContent(SignalServiceCallMessage callMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
private SignalServiceContent(SignalServiceCallMessage callMessage,
|
||||||
this.sender = sender;
|
SignalServiceAddress sender,
|
||||||
this.senderDevice = senderDevice;
|
int senderDevice,
|
||||||
this.timestamp = timestamp;
|
long timestamp,
|
||||||
this.serverTimestamp = serverTimestamp;
|
long serverReceivedTimestamp,
|
||||||
this.needsReceipt = needsReceipt;
|
long serverDeliveredTimestamp,
|
||||||
this.serializedState = serializedState;
|
boolean needsReceipt,
|
||||||
|
SignalServiceContentProto serializedState)
|
||||||
|
{
|
||||||
|
this.sender = sender;
|
||||||
|
this.senderDevice = senderDevice;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
|
this.needsReceipt = needsReceipt;
|
||||||
|
this.serializedState = serializedState;
|
||||||
|
|
||||||
this.message = Optional.absent();
|
this.message = Optional.absent();
|
||||||
this.synchronizeMessage = Optional.absent();
|
this.synchronizeMessage = Optional.absent();
|
||||||
@ -114,13 +142,22 @@ public final class SignalServiceContent {
|
|||||||
this.typingMessage = Optional.absent();
|
this.typingMessage = Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceContent(SignalServiceReceiptMessage receiptMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
private SignalServiceContent(SignalServiceReceiptMessage receiptMessage,
|
||||||
this.sender = sender;
|
SignalServiceAddress sender,
|
||||||
this.senderDevice = senderDevice;
|
int senderDevice,
|
||||||
this.timestamp = timestamp;
|
long timestamp,
|
||||||
this.serverTimestamp = serverTimestamp;
|
long serverReceivedTimestamp,
|
||||||
this.needsReceipt = needsReceipt;
|
long serverDeliveredTimestamp,
|
||||||
this.serializedState = serializedState;
|
boolean needsReceipt,
|
||||||
|
SignalServiceContentProto serializedState)
|
||||||
|
{
|
||||||
|
this.sender = sender;
|
||||||
|
this.senderDevice = senderDevice;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
|
this.needsReceipt = needsReceipt;
|
||||||
|
this.serializedState = serializedState;
|
||||||
|
|
||||||
this.message = Optional.absent();
|
this.message = Optional.absent();
|
||||||
this.synchronizeMessage = Optional.absent();
|
this.synchronizeMessage = Optional.absent();
|
||||||
@ -129,13 +166,22 @@ public final class SignalServiceContent {
|
|||||||
this.typingMessage = Optional.absent();
|
this.typingMessage = Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceContent(SignalServiceTypingMessage typingMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
private SignalServiceContent(SignalServiceTypingMessage typingMessage,
|
||||||
this.sender = sender;
|
SignalServiceAddress sender,
|
||||||
this.senderDevice = senderDevice;
|
int senderDevice,
|
||||||
this.timestamp = timestamp;
|
long timestamp,
|
||||||
this.serverTimestamp = serverTimestamp;
|
long serverReceivedTimestamp,
|
||||||
this.needsReceipt = needsReceipt;
|
long serverDeliveredTimestamp,
|
||||||
this.serializedState = serializedState;
|
boolean needsReceipt,
|
||||||
|
SignalServiceContentProto serializedState)
|
||||||
|
{
|
||||||
|
this.sender = sender;
|
||||||
|
this.senderDevice = senderDevice;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
|
this.needsReceipt = needsReceipt;
|
||||||
|
this.serializedState = serializedState;
|
||||||
|
|
||||||
this.message = Optional.absent();
|
this.message = Optional.absent();
|
||||||
this.synchronizeMessage = Optional.absent();
|
this.synchronizeMessage = Optional.absent();
|
||||||
@ -176,8 +222,12 @@ public final class SignalServiceContent {
|
|||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getServerTimestamp() {
|
public long getServerReceivedTimestamp() {
|
||||||
return serverTimestamp;
|
return serverReceivedTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getServerDeliveredTimestamp() {
|
||||||
|
return serverDeliveredTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNeedsReceipt() {
|
public boolean isNeedsReceipt() {
|
||||||
@ -217,7 +267,8 @@ public final class SignalServiceContent {
|
|||||||
metadata.getSender(),
|
metadata.getSender(),
|
||||||
metadata.getSenderDevice(),
|
metadata.getSenderDevice(),
|
||||||
metadata.getTimestamp(),
|
metadata.getTimestamp(),
|
||||||
metadata.getServerTimestamp(),
|
metadata.getServerReceivedTimestamp(),
|
||||||
|
metadata.getServerDeliveredTimestamp(),
|
||||||
metadata.isNeedsReceipt(),
|
metadata.isNeedsReceipt(),
|
||||||
serviceContentProto);
|
serviceContentProto);
|
||||||
} else if (serviceContentProto.getDataCase() == SignalServiceContentProto.DataCase.CONTENT) {
|
} else if (serviceContentProto.getDataCase() == SignalServiceContentProto.DataCase.CONTENT) {
|
||||||
@ -228,7 +279,8 @@ public final class SignalServiceContent {
|
|||||||
metadata.getSender(),
|
metadata.getSender(),
|
||||||
metadata.getSenderDevice(),
|
metadata.getSenderDevice(),
|
||||||
metadata.getTimestamp(),
|
metadata.getTimestamp(),
|
||||||
metadata.getServerTimestamp(),
|
metadata.getServerReceivedTimestamp(),
|
||||||
|
metadata.getServerDeliveredTimestamp(),
|
||||||
metadata.isNeedsReceipt(),
|
metadata.isNeedsReceipt(),
|
||||||
serviceContentProto);
|
serviceContentProto);
|
||||||
} else if (message.hasSyncMessage() && localAddress.matches(metadata.getSender())) {
|
} else if (message.hasSyncMessage() && localAddress.matches(metadata.getSender())) {
|
||||||
@ -236,7 +288,8 @@ public final class SignalServiceContent {
|
|||||||
metadata.getSender(),
|
metadata.getSender(),
|
||||||
metadata.getSenderDevice(),
|
metadata.getSenderDevice(),
|
||||||
metadata.getTimestamp(),
|
metadata.getTimestamp(),
|
||||||
metadata.getServerTimestamp(),
|
metadata.getServerReceivedTimestamp(),
|
||||||
|
metadata.getServerDeliveredTimestamp(),
|
||||||
metadata.isNeedsReceipt(),
|
metadata.isNeedsReceipt(),
|
||||||
serviceContentProto);
|
serviceContentProto);
|
||||||
} else if (message.hasCallMessage()) {
|
} else if (message.hasCallMessage()) {
|
||||||
@ -244,7 +297,8 @@ public final class SignalServiceContent {
|
|||||||
metadata.getSender(),
|
metadata.getSender(),
|
||||||
metadata.getSenderDevice(),
|
metadata.getSenderDevice(),
|
||||||
metadata.getTimestamp(),
|
metadata.getTimestamp(),
|
||||||
metadata.getServerTimestamp(),
|
metadata.getServerReceivedTimestamp(),
|
||||||
|
metadata.getServerDeliveredTimestamp(),
|
||||||
metadata.isNeedsReceipt(),
|
metadata.isNeedsReceipt(),
|
||||||
serviceContentProto);
|
serviceContentProto);
|
||||||
} else if (message.hasReceiptMessage()) {
|
} else if (message.hasReceiptMessage()) {
|
||||||
@ -252,7 +306,8 @@ public final class SignalServiceContent {
|
|||||||
metadata.getSender(),
|
metadata.getSender(),
|
||||||
metadata.getSenderDevice(),
|
metadata.getSenderDevice(),
|
||||||
metadata.getTimestamp(),
|
metadata.getTimestamp(),
|
||||||
metadata.getServerTimestamp(),
|
metadata.getServerReceivedTimestamp(),
|
||||||
|
metadata.getServerDeliveredTimestamp(),
|
||||||
metadata.isNeedsReceipt(),
|
metadata.isNeedsReceipt(),
|
||||||
serviceContentProto);
|
serviceContentProto);
|
||||||
} else if (message.hasTypingMessage()) {
|
} else if (message.hasTypingMessage()) {
|
||||||
@ -260,7 +315,8 @@ public final class SignalServiceContent {
|
|||||||
metadata.getSender(),
|
metadata.getSender(),
|
||||||
metadata.getSenderDevice(),
|
metadata.getSenderDevice(),
|
||||||
metadata.getTimestamp(),
|
metadata.getTimestamp(),
|
||||||
metadata.getServerTimestamp(),
|
metadata.getServerReceivedTimestamp(),
|
||||||
|
metadata.getServerDeliveredTimestamp(),
|
||||||
false,
|
false,
|
||||||
serviceContentProto);
|
serviceContentProto);
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ public class SignalServiceEnvelope {
|
|||||||
private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH;
|
private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH;
|
||||||
|
|
||||||
private final Envelope envelope;
|
private final Envelope envelope;
|
||||||
|
private final long serverDeliveredTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an envelope from a serialized, Base64 encoded SignalServiceEnvelope, encrypted
|
* Construct an envelope from a serialized, Base64 encoded SignalServiceEnvelope, encrypted
|
||||||
@ -65,10 +66,13 @@ public class SignalServiceEnvelope {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws InvalidVersionException
|
* @throws InvalidVersionException
|
||||||
*/
|
*/
|
||||||
public SignalServiceEnvelope(String message, String signalingKey, boolean isSignalingKeyEncrypted)
|
public SignalServiceEnvelope(String message,
|
||||||
|
String signalingKey,
|
||||||
|
boolean isSignalingKeyEncrypted,
|
||||||
|
long serverDeliveredTimestamp)
|
||||||
throws IOException, InvalidVersionException
|
throws IOException, InvalidVersionException
|
||||||
{
|
{
|
||||||
this(Base64.decode(message), signalingKey, isSignalingKeyEncrypted);
|
this(Base64.decode(message), signalingKey, isSignalingKeyEncrypted, serverDeliveredTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,7 +83,10 @@ public class SignalServiceEnvelope {
|
|||||||
* @throws InvalidVersionException
|
* @throws InvalidVersionException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public SignalServiceEnvelope(byte[] input, String signalingKey, boolean isSignalingKeyEncrypted)
|
public SignalServiceEnvelope(byte[] input,
|
||||||
|
String signalingKey,
|
||||||
|
boolean isSignalingKeyEncrypted,
|
||||||
|
long serverDeliveredTimestamp)
|
||||||
throws InvalidVersionException, IOException
|
throws InvalidVersionException, IOException
|
||||||
{
|
{
|
||||||
if (!isSignalingKeyEncrypted) {
|
if (!isSignalingKeyEncrypted) {
|
||||||
@ -96,14 +103,25 @@ public class SignalServiceEnvelope {
|
|||||||
|
|
||||||
this.envelope = Envelope.parseFrom(getPlaintext(input, cipherKey));
|
this.envelope = Envelope.parseFrom(getPlaintext(input, cipherKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalServiceEnvelope(int type, Optional<SignalServiceAddress> sender, int senderDevice, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) {
|
public SignalServiceEnvelope(int type,
|
||||||
|
Optional<SignalServiceAddress> sender,
|
||||||
|
int senderDevice,
|
||||||
|
long timestamp,
|
||||||
|
byte[] legacyMessage,
|
||||||
|
byte[] content,
|
||||||
|
long serverReceivedTimestamp,
|
||||||
|
long serverDeliveredTimestamp,
|
||||||
|
String uuid)
|
||||||
|
{
|
||||||
Envelope.Builder builder = Envelope.newBuilder()
|
Envelope.Builder builder = Envelope.newBuilder()
|
||||||
.setType(Envelope.Type.valueOf(type))
|
.setType(Envelope.Type.valueOf(type))
|
||||||
.setSourceDevice(senderDevice)
|
.setSourceDevice(senderDevice)
|
||||||
.setTimestamp(timestamp)
|
.setTimestamp(timestamp)
|
||||||
.setServerTimestamp(serverTimestamp);
|
.setServerTimestamp(serverReceivedTimestamp);
|
||||||
|
|
||||||
if (sender.isPresent()) {
|
if (sender.isPresent()) {
|
||||||
if (sender.get().getUuid().isPresent()) {
|
if (sender.get().getUuid().isPresent()) {
|
||||||
@ -122,14 +140,22 @@ public class SignalServiceEnvelope {
|
|||||||
if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage));
|
if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage));
|
||||||
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
||||||
|
|
||||||
this.envelope = builder.build();
|
this.envelope = builder.build();
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalServiceEnvelope(int type, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) {
|
public SignalServiceEnvelope(int type,
|
||||||
|
long timestamp,
|
||||||
|
byte[] legacyMessage,
|
||||||
|
byte[] content,
|
||||||
|
long serverReceivedTimestamp,
|
||||||
|
long serverDeliveredTimestamp,
|
||||||
|
String uuid)
|
||||||
|
{
|
||||||
Envelope.Builder builder = Envelope.newBuilder()
|
Envelope.Builder builder = Envelope.newBuilder()
|
||||||
.setType(Envelope.Type.valueOf(type))
|
.setType(Envelope.Type.valueOf(type))
|
||||||
.setTimestamp(timestamp)
|
.setTimestamp(timestamp)
|
||||||
.setServerTimestamp(serverTimestamp);
|
.setServerTimestamp(serverReceivedTimestamp);
|
||||||
|
|
||||||
if (uuid != null) {
|
if (uuid != null) {
|
||||||
builder.setServerGuid(uuid);
|
builder.setServerGuid(uuid);
|
||||||
@ -138,7 +164,8 @@ public class SignalServiceEnvelope {
|
|||||||
if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage));
|
if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage));
|
||||||
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
||||||
|
|
||||||
this.envelope = builder.build();
|
this.envelope = builder.build();
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUuid() {
|
public String getUuid() {
|
||||||
@ -206,10 +233,20 @@ public class SignalServiceEnvelope {
|
|||||||
return envelope.getTimestamp();
|
return envelope.getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getServerTimestamp() {
|
/**
|
||||||
|
* @return The server timestamp of when the server received the envelope.
|
||||||
|
*/
|
||||||
|
public long getServerReceivedTimestamp() {
|
||||||
return envelope.getServerTimestamp();
|
return envelope.getServerTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The server timestamp of when the envelope was delivered to us.
|
||||||
|
*/
|
||||||
|
public long getServerDeliveredTimestamp() {
|
||||||
|
return serverDeliveredTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Whether the envelope contains a SignalServiceDataMessage
|
* @return Whether the envelope contains a SignalServiceDataMessage
|
||||||
*/
|
*/
|
||||||
|
@ -6,15 +6,23 @@ public final class SignalServiceMetadata {
|
|||||||
private final SignalServiceAddress sender;
|
private final SignalServiceAddress sender;
|
||||||
private final int senderDevice;
|
private final int senderDevice;
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
private final long serverTimestamp;
|
private final long serverReceivedTimestamp;
|
||||||
|
private final long serverDeliveredTimestamp;
|
||||||
private final boolean needsReceipt;
|
private final boolean needsReceipt;
|
||||||
|
|
||||||
public SignalServiceMetadata(SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt) {
|
public SignalServiceMetadata(SignalServiceAddress sender,
|
||||||
this.sender = sender;
|
int senderDevice,
|
||||||
this.senderDevice = senderDevice;
|
long timestamp,
|
||||||
this.timestamp = timestamp;
|
long serverReceivedTimestamp,
|
||||||
this.serverTimestamp = serverTimestamp;
|
long serverDeliveredTimestamp,
|
||||||
this.needsReceipt = needsReceipt;
|
boolean needsReceipt)
|
||||||
|
{
|
||||||
|
this.sender = sender;
|
||||||
|
this.senderDevice = senderDevice;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
|
this.needsReceipt = needsReceipt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalServiceAddress getSender() {
|
public SignalServiceAddress getSender() {
|
||||||
@ -29,8 +37,12 @@ public final class SignalServiceMetadata {
|
|||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getServerTimestamp() {
|
public long getServerReceivedTimestamp() {
|
||||||
return serverTimestamp;
|
return serverReceivedTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getServerDeliveredTimestamp() {
|
||||||
|
return serverDeliveredTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNeedsReceipt() {
|
public boolean isNeedsReceipt() {
|
||||||
|
@ -198,6 +198,8 @@ public class PushServiceSocket {
|
|||||||
private static final String GROUPSV2_GROUP_CHANGES = "/v1/groups/logs/%s";
|
private static final String GROUPSV2_GROUP_CHANGES = "/v1/groups/logs/%s";
|
||||||
private static final String GROUPSV2_AVATAR_REQUEST = "/v1/groups/avatar/form";
|
private static final String GROUPSV2_AVATAR_REQUEST = "/v1/groups/avatar/form";
|
||||||
|
|
||||||
|
private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp";
|
||||||
|
|
||||||
private static final Map<String, String> NO_HEADERS = Collections.emptyMap();
|
private static final Map<String, String> NO_HEADERS = Collections.emptyMap();
|
||||||
private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler();
|
private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler();
|
||||||
|
|
||||||
@ -390,9 +392,28 @@ public class PushServiceSocket {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<SignalServiceEnvelopeEntity> getMessages() throws IOException {
|
public SignalServiceMessagesResult getMessages() throws IOException {
|
||||||
String responseText = makeServiceRequest(String.format(MESSAGE_PATH, ""), "GET", null);
|
Response response = makeServiceRequest(String.format(MESSAGE_PATH, ""), "GET", (RequestBody) null, NO_HEADERS, NO_HANDLER, Optional.absent());
|
||||||
return JsonUtil.fromJson(responseText, SignalServiceEnvelopeEntityList.class).getMessages();
|
validateServiceResponse(response);
|
||||||
|
|
||||||
|
List<SignalServiceEnvelopeEntity> envelopes;
|
||||||
|
try {
|
||||||
|
envelopes = JsonUtil.fromJson(readBodyString(response.body()), SignalServiceEnvelopeEntityList.class).getMessages();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new PushNetworkException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
long serverDeliveredTimestamp = 0;
|
||||||
|
try {
|
||||||
|
String stringValue = response.header(SERVER_DELIVERED_TIMESTAMP_HEADER);
|
||||||
|
stringValue = stringValue != null ? stringValue : "0";
|
||||||
|
|
||||||
|
serverDeliveredTimestamp = Long.parseLong(stringValue);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SignalServiceMessagesResult(envelopes, serverDeliveredTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acknowledgeMessage(String sender, long timestamp) throws IOException {
|
public void acknowledgeMessage(String sender, long timestamp) throws IOException {
|
||||||
@ -1368,9 +1389,9 @@ public class PushServiceSocket {
|
|||||||
call.enqueue(new Callback() {
|
call.enqueue(new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call call, Response response) {
|
public void onResponse(Call call, Response response) {
|
||||||
try (ResponseBody body = validateServiceResponse(response)) {
|
try (ResponseBody body = validateServiceResponse(response).body()) {
|
||||||
try {
|
try {
|
||||||
bodyFuture.set(body.string());
|
bodyFuture.set(readBodyString(body));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new PushNetworkException(e);
|
throw new PushNetworkException(e);
|
||||||
}
|
}
|
||||||
@ -1395,6 +1416,17 @@ public class PushServiceSocket {
|
|||||||
ResponseCodeHandler responseCodeHandler,
|
ResponseCodeHandler responseCodeHandler,
|
||||||
Optional<UnidentifiedAccess> unidentifiedAccessKey)
|
Optional<UnidentifiedAccess> unidentifiedAccessKey)
|
||||||
throws NonSuccessfulResponseCodeException, PushNetworkException
|
throws NonSuccessfulResponseCodeException, PushNetworkException
|
||||||
|
{
|
||||||
|
return makeServiceRequest(urlFragment, method, body, headers, responseCodeHandler, unidentifiedAccessKey).body();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response makeServiceRequest(String urlFragment,
|
||||||
|
String method,
|
||||||
|
RequestBody body,
|
||||||
|
Map<String, String> headers,
|
||||||
|
ResponseCodeHandler responseCodeHandler,
|
||||||
|
Optional<UnidentifiedAccess> unidentifiedAccessKey)
|
||||||
|
throws NonSuccessfulResponseCodeException, PushNetworkException
|
||||||
{
|
{
|
||||||
Response response = getServiceConnection(urlFragment, method, body, headers, unidentifiedAccessKey);
|
Response response = getServiceConnection(urlFragment, method, body, headers, unidentifiedAccessKey);
|
||||||
|
|
||||||
@ -1403,7 +1435,7 @@ public class PushServiceSocket {
|
|||||||
return validateServiceResponse(response);
|
return validateServiceResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResponseBody validateServiceResponse(Response response) throws NonSuccessfulResponseCodeException, PushNetworkException {
|
private Response validateServiceResponse(Response response) throws NonSuccessfulResponseCodeException, PushNetworkException {
|
||||||
int responseCode = response.code();
|
int responseCode = response.code();
|
||||||
String responseMessage = response.message();
|
String responseMessage = response.message();
|
||||||
ResponseBody responseBody = response.body();
|
ResponseBody responseBody = response.body();
|
||||||
@ -1420,7 +1452,7 @@ public class PushServiceSocket {
|
|||||||
MismatchedDevices mismatchedDevices;
|
MismatchedDevices mismatchedDevices;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mismatchedDevices = JsonUtil.fromJson(responseBody.string(), MismatchedDevices.class);
|
mismatchedDevices = JsonUtil.fromJson(readBodyString(responseBody), MismatchedDevices.class);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||||
@ -1433,7 +1465,7 @@ public class PushServiceSocket {
|
|||||||
StaleDevices staleDevices;
|
StaleDevices staleDevices;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
staleDevices = JsonUtil.fromJson(responseBody.string(), StaleDevices.class);
|
staleDevices = JsonUtil.fromJson(readBodyString(responseBody), StaleDevices.class);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -1445,7 +1477,7 @@ public class PushServiceSocket {
|
|||||||
DeviceLimit deviceLimit;
|
DeviceLimit deviceLimit;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
deviceLimit = JsonUtil.fromJson(responseBody.string(), DeviceLimit.class);
|
deviceLimit = JsonUtil.fromJson(readBodyString(responseBody), DeviceLimit.class);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -1459,7 +1491,7 @@ public class PushServiceSocket {
|
|||||||
RegistrationLockFailure accountLockFailure;
|
RegistrationLockFailure accountLockFailure;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
accountLockFailure = JsonUtil.fromJson(responseBody.string(), RegistrationLockFailure.class);
|
accountLockFailure = JsonUtil.fromJson(readBodyString(responseBody), RegistrationLockFailure.class);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||||
@ -1479,7 +1511,7 @@ public class PushServiceSocket {
|
|||||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return responseBody;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response getServiceConnection(String urlFragment, String method, RequestBody body, Map<String, String> headers, Optional<UnidentifiedAccess> unidentifiedAccess)
|
private Response getServiceConnection(String urlFragment, String method, RequestBody body, Map<String, String> headers, Optional<UnidentifiedAccess> unidentifiedAccess)
|
||||||
@ -1807,6 +1839,15 @@ public class PushServiceSocket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String readBodyString(ResponseBody body) throws IOException {
|
||||||
|
if (body != null) {
|
||||||
|
return body.string();
|
||||||
|
} else {
|
||||||
|
throw new IOException("No body!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class GcmRegistrationId {
|
private static class GcmRegistrationId {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.whispersystems.signalservice.internal.push;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class SignalServiceMessagesResult {
|
||||||
|
private final List<SignalServiceEnvelopeEntity> envelopes;
|
||||||
|
private final long serverDeliveredTimestamp;
|
||||||
|
|
||||||
|
SignalServiceMessagesResult(List<SignalServiceEnvelopeEntity> envelopes, long serverDeliveredTimestamp) {
|
||||||
|
this.envelopes = envelopes;
|
||||||
|
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SignalServiceEnvelopeEntity> getEnvelopes() {
|
||||||
|
return envelopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getServerDeliveredTimestamp() {
|
||||||
|
return serverDeliveredTimestamp;
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,8 @@ public final class SignalServiceMetadataProtobufSerializer {
|
|||||||
.setSenderDevice(metadata.getSenderDevice())
|
.setSenderDevice(metadata.getSenderDevice())
|
||||||
.setNeedsReceipt(metadata.isNeedsReceipt())
|
.setNeedsReceipt(metadata.isNeedsReceipt())
|
||||||
.setTimestamp(metadata.getTimestamp())
|
.setTimestamp(metadata.getTimestamp())
|
||||||
.setServerTimestamp(metadata.getServerTimestamp())
|
.setServerReceivedTimestamp(metadata.getServerReceivedTimestamp())
|
||||||
|
.setServerDeliveredTimestamp(metadata.getServerDeliveredTimestamp())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +23,8 @@ public final class SignalServiceMetadataProtobufSerializer {
|
|||||||
return new SignalServiceMetadata(SignalServiceAddressProtobufSerializer.fromProtobuf(metadata.getAddress()),
|
return new SignalServiceMetadata(SignalServiceAddressProtobufSerializer.fromProtobuf(metadata.getAddress()),
|
||||||
metadata.getSenderDevice(),
|
metadata.getSenderDevice(),
|
||||||
metadata.getTimestamp(),
|
metadata.getTimestamp(),
|
||||||
metadata.getServerTimestamp(),
|
metadata.getServerReceivedTimestamp(),
|
||||||
|
metadata.getServerDeliveredTimestamp(),
|
||||||
metadata.getNeedsReceipt());
|
metadata.getNeedsReceipt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,12 @@ message SignalServiceContentProto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message MetadataProto {
|
message MetadataProto {
|
||||||
optional AddressProto address = 1;
|
optional AddressProto address = 1;
|
||||||
optional int32 senderDevice = 2;
|
optional int32 senderDevice = 2;
|
||||||
optional int64 timestamp = 3;
|
optional int64 timestamp = 3;
|
||||||
optional int64 serverTimestamp = 5;
|
optional int64 serverReceivedTimestamp = 5;
|
||||||
optional bool needsReceipt = 4;
|
optional int64 serverDeliveredTimestamp = 6;
|
||||||
|
optional bool needsReceipt = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddressProto {
|
message AddressProto {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user