Merge pull request #292 from vector-im/feature/sonar_fix

Feature sonar fix and convert remaining Java files to Kotlin
This commit is contained in:
Benoit Marty 2019-07-03 18:03:23 +02:00 committed by GitHub
commit 60d46538de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 354 additions and 420 deletions

View File

@ -42,6 +42,10 @@ task clean(type: Delete) {

apply plugin: 'org.sonarqube'

// To run a sonar analysis:
// Run './gradlew sonarqube -Dsonar.login=<REPLACE_WITH_SONAR_KEY>'
// The SONAR_KEY is stored in passbolt

sonarqube {
properties {
property "sonar.projectName", "RiotX-Android"
@ -67,3 +71,23 @@ project(":vector") {
}
}
}

//project(":matrix-sdk-android") {
// sonarqube {
// properties {
// property "sonar.sources", project(":matrix-sdk-android").android.sourceSets.main.java.srcDirs
// // exclude source code from analyses separated by a colon (:)
// // property "sonar.exclusions", "**/*.*"
// }
// }
//}
//
//project(":matrix-sdk-android-rx") {
// sonarqube {
// properties {
// property "sonar.sources", project(":matrix-sdk-android-rx").android.sourceSets.main.java.srcDirs
// // exclude source code from analyses separated by a colon (:)
// // property "sonar.exclusions", "**/*.*"
// }
// }
//}

View File

@ -19,25 +19,18 @@ package im.vector.matrix.android.auth
import androidx.test.annotation.UiThreadTest
import androidx.test.rule.GrantPermissionRule
import androidx.test.runner.AndroidJUnit4
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.OkReplayRuleChainNoActivity
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.internal.auth.AuthModule
import im.vector.matrix.android.internal.di.MatrixModule
import im.vector.matrix.android.internal.di.NetworkModule
import okreplay.*
import org.junit.ClassRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.standalone.StandAloneContext.loadKoinModules
import org.koin.standalone.inject
import org.koin.test.KoinTest


@RunWith(AndroidJUnit4::class)
internal class AuthenticatorTest : InstrumentedTest, KoinTest {
internal class AuthenticatorTest : InstrumentedTest {

lateinit var authenticator: Authenticator
lateinit var okReplayInterceptor: OkReplayInterceptor

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

@ -24,7 +24,6 @@ import im.vector.matrix.android.internal.crypto.model.MXKey
import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import im.vector.matrix.android.internal.session.SessionScope
import timber.log.Timber
import java.util.*
import javax.inject.Inject
@ -54,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)
}
}

@ -68,7 +67,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
val oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE

for (device in devicesWithoutSession) {
usersDevicesToClaim.setObject(oneTimeKeyAlgorithm, device.userId, device.deviceId)
usersDevicesToClaim.setObject(device.userId, device.deviceId, oneTimeKeyAlgorithm)
}

// TODO: this has a race condition - if we try to send another message
@ -91,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
}
@ -126,11 +125,13 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
var isVerified = false
var errorMessage: String? = null

try {
olmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature)
isVerified = true
} catch (e: Exception) {
errorMessage = e.message
if (signature != null) {
try {
olmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature)
isVerified = true
} catch (e: Exception) {
errorMessage = e.message
}
}

// Check one-time key signature

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,139 +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 timber.log.Timber;

