use of org.matrix.androidsdk.core.model.MatrixError in project matrix-android-sdk by matrix-org.
the class MXCryptoImpl method ensureOlmSessionsForDevices.
/**
* Try to make sure we have established olm sessions for the given devices.
* It must be called in getCryptoHandler() thread.
* The callback is called in the UI thread.
*
* @param devicesByUser a map from userid to list of devices.
* @param callback the asynchronous callback
*/
public void ensureOlmSessionsForDevices(final Map<String, List<MXDeviceInfo>> devicesByUser, final ApiCallback<MXUsersDevicesMap<MXOlmSessionResult>> callback) {
List<MXDeviceInfo> devicesWithoutSession = new ArrayList<>();
final MXUsersDevicesMap<MXOlmSessionResult> results = new MXUsersDevicesMap<>();
Set<String> userIds = devicesByUser.keySet();
for (String userId : userIds) {
List<MXDeviceInfo> deviceInfos = devicesByUser.get(userId);
for (MXDeviceInfo deviceInfo : deviceInfos) {
String deviceId = deviceInfo.deviceId;
String key = deviceInfo.identityKey();
String sessionId = mOlmDevice.getSessionId(key);
if (TextUtils.isEmpty(sessionId)) {
devicesWithoutSession.add(deviceInfo);
}
MXOlmSessionResult olmSessionResult = new MXOlmSessionResult(deviceInfo, sessionId);
results.setObject(olmSessionResult, userId, deviceId);
}
}
if (devicesWithoutSession.size() == 0) {
Log.d(LOG_TAG, "[MXCrypto] ensureOlmSessionsForDevices: Have already sessions for all");
if (null != callback) {
getUIHandler().post(new Runnable() {
@Override
public void run() {
callback.onSuccess(results);
}
});
}
return;
}
// Devices for which we will make a /claim request
MXUsersDevicesMap<String> usersDevicesToClaim = new MXUsersDevicesMap<>();
Set<String> deviceIdentityKeysWithOlmSessionsInProgress = oneTimeKeysResponseHandler.getDeviceIdentityKeysWithOlmSessionsInProgress();
// Prepare the request for claiming one-time keys
for (MXDeviceInfo device : devicesWithoutSession) {
String deviceIdentityKey = device.identityKey();
// Claim only if a request is not yet pending
if (!deviceIdentityKeysWithOlmSessionsInProgress.contains(deviceIdentityKey)) {
usersDevicesToClaim.setObject(MXKey.KEY_SIGNED_CURVE_25519_TYPE, device.userId, device.deviceId);
}
}
Log.d(LOG_TAG, "[MXCrypto] ensureOlmSessionsForDevices: " + usersDevicesToClaim.getMap().size() + " out of " + devicesWithoutSession.size() + " sessions to claim one time keys");
Log.d(LOG_TAG, "## claimOneTimeKeysForUsersDevices() : " + usersDevicesToClaim);
OneTimeKeysResponseHandler.PendingRequest pendingRequest = new OneTimeKeysResponseHandler.PendingRequest(devicesByUser, callback, results);
oneTimeKeysResponseHandler.addPendingRequest(pendingRequest);
if (usersDevicesToClaim.getMap().isEmpty()) {
return;
}
mCryptoRestClient.claimOneTimeKeysForUsersDevices(usersDevicesToClaim, new ApiCallback<MXUsersDevicesMap<MXKey>>() {
@Override
public void onSuccess(final MXUsersDevicesMap<MXKey> oneTimeKeys) {
getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
oneTimeKeysResponseHandler.onOtkRetrieved(oneTimeKeys);
}
});
}
@Override
public void onNetworkError(Exception e) {
Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage(), e);
getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
oneTimeKeysResponseHandler.onNetworkError(e, usersDevicesToClaim);
}
});
}
@Override
public void onMatrixError(MatrixError e) {
Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage());
getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
oneTimeKeysResponseHandler.onMatrixError(e, usersDevicesToClaim);
}
});
}
@Override
public void onUnexpectedError(Exception e) {
Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage(), e);
getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
oneTimeKeysResponseHandler.onUnexpectedError(e, usersDevicesToClaim);
}
});
}
});
}
use of org.matrix.androidsdk.core.model.MatrixError in project matrix-android-sdk by matrix-org.
the class MXDeviceList method doKeyDownloadForUsers.
/**
* Download the devices keys for a set of users.
* It must be called in getEncryptingThreadHandler() thread.
* The callback is called in the UI thread.
*
* @param downloadUsers the user ids list
* @param callback the asynchronous callback
*/
private void doKeyDownloadForUsers(final List<String> downloadUsers, final ApiCallback<MXUsersDevicesMap<MXDeviceInfo>> callback) {
Log.d(LOG_TAG, "## doKeyDownloadForUsers() : doKeyDownloadForUsers " + downloadUsers);
// get the user ids which did not already trigger a keys download
final List<String> filteredUsers = addDownloadKeysPromise(downloadUsers, callback);
// if there is no new keys request
if (0 == filteredUsers.size()) {
// trigger nothing
return;
}
// sanity check
if ((null == mxSession.getDataHandler()) || (null == mxSession.getDataHandler().getStore())) {
return;
}
mIsDownloadingKeys = true;
// track the race condition while sending requests
// we defines a tag for each request
// and test if the response is the latest request one
final String downloadToken = filteredUsers.hashCode() + " " + System.currentTimeMillis();
for (String userId : filteredUsers) {
mPendingDownloadKeysRequestToken.put(userId, downloadToken);
}
mxCrypto.getCryptoRestClient().downloadKeysForUsers(filteredUsers, mxSession.getDataHandler().getStore().getEventStreamToken(), new ApiCallback<KeysQueryResponse>() {
@Override
public void onSuccess(final KeysQueryResponse keysQueryResponse) {
mxCrypto.getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
Log.d(LOG_TAG, "## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size() + " users");
MXDeviceInfo myDevice = mxCrypto.getMyDevice();
IMXCryptoStore cryptoStore = mxCrypto.getCryptoStore();
List<String> userIdsList = new ArrayList<>(filteredUsers);
for (String userId : userIdsList) {
// test if the response is the latest request one
if (!TextUtils.equals(mPendingDownloadKeysRequestToken.get(userId), downloadToken)) {
Log.e(LOG_TAG, "## doKeyDownloadForUsers() : Another update in the queue for " + userId + " not marking up-to-date");
filteredUsers.remove(userId);
} else {
Map<String, MXDeviceInfo> devices = keysQueryResponse.deviceKeys.get(userId);
Log.d(LOG_TAG, "## doKeyDownloadForUsers() : Got keys for " + userId + " : " + devices);
if (null != devices) {
Map<String, MXDeviceInfo> mutableDevices = new HashMap<>(devices);
List<String> deviceIds = new ArrayList<>(mutableDevices.keySet());
for (String deviceId : deviceIds) {
// the user has been logged out
if (null == cryptoStore) {
break;
}
// Get the potential previously store device keys for this device
MXDeviceInfo previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId);
MXDeviceInfo deviceInfo = mutableDevices.get(deviceId);
// the self device must be seen as verified
if (TextUtils.equals(deviceInfo.deviceId, myDevice.deviceId) && TextUtils.equals(userId, myDevice.userId)) {
deviceInfo.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED;
}
// Validate received keys
if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) {
// New device keys are not valid. Do not store them
mutableDevices.remove(deviceId);
if (null != previouslyStoredDeviceKeys) {
// But keep old validated ones if any
mutableDevices.put(deviceId, previouslyStoredDeviceKeys);
}
} else if (null != previouslyStoredDeviceKeys) {
// The verified status is not sync'ed with hs.
// This is a client side information, valid only for this client.
// So, transfer its previous value
mutableDevices.get(deviceId).mVerified = previouslyStoredDeviceKeys.mVerified;
}
}
// Update the store
// Note that devices which aren't in the response will be removed from the stores
cryptoStore.storeUserDevices(userId, mutableDevices);
}
// the response is the latest request one
mPendingDownloadKeysRequestToken.remove(userId);
}
}
onKeysDownloadSucceed(filteredUsers, keysQueryResponse.failures);
}
});
}
private void onFailed() {
mxCrypto.getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
List<String> userIdsList = new ArrayList<>(filteredUsers);
// test if the response is the latest request one
for (String userId : userIdsList) {
if (!TextUtils.equals(mPendingDownloadKeysRequestToken.get(userId), downloadToken)) {
Log.e(LOG_TAG, "## doKeyDownloadForUsers() : Another update in the queue for " + userId + " not marking up-to-date");
filteredUsers.remove(userId);
} else {
// the response is the latest request one
mPendingDownloadKeysRequestToken.remove(userId);
}
}
onKeysDownloadFailed(filteredUsers);
}
});
}
@Override
public void onNetworkError(Exception e) {
Log.e(LOG_TAG, "##doKeyDownloadForUsers() : onNetworkError " + e.getMessage(), e);
onFailed();
if (null != callback) {
callback.onNetworkError(e);
}
}
@Override
public void onMatrixError(MatrixError e) {
Log.e(LOG_TAG, "##doKeyDownloadForUsers() : onMatrixError " + e.getMessage());
onFailed();
if (null != callback) {
callback.onMatrixError(e);
}
}
@Override
public void onUnexpectedError(Exception e) {
Log.e(LOG_TAG, "##doKeyDownloadForUsers() : onUnexpectedError " + e.getMessage(), e);
onFailed();
if (null != callback) {
callback.onUnexpectedError(e);
}
}
});
}
use of org.matrix.androidsdk.core.model.MatrixError in project matrix-android-sdk by matrix-org.
the class CryptoRestAdapterCallback method failure.
/**
* Default failure implementation that calls the right error handler
*
* @param response the retrofit response
* @param exception the retrofit exception
*/
public void failure(Response<T> response, Exception exception) {
if (null != mEventDescription) {
String error = exception != null ? exception.getMessage() : (response != null ? response.message() : "unknown");
Log.d(LOG_TAG, "## failure(): [" + mEventDescription + "]" + " with error " + error);
}
if (exception != null && exception instanceof IOException) {
try {
if (null != mApiCallback) {
try {
mApiCallback.onNetworkError(exception);
} catch (Exception e) {
Log.e(LOG_TAG, "## failure(): onNetworkError " + exception.getLocalizedMessage(), exception);
}
}
} catch (Exception e) {
// privacy
// Log.e(LOG_TAG, "Exception NetworkError " + e.getMessage() + " while managing " + error.getUrl());
Log.e(LOG_TAG, "## failure(): NetworkError " + e.getMessage(), e);
}
} else {
// Try to convert this into a Matrix error
MatrixError mxError;
try {
HttpError error = ((HttpException) exception).getHttpError();
ResponseBody errorBody = response.errorBody();
String bodyAsString = error.getErrorBody();
mxError = new Gson().fromJson(bodyAsString, MatrixError.class);
mxError.mStatus = response.code();
mxError.mReason = response.message();
mxError.mErrorBodyAsString = bodyAsString;
} catch (Exception e) {
mxError = null;
}
if (mxError != null) {
try {
if (null != mApiCallback) {
mApiCallback.onMatrixError(mxError);
}
} catch (Exception e) {
// privacy
// Log.e(LOG_TAG, "Exception MatrixError " + e.getMessage() + " while managing " + error.getUrl());
Log.e(LOG_TAG, "## failure(): MatrixError " + e.getMessage(), e);
}
} else {
try {
if (null != mApiCallback) {
mApiCallback.onUnexpectedError(exception);
}
} catch (Exception e) {
// privacy
// Log.e(LOG_TAG, "Exception UnexpectedError " + e.getMessage() + " while managing " + error.getUrl());
Log.e(LOG_TAG, "## failure(): UnexpectedError " + e.getMessage(), e);
}
}
}
}
use of org.matrix.androidsdk.core.model.MatrixError in project matrix-android-sdk by matrix-org.
the class Room method sendEvent.
// ==============================================================================================================
// Send methods
// ==============================================================================================================
/**
* Send an event content to the room.
* The event is updated with the data provided by the server
* The provided event contains the error description.
*
* @param event the message
* @param callback the callback with the created event
*/
public void sendEvent(final Event event, final ApiCallback<Void> callback) {
// wait that the room is synced before sending messages
if (!mIsReady || !isJoined()) {
mDataHandler.updateEventState(event, Event.SentState.WAITING_RETRY);
try {
callback.onNetworkError(null);
} catch (Exception e) {
Log.e(LOG_TAG, "sendEvent exception " + e.getMessage(), e);
}
return;
}
final String prevEventId = event.eventId;
final ApiCallback<CreatedEvent> localCB = new ApiCallback<CreatedEvent>() {
@Override
public void onSuccess(final CreatedEvent createdEvent) {
if (null != getStore()) {
// remove the tmp event
getStore().deleteEvent(event);
}
// replace the tmp event id by the final one
boolean isReadMarkerUpdated = TextUtils.equals(getReadMarkerEventId(), event.eventId);
// update the event with the server response
event.eventId = createdEvent.eventId;
event.originServerTs = System.currentTimeMillis();
mDataHandler.updateEventState(event, Event.SentState.SENT);
// the message echo is not yet echoed
if (null != getStore() && !getStore().doesEventExist(createdEvent.eventId, getRoomId())) {
getStore().storeLiveRoomEvent(event);
}
// send the dedicated read receipt asap
markAllAsRead(isReadMarkerUpdated, null);
if (null != getStore()) {
getStore().commit();
}
mDataHandler.onEventSent(event, prevEventId);
try {
callback.onSuccess(null);
} catch (Exception e) {
Log.e(LOG_TAG, "sendEvent exception " + e.getMessage(), e);
}
}
@Override
public void onNetworkError(Exception e) {
event.unsentException = e;
mDataHandler.updateEventState(event, Event.SentState.UNDELIVERED);
try {
callback.onNetworkError(e);
} catch (Exception anException) {
Log.e(LOG_TAG, "sendEvent exception " + anException.getMessage(), anException);
}
}
@Override
public void onMatrixError(MatrixError e) {
event.unsentMatrixError = e;
mDataHandler.updateEventState(event, Event.SentState.UNDELIVERED);
if (MatrixError.isConfigurationErrorCode(e.errcode)) {
mDataHandler.onConfigurationError(e.errcode);
} else {
try {
callback.onMatrixError(e);
} catch (Exception anException) {
Log.e(LOG_TAG, "sendEvent exception " + anException.getMessage(), anException);
}
}
}
@Override
public void onUnexpectedError(Exception e) {
event.unsentException = e;
mDataHandler.updateEventState(event, Event.SentState.UNDELIVERED);
try {
callback.onUnexpectedError(e);
} catch (Exception anException) {
Log.e(LOG_TAG, "sendEvent exception " + anException.getMessage(), anException);
}
}
};
if (isEncrypted() && (null != mDataHandler.getCrypto())) {
mDataHandler.updateEventState(event, Event.SentState.ENCRYPTING);
// Store the "m.relates_to" data and remove them from event content before encrypting the event content
final JsonElement relatesTo;
JsonObject contentAsJsonObject = event.getContentAsJsonObject();
if (contentAsJsonObject != null && contentAsJsonObject.has("m.relates_to")) {
// Get a copy of "m.relates_to" data...
relatesTo = contentAsJsonObject.get("m.relates_to");
// ... and remove "m.relates_to" data from the content before encrypting it
contentAsJsonObject.remove("m.relates_to");
} else {
relatesTo = null;
}
// Encrypt the content before sending
mDataHandler.getCrypto().encryptEventContent(contentAsJsonObject, event.getType(), this, new ApiCallback<MXEncryptEventContentResult>() {
@Override
public void onSuccess(MXEncryptEventContentResult encryptEventContentResult) {
// update the event content with the encrypted data
event.type = encryptEventContentResult.mEventType;
// Add the "m.relates_to" data to the encrypted event here
JsonObject encryptedContent = encryptEventContentResult.mEventContent.getAsJsonObject();
if (relatesTo != null) {
encryptedContent.add("m.relates_to", relatesTo);
}
event.updateContent(encryptedContent);
mDataHandler.decryptEvent(event, null);
// sending in progress
mDataHandler.updateEventState(event, Event.SentState.SENDING);
mDataHandler.getDataRetriever().getRoomsRestClient().sendEventToRoom(event.eventId, getRoomId(), encryptEventContentResult.mEventType, encryptEventContentResult.mEventContent.getAsJsonObject(), localCB);
}
@Override
public void onNetworkError(Exception e) {
event.unsentException = e;
mDataHandler.updateEventState(event, Event.SentState.UNDELIVERED);
if (null != callback) {
callback.onNetworkError(e);
}
}
@Override
public void onMatrixError(MatrixError e) {
// update the sent state if the message encryption failed because there are unknown devices.
if ((e instanceof MXCryptoError) && TextUtils.equals(((MXCryptoError) e).errcode, MXCryptoError.UNKNOWN_DEVICES_CODE)) {
event.mSentState = Event.SentState.FAILED_UNKNOWN_DEVICES;
} else {
event.mSentState = Event.SentState.UNDELIVERED;
}
event.unsentMatrixError = e;
mDataHandler.onEventSentStateUpdated(event);
if (null != callback) {
callback.onMatrixError(e);
}
}
@Override
public void onUnexpectedError(Exception e) {
event.unsentException = e;
mDataHandler.updateEventState(event, Event.SentState.UNDELIVERED);
if (null != callback) {
callback.onUnexpectedError(e);
}
}
});
} else {
mDataHandler.updateEventState(event, Event.SentState.SENDING);
if (Event.EVENT_TYPE_MESSAGE.equals(event.getType())) {
mDataHandler.getDataRetriever().getRoomsRestClient().sendMessage(event.eventId, getRoomId(), event.getContentAsJsonObject(), localCB);
} else {
mDataHandler.getDataRetriever().getRoomsRestClient().sendEventToRoom(event.eventId, getRoomId(), event.getType(), event.getContentAsJsonObject(), localCB);
}
}
}
use of org.matrix.androidsdk.core.model.MatrixError in project matrix-android-sdk by matrix-org.
the class MatrixMessagesFragment method previewRoom.
/**
* Trigger a room preview i.e trigger an initial sync before filling the message list.
*/
private void previewRoom() {
Log.d(LOG_TAG, "Make a room preview of " + mRoom.getRoomId());
if (null != mMatrixMessagesListener) {
RoomPreviewData roomPreviewData = mMatrixMessagesListener.getRoomPreviewData();
if (null != roomPreviewData) {
if (null != roomPreviewData.getRoomResponse()) {
Log.d(LOG_TAG, "A preview data is provided with sync response");
RoomResponse roomResponse = roomPreviewData.getRoomResponse();
// initialize the timeline with the initial sync response
RoomSync roomSync = new RoomSync();
roomSync.state = new RoomSyncState();
roomSync.state.events = roomResponse.state;
roomSync.timeline = new RoomSyncTimeline();
roomSync.timeline.events = roomResponse.messages.chunk;
roomSync.timeline.limited = true;
roomSync.timeline.prevBatch = roomResponse.messages.end;
mEventTimeline.handleJoinedRoomSync(roomSync, true);
Log.d(LOG_TAG, "The room preview is done -> fill the room history");
mHasPendingInitialHistory = true;
} else {
Log.d(LOG_TAG, "A preview data is provided with no sync response : assume that it is not possible to get a room preview");
if (null != getActivity()) {
if (null != mMatrixMessagesListener) {
mMatrixMessagesListener.hideInitLoading();
}
}
}
return;
}
}
mSession.getRoomsApiClient().initialSync(mRoom.getRoomId(), new ApiCallback<RoomResponse>() {
@Override
public void onSuccess(RoomResponse roomResponse) {
// initialize the timeline with the initial sync response
RoomSync roomSync = new RoomSync();
roomSync.state = new RoomSyncState();
roomSync.state.events = roomResponse.state;
roomSync.timeline = new RoomSyncTimeline();
roomSync.timeline.events = roomResponse.messages.chunk;
mEventTimeline.handleJoinedRoomSync(roomSync, true);
Log.d(LOG_TAG, "The room preview is done -> fill the room history");
requestInitialHistory();
}
private void onError(String errorMessage) {
Log.e(LOG_TAG, "The room preview of " + mRoom.getRoomId() + "failed " + errorMessage);
if (null != getActivity()) {
getActivity().finish();
}
}
@Override
public void onNetworkError(Exception e) {
onError(e.getLocalizedMessage());
}
@Override
public void onMatrixError(MatrixError e) {
onError(e.getLocalizedMessage());
}
@Override
public void onUnexpectedError(Exception e) {
onError(e.getLocalizedMessage());
}
});
}
Aggregations