Search in sources :

Example 11 with Room

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());
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) EventTimeline(org.matrix.androidsdk.data.EventTimeline) CountDownLatch(java.util.concurrent.CountDownLatch) MXEventListener(org.matrix.androidsdk.listeners.MXEventListener) Event(org.matrix.androidsdk.rest.model.Event) JsonObject(com.google.gson.JsonObject) MatrixError(org.matrix.androidsdk.rest.model.MatrixError) Room(org.matrix.androidsdk.data.Room) RoomState(org.matrix.androidsdk.data.RoomState) Test(org.junit.Test)

Example 12 with Room

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);
}
Also used : ApiCallback(org.matrix.androidsdk.rest.callback.ApiCallback) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) CountDownLatch(java.util.concurrent.CountDownLatch) MXEventListener(org.matrix.androidsdk.listeners.MXEventListener) Event(org.matrix.androidsdk.rest.model.Event) JsonObject(com.google.gson.JsonObject) MatrixError(org.matrix.androidsdk.rest.model.MatrixError) Room(org.matrix.androidsdk.data.Room) RoomState(org.matrix.androidsdk.data.RoomState)

Example 13 with Room

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");
    }
}
Also used : Event(org.matrix.androidsdk.rest.model.Event) Room(org.matrix.androidsdk.data.Room) RoomState(org.matrix.androidsdk.data.RoomState) RoomSummary(org.matrix.androidsdk.data.RoomSummary)

Example 14 with Room

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;
}
Also used : Room(org.matrix.androidsdk.data.Room)

Example 15 with 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());
        }
    }
}
Also used : IMXStore(org.matrix.androidsdk.data.store.IMXStore) InvitedRoomSync(org.matrix.androidsdk.rest.model.sync.InvitedRoomSync) MXDecryptionException(org.matrix.androidsdk.crypto.MXDecryptionException) UnrecognizedCertificateException(org.matrix.androidsdk.ssl.UnrecognizedCertificateException) InvitedGroupSync(org.matrix.androidsdk.rest.model.group.InvitedGroupSync) RoomMember(org.matrix.androidsdk.rest.model.RoomMember) Event(org.matrix.androidsdk.rest.model.Event) List(java.util.List) ArrayList(java.util.ArrayList) MatrixError(org.matrix.androidsdk.rest.model.MatrixError) Room(org.matrix.androidsdk.data.Room)

Aggregations

Room (org.matrix.androidsdk.data.Room)60 MatrixError (org.matrix.androidsdk.rest.model.MatrixError)33 Event (org.matrix.androidsdk.rest.model.Event)27 ArrayList (java.util.ArrayList)26 CountDownLatch (java.util.concurrent.CountDownLatch)26 HashMap (java.util.HashMap)25 JsonObject (com.google.gson.JsonObject)24 Test (org.junit.Test)22 RoomState (org.matrix.androidsdk.data.RoomState)22 MXEventListener (org.matrix.androidsdk.listeners.MXEventListener)19 Context (android.content.Context)15 RoomMember (org.matrix.androidsdk.rest.model.RoomMember)12 EventTimeline (org.matrix.androidsdk.data.EventTimeline)8 IMXStore (org.matrix.androidsdk.data.store.IMXStore)8 File (java.io.File)7 MXDeviceInfo (org.matrix.androidsdk.crypto.data.MXDeviceInfo)6 ApiCallback (org.matrix.androidsdk.rest.callback.ApiCallback)6 Credentials (org.matrix.androidsdk.rest.model.login.Credentials)6 List (java.util.List)5 Uri (android.net.Uri)4