public class MXKey implements Serializable {
/**
* Key types.
*/
public static final String KEY_CURVE_25519_TYPE = "curve25519";
public static final String KEY_SIGNED_CURVE_25519_TYPE = "signed_curve25519";
//public static final String KEY_ED_25519_TYPE = "ed25519";

/**
* The type of the key.
*/
public String type;

/**
* The id of the key.
*/
public String keyId;

/**
* The key.
*/
public String value;

/**
* signature user Id to [deviceid][signature]
*/
public Map<String, Map<String, String>> signatures;

/**
* Default constructor
*/
public MXKey() {
}

/**
* Convert a map to a MXKey
*
* @param map the map to convert
*/
public MXKey(Map<String, Map<String, Object>> map) {
if ((null != map) && (map.size() > 0)) {
List<String> mapKeys = new ArrayList<>(map.keySet());

String firstEntry = mapKeys.get(0);
setKeyFullId(firstEntry);

Map<String, Object> params = map.get(firstEntry);
value = (String) params.get("key");
signatures = (Map<String, Map<String, String>>) params.get("signatures");
}
}

/**
* @return the key full id
*/
public String getKeyFullId() {
return type + ":" + keyId;
}

/**
* Update the key fields with a key full id
*
* @param keyFullId the key full id
*/
private void setKeyFullId(String keyFullId) {
if (!TextUtils.isEmpty(keyFullId)) {
try {
String[] components = keyFullId.split(":");

if (components.length == 2) {
type = components[0];
keyId = components[1];
}
} catch (Exception e) {
Timber.e(e, "## setKeyFullId() failed");
}
}
}

/**
* @return the signed data map
*/
public Map<String, Object> signalableJSONDictionary() {
Map<String, Object> map = new HashMap<>();

if (null != value) {
map.put("key", value);
}

return map;
}

/**
* Returns a signature for an user Id and a signkey
*
* @param userId the user id
* @param signkey the sign key
* @return the signature
*/
public String signatureForUserId(String userId, String signkey) {
// sanity checks
if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(signkey)) {
if ((null != signatures) && signatures.containsKey(userId)) {
return signatures.get(userId).get(signkey);
}
}

return null;
}
}

View File

@ -0,0 +1,129 @@
/*
* 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 im.vector.matrix.android.api.util.JsonDict
import timber.log.Timber
import java.util.*

data class MXKey(
/**
* The type of the key (in the example: "signed_curve25519").
*/
val type: String,

/**
* The id of the key (in the example: "AAAAFw").
*/
private val keyId: String,

/**
* The key (in the example: "IjwIcskng7YjYcn0tS8TUOT2OHHtBSfMpcfIczCgXj4").
*/
val value: String,

/**
* signature user Id to [deviceid][signature]
*/
private val signatures: Map<String, Map<String, String>>
) {

/**
* @return the signed data map
*/
fun signalableJSONDictionary(): Map<String, Any> {
val map = HashMap<String, Any>()

map["key"] = value

return map
}

/**
* Returns a signature for an user Id and a signkey
*
* @param userId the user id
* @param signkey the sign key
* @return the signature
*/
fun signatureForUserId(userId: String, signkey: String): String? {
// sanity checks
if (userId.isNotBlank() && signkey.isNotBlank()) {
if (signatures.containsKey(userId)) {
return signatures[userId]?.get(signkey)
}
}

return null
}


companion object {
/**
* Key types.
*/
const val KEY_CURVE_25519_TYPE = "curve25519"
const val KEY_SIGNED_CURVE_25519_TYPE = "signed_curve25519"
// const val KEY_ED_25519_TYPE = "ed25519"

/**
* Convert a map to a MXKey
*
* @param map the map to convert
*
* Json Example:
*
* <pre>
* "signed_curve25519:AAAAFw": {
* "key": "IjwIcskng7YjYcn0tS8TUOT2OHHtBSfMpcfIczCgXj4",
* "signatures": {
* "@userId:matrix.org": {
* "ed25519:GMJRREOASV": "EUjp6pXzK9u3SDFR\/qLbzpOi3bEREeI6qMnKzXu992HsfuDDZftfJfiUXv9b\/Hqq1og4qM\/vCQJGTHAWMmgkCg"
* }
* }
* }
* </pre>
*
* into several val members
*/
fun from(map: Map<String, JsonDict>?): MXKey? {
if (map?.isNotEmpty() == true) {
val firstKey = map.keys.first()

val components = firstKey.split(":").dropLastWhile { it.isEmpty() }

if (components.size == 2) {
val params = map[firstKey]
if (params != null) {
if (params["key"] is String) {
return MXKey(
type = components[0],
keyId = components[1],
value = params["key"] as String,
signatures = params["signatures"] as Map<String, Map<String, String>>
)
}
}
}
}

// Error case
Timber.e("## Unable to parse map")
return null
}
}
}

View File

@ -1,190 +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() {
if (null != mMap) {
return "MXUsersDevicesMap " + mMap.toString();
} else {
return "MXDeviceInfo : null map";
}
}
}

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

