Convert MXUsersDevicesMap to kotlin

This commit is contained in:
Benoit Marty 2019-07-03 17:34:08 +02:00
parent 0ca9a5f68b
commit f789fb275d
15 changed files with 164 additions and 233 deletions

View file

@ -1039,10 +1039,10 @@ internal class CryptoManager @Inject constructor(
for (userId in userIds) {
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
for (deviceId in deviceIds!!) {
val deviceInfo = devicesInRoom.getObject(deviceId, userId)
val deviceInfo = devicesInRoom.getObject(userId, deviceId)
if (deviceInfo!!.isUnknown) {
unknownDevices.setObject(deviceInfo, userId, deviceId)
if (deviceInfo?.isUnknown == true) {
unknownDevices.setObject(userId, deviceId, deviceInfo)
}
}
}

View file

@ -221,7 +221,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
Timber.v("Device list for $userId now up to date")
}
// And the response result
usersDevicesInfoMap.setObjects(devices, userId)
usersDevicesInfoMap.setObjects(userId, devices)
}
}
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
@ -239,7 +239,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
*/
suspend fun downloadKeys(userIds: List<String>?, forceDownload: Boolean): Try<MXUsersDevicesMap<MXDeviceInfo>> {
Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds")
// Map from userid -> deviceid -> DeviceInfo
// Map from userId -> deviceId -> MXDeviceInfo
val stored = MXUsersDevicesMap<MXDeviceInfo>()
// List of user ids we need to download keys for
@ -258,7 +258,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
val devices = cryptoStore.getUserDevices(userId)
// should always be true
if (devices != null) {
stored.setObjects(devices, userId)
stored.setObjects(userId, devices)
} else {
downloadUsers.add(userId)
}

View file

@ -286,7 +286,7 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor(
for (recipient in recipients) {
// TODO Change this two hard coded key to something better
contentMap.setObject(message, recipient["userId"], recipient["deviceId"])
contentMap.setObject(recipient["userId"], recipient["deviceId"], message)
}
sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId))

View file

