From d10687896a75ed92b08f590c4ef49fd618af0c1d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2020 22:45:53 +0200 Subject: [PATCH 01/10] Version++ --- CHANGES.rst | 27 +++++++++++++++++++++++++++ matrix-sdk/build.gradle | 4 ++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 27cd14680..71bb14121 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,30 @@ +Changes to Matrix Android SDK in 0.9.36 (2020-XX-XX) +======================================================= + +Features: + - + +Improvements: + - + +Bugfix: + - + +API Change: + - + +Translations: + - + +Others: + - + +Build: + - + +Test: + - + Changes to Matrix Android SDK in 0.9.35 (2020-05-20) ======================================================= diff --git a/matrix-sdk/build.gradle b/matrix-sdk/build.gradle index 2bd507e1e..4b2f67ad2 100644 --- a/matrix-sdk/build.gradle +++ b/matrix-sdk/build.gradle @@ -27,8 +27,8 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 28 - versionCode 935 - versionName "0.9.35" + versionCode 936 + versionName "0.9.36-dev" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // Enable multi dex for test From 780ea6f5a00c0a1ba5da9f64bd724c8f6be35514 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 29 May 2020 16:31:37 +0200 Subject: [PATCH 02/10] Crypto: Only create one olm session at a time per device (vector-im/riot-android#3056). WIP --- CHANGES.rst | 2 +- .../crypto/data/MXOlmSessionResult.java | 8 +- .../crypto/internal/MXCryptoImpl.java | 98 +++++---------- .../otk/OneTimeKeysResponseHandler.kt | 119 ++++++++++++++++++ .../matrix/androidsdk/crypto/CryptoTest.java | 5 +- 5 files changed, 163 insertions(+), 69 deletions(-) create mode 100644 matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt diff --git a/CHANGES.rst b/CHANGES.rst index 71bb14121..793645a5d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ Features: - Improvements: - - + - Crypto: Only create one olm session at a time per device (vector-im/riot-android#3056). Bugfix: - diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSessionResult.java b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSessionResult.java index 77dbfd892..a390b6fd6 100755 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSessionResult.java +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSessionResult.java @@ -16,9 +16,7 @@ package org.matrix.androidsdk.crypto.data; -import java.io.Serializable; - -public class MXOlmSessionResult implements Serializable { +public class MXOlmSessionResult { /** * the device */ @@ -30,6 +28,9 @@ public class MXOlmSessionResult implements Serializable { */ public String mSessionId; + // True if mSessionId has been retrieved, (even if mSessionId is null) + public Boolean hasResult; + /** * Constructor * @@ -39,5 +40,6 @@ public class MXOlmSessionResult implements Serializable { public MXOlmSessionResult(MXDeviceInfo device, String sessionId) { mDevice = device; mSessionId = sessionId; + hasResult = sessionId != null && !sessionId.isEmpty(); } } \ No newline at end of file diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java index 989accf00..27cff252f 100755 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java @@ -73,6 +73,7 @@ import org.matrix.androidsdk.crypto.interfaces.CryptoRoomState; import org.matrix.androidsdk.crypto.interfaces.CryptoSession; import org.matrix.androidsdk.crypto.interfaces.CryptoSyncResponse; +import org.matrix.androidsdk.crypto.internal.otk.OneTimeKeysResponseHandler; import org.matrix.androidsdk.crypto.keysbackup.KeysBackup; import org.matrix.androidsdk.crypto.model.crypto.KeysUploadResponse; import org.matrix.androidsdk.crypto.model.crypto.RoomKeyContent; @@ -138,6 +139,8 @@ public class MXCryptoImpl implements MXCrypto { private Map> mLastPublishedOneTimeKeys; + private OneTimeKeysResponseHandler oneTimeKeysResponseHandler = new OneTimeKeysResponseHandler(this); + // the encryption is starting private boolean mIsStarting; @@ -1107,6 +1110,7 @@ public void ensureOlmSessionsForDevices(final Map> de } if (devicesWithoutSession.size() == 0) { + Log.d(LOG_TAG, "[MXCrypto] ensureOlmSessionsForDevices: Have already sessions for all"); if (null != callback) { getUIHandler().post(new Runnable() { @Override @@ -1118,84 +1122,44 @@ public void run() { return; } - // Prepare the request for claiming one-time keys + // Devices for which we will make a /claim request MXUsersDevicesMap usersDevicesToClaim = new MXUsersDevicesMap<>(); - final String oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE; + Set deviceIdentityKeysWithOlmSessionsInProgress = oneTimeKeysResponseHandler.getDeviceIdentityKeysWithOlmSessionsInProgress(); + // Prepare the request for claiming one-time keys for (MXDeviceInfo device : devicesWithoutSession) { - usersDevicesToClaim.setObject(oneTimeKeyAlgorithm, device.userId, device.deviceId); + 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); + } } - // TODO: this has a race condition - if we try to send another message - // while we are claiming a key, we will end up claiming two and setting up - // two sessions. - // - // That should eventually resolve itself, but it's poor form. + 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>() { @Override public void onSuccess(final MXUsersDevicesMap oneTimeKeys) { getEncryptingThreadHandler().post(new Runnable() { @Override public void run() { - try { - Log.d(LOG_TAG, "## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: " + oneTimeKeys); - - Set userIds = devicesByUser.keySet(); - - for (String userId : userIds) { - List deviceInfos = devicesByUser.get(userId); - - for (MXDeviceInfo deviceInfo : deviceInfos) { - - MXKey oneTimeKey = null; - - List deviceIds = oneTimeKeys.getUserDeviceIds(userId); - - if (null != deviceIds) { - for (String deviceId : deviceIds) { - MXOlmSessionResult olmSessionResult = results.getObject(deviceId, userId); - - if (null != olmSessionResult.mSessionId) { - // We already have a result for this device - continue; - } - - MXKey key = oneTimeKeys.getObject(deviceId, userId); - - if (TextUtils.equals(key.type, oneTimeKeyAlgorithm)) { - oneTimeKey = key; - } - - if (null == oneTimeKey) { - Log.d(LOG_TAG, "## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm - + " for device " + userId + " : " + deviceId); - continue; - } - - // Update the result for this device in results - olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo); - } - } - } - } - } catch (Exception e) { - Log.e(LOG_TAG, "## ensureOlmSessionsForDevices() " + e.getMessage(), e); - } - - if (!hasBeenReleased()) { - if (null != callback) { - getUIHandler().post(new Runnable() { - @Override - public void run() { - callback.onSuccess(results); - } - }); - } - } + oneTimeKeysResponseHandler.onOtkRetrieved(oneTimeKeys); } }); } @@ -1204,6 +1168,8 @@ public void run() { public void onNetworkError(Exception e) { Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage(), e); + oneTimeKeysResponseHandler.removePendingRequest(pendingRequest); + if (null != callback) { callback.onNetworkError(e); } @@ -1213,6 +1179,8 @@ public void onNetworkError(Exception e) { public void onMatrixError(MatrixError e) { Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage()); + oneTimeKeysResponseHandler.removePendingRequest(pendingRequest); + if (null != callback) { callback.onMatrixError(e); } @@ -1222,6 +1190,8 @@ public void onMatrixError(MatrixError e) { public void onUnexpectedError(Exception e) { Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage(), e); + oneTimeKeysResponseHandler.removePendingRequest(pendingRequest); + if (null != callback) { callback.onUnexpectedError(e); } @@ -1229,7 +1199,7 @@ public void onUnexpectedError(Exception e) { }); } - private String verifyKeyAndStartSession(MXKey oneTimeKey, String userId, MXDeviceInfo deviceInfo) { + public String verifyKeyAndStartSession(MXKey oneTimeKey, String userId, MXDeviceInfo deviceInfo) { String sessionId = null; String deviceId = deviceInfo.deviceId; diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt new file mode 100644 index 000000000..1eda85fdf --- /dev/null +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.androidsdk.crypto.internal.otk + +import org.matrix.androidsdk.core.Log +import org.matrix.androidsdk.core.callback.ApiCallback +import org.matrix.androidsdk.crypto.data.MXDeviceInfo +import org.matrix.androidsdk.crypto.data.MXKey +import org.matrix.androidsdk.crypto.data.MXOlmSessionResult +import org.matrix.androidsdk.crypto.data.MXUsersDevicesMap +import org.matrix.androidsdk.crypto.internal.MXCryptoImpl + +/** + * This class keep the several requests to get OneTimeKeys. + * Several requests may ask for the same deviceId, so this class spread the answer to all the caller + */ +class OneTimeKeysResponseHandler( + private val mxCryptoImpl: MXCryptoImpl +) { + companion object { + private const val LOG_TAG = "OneTimeKeysResponseHandler" + } + + data class PendingRequest( + val devicesByUser: Map>, + /** + * The callback which will be called when the result are full + */ + val callback: ApiCallback>, + val aggregatedResult: MXUsersDevicesMap + ) + + private val pendingRequests = mutableListOf() + + fun addPendingRequest(pendingRequest: PendingRequest) { + pendingRequests.add(pendingRequest) + } + + fun removePendingRequest(pendingRequest: PendingRequest) { + pendingRequests.remove(pendingRequest) + } + + /** + * Return the list of device identity keys we are establishing an olm session with. + */ + fun getDeviceIdentityKeysWithOlmSessionsInProgress(): Set { + return pendingRequests.toList() + .map { it.devicesByUser } + .flatMap { it.values } + .flatten() + .map { it.identityKey() } + .toSet() + } + + fun onOtkRetrieved(oneTimeKeys: MXUsersDevicesMap) { + Log.d(LOG_TAG, "## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys") + + val cacheOfSessionId = mutableMapOf() + + // Spread the result to all pending requests + pendingRequests.toList().forEach { pendingRequest -> + pendingRequest.aggregatedResult.map.keys.forEach { userId -> + pendingRequest.aggregatedResult.map[userId]!!.keys.forEach { deviceId -> + val result = pendingRequest.aggregatedResult.getObject(deviceId, userId) + + if (result.mSessionId != null) { + // We already have a result for this device + Unit + } else { + // Check in cache + val sessionId = cacheOfSessionId.getOrPut(result.mDevice) { + val key = oneTimeKeys.getObject(deviceId, userId) + .takeIf { it.type == MXKey.KEY_SIGNED_CURVE_25519_TYPE } + + if (key == null) { + Log.d(LOG_TAG, "## ensureOlmSessionsForDevices() : No one-time keys " + MXKey.KEY_SIGNED_CURVE_25519_TYPE + + " for device " + userId + " : " + deviceId) + null + } else { + mxCryptoImpl.verifyKeyAndStartSession(key, userId, result.mDevice) + } + } + + result.mSessionId = sessionId + result.hasResult = true + } + } + } + } + + // Ok, call the callback if all sessions are established + if (!mxCryptoImpl.hasBeenReleased()) { + pendingRequests.toList().forEach { pendingRequest -> + if (pendingRequest.aggregatedResult.map.all { a -> a.value.all { b -> b.value.hasResult } }) { + mxCryptoImpl.getUIHandler().post { + pendingRequest.callback.onSuccess(pendingRequest.aggregatedResult) + + // Also remove the pending request + pendingRequests.remove(pendingRequest) + } + } + } + } + } +} \ No newline at end of file diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoTest.java index 96afdeb5d..75ec1c8a1 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoTest.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoTest.java @@ -21,9 +21,10 @@ import android.content.Context; import android.os.SystemClock; +import android.text.TextUtils; + import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import android.text.TextUtils; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; @@ -471,6 +472,7 @@ public void onSuccess(MXUsersDevicesMap info) { Assert.assertNotNull(sessionWithAliceDevice); Assert.assertNotNull(sessionWithAliceDevice.mSessionId); + Assert.assertTrue(sessionWithAliceDevice.hasResult); Assert.assertEquals("AliceDevice", sessionWithAliceDevice.mDevice.deviceId); MXSession bobSession2 = mTestHelper.createNewSession(bobSession, mCryptoTestHelper.getDefaultSessionParams()); @@ -549,6 +551,7 @@ public void onSuccess(MXUsersDevicesMap info) { MXOlmSessionResult sessionWithAliceDevice2 = result2.getObject("AliceDevice", aliceSession.getMyUserId()); Assert.assertNotNull(sessionWithAliceDevice2); Assert.assertNotNull(sessionWithAliceDevice2.mSessionId); + Assert.assertTrue(sessionWithAliceDevice2.hasResult); Assert.assertEquals("AliceDevice", sessionWithAliceDevice2.mDevice.deviceId); bobSession.clear(context); From a0d54ccf19917e83d4905e96db22dcf753d43f00 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 3 Jun 2020 18:56:04 +0200 Subject: [PATCH 03/10] Be robust to NPE --- .../crypto/internal/otk/OneTimeKeysResponseHandler.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt index 1eda85fdf..8feffcba5 100644 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt @@ -75,7 +75,7 @@ class OneTimeKeysResponseHandler( pendingRequests.toList().forEach { pendingRequest -> pendingRequest.aggregatedResult.map.keys.forEach { userId -> pendingRequest.aggregatedResult.map[userId]!!.keys.forEach { deviceId -> - val result = pendingRequest.aggregatedResult.getObject(deviceId, userId) + val result = pendingRequest.aggregatedResult.getObject(deviceId, userId)!! if (result.mSessionId != null) { // We already have a result for this device @@ -84,7 +84,7 @@ class OneTimeKeysResponseHandler( // Check in cache val sessionId = cacheOfSessionId.getOrPut(result.mDevice) { val key = oneTimeKeys.getObject(deviceId, userId) - .takeIf { it.type == MXKey.KEY_SIGNED_CURVE_25519_TYPE } + ?.takeIf { it.type == MXKey.KEY_SIGNED_CURVE_25519_TYPE } if (key == null) { Log.d(LOG_TAG, "## ensureOlmSessionsForDevices() : No one-time keys " + MXKey.KEY_SIGNED_CURVE_25519_TYPE From d3ac2f4710bffb5ece3d927c71da18aa5fcead95 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 3 Jun 2020 20:40:31 +0200 Subject: [PATCH 04/10] Also handle error cases --- .../crypto/internal/MXCryptoImpl.java | 33 +++++++------ .../otk/OneTimeKeysResponseHandler.kt | 47 +++++++++++++++++++ 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java index 27cff252f..eadde0938 100755 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java @@ -1168,33 +1168,36 @@ public void run() { public void onNetworkError(Exception e) { Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage(), e); - oneTimeKeysResponseHandler.removePendingRequest(pendingRequest); - - if (null != callback) { - callback.onNetworkError(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()); - oneTimeKeysResponseHandler.removePendingRequest(pendingRequest); - - if (null != callback) { - callback.onMatrixError(e); - } + 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); - oneTimeKeysResponseHandler.removePendingRequest(pendingRequest); - - if (null != callback) { - callback.onUnexpectedError(e); - } + getEncryptingThreadHandler().post(new Runnable() { + @Override + public void run() { + oneTimeKeysResponseHandler.onUnexpectedError(e, usersDevicesToClaim); + } + }); } }); } diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt index 8feffcba5..33eaa6f7a 100644 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt @@ -18,6 +18,7 @@ package org.matrix.androidsdk.crypto.internal.otk import org.matrix.androidsdk.core.Log import org.matrix.androidsdk.core.callback.ApiCallback +import org.matrix.androidsdk.core.model.MatrixError import org.matrix.androidsdk.crypto.data.MXDeviceInfo import org.matrix.androidsdk.crypto.data.MXKey import org.matrix.androidsdk.crypto.data.MXOlmSessionResult @@ -116,4 +117,50 @@ class OneTimeKeysResponseHandler( } } } + + fun onNetworkError(e: Exception, usersDevicesToClaim: MXUsersDevicesMap) { + onError(usersDevicesToClaim) { + it.callback.onNetworkError(e) + } + } + + fun onMatrixError(e: MatrixError, usersDevicesToClaim: MXUsersDevicesMap) { + onError(usersDevicesToClaim) { + it.callback.onMatrixError(e) + } + } + + fun onUnexpectedError(e: Exception, usersDevicesToClaim: MXUsersDevicesMap) { + onError(usersDevicesToClaim) { + it.callback.onUnexpectedError(e) + } + } + + private fun onError(usersDevicesToClaim: MXUsersDevicesMap, callCallback: ((PendingRequest) -> Unit)) { + // Spread the failure to all the pending requests which are waiting for this session + val concernedPendingRequests = mutableSetOf() + + pendingRequests.toList().forEach { pendingRequest -> + pendingRequest.aggregatedResult.map.keys.forEach mainForEach@{ userId -> + pendingRequest.aggregatedResult.map[userId]!!.keys.forEach { deviceId -> + if (usersDevicesToClaim.getObject(deviceId, userId) == MXKey.KEY_SIGNED_CURVE_25519_TYPE) { + // This pending request was waiting for a key + concernedPendingRequests.add(pendingRequest) + return@mainForEach + } + } + } + } + + if (!mxCryptoImpl.hasBeenReleased()) { + concernedPendingRequests.forEach { + mxCryptoImpl.getUIHandler().post { + callCallback.invoke(it) + } + } + } + + // Also remove the pending requests + pendingRequests.removeAll(concernedPendingRequests) + } } \ No newline at end of file From 8013ae23df2761092a8e2a8bb7855e780d47cd0b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 4 Jun 2020 09:55:47 +0200 Subject: [PATCH 05/10] Move the label, thanks to Giom's review --- .../crypto/internal/otk/OneTimeKeysResponseHandler.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt index 33eaa6f7a..f383b2a33 100644 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt @@ -140,8 +140,8 @@ class OneTimeKeysResponseHandler( // Spread the failure to all the pending requests which are waiting for this session val concernedPendingRequests = mutableSetOf() - pendingRequests.toList().forEach { pendingRequest -> - pendingRequest.aggregatedResult.map.keys.forEach mainForEach@{ userId -> + pendingRequests.toList().forEach mainForEach@{ pendingRequest -> + pendingRequest.aggregatedResult.map.keys.forEach { userId -> pendingRequest.aggregatedResult.map[userId]!!.keys.forEach { deviceId -> if (usersDevicesToClaim.getObject(deviceId, userId) == MXKey.KEY_SIGNED_CURVE_25519_TYPE) { // This pending request was waiting for a key From c4509211c2e28b5ae4f2b052c8c9f2bb049fed85 Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 25 Jun 2020 11:20:05 +0200 Subject: [PATCH 06/10] Bug Fix - Killed Application misses some notifications (#552) * Bug Fix - Killed Application miss the first notification When the application is killed the first push notification is never displayed. This was due to a bug in the RoomAccountData definition. Indeed some items of this class were not Serializable. This make fail the loading of RoomAccountData from the store on new application launch -> The store is considered corrupted -> the stream token is reseted The first event which awakes the app is handled during an initial sync, so no push is applied for it... * Let TaggedEventsContent implement Serializable too even if this is not required for the moment --- CHANGES.rst | 2 +- .../androidsdk/data/RoomAccountData.java | 19 +++++++++---------- .../androidsdk/rest/model/TaggedEvents.kt | 5 +++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 793645a5d..4a760cca7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,7 +8,7 @@ Improvements: - Crypto: Only create one olm session at a time per device (vector-im/riot-android#3056). Bugfix: - - + - Killed Application misses some notifications API Change: - diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java index e9409ea66..43e5c6b0d 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java @@ -54,8 +54,8 @@ public class RoomAccountData implements java.io.Serializable { // The events the user wants to hide in this room. private Map hiddenEvents = null; - // Store the content of all the provided events by using their event type. - private Map eventContentsMap = new HashMap<>(); + // Store all the provided events by using their event type. + private Map eventsMap = new HashMap<>(); /** * Process an event that modifies room account data (like m.tag event). @@ -79,13 +79,8 @@ public void handleEvent(Event event) { hiddenEvents = taggedEventContent.getHiddenEvents(); } - // Store by default the content of all the provided events. - if (jsonObject != null) { - eventContentsMap.put(eventType, jsonObject); - } else { - // Store an empty JsonObject - eventContentsMap.put(eventType, new JsonObject()); - } + // Store by default all the provided events. + eventsMap.put(eventType, event); } /** @@ -189,7 +184,11 @@ public TaggedEventInfo hiddenEventInfo(String eventId) { */ @Nullable public JsonObject eventContent(String eventType) { - return eventContentsMap.get(eventType); + Event event = eventsMap.get(eventType); + if (event != null) { + return event.getContentAsJsonObject(); + } + return null; } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/TaggedEvents.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/TaggedEvents.kt index 6fa81b623..6404f79c9 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/TaggedEvents.kt +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/TaggedEvents.kt @@ -17,6 +17,7 @@ package org.matrix.androidsdk.rest.model import com.google.gson.annotations.SerializedName +import java.io.Serializable /** * Class used to parse the content of a m.tagged_events type event. @@ -30,7 +31,7 @@ data class TaggedEventsContent ( @JvmField @SerializedName("tags") var tags: Map> = emptyMap() -) { +) : Serializable { fun getFavouriteEvents(): Map { return tags[TAG_FAVOURITE] ?: emptyMap() } @@ -75,7 +76,7 @@ data class TaggedEventInfo(@JvmField @JvmField @SerializedName("tagged_at") var taggedAt: Long? = null -) { +) : Serializable { companion object { fun with(keywords: List?, originServerTs: Long?, taggedAt: Long?): TaggedEventInfo { return TaggedEventInfo().apply { From 8a828cc6ffcf5221ae009ea0d575994786aabf0f Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Mon, 29 Jun 2020 09:28:23 +0200 Subject: [PATCH 07/10] Fix java.lang.NullPointerException: at org.matrix.androidsdk.data.RoomAccountData.handleEvent (RoomAccountData.java:83) --- .../androidsdk/data/RoomAccountData.java | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java index 43e5c6b0d..6dcfba5af 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java @@ -64,23 +64,25 @@ public class RoomAccountData implements java.io.Serializable { */ public void handleEvent(Event event) { final String eventType = event.getType(); - final JsonObject jsonObject = event.getContentAsJsonObject(); - - if (eventType.equals(Event.EVENT_TYPE_TAGS)) { - roomTags = RoomTag.roomTagsWithTagEvent(event); - } else if (eventType.equals(Event.EVENT_TYPE_URL_PREVIEW)) { - if (jsonObject != null && jsonObject.has(AccountDataElement.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE)) { - final boolean disabled = jsonObject.get(AccountDataElement.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE).getAsBoolean(); - isURLPreviewAllowedByUser = !disabled; + if (eventType != null) { + final JsonObject jsonObject = event.getContentAsJsonObject(); + + if (eventType.equals(Event.EVENT_TYPE_TAGS)) { + roomTags = RoomTag.roomTagsWithTagEvent(event); + } else if (eventType.equals(Event.EVENT_TYPE_URL_PREVIEW)) { + if (jsonObject != null && jsonObject.has(AccountDataElement.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE)) { + final boolean disabled = jsonObject.get(AccountDataElement.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE).getAsBoolean(); + isURLPreviewAllowedByUser = !disabled; + } + } else if (eventType.equals(Event.EVENT_TYPE_TAGGED_EVENTS)) { + final TaggedEventsContent taggedEventContent = JsonUtils.toTaggedEventsContent(jsonObject); + favouriteEvents = taggedEventContent.getFavouriteEvents(); + hiddenEvents = taggedEventContent.getHiddenEvents(); } - } else if (eventType.equals(Event.EVENT_TYPE_TAGGED_EVENTS)) { - final TaggedEventsContent taggedEventContent = JsonUtils.toTaggedEventsContent(jsonObject); - favouriteEvents = taggedEventContent.getFavouriteEvents(); - hiddenEvents = taggedEventContent.getHiddenEvents(); - } - // Store by default all the provided events. - eventsMap.put(eventType, event); + // Store by default all the provided events. + eventsMap.put(eventType, event); + } } /** From 58f933d1e070e441a9f57c63aece7a42725fd68d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 19 Aug 2020 15:36:26 +0200 Subject: [PATCH 08/10] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index e68f9b502..796a7add6 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,15 @@ [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=matrix.android.sdk&metric=vulnerabilities)](https://sonarcloud.io/project/issues?id=matrix.android.sdk&resolved=false&types=VULNERABILITY) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=matrix.android.sdk&metric=bugs)](https://sonarcloud.io/project/issues?id=matrix.android.sdk&resolved=false&types=BUG) +Important Announcement +====================== + +This SDK is deprecated and the core team does not work anymore on it. + +We strongly recommends that new projects use [the new Android Matrix SDK](https://github.com/matrix-org/matrix-android-sdk2). + +We can provide best effort support for existing projects that are still using this SDK though. + matrix-android-sdk ================== The [Matrix] SDK for Android wraps the Matrix REST API calls in asynchronous Java methods and provides basic structures for storing and handling data. From 97c657d35af640b16e1cc55825d1766e34cb468d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 8 Sep 2020 17:08:19 +0200 Subject: [PATCH 09/10] Fix a crash on the crypto code (race condition?) --- CHANGES.rst | 1 + .../crypto/internal/MXCryptoImpl.java | 2 +- .../verification/VerificationManager.kt | 24 +++++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 4a760cca7..4d4c43f99 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,7 @@ Improvements: Bugfix: - Killed Application misses some notifications + - Fix a crash on the crypto code (race condition?) API Change: - diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java index eadde0938..1bb091a58 100755 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java @@ -303,7 +303,7 @@ public MXCryptoImpl(@NonNull CryptoSession matrixSession, mCryptoStore.storeUserDevices(mSession.getMyUserId(), myDevices); // Create the VerificationManager before setting the CryptoEventsListener, to avoid crash (vector-im/riot-android#3396) - mShortCodeVerificationManager = new VerificationManager(mSession); + mShortCodeVerificationManager = new VerificationManager(mSession, this); mSession.getDataHandler().setCryptoEventsListener(mEventListener); diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/verification/VerificationManager.kt b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/verification/VerificationManager.kt index d02093753..deb1a1849 100644 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/verification/VerificationManager.kt +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/verification/VerificationManager.kt @@ -22,6 +22,7 @@ import org.matrix.androidsdk.core.JsonUtility import org.matrix.androidsdk.core.Log import org.matrix.androidsdk.core.callback.ApiCallback import org.matrix.androidsdk.core.model.MatrixError +import org.matrix.androidsdk.crypto.MXCrypto import org.matrix.androidsdk.crypto.data.MXDeviceInfo import org.matrix.androidsdk.crypto.data.MXUsersDevicesMap import org.matrix.androidsdk.crypto.interfaces.CryptoEvent @@ -35,7 +36,10 @@ import kotlin.collections.HashMap * Short codes interactive verification is a more user friendly way of verifying devices * that is still maintaining a good level of security (alternative to the 43-character strings compare method). */ -class VerificationManager(val session: CryptoSession) : VerificationTransaction.Listener { +class VerificationManager ( + private val session: CryptoSession, + private val mxCrypto: MXCrypto +) : VerificationTransaction.Listener { interface VerificationManagerListener { fun transactionCreated(tx: VerificationTransaction) @@ -50,7 +54,7 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. // Event received from the sync fun onToDeviceEvent(event: CryptoEvent) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { when (event.getType()) { CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_START -> { onStartRequestReceived(event) @@ -116,7 +120,7 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. } fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { - session.requireCrypto().setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, + mxCrypto.setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, deviceID, userId, object : ApiCallback { @@ -210,27 +214,27 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. startReq: KeyVerificationStart, success: (MXUsersDevicesMap) -> Unit, error: () -> Unit) { - session.requireCrypto().getDeviceList().downloadKeys(listOf(otherUserId), true, object : ApiCallback> { + mxCrypto.getDeviceList().downloadKeys(listOf(otherUserId), true, object : ApiCallback> { override fun onUnexpectedError(e: Exception) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { error() } } override fun onNetworkError(e: Exception) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { error() } } override fun onMatrixError(e: MatrixError) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { error() } } override fun onSuccess(info: MXUsersDevicesMap) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { if (info.getUserDeviceIds(otherUserId).contains(startReq.fromDevice)) { success(info) } else { @@ -371,7 +375,7 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. if (KeyVerificationStart.VERIF_METHOD_SAS == method) { val tx = OutgoingSASVerificationRequest(txID, userId, deviceID) addTransaction(tx) - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { tx.start(session) } return txID @@ -387,7 +391,7 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. val buff = StringBuffer() buff .append(session.myUserId).append("|") - .append(session.requireCrypto().myDevice.deviceId).append("|") + .append(mxCrypto.myDevice.deviceId).append("|") .append(userId).append("|") .append(deviceID).append("|") .append(UUID.randomUUID().toString()) From d4a08c3f1bc03fa523d7465dbb1cf62488a54814 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Sep 2020 16:03:06 +0200 Subject: [PATCH 10/10] Prepare release 0.9.36 --- CHANGES.rst | 20 +------------------- matrix-sdk/build.gradle | 2 +- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 4d4c43f99..b7d87db0c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,9 +1,6 @@ -Changes to Matrix Android SDK in 0.9.36 (2020-XX-XX) +Changes to Matrix Android SDK in 0.9.36 (2020-09-14) ======================================================= -Features: - - - Improvements: - Crypto: Only create one olm session at a time per device (vector-im/riot-android#3056). @@ -11,21 +8,6 @@ Bugfix: - Killed Application misses some notifications - Fix a crash on the crypto code (race condition?) -API Change: - - - -Translations: - - - -Others: - - - -Build: - - - -Test: - - - Changes to Matrix Android SDK in 0.9.35 (2020-05-20) ======================================================= diff --git a/matrix-sdk/build.gradle b/matrix-sdk/build.gradle index 4b2f67ad2..1ac938971 100644 --- a/matrix-sdk/build.gradle +++ b/matrix-sdk/build.gradle @@ -28,7 +28,7 @@ android { minSdkVersion 16 targetSdkVersion 28 versionCode 936 - versionName "0.9.36-dev" + versionName "0.9.36" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // Enable multi dex for test