@ -20,11 +20,14 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* This class represents the response to /keys/query request made by claimOneTimeKeysForUsersDevices.
* This class represents the response to /keys/claim request made by claimOneTimeKeysForUsersDevices.
*/
@JsonClass(generateAdapter = true)
data class KeysClaimBody(

/**
* The time (in milliseconds) to wait when downloading keys from remote servers. 10 seconds is the recommended default.
*/
@Json(name = "timeout")
var timeout: Int? = null,


View File

@ -20,7 +20,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* This class represents the response to /keys/query request made by claimOneTimeKeysForUsersDevices.
* This class represents the response to /keys/claim request made by claimOneTimeKeysForUsersDevices.
*/
@JsonClass(generateAdapter = true)
data class KeysClaimResponse(

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

@ -16,9 +16,9 @@

package im.vector.matrix.android.internal.crypto.store.db.model

import io.realm.RealmObject
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import io.realm.RealmObject

internal open class IncomingRoomKeyRequestEntity(
var requestId: String? = null,
@ -32,11 +32,11 @@ internal open class IncomingRoomKeyRequestEntity(
) : RealmObject() {

fun toIncomingRoomKeyRequest(): IncomingRoomKeyRequest {
return IncomingRoomKeyRequest().apply {
requestId = requestId
userId = userId
deviceId = deviceId
requestBody = RoomKeyRequestBody().apply {
return IncomingRoomKeyRequest().also {
it.requestId = requestId
it.userId = userId
it.deviceId = deviceId
it.requestBody = RoomKeyRequestBody().apply {
algorithm = requestBodyAlgorithm
roomId = requestBodyRoomId
senderKey = requestBodySenderKey

View File

@ -23,10 +23,8 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.KeysClaimBody
import im.vector.matrix.android.internal.crypto.model.rest.KeysClaimResponse
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.SessionScope
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>> {
@ -46,30 +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) {
try {
keysMap[deviceId] = MXKey(mapByUserId[deviceId])
} catch (e: Exception) {
Timber.e(e, "## claimOneTimeKeysForUsersDevices : fail to create a MXKey ")
val mxKey = MXKey.from(mapByUserId[deviceId])

if (mxKey != null) {
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

@ -103,7 +103,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")

View File

@ -63,6 +63,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
else -> {

//If the event can be displayed, display it as is
Timber.w("NotifiableEventResolver Received an unsupported event matching a bing rule")
//TODO Better event text display
val bodyPreview = event.type

@ -75,11 +76,6 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
title = stringProvider.getString(R.string.notification_unknown_new_event),
soundName = null,
type = event.type)


//Unsupported event
Timber.w("NotifiableEventResolver Received an unsupported event matching a bing rule")
return null
}
}
}
@ -96,8 +92,8 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
// Ok room is not known in store, but we can still display something
val body =
event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body
?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event)
?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event)
val roomName = stringProvider.getString(R.string.notification_unknown_room_name)
val senderDisplayName = event.senderName ?: event.root.senderId

@ -115,8 +111,8 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
return notifiableEvent
} else {
val body = event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body
?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event)
?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event)
val roomName = room.roomSummary()?.displayName ?: ""
val senderDisplayName = event.senderName ?: event.root.senderId

@ -138,15 +134,15 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
// TODO They will be not displayed the first time (known limitation)
notifiableEvent.roomAvatarPath = session.contentUrlResolver()
.resolveThumbnail(room.roomSummary()?.avatarUrl,
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE)
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE)

notifiableEvent.senderAvatarPath = session.contentUrlResolver()
.resolveThumbnail(event.senderAvatar,
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE)
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE)

return notifiableEvent
}
@ -159,7 +155,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
val dName = event.senderId?.let { session.getUser(it)?.displayName }
if (Membership.INVITE == content.membership) {
val body = noticeEventFormatter.format(event, dName)
?: stringProvider.getString(R.string.notification_new_invitation)
?: stringProvider.getString(R.string.notification_new_invitation)
return InviteNotifiableEvent(
session.sessionParams.credentials.userId,
eventId = event.eventId!!,