@ -53,7 +53,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
}
val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId)
results.setObject(olmSessionResult, userId, deviceId)
results.setObject(userId, deviceId, olmSessionResult)
}
}
@ -90,12 +90,12 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
val deviceIds = it.getUserDeviceIds(userId)
if (null != deviceIds) {
for (deviceId in deviceIds) {
val olmSessionResult = results.getObject(deviceId, userId)
val olmSessionResult = results.getObject(userId, deviceId)
if (olmSessionResult!!.sessionId != null) {
// We already have a result for this device
continue
}
val key = it.getObject(deviceId, userId)
val key = it.getObject(userId, deviceId)
if (key?.type == oneTimeKeyAlgorithm) {
oneTimeKey = key
}

View file

@ -309,7 +309,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
.handle(devicesByUser)
.flatMap {
val body = request.requestBody
val olmSessionResult = it.getObject(deviceId, userId)
val olmSessionResult = it.getObject(userId, deviceId)
if (olmSessionResult?.sessionId == null) {
// no session with this device, probably because there
// were no one-time keys.
@ -325,7 +325,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
val sendToDeviceMap = MXUsersDevicesMap<Any>()
sendToDeviceMap.setObject(encodedPayload, userId, deviceId)
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
sendToDeviceTask.execute(sendToDeviceParams)

View file

@ -118,8 +118,8 @@ internal class MXMegolmEncryption(
for (userId in userIds) {
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
for (deviceId in deviceIds!!) {
val deviceInfo = devicesInRoom.getObject(deviceId, userId)
if (null == safeSession.sharedWithDevices.getObject(deviceId, userId)) {
val deviceInfo = devicesInRoom.getObject(userId, deviceId)
if (deviceInfo != null && null == safeSession.sharedWithDevices.getObject(userId, deviceId)) {
if (!shareMap.containsKey(userId)) {
shareMap[userId] = ArrayList()
}
@ -201,7 +201,7 @@ internal class MXMegolmEncryption(
for (userId in userIds) {
val devicesToShareWith = devicesByUser[userId]
for ((deviceID) in devicesToShareWith!!) {
val sessionResult = it.getObject(deviceID, userId)
val sessionResult = it.getObject(userId, deviceID)
if (sessionResult?.sessionId == null) {
// no session with this device, probably because there
// were no one-time keys.
@ -218,7 +218,7 @@ internal class MXMegolmEncryption(
}
Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
//noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument
contentMap.setObject(messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo)), userId, deviceID)
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo)))
haveTargets = true
}
}
@ -239,7 +239,7 @@ internal class MXMegolmEncryption(
for (userId in devicesByUser.keys) {
val devicesToShareWith = devicesByUser[userId]
for ((deviceId) in devicesToShareWith!!) {
session.sharedWithDevices.setObject(chainIndex, userId, deviceId)
session.sharedWithDevices.setObject(userId, deviceId, chainIndex)
}
}
Unit
@ -303,10 +303,10 @@ internal class MXMegolmEncryption(
for (userId in it.userIds) {
val deviceIds = it.getUserDeviceIds(userId) ?: continue
for (deviceId in deviceIds) {
val deviceInfo = it.getObject(deviceId, userId) ?: continue
val deviceInfo = it.getObject(userId, deviceId) ?: continue
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) {
// The device is not yet known by the user
unknownDevices.setObject(deviceInfo, userId, deviceId)
unknownDevices.setObject(userId, deviceId, deviceInfo)
continue
}
if (deviceInfo.isBlocked) {
@ -322,7 +322,7 @@ internal class MXMegolmEncryption(
// Don't bother sending to ourself
continue
}
devicesInRoom.setObject(deviceInfo, userId, deviceId)
devicesInRoom.setObject(userId, deviceId, deviceInfo)
}
}
if (unknownDevices.isEmpty) {

View file

@ -64,7 +64,7 @@ internal class MXOutboundSessionInfo(
val deviceIds = sharedWithDevices.getUserDeviceIds(userId)
for (deviceId in deviceIds!!) {
if (null == devicesInRoom.getObject(deviceId, userId)) {
if (null == devicesInRoom.getObject(userId, deviceId)) {
Timber.v("## sharedWithTooManyDevices() : Starting new session because we shared with $userId:$deviceId")
return true
}

View file

@ -1,186 +0,0 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2018 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 im.vector.matrix.android.internal.crypto.model;
import android.text.TextUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class MXUsersDevicesMap<E> implements Serializable {
// The device keys as returned by the homeserver: a map of a map (userId -> deviceId -> Object).
private final Map<String, Map<String, E>> mMap = new HashMap<>();
/**
* @return the inner map
*/
public Map<String, Map<String, E>> getMap() {
return mMap;
}
/**
* Default constructor constructor
*/
public MXUsersDevicesMap() {
}
/**
* The constructor
*
* @param map the map
*/
public MXUsersDevicesMap(Map<String, Map<String, E>> map) {
if (null != map) {
Set<String> keys = map.keySet();
for (String key : keys) {
mMap.put(key, new HashMap<>(map.get(key)));
}
}
}
/**
* @return a deep copy
*/
public MXUsersDevicesMap<E> deepCopy() {
MXUsersDevicesMap<E> copy = new MXUsersDevicesMap<>();
Set<String> keys = mMap.keySet();
for (String key : keys) {
copy.mMap.put(key, new HashMap<>(mMap.get(key)));
}
return copy;
}
/**
* @return the user Ids
*/
public List<String> getUserIds() {
return new ArrayList<>(mMap.keySet());
}
/**
* Provides the device ids list for an user id
*
* @param userId the user id
* @return the device ids list
*/
public List<String> getUserDeviceIds(String userId) {
if (!TextUtils.isEmpty(userId) && mMap.containsKey(userId)) {
return new ArrayList<>(mMap.get(userId).keySet());
}
return null;
}
/**
* Provides the object for a device id and an user Id
*
* @param deviceId the device id
* @param userId the object id
* @return the object
*/
public E getObject(String deviceId, String userId) {
if (!TextUtils.isEmpty(userId) && mMap.containsKey(userId) && !TextUtils.isEmpty(deviceId)) {
return mMap.get(userId).get(deviceId);
}
return null;
}
/**
* Set an object for a dedicated user Id and device Id
*
* @param object the object to set
* @param userId the user Id
* @param deviceId the device id
*/
public void setObject(E object, String userId, String deviceId) {
if ((null != object) && !TextUtils.isEmpty(userId) && !TextUtils.isEmpty(deviceId)) {
Map<String, E> subMap = mMap.get(userId);
if (null == subMap) {
subMap = new HashMap<>();
mMap.put(userId, subMap);
}
subMap.put(deviceId, object);
}
}
/**
* Defines the objects map for an user Id
*
* @param objectsPerDevices the objects maps
* @param userId the user id
*/
public void setObjects(Map<String, E> objectsPerDevices, String userId) {
if (!TextUtils.isEmpty(userId)) {
if (null == objectsPerDevices) {
mMap.remove(userId);
} else {
mMap.put(userId, new HashMap<>(objectsPerDevices));
}
}
}
/**
* Removes objects for a dedicated user
*
* @param userId the user id.
*/
public void removeUserObjects(String userId) {
if (!TextUtils.isEmpty(userId)) {
mMap.remove(userId);
}
}
/**
* Clear the internal dictionary
*/
public void removeAllObjects() {
mMap.clear();
}
/**
* Add entries from another MXUsersDevicesMap
*
* @param other the other one
*/
public void addEntriesFromMap(MXUsersDevicesMap<E> other) {
if (null != other) {
mMap.putAll(other.getMap());
}
}
public boolean isEmpty() {
return mMap.isEmpty();
}
@Override
public String toString() {
return "MXUsersDevicesMap " + mMap.toString();
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright 2019 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 im.vector.matrix.android.internal.crypto.model
import java.util.*
class MXUsersDevicesMap<E> {
// A map of maps (userId -> (deviceId -> Object)).
val map = HashMap<String /* userId */, HashMap<String /* deviceId */, E>>()
/**
* @return the user Ids
*/
val userIds: List<String>
get() = ArrayList(map.keys)
val isEmpty: Boolean
get() = map.isEmpty()
/**
* Provides the device ids list for a user id
* FIXME Should maybe return emptyList and not null, to avoid many !! in the code
*
* @param userId the user id
* @return the device ids list
*/
fun getUserDeviceIds(userId: String?): List<String>? {
return if (userId?.isNotBlank() == true && map.containsKey(userId)) {
map[userId]!!.keys.toList()
} else null
}
/**
* Provides the object for a device id and a user Id
*
* @param deviceId the device id
* @param userId the object id
* @return the object
*/
fun getObject(userId: String?, deviceId: String?): E? {
return if (userId?.isNotBlank() == true && deviceId?.isNotBlank() == true && map.containsKey(userId)) {
map[userId]!![deviceId]
} else null
}
/**
* Set an object for a dedicated user Id and device Id
*
* @param userId the user Id
* @param deviceId the device id
* @param o the object to set
*/
fun setObject(userId: String?, deviceId: String?, o: E?) {
if (null != o && userId?.isNotBlank() == true && deviceId?.isNotBlank() == true) {
if (map[userId] == null) {
map[userId] = HashMap()
}
map[userId]!![deviceId] = o
}
}
/**
* Defines the objects map for a user Id
*
* @param objectsPerDevices the objects maps
* @param userId the user id
*/
fun setObjects(userId: String?, objectsPerDevices: Map<String, E>?) {
if (userId?.isNotBlank() == true) {
if (null == objectsPerDevices) {
map.remove(userId)
} else {
map[userId] = HashMap(objectsPerDevices)
}
}
}
/**
* Removes objects for a dedicated user
*
* @param userId the user id.
*/
fun removeUserObjects(userId: String?) {
if (userId?.isNotBlank() == true) {
map.remove(userId)
}
}
/**
* Clear the internal dictionary
*/
fun removeAllObjects() {
map.clear()
}
/**
* Add entries from another MXUsersDevicesMap
*
* @param other the other one
*/
fun addEntriesFromMap(other: MXUsersDevicesMap<E>?) {
if (null != other) {
map.putAll(other.map)
}
}
override fun toString(): String {
return "MXUsersDevicesMap $map"
}
}

View file

@ -18,8 +18,9 @@ package im.vector.matrix.android.internal.crypto.model.rest
class SendToDeviceBody {
// `Any` should implement SendToDeviceObject, but we cannot use interface here because of Gson serialization
/**
* `Any` should implement [SendToDeviceObject], but we cannot use interface here because of Json serialization
*
* The messages to send. A map from user ID, to a map from device ID to message body.
* The device ID may also be *, meaning all known devices for the user.
*/

View file

@ -25,7 +25,6 @@ import im.vector.matrix.android.internal.crypto.model.rest.KeysClaimResponse
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import timber.log.Timber
import java.util.*
import javax.inject.Inject
internal interface ClaimOneTimeKeysForUsersDeviceTask : Task<ClaimOneTimeKeysForUsersDeviceTask.Params, MXUsersDevicesMap<MXKey>> {
@ -45,31 +44,25 @@ internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor(private
apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body)
}.flatMap { keysClaimResponse ->
Try {
val map = HashMap<String, Map<String, MXKey>>()
val map = MXUsersDevicesMap<MXKey>()
if (null != keysClaimResponse.oneTimeKeys) {
for (userId in keysClaimResponse.oneTimeKeys!!.keys) {
val mapByUserId = keysClaimResponse.oneTimeKeys!![userId]
val keysMap = HashMap<String, MXKey>()
for (deviceId in mapByUserId!!.keys) {
val mxKey = MXKey.from(mapByUserId[deviceId])
if (mxKey != null) {
keysMap[deviceId] = mxKey
map.setObject(userId, deviceId, mxKey)
} else {
Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey")
}
}
if (keysMap.size != 0) {
map[userId] = keysMap
}
}
}
MXUsersDevicesMap(map)
map
}
}
}

View file

@ -223,7 +223,7 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
.fold(
{ error() },
{
if (it.getUserDeviceIds(otherUserId).contains(startReq.fromDevice)) {
if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) {
success(it)
} else {
error()
@ -410,7 +410,7 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
fun cancelTransaction(transactionId: String, userId: String, userDevice: String, code: CancelCode) {
val cancelMessage = KeyVerificationCancel.create(transactionId, code)
val contentMap = MXUsersDevicesMap<Any>()
contentMap.setObject(cancelMessage, userId, userDevice)
contentMap.setObject(userId, userDevice, cancelMessage)
sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId))
.dispatchTo(object : MatrixCallback<Unit> {

View file

@ -21,12 +21,11 @@ import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRe
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationKey
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationMac
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.task.TaskExecutor
@ -61,22 +60,22 @@ internal class OutgoingSASVerificationRequest(
override val uxState: OutgoingSasVerificationRequest.UxState
get() {
return when (state) {
SasVerificationTxState.None -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_START
SasVerificationTxState.None -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_START
SasVerificationTxState.SendingStart,
SasVerificationTxState.Started,
SasVerificationTxState.OnAccepted,
SasVerificationTxState.SendingKey,
SasVerificationTxState.KeySent,
SasVerificationTxState.OnKeyReceived -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT
SasVerificationTxState.OnKeyReceived -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT
SasVerificationTxState.ShortCodeReady -> OutgoingSasVerificationRequest.UxState.SHOW_SAS
SasVerificationTxState.ShortCodeAccepted,
SasVerificationTxState.SendingMac,
SasVerificationTxState.MacSent,
SasVerificationTxState.Verifying -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_VERIFICATION
SasVerificationTxState.Verified -> OutgoingSasVerificationRequest.UxState.VERIFIED
SasVerificationTxState.OnCancelled -> OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME
SasVerificationTxState.Cancelled -> OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER
else -> OutgoingSasVerificationRequest.UxState.UNKNOWN
SasVerificationTxState.Verifying -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_VERIFICATION
SasVerificationTxState.Verified -> OutgoingSasVerificationRequest.UxState.VERIFIED
SasVerificationTxState.OnCancelled -> OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME
SasVerificationTxState.Cancelled -> OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER
else -> OutgoingSasVerificationRequest.UxState.UNKNOWN
}
}
@ -102,8 +101,6 @@ internal class OutgoingSASVerificationRequest(
startMessage.shortAuthenticationStrings = KNOWN_SHORT_CODES
startReq = startMessage
val contentMap = MXUsersDevicesMap<Any>()
contentMap.setObject(startMessage, otherUserId, otherDeviceId)
state = SasVerificationTxState.SendingStart
sendToOther(

View file

@ -285,7 +285,7 @@ internal abstract class SASVerificationTransaction(
onErrorReason: CancelCode,
onDone: (() -> Unit)?) {
val contentMap = MXUsersDevicesMap<Any>()
contentMap.setObject(keyToDevice, otherUserId, otherDeviceId)
contentMap.setObject(otherUserId, otherDeviceId, keyToDevice)
sendToDeviceTask.configureWith(SendToDeviceTask.Params(type, contentMap, transactionId))
.dispatchTo(object : MatrixCallback<Unit> {

View file

@ -94,7 +94,7 @@ class KeyRequestHandler @Inject constructor(val context: Context,
//Add a notification for every incoming request
session.downloadKeys(Arrays.asList(userId), false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
val deviceInfo = data.getObject(deviceId, userId)
val deviceInfo = data.getObject(userId, deviceId)
if (null == deviceInfo) {
Timber.e("## displayKeyShareDialog() : No details found for device $userId:$deviceId")