use of org.matrix.androidsdk.data.Room in project matrix-android-sdk by matrix-org.
the class CryptoTest method test17_testLateRoomKey.
@Test
public void test17_testLateRoomKey() throws Exception {
Log.e(LOG_TAG, "test17_testLateRoomKey");
final HashMap<String, Object> results = new HashMap<>();
doE2ETestWithAliceAndBobInARoom(true);
mBobSession.getCrypto().setWarnOnUnknownDevices(false);
mAliceSession.getCrypto().setWarnOnUnknownDevices(false);
String messageFromAlice = "Hello I'm Alice!";
final Room roomFromBobPOV = mBobSession.getDataHandler().getRoom(mRoomId);
final Room roomFromAlicePOV = mAliceSession.getDataHandler().getRoom(mRoomId);
assertTrue(roomFromBobPOV.isEncrypted());
assertTrue(roomFromAlicePOV.isEncrypted());
final CountDownLatch lock1 = new CountDownLatch(2);
MXEventListener bobEventListener = new MXEventListener() {
@Override
public void onToDeviceEvent(Event event) {
if (!results.containsKey("onToDeviceEvent")) {
results.put("onToDeviceEvent", event);
lock1.countDown();
}
}
};
mBobSession.getDataHandler().addListener(bobEventListener);
final ArrayList<Event> receivedEvents = new ArrayList<>();
EventTimeline.EventTimelineListener eventTimelineListener = new EventTimeline.EventTimelineListener() {
public void onEvent(Event event, EventTimeline.Direction direction, RoomState roomState) {
if (TextUtils.equals(event.getType(), Event.EVENT_TYPE_MESSAGE)) {
receivedEvents.add(event);
lock1.countDown();
}
}
};
roomFromBobPOV.getLiveTimeLine().addEventTimelineListener(eventTimelineListener);
roomFromAlicePOV.sendEvent(buildTextEvent(messageFromAlice, mAliceSession), new ApiCallback<Void>() {
@Override
public void onSuccess(Void info) {
}
@Override
public void onNetworkError(Exception e) {
}
@Override
public void onMatrixError(MatrixError e) {
}
@Override
public void onUnexpectedError(Exception e) {
}
});
lock1.await(2000, TimeUnit.MILLISECONDS);
assertTrue(results.containsKey("onToDeviceEvent"));
assertTrue(1 == receivedEvents.size());
Event event = receivedEvents.get(0);
assertTrue(checkEncryptedEvent(event, mRoomId, messageFromAlice, mAliceSession));
// Reinject a modified version of the received room_key event from Alice.
// From Bob pov, that mimics Alice resharing her keys but with an advanced outbound group session.
Event toDeviceEvent = (Event) results.get("onToDeviceEvent");
String sessionId = toDeviceEvent.getContentAsJsonObject().get("session_id").getAsString();
String senderKey = toDeviceEvent.senderKey();
// remove the session
mBobSession.getCrypto().getOlmDevice().removeInboundGroupSession(sessionId, senderKey);
event.setClearData(null);
// check that the message cannot be decrypted
assertTrue(!mBobSession.getDataHandler().decryptEvent(event, null));
// check the error code
assertTrue(TextUtils.equals(event.getCryptoError().errcode, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE));
receivedEvents.clear();
final CountDownLatch lock2 = new CountDownLatch(1);
roomFromBobPOV.addEventListener(new MXEventListener() {
@Override
public void onEventDecrypted(Event event) {
results.put("onEventDecrypted", "onEventDecrypted");
receivedEvents.add(event);
lock2.countDown();
}
});
event.setClearData(null);
// reinject the session key
mBobSession.getDataHandler().onToDeviceEvent(toDeviceEvent);
// the message should be decrypted later
lock2.await(1000, TimeUnit.MILLISECONDS);
assertTrue(results.containsKey("onEventDecrypted"));
assertTrue(1 == receivedEvents.size());
assertTrue(checkEncryptedEvent(receivedEvents.get(0), mRoomId, messageFromAlice, mAliceSession));
assertTrue(null == receivedEvents.get(0).getCryptoError());
}
use of org.matrix.androidsdk.data.Room in project matrix-android-sdk by matrix-org.
the class CryptoTest method doE2ETestWithAliceAndBobInARoomWithCryptedMessages.
private void doE2ETestWithAliceAndBobInARoomWithCryptedMessages(boolean cryptedBob) throws Exception {
doE2ETestWithAliceAndBobInARoom(cryptedBob);
if (null != mBobSession.getCrypto()) {
mBobSession.getCrypto().setWarnOnUnknownDevices(false);
}
if (null != mAliceSession.getCrypto()) {
mAliceSession.getCrypto().setWarnOnUnknownDevices(false);
}
final Room roomFromBobPOV = mBobSession.getDataHandler().getRoom(mRoomId);
final Room roomFromAlicePOV = mAliceSession.getDataHandler().getRoom(mRoomId);
mMessagesCount = 0;
final ArrayList<CountDownLatch> list = new ArrayList<>();
MXEventListener bobEventsListener = new MXEventListener() {
@Override
public void onLiveEvent(Event event, RoomState roomState) {
if (TextUtils.equals(event.getType(), Event.EVENT_TYPE_MESSAGE) && !TextUtils.equals(event.getSender(), mBobSession.getMyUserId())) {
mMessagesCount++;
list.get(0).countDown();
}
}
};
roomFromBobPOV.addEventListener(bobEventsListener);
ApiCallback<Void> callback = new ApiCallback<Void>() {
@Override
public void onSuccess(Void info) {
list.get(0).countDown();
}
@Override
public void onNetworkError(Exception e) {
}
@Override
public void onMatrixError(MatrixError e) {
}
@Override
public void onUnexpectedError(Exception e) {
}
};
final HashMap<String, Object> results = new HashMap<>();
CountDownLatch lock = new CountDownLatch(3);
list.clear();
list.add(lock);
mBobSession.getDataHandler().addListener(new MXEventListener() {
@Override
public void onToDeviceEvent(Event event) {
results.put("onToDeviceEvent", event);
list.get(0).countDown();
}
});
roomFromAlicePOV.sendEvent(buildTextEvent(messagesFromAlice.get(0), mAliceSession), callback);
lock.await(1000, TimeUnit.MILLISECONDS);
assertTrue(results.containsKey("onToDeviceEvent"));
assertTrue(mMessagesCount == 1);
lock = new CountDownLatch(1);
list.clear();
list.add(lock);
roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob.get(0), mBobSession), callback);
// android does not echo the messages sent from itself
mMessagesCount++;
lock.await(1000, TimeUnit.MILLISECONDS);
assertTrue(mMessagesCount == 2);
lock = new CountDownLatch(1);
list.clear();
list.add(lock);
roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob.get(1), mBobSession), callback);
// android does not echo the messages sent from itself
mMessagesCount++;
lock.await(1000, TimeUnit.MILLISECONDS);
assertTrue(mMessagesCount == 3);
lock = new CountDownLatch(1);
list.clear();
list.add(lock);
roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob.get(2), mBobSession), callback);
// android does not echo the messages sent from itself
mMessagesCount++;
lock.await(1000, TimeUnit.MILLISECONDS);
assertTrue(mMessagesCount == 4);
lock = new CountDownLatch(2);
list.clear();
list.add(lock);
roomFromAlicePOV.sendEvent(buildTextEvent(messagesFromAlice.get(1), mAliceSession), callback);
lock.await(1000, TimeUnit.MILLISECONDS);
assertTrue(mMessagesCount == 5);
}
use of org.matrix.androidsdk.data.Room in project matrix-android-sdk by matrix-org.
the class MXDataHandler method deleteRoomEvent.
/**
* Delete an event.
*
* @param event The event to be stored.
*/
public void deleteRoomEvent(Event event) {
if (isAlive()) {
Room room = getRoom(event.roomId);
if (null != room) {
mStore.deleteEvent(event);
Event lastEvent = mStore.getLatestEvent(event.roomId);
RoomState beforeLiveRoomState = room.getState().deepCopy();
RoomSummary summary = mStore.getSummary(event.roomId);
if (null == summary) {
summary = new RoomSummary(null, lastEvent, beforeLiveRoomState, mCredentials.userId);
} else {
summary.setLatestReceivedEvent(lastEvent, beforeLiveRoomState);
}
if (TextUtils.equals(summary.getReadReceiptEventId(), event.eventId)) {
summary.setReadReceiptEventId(lastEvent.eventId);
}
if (TextUtils.equals(summary.getReadMarkerEventId(), event.eventId)) {
summary.setReadMarkerEventId(lastEvent.eventId);
}
mStore.storeSummary(summary);
}
} else {
Log.e(LOG_TAG, "deleteRoomEvent : the session is not anymore active");
}
}
use of org.matrix.androidsdk.data.Room in project matrix-android-sdk by matrix-org.
the class MXDataHandler method getRoom.
/**
* Get the room object from the corresponding room id.
*
* @param store the dedicated store
* @param roomId the room id
* @param create create the room it does not exist.
* @return the corresponding room
*/
public Room getRoom(IMXStore store, String roomId, boolean create) {
if (!isAlive()) {
Log.e(LOG_TAG, "getRoom : the session is not anymore active");
return null;
}
// sanity check
if (TextUtils.isEmpty(roomId)) {
return null;
}
Room room;
synchronized (this) {
room = store.getRoom(roomId);
if ((room == null) && create) {
Log.d(LOG_TAG, "## getRoom() : create the room " + roomId);
room = new Room();
room.init(store, roomId, this);
store.storeRoom(room);
} else if ((null != room) && (null == room.getDataHandler())) {
// GA reports that some rooms have no data handler
// so ensure that it is not properly set
Log.e(LOG_TAG, "getRoom " + roomId + " was not initialized");
room.init(store, roomId, this);
store.storeRoom(room);
}
}
return room;
}
use of org.matrix.androidsdk.data.Room in project matrix-android-sdk by matrix-org.
the class MXDataHandler method manageResponse.
/**
* Manage the sync response in the UI thread.
*
* @param syncResponse the syncResponse to manage.
* @param fromToken the start sync token
* @param isCatchingUp true when there is a pending catch-up
*/
private void manageResponse(final SyncResponse syncResponse, final String fromToken, final boolean isCatchingUp) {
if (!isAlive()) {
Log.e(LOG_TAG, "manageResponse : ignored because the session has been closed");
return;
}
boolean isInitialSync = (null == fromToken);
boolean isEmptyResponse = true;
// sanity check
if (null != syncResponse) {
Log.d(LOG_TAG, "onSyncComplete");
// to ensure to decrypt them properly
if ((null != syncResponse.toDevice) && (null != syncResponse.toDevice.events) && (syncResponse.toDevice.events.size() > 0)) {
Log.d(LOG_TAG, "manageResponse : receives " + syncResponse.toDevice.events.size() + " toDevice events");
for (Event toDeviceEvent : syncResponse.toDevice.events) {
handleToDeviceEvent(toDeviceEvent);
}
}
// to be able to update direct chats dictionary during invites handling.
if (null != syncResponse.accountData) {
Log.d(LOG_TAG, "Received " + syncResponse.accountData.size() + " accountData events");
manageAccountData(syncResponse.accountData, isInitialSync);
}
// sanity check
if (null != syncResponse.rooms) {
// joined rooms events
if ((null != syncResponse.rooms.join) && (syncResponse.rooms.join.size() > 0)) {
Log.d(LOG_TAG, "Received " + syncResponse.rooms.join.size() + " joined rooms");
Set<String> roomIds = syncResponse.rooms.join.keySet();
// Handle first joined rooms
for (String roomId : roomIds) {
try {
if (null != mLeftRoomsStore.getRoom(roomId)) {
Log.d(LOG_TAG, "the room " + roomId + " moves from left to the joined ones");
mLeftRoomsStore.deleteRoom(roomId);
}
getRoom(roomId).handleJoinedRoomSync(syncResponse.rooms.join.get(roomId), isInitialSync);
} catch (Exception e) {
Log.e(LOG_TAG, "## manageResponse() : handleJoinedRoomSync failed " + e.getMessage() + " for room " + roomId);
}
}
isEmptyResponse = false;
}
// invited room management
if ((null != syncResponse.rooms.invite) && (syncResponse.rooms.invite.size() > 0)) {
Log.d(LOG_TAG, "Received " + syncResponse.rooms.invite.size() + " invited rooms");
Set<String> roomIds = syncResponse.rooms.invite.keySet();
HashMap<String, List<String>> updatedDirectChatRoomsDict = null;
boolean hasChanged = false;
for (String roomId : roomIds) {
try {
Log.d(LOG_TAG, "## manageResponse() : the user has been invited to " + roomId);
if (null != mLeftRoomsStore.getRoom(roomId)) {
Log.d(LOG_TAG, "the room " + roomId + " moves from left to the invited ones");
mLeftRoomsStore.deleteRoom(roomId);
}
Room room = getRoom(roomId);
InvitedRoomSync invitedRoomSync = syncResponse.rooms.invite.get(roomId);
room.handleInvitedRoomSync(invitedRoomSync);
// Handle here the invites to a direct chat.
if (room.isDirectChatInvitation()) {
// Retrieve the inviter user id.
String participantUserId = null;
for (Event event : invitedRoomSync.inviteState.events) {
if (null != event.sender) {
participantUserId = event.sender;
break;
}
}
if (null != participantUserId) {
// Prepare the updated dictionary.
if (null == updatedDirectChatRoomsDict) {
if (null != this.getStore().getDirectChatRoomsDict()) {
// Consider the current dictionary.
updatedDirectChatRoomsDict = new HashMap<>(this.getStore().getDirectChatRoomsDict());
} else {
updatedDirectChatRoomsDict = new HashMap<>();
}
}
ArrayList<String> roomIdsList;
if (updatedDirectChatRoomsDict.containsKey(participantUserId)) {
roomIdsList = new ArrayList<>(updatedDirectChatRoomsDict.get(participantUserId));
} else {
roomIdsList = new ArrayList<>();
}
// Check whether the room was not yet seen as direct chat
if (roomIdsList.indexOf(roomId) < 0) {
Log.d(LOG_TAG, "## manageResponse() : add this new invite in direct chats");
// update room list with the new room
roomIdsList.add(roomId);
updatedDirectChatRoomsDict.put(participantUserId, roomIdsList);
hasChanged = true;
}
}
}
} catch (Exception e) {
Log.e(LOG_TAG, "## manageResponse() : handleInvitedRoomSync failed " + e.getMessage() + " for room " + roomId);
}
}
isEmptyResponse = false;
if (hasChanged) {
mAccountDataRestClient.setAccountData(mCredentials.userId, AccountDataRestClient.ACCOUNT_DATA_TYPE_DIRECT_MESSAGES, updatedDirectChatRoomsDict, new ApiCallback<Void>() {
@Override
public void onSuccess(Void info) {
Log.d(LOG_TAG, "## manageResponse() : succeeds");
}
@Override
public void onNetworkError(Exception e) {
Log.e(LOG_TAG, "## manageResponse() : update account data failed " + e.getMessage());
// TODO: we should try again.
}
@Override
public void onMatrixError(MatrixError e) {
Log.e(LOG_TAG, "## manageResponse() : update account data failed " + e.getMessage());
}
@Override
public void onUnexpectedError(Exception e) {
Log.e(LOG_TAG, "## manageResponse() : update account data failed " + e.getMessage());
}
});
}
}
// when inviting after leaving a room, the room is defined in the both leave & invite rooms list.
if ((null != syncResponse.rooms.leave) && (syncResponse.rooms.leave.size() > 0)) {
Log.d(LOG_TAG, "Received " + syncResponse.rooms.leave.size() + " left rooms");
Set<String> roomIds = syncResponse.rooms.leave.keySet();
for (String roomId : roomIds) {
// RoomSync leftRoomSync = syncResponse.rooms.leave.get(roomId);
// Presently we remove the existing room from the rooms list.
// FIXME SYNC V2 Archive/Display the left rooms!
// For that create 'handleArchivedRoomSync' method
String membership = RoomMember.MEMBERSHIP_LEAVE;
Room room = getRoom(roomId);
// check if the room still exists.
if (null != room) {
// use 'handleJoinedRoomSync' to pass the last events to the room before leaving it.
// The room will then able to notify its listeners.
room.handleJoinedRoomSync(syncResponse.rooms.leave.get(roomId), isInitialSync);
RoomMember member = room.getMember(getUserId());
if (null != member) {
membership = member.membership;
}
Log.d(LOG_TAG, "## manageResponse() : leave the room " + roomId);
}
if (!TextUtils.equals(membership, RoomMember.MEMBERSHIP_KICK) && !TextUtils.equals(membership, RoomMember.MEMBERSHIP_BAN)) {
// ensure that the room data are properly deleted
this.getStore().deleteRoom(roomId);
onLeaveRoom(roomId);
} else {
onRoomKick(roomId);
}
// don't add to the left rooms if the user has been kicked / banned
if ((mAreLeftRoomsSynced) && TextUtils.equals(membership, RoomMember.MEMBERSHIP_LEAVE)) {
Room leftRoom = getRoom(mLeftRoomsStore, roomId, true);
leftRoom.handleJoinedRoomSync(syncResponse.rooms.leave.get(roomId), isInitialSync);
}
}
isEmptyResponse = false;
}
}
// groups
if (null != syncResponse.groups) {
// Handle invited groups
if ((null != syncResponse.groups.invite) && !syncResponse.groups.invite.isEmpty()) {
// Handle invited groups
for (String groupId : syncResponse.groups.invite.keySet()) {
InvitedGroupSync invitedGroupSync = syncResponse.groups.invite.get(groupId);
mGroupsManager.onNewGroupInvitation(groupId, invitedGroupSync.profile, invitedGroupSync.inviter, !isInitialSync);
}
}
// Handle joined groups
if ((null != syncResponse.groups.join) && !syncResponse.groups.join.isEmpty()) {
for (String groupId : syncResponse.groups.join.keySet()) {
mGroupsManager.onJoinGroup(groupId, !isInitialSync);
}
}
// Handle left groups
if ((null != syncResponse.groups.leave) && !syncResponse.groups.leave.isEmpty()) {
// Handle joined groups
for (String groupId : syncResponse.groups.leave.keySet()) {
mGroupsManager.onLeaveGroup(groupId, !isInitialSync);
}
}
}
// Handle presence of other users
if ((null != syncResponse.presence) && (null != syncResponse.presence.events)) {
Log.d(LOG_TAG, "Received " + syncResponse.presence.events.size() + " presence events");
for (Event presenceEvent : syncResponse.presence.events) {
handlePresenceEvent(presenceEvent);
}
}
if (null != mCrypto) {
mCrypto.onSyncCompleted(syncResponse, fromToken, isCatchingUp);
}
IMXStore store = getStore();
if (!isEmptyResponse && (null != store)) {
store.setEventStreamToken(syncResponse.nextBatch);
store.commit();
}
}
if (isInitialSync) {
if (!isCatchingUp) {
startCrypto(true);
} else {
// the events thread sends a dummy initial sync event
// when the application is restarted.
mIsStartingCryptoWithInitialSync = !isEmptyResponse;
}
onInitialSyncComplete((null != syncResponse) ? syncResponse.nextBatch : null);
} else {
if (!isCatchingUp) {
startCrypto(mIsStartingCryptoWithInitialSync);
}
try {
onLiveEventsChunkProcessed(fromToken, (null != syncResponse) ? syncResponse.nextBatch : fromToken);
} catch (Exception e) {
Log.e(LOG_TAG, "onLiveEventsChunkProcessed failed " + e.getMessage());
}
try {
// check if an incoming call has been received
mCallsManager.checkPendingIncomingCalls();
} catch (Exception e) {
Log.e(LOG_TAG, "checkPendingIncomingCalls failed " + e + " " + e.getMessage());
}
}
}
Aggregations