mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 01:15:54 +03:00
Merge tag 'v1.3.12' into sc
Change-Id: I66c0120371c3f52295f033ec21702c08381b5b10 Conflicts: build.gradle vector/build.gradle vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt vector/src/main/java/im/vector/app/features/navigation/Navigator.kt vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt vector/src/main/res/drawable/highlighted_message_background.xml vector/src/main/res/layout/composer_layout_constraint_set_compact.xml vector/src/main/res/layout/composer_layout_constraint_set_expanded.xml vector/src/main/res/layout/fragment_home_detail.xml vector/src/main/res/layout/fragment_login_server_selection.xml vector/src/main/res/layout/fragment_room_detail.xml vector/src/main/res/layout/item_bottom_sheet_message_preview.xml vector/src/main/res/layout/item_expandable_textview.xml vector/src/main/res/layout/item_login_header.xml vector/src/main/res/layout/item_radio.xml vector/src/main/res/layout/item_room.xml vector/src/main/res/layout/item_timeline_event_base.xml vector/src/main/res/layout/item_timeline_event_call_tile_stub.xml vector/src/main/res/layout/item_timeline_event_code_block_stub.xml vector/src/main/res/layout/item_timeline_event_media_message_stub.xml vector/src/main/res/layout/item_timeline_event_poll_stub.xml vector/src/main/res/layout/item_timeline_event_redacted_stub.xml vector/src/main/res/layout/item_timeline_event_voice_stub.xml vector/src/main/res/layout/view_stub_room_profile_header.xml vector/src/main/res/layout/view_url_preview.xml vector/src/main/res/xml/vector_settings_labs.xml vector/src/main/res/xml/vector_settings_root.xml
This commit is contained in:
commit
acdd4e24b9
660 changed files with 8171 additions and 4005 deletions
23
.github/workflows/sync-from-external-sources.yml
vendored
23
.github/workflows/sync-from-external-sources.yml
vendored
|
@ -70,4 +70,27 @@ jobs:
|
||||||
body: |
|
body: |
|
||||||
- Update SAS Strings from matrix-doc.
|
- Update SAS Strings from matrix-doc.
|
||||||
branch: sync-sas-strings
|
branch: sync-sas-strings
|
||||||
|
base: develop
|
||||||
|
|
||||||
|
sync-analytics-plan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Skip in forks
|
||||||
|
if: github.repository == 'vector-im/element-android'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Run analytics import script
|
||||||
|
run: ./tools/import_analytic_plan.sh
|
||||||
|
- name: Create Pull Request for analytics plan
|
||||||
|
uses: peter-evans/create-pull-request@v3
|
||||||
|
with:
|
||||||
|
commit-message: Sync analytics plan
|
||||||
|
title: Sync analytics plan
|
||||||
|
body: |
|
||||||
|
### Update analytics plan
|
||||||
|
Reviewers:
|
||||||
|
- [ ] Please remove usage of Event or Enum which may have been removed or updated
|
||||||
|
- [ ] please ensure new Events or new Enums are used to send analytics by pushing new commit(s) to this PR.
|
||||||
|
|
||||||
|
*Note*: Change are coming from [this project](https://github.com/matrix-org/matrix-analytics-events)
|
||||||
|
branch: sync-analytics-plan
|
||||||
base: develop
|
base: develop
|
104
.github/workflows/triage-move-labelled.yml
vendored
104
.github/workflows/triage-move-labelled.yml
vendored
|
@ -6,7 +6,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
move_needs_info_issues:
|
move_needs_info_issues:
|
||||||
name: Move X-Needs-Info issues to Need info on triage board
|
name: X-Needs-Info issues to Need info column on triage board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338
|
- uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338
|
||||||
|
@ -17,22 +17,24 @@ jobs:
|
||||||
label-name: "X-Needs-Info"
|
label-name: "X-Needs-Info"
|
||||||
|
|
||||||
add_priority_design_issues_to_project:
|
add_priority_design_issues_to_project:
|
||||||
name: Move priority X-Needs-Design issues to Design project board
|
name: P1 X-Needs-Design to Design project board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
|
contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
|
||||||
(contains(github.event.issue.labels.*.name, 'O-Frequent') ||
|
(contains(github.event.issue.labels.*.name, 'S-Critical') &&
|
||||||
contains(github.event.issue.labels.*.name, 'O-Occasional')) &&
|
(contains(github.event.issue.labels.*.name, 'O-Frequent') ||
|
||||||
(contains(github.event.issue.labels.*.name, 'S-Critical') ||
|
contains(github.event.issue.labels.*.name, 'O-Occasional')) ||
|
||||||
contains(github.event.issue.labels.*.name, 'S-Major') ||
|
contains(github.event.issue.labels.*.name, 'S-Major') &&
|
||||||
contains(github.event.issue.labels.*.name, 'S-Minor'))
|
contains(github.event.issue.labels.*.name, 'O-Frequent') ||
|
||||||
|
contains(github.event.issue.labels.*.name, 'A11y') &&
|
||||||
|
contains(github.event.issue.labels.*.name, 'O-Frequent'))
|
||||||
steps:
|
steps:
|
||||||
- uses: octokit/graphql-action@v2.x
|
- uses: octokit/graphql-action@v2.x
|
||||||
id: add_to_project
|
id: add_to_project
|
||||||
with:
|
with:
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
query: |
|
query: |
|
||||||
mutation add_to_project($projectid:String!,$contentid:String!) {
|
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||||
projectNextItem {
|
projectNextItem {
|
||||||
id
|
id
|
||||||
|
@ -45,40 +47,33 @@ jobs:
|
||||||
PROJECT_ID: "PN_kwDOAM0swc0sUA"
|
PROJECT_ID: "PN_kwDOAM0swc0sUA"
|
||||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|
||||||
move_spaces_issues:
|
# delight_issues_to_board:
|
||||||
name: Move Spaces issues to Delight project board
|
# name: Spaces issues to new Delight project board
|
||||||
runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
if: >
|
# if: >
|
||||||
contains(github.event.issue.labels.*.name, 'A-Spaces') ||
|
# contains(github.event.issue.labels.*.name, 'A-Spaces') ||
|
||||||
contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
|
# contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
|
||||||
contains(github.event.issue.labels.*.name, 'A-Subspaces')
|
# contains(github.event.issue.labels.*.name, 'A-Subspaces')
|
||||||
steps:
|
# steps:
|
||||||
- uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338
|
# - uses: octokit/graphql-action@v2.x
|
||||||
with:
|
# with:
|
||||||
action-token: "${{ secrets.ELEMENT_BOT_TOKEN }}"
|
# headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
project-url: "https://github.com/orgs/vector-im/projects/6"
|
# query: |
|
||||||
column-name: "📥 Inbox"
|
# mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||||
label-name: "A-Spaces"
|
# addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||||
- uses: octokit/graphql-action@v2.x
|
# projectNextItem {
|
||||||
id: add_to_delight2
|
# id
|
||||||
with:
|
# }
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
# }
|
||||||
query: |
|
# }
|
||||||
mutation add_to_project($projectid:String!,$contentid:String!) {
|
# projectid: ${{ env.PROJECT_ID }}
|
||||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
# contentid: ${{ github.event.issue.node_id }}
|
||||||
projectNextItem {
|
# env:
|
||||||
id
|
# PROJECT_ID: "PN_kwDOAM0swc1HvQ"
|
||||||
}
|
# GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
}
|
|
||||||
}
|
|
||||||
projectid: ${{ env.PROJECT_ID }}
|
|
||||||
contentid: ${{ github.event.issue.node_id }}
|
|
||||||
env:
|
|
||||||
PROJECT_ID: "PN_kwDOAM0swc1HvQ"
|
|
||||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
|
||||||
|
|
||||||
move_voice-message_issues:
|
move_voice-message_issues:
|
||||||
name: Move A-Voice Messages to Voice message board
|
name: A-Voice Messages to voice message board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'A-Voice Messages')
|
contains(github.event.issue.labels.*.name, 'A-Voice Messages')
|
||||||
|
@ -87,7 +82,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
query: |
|
query: |
|
||||||
mutation add_to_project($projectid:String!,$contentid:String!) {
|
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||||
projectNextItem {
|
projectNextItem {
|
||||||
id
|
id
|
||||||
|
@ -101,7 +96,7 @@ jobs:
|
||||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|
||||||
move_threads_issues:
|
move_threads_issues:
|
||||||
name: Move A-Threads to Thread board
|
name: A-Threads to Thread board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'A-Threads')
|
contains(github.event.issue.labels.*.name, 'A-Threads')
|
||||||
|
@ -110,7 +105,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
query: |
|
query: |
|
||||||
mutation add_to_project($projectid:String!,$contentid:String!) {
|
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||||
projectNextItem {
|
projectNextItem {
|
||||||
id
|
id
|
||||||
|
@ -122,3 +117,26 @@ jobs:
|
||||||
env:
|
env:
|
||||||
PROJECT_ID: "PN_kwDOAM0swc0rRA"
|
PROJECT_ID: "PN_kwDOAM0swc0rRA"
|
||||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|
||||||
|
move_message_bubbles_issues:
|
||||||
|
name: A-Message-Bubbles to Message bubbles board
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: >
|
||||||
|
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles')
|
||||||
|
steps:
|
||||||
|
- uses: octokit/graphql-action@v2.x
|
||||||
|
with:
|
||||||
|
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
|
query: |
|
||||||
|
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||||
|
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||||
|
projectNextItem {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
projectid: ${{ env.PROJECT_ID }}
|
||||||
|
contentid: ${{ github.event.issue.node_id }}
|
||||||
|
env:
|
||||||
|
PROJECT_ID: "PN_kwDOAM0swc3m-g"
|
||||||
|
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|
2
.github/workflows/triage-priority-bugs.yml
vendored
2
.github/workflows/triage-priority-bugs.yml
vendored
|
@ -1,4 +1,4 @@
|
||||||
name: Move P1 issues into the P1 column for the App Team and Crypto team
|
name: Move P1 bugs to boards
|
||||||
|
|
||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
<w>pbkdf</w>
|
<w>pbkdf</w>
|
||||||
<w>pids</w>
|
<w>pids</w>
|
||||||
<w>pkcs</w>
|
<w>pkcs</w>
|
||||||
|
<w>posthog</w>
|
||||||
<w>previewable</w>
|
<w>previewable</w>
|
||||||
<w>previewables</w>
|
<w>previewables</w>
|
||||||
<w>pstn</w>
|
<w>pstn</w>
|
||||||
|
|
62
CHANGES.md
62
CHANGES.md
|
@ -1,3 +1,65 @@
|
||||||
|
Changes in Element v1.3.12 (2021-12-20)
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Bugfixes 🐛
|
||||||
|
----------
|
||||||
|
- Fixing emoji related crashes on android 8.1.1 and below ([#4769](https://github.com/vector-im/element-android/issues/4769))
|
||||||
|
|
||||||
|
|
||||||
|
Changes in Element v1.3.11 (2021-12-17)
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Bugfixes 🐛
|
||||||
|
----------
|
||||||
|
- Fixing proximity sensor still being active after a call ([#2467](https://github.com/vector-im/element-android/issues/2467))
|
||||||
|
- Fix name and shield are truncated in the room detail screen ([#4700](https://github.com/vector-im/element-android/issues/4700))
|
||||||
|
- Call banner: center text vertically ([#4710](https://github.com/vector-im/element-android/issues/4710))
|
||||||
|
- Fixes unable to render messages by allowing them to render whilst the emoji library is initialising ([#4733](https://github.com/vector-im/element-android/issues/4733))
|
||||||
|
- Fix app crash uppon long press on a reply event ([#4742](https://github.com/vector-im/element-android/issues/4742))
|
||||||
|
- Fixes crash when launching rooms which contain emojis in the emote content on android 12+ ([#4743](https://github.com/vector-im/element-android/issues/4743))
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
- Avoids leaking the activity windows when loading dialogs are displaying ([#4713](https://github.com/vector-im/element-android/issues/4713))
|
||||||
|
|
||||||
|
|
||||||
|
Changes in Element v1.3.10 (2021-12-14)
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Features ✨
|
||||||
|
----------
|
||||||
|
- Poll Feature - Render in timeline ([#4653](https://github.com/vector-im/element-android/issues/4653))
|
||||||
|
- Updates URL previews to match latest designs ([#4278](https://github.com/vector-im/element-android/issues/4278))
|
||||||
|
- Setup Analytics framework using PostHog. Analytics are disabled by default. Opt-in screen not automatically displayed yet. ([#4559](https://github.com/vector-im/element-android/issues/4559))
|
||||||
|
- Create a legal screen in the setting to group all the different policies. ([#4660](https://github.com/vector-im/element-android/issues/4660))
|
||||||
|
- Add a help section in the settings. ([#4638](https://github.com/vector-im/element-android/issues/4638))
|
||||||
|
- MSC2732: Olm fallback keys ([#3473](https://github.com/vector-im/element-android/issues/3473))
|
||||||
|
|
||||||
|
Bugfixes 🐛
|
||||||
|
----------
|
||||||
|
- Fixes message menu showing when copying message urls ([#4324](https://github.com/vector-im/element-android/issues/4324))
|
||||||
|
- Fix lots of integration tests by introducing TestMatrix class and MatrixWorkerFactory. ([#4546](https://github.com/vector-im/element-android/issues/4546))
|
||||||
|
- Fix empty Dev Tools screen issue. ([#4592](https://github.com/vector-im/element-android/issues/4592))
|
||||||
|
- Fix for outgoing voip call via sip bridge failing after 1 minute. ([#4621](https://github.com/vector-im/element-android/issues/4621))
|
||||||
|
- Update log warning for call selection during voip calls. ([#4636](https://github.com/vector-im/element-android/issues/4636))
|
||||||
|
- Fix possible crash when having identical subspaces in multiple root spaces ([#4693](https://github.com/vector-im/element-android/issues/4693))
|
||||||
|
- Fix a crash in the timeline with some Emojis. Also migrate to androidx.emoji2 ([#4698](https://github.com/vector-im/element-android/issues/4698))
|
||||||
|
- At the very first room search after opening the app sometimes no results are displayed ([#4600](https://github.com/vector-im/element-android/issues/4600))
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
- Upgrade OLM to v3.2.7 and get it from our maven repository. ([#4647](https://github.com/vector-im/element-android/issues/4647))
|
||||||
|
- Add explicit dependency location, regarding the several maven repository. Also update some libraries (flexbox and alerter), and do some cleanup. ([#4670](https://github.com/vector-im/element-android/issues/4670))
|
||||||
|
- Introducing feature flagging to the login and notification settings flows ([#4626](https://github.com/vector-im/element-android/issues/4626))
|
||||||
|
- There is no need to call job.cancel() when we are using viewModelScope() ([#4602](https://github.com/vector-im/element-android/issues/4602))
|
||||||
|
- Debounce some clicks ([#4645](https://github.com/vector-im/element-android/issues/4645))
|
||||||
|
- Improve issue automation workflows ([#4617](https://github.com/vector-im/element-android/issues/4617))
|
||||||
|
- Add automation to move message bubbles issues to message bubbles board. ([#4666](https://github.com/vector-im/element-android/issues/4666))
|
||||||
|
- Fix graphql warning in issue workflow automation ([#4671](https://github.com/vector-im/element-android/issues/4671))
|
||||||
|
- Cleanup the layout files ([#4604](https://github.com/vector-im/element-android/issues/4604))
|
||||||
|
- Cleanup id ref. Use type views instead ([#4650](https://github.com/vector-im/element-android/issues/4650))
|
||||||
|
|
||||||
|
|
||||||
Changes in Element v1.3.9 (2021-12-01)
|
Changes in Element v1.3.9 (2021-12-01)
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
|
|
69
build.gradle
69
build.gradle
|
@ -1,12 +1,11 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
|
||||||
apply from: 'dependencies.gradle'
|
apply from: 'dependencies.gradle'
|
||||||
|
apply from: 'dependencies_groups.gradle'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
|
||||||
maven {
|
maven {
|
||||||
url "https://plugins.gradle.org/m2/"
|
url "https://plugins.gradle.org/m2/"
|
||||||
}
|
}
|
||||||
|
@ -37,50 +36,50 @@ allprojects {
|
||||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// For olm library. This has to be declared first, to ensure that Olm library is not downloaded from another repo
|
// For olm library.
|
||||||
|
maven {
|
||||||
|
url 'https://gitlab.matrix.org/api/v4/projects/27/packages/maven'
|
||||||
|
content {
|
||||||
|
groups.olm.regex.each { includeGroupByRegex it }
|
||||||
|
groups.olm.group.each { includeGroup it }
|
||||||
|
}
|
||||||
|
}
|
||||||
maven {
|
maven {
|
||||||
url 'https://jitpack.io'
|
url 'https://jitpack.io'
|
||||||
content {
|
content {
|
||||||
// Use this repo only for olm library
|
groups.jitpack.regex.each { includeGroupByRegex it }
|
||||||
includeGroupByRegex "org\\.matrix\\.gitlab\\.matrix-org"
|
groups.jitpack.group.each { includeGroup it }
|
||||||
// And also for FilePicker
|
|
||||||
includeGroupByRegex "com\\.github\\.jaiselrahman"
|
|
||||||
// And monarchy
|
|
||||||
includeGroupByRegex "com\\.github\\.Zhuinden"
|
|
||||||
// And ucrop
|
|
||||||
includeGroupByRegex "com\\.github\\.yalantis"
|
|
||||||
// JsonViewer
|
|
||||||
includeGroupByRegex 'com\\.github\\.BillCarsonFr'
|
|
||||||
// PhotoView
|
|
||||||
includeGroupByRegex 'com\\.github\\.chrisbanes'
|
|
||||||
// PFLockScreen-Android
|
|
||||||
includeGroupByRegex 'com\\.github\\.vector-im'
|
|
||||||
// DraggableView
|
|
||||||
includeGroupByRegex 'com\\.github\\.hyuwah'
|
|
||||||
|
|
||||||
// UnifiedPush
|
|
||||||
includeGroupByRegex 'com\\.github\\.UnifiedPush'
|
|
||||||
// SchildiChat
|
|
||||||
includeGroupByRegex 'com\\.github\\.SchildiChat'
|
|
||||||
|
|
||||||
// Chat effects
|
|
||||||
includeGroupByRegex 'com\\.github\\.jetradarmobile'
|
|
||||||
includeGroupByRegex 'nl\\.dionsegijn'
|
|
||||||
|
|
||||||
// Voice RecordView
|
|
||||||
includeGroupByRegex 'com\\.github\\.Armen101'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
|
|
||||||
// Jitsi repo
|
// Jitsi repo
|
||||||
maven {
|
maven {
|
||||||
url "https://github.com/vector-im/jitsi_libre_maven/raw/main/android-sdk-3.10.0"
|
url "https://github.com/vector-im/jitsi_libre_maven/raw/main/android-sdk-3.10.0"
|
||||||
// Note: to test Jitsi release you can use a local file like this:
|
// Note: to test Jitsi release you can use a local file like this:
|
||||||
// url "file:///Users/bmarty/workspaces/jitsi_libre_maven/android-sdk-3.10.0"
|
// url "file:///Users/bmarty/workspaces/jitsi_libre_maven/android-sdk-3.10.0"
|
||||||
|
content {
|
||||||
|
groups.jitsi.regex.each { includeGroupByRegex it }
|
||||||
|
groups.jitsi.group.each { includeGroup it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
google {
|
||||||
|
content {
|
||||||
|
groups.google.regex.each { includeGroupByRegex it }
|
||||||
|
groups.google.group.each { includeGroup it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mavenCentral {
|
||||||
|
content {
|
||||||
|
groups.mavenCentral.regex.each { includeGroupByRegex it }
|
||||||
|
groups.mavenCentral.group.each { includeGroup it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//noinspection JcenterRepositoryObsolete
|
||||||
|
jcenter {
|
||||||
|
content {
|
||||||
|
groups.jcenter.regex.each { includeGroupByRegex it }
|
||||||
|
groups.jcenter.group.each { includeGroup it }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
||||||
|
|
|
@ -7,11 +7,11 @@ ext.versions = [
|
||||||
'targetCompat' : JavaVersion.VERSION_1_8,
|
'targetCompat' : JavaVersion.VERSION_1_8,
|
||||||
]
|
]
|
||||||
|
|
||||||
def gradle = "7.0.3"
|
def gradle = "7.0.4"
|
||||||
// Ref: https://kotlinlang.org/releases.html
|
// Ref: https://kotlinlang.org/releases.html
|
||||||
def kotlin = "1.5.31"
|
def kotlin = "1.5.31"
|
||||||
def kotlinCoroutines = "1.5.2"
|
def kotlinCoroutines = "1.5.2"
|
||||||
def dagger = "2.40.3"
|
def dagger = "2.40.5"
|
||||||
def retrofit = "2.9.0"
|
def retrofit = "2.9.0"
|
||||||
def arrow = "0.8.2"
|
def arrow = "0.8.2"
|
||||||
def markwon = "4.6.2"
|
def markwon = "4.6.2"
|
||||||
|
@ -19,7 +19,7 @@ def moshi = "1.12.0"
|
||||||
def lifecycle = "2.4.0"
|
def lifecycle = "2.4.0"
|
||||||
def flowBinding = "1.2.0"
|
def flowBinding = "1.2.0"
|
||||||
def epoxy = "4.6.2"
|
def epoxy = "4.6.2"
|
||||||
def mavericks = "2.4.0"
|
def mavericks = "2.5.0"
|
||||||
def glide = "4.12.0"
|
def glide = "4.12.0"
|
||||||
def bigImageViewer = "1.8.1"
|
def bigImageViewer = "1.8.1"
|
||||||
def jjwt = "0.11.2"
|
def jjwt = "0.11.2"
|
||||||
|
|
203
dependencies_groups.gradle
Normal file
203
dependencies_groups.gradle
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
ext.groups = [
|
||||||
|
jitpack : [
|
||||||
|
regex: [
|
||||||
|
],
|
||||||
|
group: [
|
||||||
|
'com.github.Armen101',
|
||||||
|
'com.github.BillCarsonFr',
|
||||||
|
'com.github.chrisbanes',
|
||||||
|
'com.github.hyuwah',
|
||||||
|
'com.github.jetradarmobile',
|
||||||
|
'com.github.SchildiChat',
|
||||||
|
'com.github.tapadoo',
|
||||||
|
'com.github.UnifiedPush',
|
||||||
|
'com.github.vector-im',
|
||||||
|
'com.github.yalantis',
|
||||||
|
'com.github.Zhuinden',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
olm : [
|
||||||
|
regex: [
|
||||||
|
],
|
||||||
|
group: [
|
||||||
|
'org.matrix.android',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
jitsi : [
|
||||||
|
regex: [
|
||||||
|
],
|
||||||
|
group: [
|
||||||
|
'com.facebook.react',
|
||||||
|
'org.jitsi.react',
|
||||||
|
'org.webkit',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
google : [
|
||||||
|
regex: [
|
||||||
|
'androidx\\..*',
|
||||||
|
'com\\.android\\.tools\\..*',
|
||||||
|
'com\\.google\\.android\\..*',
|
||||||
|
],
|
||||||
|
group: [
|
||||||
|
'com.google.firebase',
|
||||||
|
'com.android',
|
||||||
|
'com.android.tools',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
mavenCentral: [
|
||||||
|
regex: [
|
||||||
|
],
|
||||||
|
group: [
|
||||||
|
'com.adevinta.android',
|
||||||
|
'com.airbnb.android',
|
||||||
|
'com.almworks.sqlite4java',
|
||||||
|
'com.arthenica',
|
||||||
|
'com.atlassian.commonmark',
|
||||||
|
'com.atlassian.pom',
|
||||||
|
'com.beust',
|
||||||
|
'com.davemorrissey.labs',
|
||||||
|
'com.dropbox.core',
|
||||||
|
'com.facebook.fresco',
|
||||||
|
'com.facebook.infer.annotation',
|
||||||
|
'com.facebook.soloader',
|
||||||
|
'com.facebook.stetho',
|
||||||
|
'com.fasterxml',
|
||||||
|
'com.fasterxml.jackson',
|
||||||
|
'com.fasterxml.jackson.core',
|
||||||
|
'com.gabrielittner.threetenbp',
|
||||||
|
'com.getkeepsafe.relinker',
|
||||||
|
'com.github.bumptech.glide',
|
||||||
|
'com.github.filippudak',
|
||||||
|
'com.github.filippudak.progresspieview',
|
||||||
|
'com.github.javaparser',
|
||||||
|
'com.github.piasy',
|
||||||
|
'com.github.shyiko.klob',
|
||||||
|
'com.google',
|
||||||
|
'com.google.auto.service',
|
||||||
|
'com.google.auto.value',
|
||||||
|
'com.google.code.findbugs',
|
||||||
|
'com.google.code.gson',
|
||||||
|
'com.google.dagger',
|
||||||
|
'com.google.devtools.ksp',
|
||||||
|
'com.google.errorprone',
|
||||||
|
'com.google.googlejavaformat',
|
||||||
|
'com.google.guava',
|
||||||
|
'com.google.j2objc',
|
||||||
|
'com.google.jimfs',
|
||||||
|
'com.google.protobuf',
|
||||||
|
'com.google.zxing',
|
||||||
|
'com.googlecode.htmlcompressor',
|
||||||
|
'com.googlecode.json-simple',
|
||||||
|
'com.googlecode.libphonenumber',
|
||||||
|
'com.ibm.icu',
|
||||||
|
'com.jakewharton.android.repackaged',
|
||||||
|
'com.jakewharton.timber',
|
||||||
|
'com.linkedin.dexmaker',
|
||||||
|
'com.nulab-inc',
|
||||||
|
'com.otaliastudios.opengl',
|
||||||
|
'com.parse.bolts',
|
||||||
|
'com.pinterest',
|
||||||
|
'com.pinterest.ktlint',
|
||||||
|
'com.posthog.android',
|
||||||
|
'com.squareup',
|
||||||
|
'com.squareup.duktape',
|
||||||
|
'com.squareup.moshi',
|
||||||
|
'com.squareup.okhttp3',
|
||||||
|
'com.squareup.okio',
|
||||||
|
'com.squareup.retrofit2',
|
||||||
|
'com.sun.activation',
|
||||||
|
'com.sun.istack',
|
||||||
|
'com.sun.xml.bind',
|
||||||
|
'com.sun.xml.bind.mvn',
|
||||||
|
'com.sun.xml.fastinfoset',
|
||||||
|
'com.thoughtworks.qdox',
|
||||||
|
'com.vanniktech',
|
||||||
|
'commons-cli',
|
||||||
|
'commons-codec',
|
||||||
|
'commons-io',
|
||||||
|
'commons-logging',
|
||||||
|
'info.picocli',
|
||||||
|
'io.arrow-kt',
|
||||||
|
'io.github.detekt.sarif4k',
|
||||||
|
'io.github.reactivecircus.flowbinding',
|
||||||
|
'io.jsonwebtoken',
|
||||||
|
'io.kindedj',
|
||||||
|
'io.mockk',
|
||||||
|
'io.noties.markwon',
|
||||||
|
'io.reactivex.rxjava2',
|
||||||
|
'io.realm',
|
||||||
|
'it.unimi.dsi',
|
||||||
|
'jakarta.activation',
|
||||||
|
'jakarta.xml.bind',
|
||||||
|
'javax.annotation',
|
||||||
|
'javax.inject',
|
||||||
|
'jline',
|
||||||
|
'jp.wasabeef',
|
||||||
|
'junit',
|
||||||
|
'me.leolin',
|
||||||
|
'me.saket',
|
||||||
|
'net.bytebuddy',
|
||||||
|
'net.java',
|
||||||
|
'net.java.dev.jna',
|
||||||
|
'net.lachlanmckee',
|
||||||
|
'net.ltgt.gradle.incap',
|
||||||
|
'net.sf.jopt-simple',
|
||||||
|
'net.sf.kxml',
|
||||||
|
'nl.dionsegijn',
|
||||||
|
'org.amshove.kluent',
|
||||||
|
'org.apache',
|
||||||
|
'org.apache.ant',
|
||||||
|
'org.apache.commons',
|
||||||
|
'org.apache.httpcomponents',
|
||||||
|
'org.apache.sanselan',
|
||||||
|
'org.bouncycastle',
|
||||||
|
'org.checkerframework',
|
||||||
|
'org.codehaus',
|
||||||
|
'org.codehaus.groovy',
|
||||||
|
'org.codehaus.mojo',
|
||||||
|
'org.eclipse.ee4j',
|
||||||
|
'org.ec4j.core',
|
||||||
|
'org.glassfish.jaxb',
|
||||||
|
'org.hamcrest',
|
||||||
|
'org.jetbrains',
|
||||||
|
'org.jetbrains.intellij.deps',
|
||||||
|
'org.jetbrains.kotlin',
|
||||||
|
'org.jetbrains.kotlinx',
|
||||||
|
'org.jsoup',
|
||||||
|
'org.junit',
|
||||||
|
'org.junit.jupiter',
|
||||||
|
'org.junit.platform',
|
||||||
|
'org.jvnet.staxex',
|
||||||
|
'org.mockito',
|
||||||
|
'org.mongodb',
|
||||||
|
'org.objenesis',
|
||||||
|
'org.opentest4j',
|
||||||
|
'org.ow2',
|
||||||
|
'org.ow2.asm',
|
||||||
|
'org.ow2.asm',
|
||||||
|
'org.reactivestreams',
|
||||||
|
'org.robolectric',
|
||||||
|
'org.slf4j',
|
||||||
|
'org.sonatype.oss',
|
||||||
|
'org.testng',
|
||||||
|
'org.threeten',
|
||||||
|
'xerces',
|
||||||
|
'xml-apis',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
jcenter : [
|
||||||
|
regex: [
|
||||||
|
],
|
||||||
|
group: [
|
||||||
|
'com.amulyakhare',
|
||||||
|
'com.otaliastudios',
|
||||||
|
'com.yqritc',
|
||||||
|
// https://github.com/cmelchior/realmfieldnameshelper/issues/42
|
||||||
|
'dk.ilios',
|
||||||
|
'im.dlg',
|
||||||
|
'me.dm7.barcodescanner',
|
||||||
|
'me.gujun.android',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
16
docs/analytics.md
Normal file
16
docs/analytics.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Analytics in Element
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Element is using PostHog to send analytics event.
|
||||||
|
We ask for the user to give consent before sending any analytics data.
|
||||||
|
|
||||||
|
## How to add a new Event
|
||||||
|
|
||||||
|
The analytics plan is shared between all Element clients. To add an Event, please open a PR to this project: https://github.com/matrix-org/matrix-analytics-events
|
||||||
|
|
||||||
|
Then, once the PR has been merged, you can run the tool `import_analytic_plan.sh` to import the plan to Element, and then you can use the new Event. Note that this tool is run by Github action once a week.
|
||||||
|
|
||||||
|
## Forks of Element
|
||||||
|
|
||||||
|
Analytics on forks are disabled by default. Please refer to AnalyticsConfig and there implementation to setup analytics on your project.
|
|
@ -1,2 +1,2 @@
|
||||||
Hlavní změny v této verzi: Opravy chyb týkající se především oznámení.
|
Hlavní změny v této verzi: Opravy chyb týkající se především oznámení.
|
||||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/cs-CZ/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Hlavní změny v této verzi: Opravy chyb!
|
||||||
|
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
Hauptänderungen: Fehler bei Benachrichtigungen gefixt
|
Hauptänderungen: Fehler bei Benachrichtigungen gefixt
|
||||||
Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/de-DE/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/de-DE/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Änderungen: Verschiedene Fehler behoben
|
||||||
|
Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
2
fastlane/metadata/android/en-US/changelogs/40103100.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40103100.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: Add support for polls (in labs). New URL preview design.
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.10
|
2
fastlane/metadata/android/en-US/changelogs/40103110.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40103110.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: Bug fixes!
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.11
|
2
fastlane/metadata/android/en-US/changelogs/40103120.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40103120.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: Bug fixes!
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.12
|
|
@ -1,2 +1,2 @@
|
||||||
Põhilised muutused selles versioonis: erinevad veaparandused, neist enamus on seotud teavitustega.
|
Põhilised muutused selles versioonis: erinevad veaparandused, neist enamus on seotud teavitustega.
|
||||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/et/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Põhilised muutused selles versioonis: pinu veaparandusi!
|
||||||
|
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
تغییرات اصلی در این نگارش: رفع اشکالهایی عمدتاً مربوط به آگاهیها.
|
تغییرات اصلی در این نگارش: رفع اشکالهایی عمدتاً مربوط به آگاهیها.
|
||||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/fa/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/fa/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
تغییرات عمده در این نگارش: رفع مشکلها!
|
||||||
|
گزارش دگرکونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
Principaux changements pour cette version : corrections de problèmes, principalement sur les notifications
|
Principaux changements pour cette version : corrections de problèmes, principalement sur les notifications
|
||||||
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/fr-FR/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Principaux changements pour cette version : corrections de bugs !
|
||||||
|
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
Fő változás ebben a verzióban: Értesítési hibajavítások
|
Fő változás ebben a verzióban: Értesítési hibajavítások
|
||||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/hu-HU/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/hu-HU/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Főbb változtatások ebben a verzióban: Hibajavítások
|
||||||
|
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
Perubahan utama di versi ini: Perbaikan bug terutama untuk notifikasinya.
|
Perubahan utama di versi ini: Perbaikan bug terutama untuk notifikasinya.
|
||||||
Changelog lengkap: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Changelog lengkap: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/id/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Perubahan utama di versi ini: Beberapa perbaikan bug!
|
||||||
|
Changelog lengkap: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -26,7 +26,7 @@ Element menempatkan Anda dalam kendali dengan cara yang berbeda:
|
||||||
2. Host sendiri akun Anda dengan menjalankan server pada infrastruktur IT Anda sendiri
|
2. Host sendiri akun Anda dengan menjalankan server pada infrastruktur IT Anda sendiri
|
||||||
3. Daftar untuk akun di server khusus dengan berlangganan platform hosting Layanan Matrix Element
|
3. Daftar untuk akun di server khusus dengan berlangganan platform hosting Layanan Matrix Element
|
||||||
|
|
||||||
<b>Pesan terbuka dan kolaborasi</b>
|
<b>Perpesanan dan kolaborasi terbuka</b>
|
||||||
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain atau bahkan menggunakan aplikasi perpesanan yang berbeda.
|
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain atau bahkan menggunakan aplikasi perpesanan yang berbeda.
|
||||||
|
|
||||||
<b>Sangat aman</b>
|
<b>Sangat aman</b>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
Modifiche principali in questa versione: correzioni riguardo le notifiche.
|
Modifiche principali in questa versione: correzioni riguardo le notifiche.
|
||||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/it-IT/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/it-IT/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Modifiche principali in questa versione: correzioni di errori!
|
||||||
|
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
Principais mudanças nesta versão: Consertos de bugs principalmente quanto às notificações.
|
Principais mudanças nesta versão: Consertos de bugs principalmente quanto às notificações.
|
||||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/pt-BR/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Principais mudanças nesta versão: Consertos de bugs!
|
||||||
|
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
2
fastlane/metadata/android/sk/changelogs/40103070.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40103070.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Hlavné zmeny v tejto verzii: Opravy chýb týkajúce sa najmä oznámení.
|
||||||
|
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
2
fastlane/metadata/android/sk/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Hlavné zmeny v tejto verzii: Opravy chýb!
|
||||||
|
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1 +1 @@
|
||||||
Zabezpečené konverzácie a VoIP. Ochráňte vaše údaje pred tretími stranami.
|
Skupinový messenger - šifrované správy, skupinové konverzácie a videohovory
|
||||||
|
|
2
fastlane/metadata/android/sq/changelogs/40103070.txt
Normal file
2
fastlane/metadata/android/sq/changelogs/40103070.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Ndryshimet kryesore në këtë version: Ndreqje të metash të lidhura kryesisht me njoftimet.
|
||||||
|
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
2
fastlane/metadata/android/sq/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/sq/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Ndryshimet kryesore në këtë version: Ndreqje të metash!
|
||||||
|
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
Huvudsakliga ändringar i den här versionen: Buggfixar som huvudsakligen rör aviseringar.
|
Huvudsakliga ändringar i den här versionen: Buggfixar som huvudsakligen rör aviseringar.
|
||||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/sv-SE/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/sv-SE/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Huvudsakliga ändringar i den här versionen: Buggfixar!
|
||||||
|
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
Основні зміни в цій версії: виправлення помилок в основному у повідомленнях.
|
Основні зміни в цій версії: виправлення помилок в основному у повідомленнях.
|
||||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/uk/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Основні зміни у цій версії: Виправлення помилок!
|
||||||
|
Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,7 +1,7 @@
|
||||||
Element — це і безпечний месенджер, і застосунок для співпраці команди, який ідеально підходить для групових бесід під час віддаленої роботи. Цей застосунок для спілкування застосовує наскрізне шифрування для забезпечення відеоконференцій, обміну файлами та голосових викликів.
|
Element — це і безпечний месенджер, і застосунок для співпраці команди, який ідеально підходить спілкування групами під час віддаленої роботи. Цей застосунок для спілкування використовує наскрізне шифрування для забезпечення відеоконференцій, обміну файлами та голосових викликів.
|
||||||
|
|
||||||
<b>Можливості Element включають:</b>
|
<b>Можливості Element включають:</b>
|
||||||
- Розширені засоби спілкування в Інтернеті
|
- Розширені засоби онлайн-спілкування
|
||||||
- Повністю зашифровані повідомлення для надання можливості безпечнішого корпоративного спілкування, навіть для віддалених працівників
|
- Повністю зашифровані повідомлення для надання можливості безпечнішого корпоративного спілкування, навіть для віддалених працівників
|
||||||
- Децентралізований чат на основі відкритого коду Matrix
|
- Децентралізований чат на основі відкритого коду Matrix
|
||||||
- Безпечний обмін файлами із зашифрованими даними для керування проєктами
|
- Безпечний обмін файлами із зашифрованими даними для керування проєктами
|
||||||
|
@ -33,10 +33,10 @@ Element надає такі можливості на вибір:
|
||||||
Справжнє наскрізне шифрування (лише учасники бесіди можуть розшифровувати повідомлення) та взаємне підписування пристроїв.
|
Справжнє наскрізне шифрування (лише учасники бесіди можуть розшифровувати повідомлення) та взаємне підписування пристроїв.
|
||||||
|
|
||||||
<b>Повноцінні спілкування та інтеграція</b>
|
<b>Повноцінні спілкування та інтеграція</b>
|
||||||
Обмін повідомленнями, голосові та відеовиклики, обмін файлами, спільний доступ до екрана та ціла купа інтеграцій, ботів та розширень. Створюйте кімнати, спільноти, залишайтеся на зв’язку та виконуйте завдання.
|
Обмін повідомленнями, голосові та відеовиклики, обмін файлами, спільний доступ до екрана та ціла купа інтеграцій, ботів та віджетів. Створюйте кімнати, спільноти, залишайтеся на зв’язку та виконуйте завдання.
|
||||||
|
|
||||||
<b>Продовжуйте, де зупинилися</b>
|
<b>Продовжуйте, де зупинилися</b>
|
||||||
Залишайтеся на зв'язку, де б ви не знаходились, з повністю синхронізованою історією повідомлень на всіх своїх пристроях та в Інтернеті за адресою https://app.element.io
|
Залишайтеся на зв'язку, де б ви не знаходились, з повністю синхронізованою історією повідомлень на всіх своїх пристроях та в Інтернеті за адресою https://app.element.io
|
||||||
|
|
||||||
<b>Відкритий код</b>
|
<b>Відкритий код</b>
|
||||||
Element для Android це проєкт з відкритим кодом, розміщений GitHub. Будь ласка, повідомте про помилки та/або сприяйте його розвитку на https://github.com/vector-im/element-android
|
Element для Android — це проєкт з відкритим кодом, розміщений на GitHub. Повідомляйте про помилки та/або допомагайте його розвитку на https://github.com/vector-im/element-android
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
此版本的主要变化:主要关于通知的错误修复。
|
此版本的主要变化:主要关于通知的错误修复。
|
||||||
完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/zh-CN/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/zh-CN/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
此版本主要变化:Bug 修复!
|
||||||
|
完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
|
@ -1,2 +1,2 @@
|
||||||
此版本中的主要變動:主要關於通知的臭蟲修復。
|
此版本中的主要變動:主要關於通知的臭蟲修復。
|
||||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
||||||
|
|
2
fastlane/metadata/android/zh-TW/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
此版本中的主要變動:臭蟲修復!
|
||||||
|
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=00b273629df4ce46e68df232161d5a7c4e495b9a029ce6e0420f071e21316867
|
distributionSha256Sum=dd54e87b4d7aa8ff3c6afb0f7805aa121d4b70bca55b8c9b1b896eb103184582
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/appBarLayout">
|
app:layout_constraintTop_toBottomOf="@id/appBarLayout">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -334,7 +334,6 @@
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
<item name="android:visibility">visible</item>
|
<item name="android:visibility">visible</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<style name="Theme.Debug.Light" parent="Theme.MaterialComponents.Light.NoActionBar">
|
<style name="Theme.Debug.Light" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||||
<!-- Keep all default value -->
|
<!-- Keep all default value -->
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,5 +11,3 @@
|
||||||
<changeImageTransform />
|
<changeImageTransform />
|
||||||
|
|
||||||
</transitionSet>
|
</transitionSet>
|
||||||
|
|
||||||
|
|
||||||
|
|
6
library/ui-styles/src/main/res/values-sw600dp/tablet.xml
Normal file
6
library/ui-styles/src/main/res/values-sw600dp/tablet.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<dimen name="width_percent">0.6</dimen>
|
||||||
|
|
||||||
|
</resources>
|
6
library/ui-styles/src/main/res/values-sw720dp/tablet.xml
Normal file
6
library/ui-styles/src/main/res/values-sw720dp/tablet.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<dimen name="width_percent">0.5</dimen>
|
||||||
|
|
||||||
|
</resources>
|
|
@ -32,7 +32,6 @@
|
||||||
<dimen name="call_pip_width">88dp</dimen>
|
<dimen name="call_pip_width">88dp</dimen>
|
||||||
<dimen name="call_pip_radius">8dp</dimen>
|
<dimen name="call_pip_radius">8dp</dimen>
|
||||||
|
|
||||||
|
|
||||||
<dimen name="item_form_min_height">76dp</dimen>
|
<dimen name="item_form_min_height">76dp</dimen>
|
||||||
|
|
||||||
<!-- Max width for some buttons -->
|
<!-- Max width for some buttons -->
|
||||||
|
@ -41,4 +40,6 @@
|
||||||
<!-- Navigation Drawer -->
|
<!-- Navigation Drawer -->
|
||||||
<dimen name="navigation_drawer_max_width">320dp</dimen>
|
<dimen name="navigation_drawer_max_width">320dp</dimen>
|
||||||
|
|
||||||
|
<!-- Preview Url -->
|
||||||
|
<dimen name="preview_url_view_corner_radius">8dp</dimen>
|
||||||
</resources>
|
</resources>
|
|
@ -20,7 +20,6 @@
|
||||||
<color name="palette_prune">#5C56F5</color>
|
<color name="palette_prune">#5C56F5</color>
|
||||||
<color name="palette_links">#0086E6</color>
|
<color name="palette_links">#0086E6</color>
|
||||||
|
|
||||||
|
|
||||||
<!-- For light themes -->
|
<!-- For light themes -->
|
||||||
<color name="palette_gray_25">#F4F6FA</color>
|
<color name="palette_gray_25">#F4F6FA</color>
|
||||||
<color name="palette_gray_50">#E3E8F0</color>
|
<color name="palette_gray_50">#E3E8F0</color>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<declare-styleable name="BadgeFloatingActionButton">
|
<declare-styleable name="BadgeFloatingActionButton">
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
<item name="android:backgroundTint">@android:color/black</item>
|
<item name="android:backgroundTint">@android:color/black</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<style name="Widget.Vector.Button.Outlined.SocialLogin.Facebook">
|
<style name="Widget.Vector.Button.Outlined.SocialLogin.Facebook">
|
||||||
<item name="icon">@drawable/ic_social_facebook</item>
|
<item name="icon">@drawable/ic_social_facebook</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -68,7 +67,6 @@
|
||||||
<item name="android:backgroundTint">#3877EA</item>
|
<item name="android:backgroundTint">#3877EA</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<style name="Widget.Vector.Button.Outlined.SocialLogin.Twitter">
|
<style name="Widget.Vector.Button.Outlined.SocialLogin.Twitter">
|
||||||
<item name="icon">@drawable/ic_social_twitter</item>
|
<item name="icon">@drawable/ic_social_twitter</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -85,7 +83,6 @@
|
||||||
<item name="android:backgroundTint">#5D9EC9</item>
|
<item name="android:backgroundTint">#5D9EC9</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<style name="Widget.Vector.Button.Outlined.SocialLogin.Apple">
|
<style name="Widget.Vector.Button.Outlined.SocialLogin.Apple">
|
||||||
<item name="icon">@drawable/ic_social_apple</item>
|
<item name="icon">@drawable/ic_social_apple</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -118,5 +115,4 @@
|
||||||
<item name="android:backgroundTint">@android:color/black</item>
|
<item name="android:backgroundTint">@android:color/black</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
6
library/ui-styles/src/main/res/values/tablet.xml
Normal file
6
library/ui-styles/src/main/res/values/tablet.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<dimen name="width_percent">1</dimen>
|
||||||
|
|
||||||
|
</resources>
|
|
@ -9,7 +9,7 @@ buildscript {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "io.realm:realm-gradle-plugin:10.8.1"
|
classpath "io.realm:realm-gradle-plugin:10.9.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ android {
|
||||||
// that the app's state is completely cleared between tests.
|
// that the app's state is completely cleared between tests.
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
|
|
||||||
buildConfigField "String", "SDK_VERSION", "\"1.3.9\""
|
buildConfigField "String", "SDK_VERSION", "\"1.3.12\""
|
||||||
|
|
||||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||||
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
|
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
|
||||||
|
@ -140,8 +140,8 @@ dependencies {
|
||||||
implementation libs.arrow.core
|
implementation libs.arrow.core
|
||||||
implementation libs.arrow.instances
|
implementation libs.arrow.instances
|
||||||
|
|
||||||
// olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm
|
// olm lib is now hosted by maven at https://gitlab.matrix.org/api/v4/projects/27/packages/maven
|
||||||
implementation 'org.matrix.gitlab.matrix-org:olm:3.2.4'
|
implementation 'org.matrix.android:olm:3.2.7'
|
||||||
|
|
||||||
// DI
|
// DI
|
||||||
implementation libs.dagger.dagger
|
implementation libs.dagger.dagger
|
||||||
|
@ -158,10 +158,10 @@ dependencies {
|
||||||
implementation libs.apache.commonsImaging
|
implementation libs.apache.commonsImaging
|
||||||
|
|
||||||
// Phone number https://github.com/google/libphonenumber
|
// Phone number https://github.com/google/libphonenumber
|
||||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.38'
|
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.39'
|
||||||
|
|
||||||
testImplementation libs.tests.junit
|
testImplementation libs.tests.junit
|
||||||
testImplementation 'org.robolectric:robolectric:4.7.2'
|
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||||
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||||
testImplementation libs.mockk.mockk
|
testImplementation libs.mockk.mockk
|
||||||
|
|
|
@ -20,9 +20,10 @@ import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
|
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
|
||||||
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
@ -30,7 +31,6 @@ import kotlinx.coroutines.withTimeout
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.matrix.android.sdk.api.Matrix
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
|
@ -45,7 +45,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||||
import java.util.ArrayList
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
@ -56,13 +56,14 @@ import java.util.concurrent.TimeUnit
|
||||||
*/
|
*/
|
||||||
class CommonTestHelper(context: Context) {
|
class CommonTestHelper(context: Context) {
|
||||||
|
|
||||||
val matrix: Matrix
|
internal val matrix: TestMatrix
|
||||||
|
val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||||
|
|
||||||
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
||||||
|
|
||||||
init {
|
init {
|
||||||
UiThreadStatement.runOnUiThread {
|
UiThreadStatement.runOnUiThread {
|
||||||
Matrix.initialize(
|
TestMatrix.initialize(
|
||||||
context,
|
context,
|
||||||
MatrixConfiguration(
|
MatrixConfiguration(
|
||||||
applicationFlavor = "TestFlavor",
|
applicationFlavor = "TestFlavor",
|
||||||
|
@ -70,7 +71,7 @@ class CommonTestHelper(context: Context) {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
matrix = Matrix.getInstance(context)
|
matrix = TestMatrix.getInstance(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session {
|
fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session {
|
||||||
|
@ -95,33 +96,47 @@ class CommonTestHelper(context: Context) {
|
||||||
*
|
*
|
||||||
* @param session the session to sync
|
* @param session the session to sync
|
||||||
*/
|
*/
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis * 10) {
|
||||||
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
|
|
||||||
val lock = CountDownLatch(1)
|
val lock = CountDownLatch(1)
|
||||||
|
coroutineScope.launch {
|
||||||
val job = GlobalScope.launch(Dispatchers.Main) {
|
session.startSync(true)
|
||||||
session.open()
|
val syncLiveData = session.getSyncStateLive()
|
||||||
}
|
val syncObserver = object : Observer<SyncState> {
|
||||||
runBlocking { job.join() }
|
override fun onChanged(t: SyncState?) {
|
||||||
|
if (session.hasAlreadySynced()) {
|
||||||
session.startSync(true)
|
lock.countDown()
|
||||||
|
syncLiveData.removeObserver(this)
|
||||||
val syncLiveData = runBlocking(Dispatchers.Main) {
|
}
|
||||||
session.getSyncStateLive()
|
|
||||||
}
|
|
||||||
val syncObserver = object : Observer<SyncState> {
|
|
||||||
override fun onChanged(t: SyncState?) {
|
|
||||||
if (session.hasAlreadySynced()) {
|
|
||||||
lock.countDown()
|
|
||||||
syncLiveData.removeObserver(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
syncLiveData.observeForever(syncObserver)
|
||||||
}
|
}
|
||||||
GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
|
|
||||||
|
|
||||||
await(lock, timeout)
|
await(lock, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This methods clear the cache and waits for initialSync
|
||||||
|
*
|
||||||
|
* @param session the session to sync
|
||||||
|
*/
|
||||||
|
fun clearCacheAndSync(session: Session, timeout: Long = TestConstants.timeOutMillis) {
|
||||||
|
waitWithLatch(timeout) { latch ->
|
||||||
|
session.clearCache()
|
||||||
|
val syncLiveData = session.getSyncStateLive()
|
||||||
|
val syncObserver = object : Observer<SyncState> {
|
||||||
|
override fun onChanged(t: SyncState?) {
|
||||||
|
if (session.hasAlreadySynced()) {
|
||||||
|
Timber.v("Clear cache and synced")
|
||||||
|
syncLiveData.removeObserver(this)
|
||||||
|
latch.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syncLiveData.observeForever(syncObserver)
|
||||||
|
session.startSync(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends text messages in a room
|
* Sends text messages in a room
|
||||||
*
|
*
|
||||||
|
@ -130,46 +145,57 @@ class CommonTestHelper(context: Context) {
|
||||||
* @param nbOfMessages the number of time the message will be sent
|
* @param nbOfMessages the number of time the message will be sent
|
||||||
*/
|
*/
|
||||||
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
|
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
|
||||||
val timeline = room.createTimeline(null, TimelineSettings(10))
|
|
||||||
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
|
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
|
||||||
val latch = CountDownLatch(1)
|
val timeline = room.createTimeline(null, TimelineSettings(10))
|
||||||
val timelineListener = object : Timeline.Listener {
|
timeline.start()
|
||||||
override fun onTimelineFailure(throwable: Throwable) {
|
waitWithLatch(timeout + 1_000L * nbOfMessages) { latch ->
|
||||||
}
|
val timelineListener = object : Timeline.Listener {
|
||||||
|
override fun onTimelineFailure(throwable: Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
override fun onNewTimelineEvents(eventIds: List<String>) {
|
override fun onNewTimelineEvents(eventIds: List<String>) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
||||||
val newMessages = snapshot
|
val newMessages = snapshot
|
||||||
.filter { it.root.sendState == SendState.SYNCED }
|
.filter { it.root.sendState == SendState.SYNCED }
|
||||||
.filter { it.root.getClearType() == EventType.MESSAGE }
|
.filter { it.root.getClearType() == EventType.MESSAGE }
|
||||||
.filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true }
|
.filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true }
|
||||||
|
|
||||||
if (newMessages.size == nbOfMessages) {
|
Timber.v("New synced message size: ${newMessages.size}")
|
||||||
sentEvents.addAll(newMessages)
|
if (newMessages.size == nbOfMessages) {
|
||||||
// Remove listener now, if not at the next update sendEvents could change
|
sentEvents.addAll(newMessages)
|
||||||
timeline.removeListener(this)
|
// Remove listener now, if not at the next update sendEvents could change
|
||||||
latch.countDown()
|
timeline.removeListener(this)
|
||||||
|
latch.countDown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
timeline.addListener(timelineListener)
|
||||||
|
sendTextMessagesBatched(room, message, nbOfMessages)
|
||||||
}
|
}
|
||||||
timeline.start()
|
|
||||||
timeline.addListener(timelineListener)
|
|
||||||
for (i in 0 until nbOfMessages) {
|
|
||||||
room.sendTextMessage(message + " #" + (i + 1))
|
|
||||||
}
|
|
||||||
// Wait 3 second more per message
|
|
||||||
await(latch, timeout = timeout + 3_000L * nbOfMessages)
|
|
||||||
timeline.dispose()
|
timeline.dispose()
|
||||||
|
|
||||||
// Check that all events has been created
|
// Check that all events has been created
|
||||||
assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong())
|
assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong())
|
||||||
|
|
||||||
return sentEvents
|
return sentEvents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will send nb of messages provided by count parameter but waits a bit every 10 messages to avoid gap in sync
|
||||||
|
*/
|
||||||
|
private fun sendTextMessagesBatched(room: Room, message: String, count: Int) {
|
||||||
|
(1 until count + 1)
|
||||||
|
.map { "$message #$it" }
|
||||||
|
.chunked(10)
|
||||||
|
.forEach { batchedMessages ->
|
||||||
|
batchedMessages.forEach { formattedMessage ->
|
||||||
|
room.sendTextMessage(formattedMessage)
|
||||||
|
}
|
||||||
|
Thread.sleep(1_000L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PRIVATE METHODS *****************************************************************************
|
// PRIVATE METHODS *****************************************************************************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -239,10 +265,10 @@ class CommonTestHelper(context: Context) {
|
||||||
|
|
||||||
assertTrue(registrationResult is RegistrationResult.Success)
|
assertTrue(registrationResult is RegistrationResult.Success)
|
||||||
val session = (registrationResult as RegistrationResult.Success).session
|
val session = (registrationResult as RegistrationResult.Success).session
|
||||||
|
session.open()
|
||||||
if (sessionTestParams.withInitialSync) {
|
if (sessionTestParams.withInitialSync) {
|
||||||
syncSession(session, 60_000)
|
syncSession(session, 60_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +293,7 @@ class CommonTestHelper(context: Context) {
|
||||||
.getLoginWizard()
|
.getLoginWizard()
|
||||||
.login(userName, password, "myDevice")
|
.login(userName, password, "myDevice")
|
||||||
}
|
}
|
||||||
|
session.open()
|
||||||
if (sessionTestParams.withInitialSync) {
|
if (sessionTestParams.withInitialSync) {
|
||||||
syncSession(session)
|
syncSession(session)
|
||||||
}
|
}
|
||||||
|
@ -332,22 +358,21 @@ class CommonTestHelper(context: Context) {
|
||||||
assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
|
assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
|
||||||
fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
|
while (true) {
|
||||||
GlobalScope.launch {
|
delay(1000)
|
||||||
while (true) {
|
if (condition()) {
|
||||||
delay(1000)
|
latch.countDown()
|
||||||
if (condition()) {
|
return
|
||||||
latch.countDown()
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, block: (CountDownLatch) -> Unit) {
|
fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, dispatcher: CoroutineDispatcher = Dispatchers.Main, block: suspend (CountDownLatch) -> Unit) {
|
||||||
val latch = CountDownLatch(1)
|
val latch = CountDownLatch(1)
|
||||||
block(latch)
|
coroutineScope.launch(dispatcher) {
|
||||||
|
block(latch)
|
||||||
|
}
|
||||||
await(latch, timeout)
|
await(latch, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,6 @@ package org.matrix.android.sdk.common
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
|
@ -31,6 +27,7 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
|
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction
|
import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction
|
||||||
|
@ -44,16 +41,16 @@ import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
|
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||||
|
|
||||||
private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!")
|
private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!")
|
||||||
private val messagesFromBob: List<String> = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.")
|
private val messagesFromBob: List<String> = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.")
|
||||||
|
@ -64,27 +61,33 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
* @return alice session
|
* @return alice session
|
||||||
*/
|
*/
|
||||||
fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData {
|
fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
||||||
|
|
||||||
val roomId = mTestHelper.runBlockingTest {
|
val roomId = testHelper.runBlockingTest {
|
||||||
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
|
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encryptedRoom) {
|
if (encryptedRoom) {
|
||||||
val room = aliceSession.getRoom(roomId)!!
|
testHelper.waitWithLatch { latch ->
|
||||||
|
val room = aliceSession.getRoom(roomId)!!
|
||||||
mTestHelper.runBlockingTest {
|
|
||||||
room.enableEncryption()
|
room.enableEncryption()
|
||||||
|
val roomSummaryLive = room.getRoomSummaryLive()
|
||||||
|
val roomSummaryObserver = object : Observer<Optional<RoomSummary>> {
|
||||||
|
override fun onChanged(roomSummary: Optional<RoomSummary>) {
|
||||||
|
if (roomSummary.getOrNull()?.isEncrypted.orFalse()) {
|
||||||
|
roomSummaryLive.removeObserver(this)
|
||||||
|
latch.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roomSummaryLive.observeForever(roomSummaryObserver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CryptoTestData(roomId, listOf(aliceSession))
|
return CryptoTestData(roomId, listOf(aliceSession))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return alice and bob sessions
|
* @return alice and bob sessions
|
||||||
*/
|
*/
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData {
|
fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData {
|
||||||
val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom)
|
val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom)
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -92,54 +95,37 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
|
|
||||||
val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
|
val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
|
||||||
|
|
||||||
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
|
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
|
||||||
|
|
||||||
val lock1 = CountDownLatch(1)
|
testHelper.waitWithLatch { latch ->
|
||||||
|
val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
|
||||||
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
|
val newRoomObserver = object : Observer<List<RoomSummary>> {
|
||||||
bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
|
override fun onChanged(t: List<RoomSummary>?) {
|
||||||
}
|
if (t?.isNotEmpty() == true) {
|
||||||
|
bobRoomSummariesLive.removeObserver(this)
|
||||||
val newRoomObserver = object : Observer<List<RoomSummary>> {
|
latch.countDown()
|
||||||
override fun onChanged(t: List<RoomSummary>?) {
|
}
|
||||||
if (t?.isNotEmpty() == true) {
|
|
||||||
lock1.countDown()
|
|
||||||
bobRoomSummariesLive.removeObserver(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
|
||||||
bobRoomSummariesLive.observeForever(newRoomObserver)
|
bobRoomSummariesLive.observeForever(newRoomObserver)
|
||||||
}
|
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
|
||||||
aliceRoom.invite(bobSession.myUserId)
|
aliceRoom.invite(bobSession.myUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.await(lock1)
|
testHelper.waitWithLatch { latch ->
|
||||||
|
val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
|
||||||
val lock = CountDownLatch(1)
|
val roomJoinedObserver = object : Observer<List<RoomSummary>> {
|
||||||
|
override fun onChanged(t: List<RoomSummary>?) {
|
||||||
val roomJoinedObserver = object : Observer<List<RoomSummary>> {
|
if (bobSession.getRoom(aliceRoomId)
|
||||||
override fun onChanged(t: List<RoomSummary>?) {
|
?.getRoomMember(bobSession.myUserId)
|
||||||
if (bobSession.getRoom(aliceRoomId)
|
?.membership == Membership.JOIN) {
|
||||||
?.getRoomMember(aliceSession.myUserId)
|
bobRoomSummariesLive.removeObserver(this)
|
||||||
?.membership == Membership.JOIN) {
|
latch.countDown()
|
||||||
lock.countDown()
|
}
|
||||||
bobRoomSummariesLive.removeObserver(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
|
||||||
bobRoomSummariesLive.observeForever(roomJoinedObserver)
|
bobRoomSummariesLive.observeForever(roomJoinedObserver)
|
||||||
|
bobSession.joinRoom(aliceRoomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.runBlockingTest { bobSession.joinRoom(aliceRoomId) }
|
|
||||||
|
|
||||||
mTestHelper.await(lock)
|
|
||||||
|
|
||||||
// Ensure bob can send messages to the room
|
// Ensure bob can send messages to the room
|
||||||
// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
|
// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
|
||||||
// assertNotNull(roomFromBobPOV.powerLevels)
|
// assertNotNull(roomFromBobPOV.powerLevels)
|
||||||
|
@ -171,13 +157,13 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
* @Return Sam session
|
* @Return Sam session
|
||||||
*/
|
*/
|
||||||
fun createSamAccountAndInviteToTheRoom(room: Room): Session {
|
fun createSamAccountAndInviteToTheRoom(room: Room): Session {
|
||||||
val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams)
|
val samSession = testHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams)
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
room.invite(samSession.myUserId, null)
|
room.invite(samSession.myUserId, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
samSession.joinRoom(room.roomId, null, emptyList())
|
samSession.joinRoom(room.roomId, null, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,23 +180,20 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
val bobSession = cryptoTestData.secondSession!!
|
val bobSession = cryptoTestData.secondSession!!
|
||||||
|
|
||||||
bobSession.cryptoService().setWarnOnUnknownDevices(false)
|
bobSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
|
||||||
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
|
||||||
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
|
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
|
||||||
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
||||||
|
|
||||||
// Alice sends a message
|
// Alice sends a message
|
||||||
mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1)
|
testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1)
|
||||||
// roomFromAlicePOV.sendTextMessage(messagesFromAlice[0])
|
|
||||||
|
|
||||||
// Bob send 3 messages
|
// Bob send 3 messages
|
||||||
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1)
|
testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1)
|
||||||
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1)
|
testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1)
|
||||||
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1)
|
testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1)
|
||||||
// Alice sends a message
|
// Alice sends a message
|
||||||
mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1)
|
testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1)
|
||||||
|
|
||||||
return cryptoTestData
|
return cryptoTestData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,60 +239,44 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun createDM(alice: Session, bob: Session): String {
|
fun createDM(alice: Session, bob: Session): String {
|
||||||
val roomId = mTestHelper.runBlockingTest {
|
var roomId: String = ""
|
||||||
alice.createDirectRoom(bob.myUserId)
|
testHelper.waitWithLatch { latch ->
|
||||||
}
|
roomId = alice.createDirectRoom(bob.myUserId)
|
||||||
|
val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { })
|
||||||
mTestHelper.waitWithLatch { latch ->
|
|
||||||
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
|
|
||||||
bob.getRoomSummariesLive(roomSummaryQueryParams { })
|
|
||||||
}
|
|
||||||
|
|
||||||
val newRoomObserver = object : Observer<List<RoomSummary>> {
|
val newRoomObserver = object : Observer<List<RoomSummary>> {
|
||||||
override fun onChanged(t: List<RoomSummary>?) {
|
override fun onChanged(t: List<RoomSummary>?) {
|
||||||
val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1
|
val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1
|
||||||
if (indexOfFirst != -1) {
|
if (indexOfFirst != -1) {
|
||||||
latch.countDown()
|
|
||||||
bobRoomSummariesLive.removeObserver(this)
|
bobRoomSummariesLive.removeObserver(this)
|
||||||
|
latch.countDown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bobRoomSummariesLive.observeForever(newRoomObserver)
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
|
||||||
bobRoomSummariesLive.observeForever(newRoomObserver)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
|
val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { })
|
||||||
bob.getRoomSummariesLive(roomSummaryQueryParams { })
|
|
||||||
}
|
|
||||||
|
|
||||||
val newRoomObserver = object : Observer<List<RoomSummary>> {
|
val newRoomObserver = object : Observer<List<RoomSummary>> {
|
||||||
override fun onChanged(t: List<RoomSummary>?) {
|
override fun onChanged(t: List<RoomSummary>?) {
|
||||||
if (bob.getRoom(roomId)
|
if (bob.getRoom(roomId)
|
||||||
?.getRoomMember(bob.myUserId)
|
?.getRoomMember(bob.myUserId)
|
||||||
?.membership == Membership.JOIN) {
|
?.membership == Membership.JOIN) {
|
||||||
latch.countDown()
|
|
||||||
bobRoomSummariesLive.removeObserver(this)
|
bobRoomSummariesLive.removeObserver(this)
|
||||||
|
latch.countDown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bobRoomSummariesLive.observeForever(newRoomObserver)
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
bob.joinRoom(roomId)
|
||||||
bobRoomSummariesLive.observeForever(newRoomObserver)
|
|
||||||
}
|
|
||||||
|
|
||||||
mTestHelper.runBlockingTest { bob.joinRoom(roomId) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return roomId
|
return roomId
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initializeCrossSigning(session: Session) {
|
fun initializeCrossSigning(session: Session) {
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
session.cryptoService().crossSigningService()
|
session.cryptoService().crossSigningService()
|
||||||
.initializeCrossSigning(
|
.initializeCrossSigning(
|
||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
|
@ -346,8 +313,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
var bobPovTx: IncomingSasVerificationTransaction? = null
|
var bobPovTx: IncomingSasVerificationTransaction? = null
|
||||||
|
|
||||||
// wait for alice to get the ready
|
// wait for alice to get the ready
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
|
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
|
||||||
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
|
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
|
||||||
if (bobPovTx?.state == VerificationTxState.OnStarted) {
|
if (bobPovTx?.state == VerificationTxState.OnStarted) {
|
||||||
|
@ -359,16 +326,16 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction
|
alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction
|
||||||
Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}")
|
Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}")
|
||||||
alicePovTx?.state == VerificationTxState.ShortCodeReady
|
alicePovTx?.state == VerificationTxState.ShortCodeReady
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// wait for alice to get the ready
|
// wait for alice to get the ready
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
|
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
|
||||||
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
|
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
|
||||||
if (bobPovTx?.state == VerificationTxState.OnStarted) {
|
if (bobPovTx?.state == VerificationTxState.OnStarted) {
|
||||||
|
@ -383,38 +350,38 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
bobPovTx!!.userHasVerifiedShortCode()
|
bobPovTx!!.userHasVerifiedShortCode()
|
||||||
alicePovTx!!.userHasVerifiedShortCode()
|
alicePovTx!!.userHasVerifiedShortCode()
|
||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
|
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
|
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData {
|
fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
||||||
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
|
||||||
val roomId = mTestHelper.runBlockingTest {
|
val roomId = testHelper.runBlockingTest {
|
||||||
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
|
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
|
||||||
}
|
}
|
||||||
val room = aliceSession.getRoom(roomId)!!
|
val room = aliceSession.getRoom(roomId)!!
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
room.enableEncryption()
|
room.enableEncryption()
|
||||||
}
|
}
|
||||||
|
|
||||||
val sessions = mutableListOf(aliceSession)
|
val sessions = mutableListOf(aliceSession)
|
||||||
for (index in 1 until numberOfMembers) {
|
for (index in 1 until numberOfMembers) {
|
||||||
val session = mTestHelper.createAccount("User_$index", defaultSessionParams)
|
val session = testHelper.createAccount("User_$index", defaultSessionParams)
|
||||||
mTestHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
|
testHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
|
||||||
println("TEST -> " + session.myUserId + " invited")
|
println("TEST -> " + session.myUserId + " invited")
|
||||||
mTestHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) }
|
testHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) }
|
||||||
println("TEST -> " + session.myUserId + " joined")
|
println("TEST -> " + session.myUserId + " joined")
|
||||||
sessions.add(session)
|
sessions.add(session)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.android.sdk.common
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force foreground for testing
|
||||||
|
*/
|
||||||
|
internal class TestBackgroundDetectionObserver : BackgroundDetectionObserver {
|
||||||
|
|
||||||
|
override val isInBackground: Boolean = false
|
||||||
|
|
||||||
|
override fun register(listener: BackgroundDetectionObserver.Listener) = Unit
|
||||||
|
|
||||||
|
override fun unregister(listener: BackgroundDetectionObserver.Listener) = Unit
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.matrix.android.sdk.api
|
package org.matrix.android.sdk.common
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
@ -24,27 +24,27 @@ import androidx.work.Configuration
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import org.matrix.android.sdk.BuildConfig
|
||||||
|
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
||||||
import org.matrix.android.sdk.api.legacy.LegacySessionImporter
|
import org.matrix.android.sdk.api.legacy.LegacySessionImporter
|
||||||
import org.matrix.android.sdk.api.network.ApiInterceptorListener
|
import org.matrix.android.sdk.api.network.ApiInterceptorListener
|
||||||
import org.matrix.android.sdk.api.network.ApiPath
|
import org.matrix.android.sdk.api.network.ApiPath
|
||||||
import org.matrix.android.sdk.api.raw.RawService
|
import org.matrix.android.sdk.api.raw.RawService
|
||||||
import org.matrix.android.sdk.common.DaggerTestMatrixComponent
|
|
||||||
import org.matrix.android.sdk.internal.SessionManager
|
import org.matrix.android.sdk.internal.SessionManager
|
||||||
import org.matrix.android.sdk.internal.network.ApiInterceptor
|
import org.matrix.android.sdk.internal.network.ApiInterceptor
|
||||||
import org.matrix.android.sdk.internal.network.UserAgentHolder
|
import org.matrix.android.sdk.internal.network.UserAgentHolder
|
||||||
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
|
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
||||||
import org.matrix.olm.OlmManager
|
import org.matrix.olm.OlmManager
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main entry point to the matrix sdk.
|
* This mimics the Matrix class but using TestMatrixComponent internally instead of regular MatrixComponent.
|
||||||
* To get the singleton instance, use getInstance static method.
|
|
||||||
*/
|
*/
|
||||||
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
|
internal class TestMatrix constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
|
||||||
|
|
||||||
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter
|
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter
|
||||||
@Inject internal lateinit var authenticationService: AuthenticationService
|
@Inject internal lateinit var authenticationService: AuthenticationService
|
||||||
|
@ -55,15 +55,18 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||||
@Inject internal lateinit var sessionManager: SessionManager
|
@Inject internal lateinit var sessionManager: SessionManager
|
||||||
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
||||||
@Inject internal lateinit var apiInterceptor: ApiInterceptor
|
@Inject internal lateinit var apiInterceptor: ApiInterceptor
|
||||||
|
@Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
|
||||||
|
|
||||||
private val uiHandler = Handler(Looper.getMainLooper())
|
private val uiHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Monarchy.init(context)
|
Monarchy.init(context)
|
||||||
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
|
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
|
||||||
if (context.applicationContext !is Configuration.Provider) {
|
val configuration = Configuration.Builder()
|
||||||
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
|
.setExecutor(Executors.newCachedThreadPool())
|
||||||
}
|
.setWorkerFactory(matrixWorkerFactory)
|
||||||
|
.build()
|
||||||
|
WorkManager.initialize(context, configuration)
|
||||||
uiHandler.post {
|
uiHandler.post {
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
|
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
|
||||||
}
|
}
|
||||||
|
@ -93,21 +96,21 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private lateinit var instance: Matrix
|
private lateinit var instance: TestMatrix
|
||||||
private val isInit = AtomicBoolean(false)
|
private val isInit = AtomicBoolean(false)
|
||||||
|
|
||||||
fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
|
fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
|
||||||
if (isInit.compareAndSet(false, true)) {
|
if (isInit.compareAndSet(false, true)) {
|
||||||
instance = Matrix(context.applicationContext, matrixConfiguration)
|
instance = TestMatrix(context.applicationContext, matrixConfiguration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInstance(context: Context): Matrix {
|
fun getInstance(context: Context): TestMatrix {
|
||||||
if (isInit.compareAndSet(false, true)) {
|
if (isInit.compareAndSet(false, true)) {
|
||||||
val appContext = context.applicationContext
|
val appContext = context.applicationContext
|
||||||
if (appContext is MatrixConfiguration.Provider) {
|
if (appContext is MatrixConfiguration.Provider) {
|
||||||
val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration()
|
val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration()
|
||||||
instance = Matrix(appContext, matrixConfiguration)
|
instance = TestMatrix(appContext, matrixConfiguration)
|
||||||
} else {
|
} else {
|
||||||
throw IllegalStateException("Matrix is not initialized properly." +
|
throw IllegalStateException("Matrix is not initialized properly." +
|
||||||
" You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.")
|
" You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.")
|
|
@ -34,12 +34,13 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
|
||||||
NetworkModule::class,
|
NetworkModule::class,
|
||||||
AuthModule::class,
|
AuthModule::class,
|
||||||
RawModule::class,
|
RawModule::class,
|
||||||
SystemModule::class,
|
SystemModule::class
|
||||||
TestNetworkModule::class
|
|
||||||
])
|
])
|
||||||
@MatrixScope
|
@MatrixScope
|
||||||
internal interface TestMatrixComponent : MatrixComponent {
|
internal interface TestMatrixComponent : MatrixComponent {
|
||||||
|
|
||||||
|
fun inject(matrix: TestMatrix)
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(@BindsInstance context: Context,
|
fun create(@BindsInstance context: Context,
|
||||||
|
|
|
@ -18,10 +18,39 @@ package org.matrix.android.sdk.common
|
||||||
|
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
import org.matrix.android.sdk.internal.di.MatrixComponent
|
import org.matrix.android.sdk.internal.di.MatrixComponent
|
||||||
|
import org.matrix.android.sdk.internal.di.MatrixScope
|
||||||
|
import org.matrix.android.sdk.internal.session.MockHttpInterceptor
|
||||||
|
import org.matrix.android.sdk.internal.session.TestInterceptor
|
||||||
|
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
internal abstract class TestModule {
|
internal abstract class TestModule {
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent
|
abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent
|
||||||
|
|
||||||
|
@Module
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
val interceptors = ArrayList<TestInterceptor>()
|
||||||
|
|
||||||
|
fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId }
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@JvmStatic
|
||||||
|
@MockHttpInterceptor
|
||||||
|
fun providesTestInterceptor(): TestInterceptor? {
|
||||||
|
return MockOkHttpInterceptor().also {
|
||||||
|
interceptors.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@JvmStatic
|
||||||
|
@MatrixScope
|
||||||
|
fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver {
|
||||||
|
return TestBackgroundDetectionObserver()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,12 +36,12 @@ import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class PreShareKeysTest : InstrumentedTest {
|
class PreShareKeysTest : InstrumentedTest {
|
||||||
|
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val testHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun ensure_outbound_session_happy_path() {
|
fun ensure_outbound_session_happy_path() {
|
||||||
val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val e2eRoomID = testData.roomId
|
val e2eRoomID = testData.roomId
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
val bobSession = testData.secondSession!!
|
val bobSession = testData.secondSession!!
|
||||||
|
@ -58,12 +58,12 @@ class PreShareKeysTest : InstrumentedTest {
|
||||||
Log.d("#Test", "Room Key Received from alice $preShareCount")
|
Log.d("#Test", "Room Key Received from alice $preShareCount")
|
||||||
|
|
||||||
// Force presharing of new outbound key
|
// Force presharing of new outbound key
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it)
|
aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
val newGossipCount = bobSession.cryptoService().getGossipingEvents().count {
|
val newGossipCount = bobSession.cryptoService().getGossipingEvents().count {
|
||||||
it.senderId == aliceSession.myUserId &&
|
it.senderId == aliceSession.myUserId &&
|
||||||
it.getClearType() == EventType.ROOM_KEY
|
it.getClearType() == EventType.ROOM_KEY
|
||||||
|
@ -88,16 +88,16 @@ class PreShareKeysTest : InstrumentedTest {
|
||||||
assertEquals("The session received by bob should match what alice sent", 0, sharedIndex)
|
assertEquals("The session received by bob should match what alice sent", 0, sharedIndex)
|
||||||
|
|
||||||
// Just send a real message as test
|
// Just send a real message as test
|
||||||
val sentEvent = mTestHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first()
|
val sentEvent = testHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first()
|
||||||
|
|
||||||
assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session")
|
assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session")
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
|
bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
mTestHelper.signOutAndClose(bobSession)
|
testHelper.signOutAndClose(bobSession)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,8 @@ import kotlin.coroutines.resume
|
||||||
class UnwedgingTest : InstrumentedTest {
|
class UnwedgingTest : InstrumentedTest {
|
||||||
|
|
||||||
private lateinit var messagesReceivedByBob: List<TimelineEvent>
|
private lateinit var messagesReceivedByBob: List<TimelineEvent>
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val testHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun init() {
|
fun init() {
|
||||||
|
@ -85,7 +85,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testUnwedging() {
|
fun testUnwedging() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val aliceRoomId = cryptoTestData.roomId
|
val aliceRoomId = cryptoTestData.roomId
|
||||||
|
@ -133,7 +133,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
roomFromAlicePOV.sendTextMessage("First message")
|
roomFromAlicePOV.sendTextMessage("First message")
|
||||||
|
|
||||||
// Wait for the message to be received by Bob
|
// Wait for the message to be received by Bob
|
||||||
mTestHelper.await(latch)
|
testHelper.await(latch)
|
||||||
bobTimeline.removeListener(bobEventsListener)
|
bobTimeline.removeListener(bobEventsListener)
|
||||||
|
|
||||||
messagesReceivedByBob.size shouldBe 1
|
messagesReceivedByBob.size shouldBe 1
|
||||||
|
@ -161,7 +161,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
roomFromAlicePOV.sendTextMessage("Second message")
|
roomFromAlicePOV.sendTextMessage("Second message")
|
||||||
|
|
||||||
// Wait for the message to be received by Bob
|
// Wait for the message to be received by Bob
|
||||||
mTestHelper.await(latch)
|
testHelper.await(latch)
|
||||||
bobTimeline.removeListener(bobEventsListener)
|
bobTimeline.removeListener(bobEventsListener)
|
||||||
|
|
||||||
messagesReceivedByBob.size shouldBe 2
|
messagesReceivedByBob.size shouldBe 2
|
||||||
|
@ -179,7 +179,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId)
|
aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId)
|
||||||
|
|
||||||
// Wait for the message to be received by Bob
|
// Wait for the message to be received by Bob
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
bobEventsListener = createEventListener(it, 3)
|
bobEventsListener = createEventListener(it, 3)
|
||||||
bobTimeline.addListener(bobEventsListener)
|
bobTimeline.addListener(bobEventsListener)
|
||||||
messagesReceivedByBob = emptyList()
|
messagesReceivedByBob = emptyList()
|
||||||
|
@ -201,11 +201,11 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType())
|
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType())
|
||||||
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[2].root.getClearType())
|
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[2].root.getClearType())
|
||||||
// Bob Should not be able to decrypt last message, because session could not be sent as the olm channel was wedged
|
// Bob Should not be able to decrypt last message, because session could not be sent as the olm channel was wedged
|
||||||
mTestHelper.await(bobFinalLatch)
|
testHelper.await(bobFinalLatch)
|
||||||
bobTimeline.removeListener(bobHasThreeDecryptedEventsListener)
|
bobTimeline.removeListener(bobHasThreeDecryptedEventsListener)
|
||||||
|
|
||||||
// It's a trick to force key request on fail to decrypt
|
// It's a trick to force key request on fail to decrypt
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
bobSession.cryptoService().crossSigningService()
|
bobSession.cryptoService().crossSigningService()
|
||||||
.initializeCrossSigning(
|
.initializeCrossSigning(
|
||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
|
@ -222,8 +222,8 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until we received back the key
|
// Wait until we received back the key
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
// we should get back the key and be able to decrypt
|
// we should get back the key and be able to decrypt
|
||||||
val result = tryOrNull {
|
val result = tryOrNull {
|
||||||
bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "")
|
bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "")
|
||||||
|
@ -235,7 +235,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
|
|
||||||
bobTimeline.dispose()
|
bobTimeline.dispose()
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {
|
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {
|
||||||
|
|
|
@ -45,14 +45,14 @@ import kotlin.coroutines.resume
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
class XSigningTest : InstrumentedTest {
|
class XSigningTest : InstrumentedTest {
|
||||||
|
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val testHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_InitializeAndStoreKeys() {
|
fun test_InitializeAndStoreKeys() {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
aliceSession.cryptoService().crossSigningService()
|
aliceSession.cryptoService().crossSigningService()
|
||||||
.initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
.initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
|
@ -79,12 +79,12 @@ class XSigningTest : InstrumentedTest {
|
||||||
|
|
||||||
assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
|
assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_CrossSigningCheckBobSeesTheKeys() {
|
fun test_CrossSigningCheckBobSeesTheKeys() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
@ -98,21 +98,21 @@ class XSigningTest : InstrumentedTest {
|
||||||
password = TestConstants.PASSWORD
|
password = TestConstants.PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
promise.resume(aliceAuthParams)
|
promise.resume(aliceAuthParams)
|
||||||
}
|
}
|
||||||
}, it)
|
}, it)
|
||||||
}
|
}
|
||||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
testHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
promise.resume(bobAuthParams)
|
promise.resume(bobAuthParams)
|
||||||
}
|
}
|
||||||
}, it) }
|
}, it) }
|
||||||
|
|
||||||
// Check that alice can see bob keys
|
// Check that alice can see bob keys
|
||||||
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
|
testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
|
||||||
|
|
||||||
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
|
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
|
||||||
assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey())
|
assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey())
|
||||||
|
@ -124,13 +124,13 @@ class XSigningTest : InstrumentedTest {
|
||||||
|
|
||||||
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
|
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
mTestHelper.signOutAndClose(bobSession)
|
testHelper.signOutAndClose(bobSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_CrossSigningTestAliceTrustBobNewDevice() {
|
fun test_CrossSigningTestAliceTrustBobNewDevice() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
@ -144,12 +144,12 @@ class XSigningTest : InstrumentedTest {
|
||||||
password = TestConstants.PASSWORD
|
password = TestConstants.PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
testHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
promise.resume(aliceAuthParams)
|
promise.resume(aliceAuthParams)
|
||||||
}
|
}
|
||||||
}, it) }
|
}, it) }
|
||||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
testHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
promise.resume(bobAuthParams)
|
promise.resume(bobAuthParams)
|
||||||
}
|
}
|
||||||
|
@ -157,21 +157,21 @@ class XSigningTest : InstrumentedTest {
|
||||||
|
|
||||||
// Check that alice can see bob keys
|
// Check that alice can see bob keys
|
||||||
val bobUserId = bobSession.myUserId
|
val bobUserId = bobSession.myUserId
|
||||||
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) }
|
testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) }
|
||||||
|
|
||||||
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId)
|
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId)
|
||||||
assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false)
|
assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false)
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) }
|
testHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) }
|
||||||
|
|
||||||
// Now bobs logs in on a new device and verifies it
|
// Now bobs logs in on a new device and verifies it
|
||||||
// We will want to test that in alice POV, this new device would be trusted by cross signing
|
// We will want to test that in alice POV, this new device would be trusted by cross signing
|
||||||
|
|
||||||
val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true))
|
val bobSession2 = testHelper.logIntoAccount(bobUserId, SessionTestParams(true))
|
||||||
val bobSecondDeviceId = bobSession2.sessionParams.deviceId!!
|
val bobSecondDeviceId = bobSession2.sessionParams.deviceId!!
|
||||||
|
|
||||||
// Check that bob first session sees the new login
|
// Check that bob first session sees the new login
|
||||||
val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
val data = testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||||
bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
|
bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,12 +183,12 @@ class XSigningTest : InstrumentedTest {
|
||||||
assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice)
|
assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice)
|
||||||
|
|
||||||
// Manually mark it as trusted from first session
|
// Manually mark it as trusted from first session
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it)
|
bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now alice should cross trust bob's second device
|
// Now alice should cross trust bob's second device
|
||||||
val data2 = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
val data2 = testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||||
aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
|
aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,8 +200,8 @@ class XSigningTest : InstrumentedTest {
|
||||||
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
|
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
|
||||||
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
|
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
mTestHelper.signOutAndClose(bobSession)
|
testHelper.signOutAndClose(bobSession)
|
||||||
mTestHelper.signOutAndClose(bobSession2)
|
testHelper.signOutAndClose(bobSession2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,9 @@ import java.util.concurrent.CountDownLatch
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
class EncryptionTest : InstrumentedTest {
|
class EncryptionTest : InstrumentedTest {
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val testHelper = CommonTestHelper(context())
|
||||||
|
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_EncryptionEvent() {
|
fun test_EncryptionEvent() {
|
||||||
|
@ -69,7 +70,7 @@ class EncryptionTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
|
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val room = aliceSession.getRoom(cryptoTestData.roomId)!!
|
val room = aliceSession.getRoom(cryptoTestData.roomId)!!
|
||||||
|
@ -101,12 +102,12 @@ class EncryptionTest : InstrumentedTest {
|
||||||
timeline.addListener(timelineListener)
|
timeline.addListener(timelineListener)
|
||||||
|
|
||||||
action.invoke(room)
|
action.invoke(room)
|
||||||
|
testHelper.await(latch)
|
||||||
mTestHelper.await(latch)
|
|
||||||
timeline.dispose()
|
timeline.dispose()
|
||||||
|
testHelper.waitWithLatch {
|
||||||
room.isEncrypted() shouldBe roomShouldBeEncrypted
|
room.isEncrypted() shouldBe roomShouldBeEncrypted
|
||||||
|
it.countDown()
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
}
|
||||||
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import org.matrix.android.sdk.internal.crypto.GossipingRequestState
|
import org.matrix.android.sdk.internal.crypto.GossipingRequestState
|
||||||
|
@ -55,7 +54,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
|
@ -63,15 +61,14 @@ import kotlin.coroutines.resume
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class KeyShareTests : InstrumentedTest {
|
class KeyShareTests : InstrumentedTest {
|
||||||
|
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val commonTestHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_DoNotSelfShareIfNotTrusted() {
|
fun test_DoNotSelfShareIfNotTrusted() {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
// Create an encrypted room and add a message
|
// Create an encrypted room and add a message
|
||||||
val roomId = mTestHelper.runBlockingTest {
|
val roomId = commonTestHelper.runBlockingTest {
|
||||||
aliceSession.createRoom(
|
aliceSession.createRoom(
|
||||||
CreateRoomParams().apply {
|
CreateRoomParams().apply {
|
||||||
visibility = RoomDirectoryVisibility.PRIVATE
|
visibility = RoomDirectoryVisibility.PRIVATE
|
||||||
|
@ -83,11 +80,11 @@ class KeyShareTests : InstrumentedTest {
|
||||||
assertNotNull(room)
|
assertNotNull(room)
|
||||||
Thread.sleep(4_000)
|
Thread.sleep(4_000)
|
||||||
assertTrue(room?.isEncrypted() == true)
|
assertTrue(room?.isEncrypted() == true)
|
||||||
val sentEventId = mTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
|
val sentEventId = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
|
||||||
|
|
||||||
// Open a new sessionx
|
// Open a new sessionx
|
||||||
|
|
||||||
val aliceSession2 = mTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
||||||
|
|
||||||
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
|
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
|
||||||
|
|
||||||
|
@ -105,25 +102,24 @@ class KeyShareTests : InstrumentedTest {
|
||||||
// Try to request
|
// Try to request
|
||||||
aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root)
|
aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root)
|
||||||
|
|
||||||
val waitLatch = CountDownLatch(1)
|
|
||||||
val eventMegolmSessionId = receivedEvent.root.content.toModel<EncryptedEventContent>()?.sessionId
|
val eventMegolmSessionId = receivedEvent.root.content.toModel<EncryptedEventContent>()?.sessionId
|
||||||
|
|
||||||
var outGoingRequestId: String? = null
|
var outGoingRequestId: String? = null
|
||||||
|
|
||||||
mTestHelper.retryPeriodicallyWithLatch(waitLatch) {
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
.filter { req ->
|
aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||||
// filter out request that was known before
|
.filter { req ->
|
||||||
!outgoingRequestsBefore.any { req.requestId == it.requestId }
|
// filter out request that was known before
|
||||||
}
|
!outgoingRequestsBefore.any { req.requestId == it.requestId }
|
||||||
.let {
|
}
|
||||||
val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
|
.let {
|
||||||
outGoingRequestId = outgoing?.requestId
|
val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
|
||||||
outgoing != null
|
outGoingRequestId = outgoing?.requestId
|
||||||
}
|
outgoing != null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mTestHelper.await(waitLatch)
|
|
||||||
|
|
||||||
Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId")
|
Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId")
|
||||||
|
|
||||||
val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||||
|
@ -134,8 +130,8 @@ class KeyShareTests : InstrumentedTest {
|
||||||
|
|
||||||
// The first session should see an incoming request
|
// The first session should see an incoming request
|
||||||
// the request should be refused, because the device is not trusted
|
// the request should be refused, because the device is not trusted
|
||||||
mTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
// DEBUG LOGS
|
// DEBUG LOGS
|
||||||
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
||||||
Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
|
Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
|
||||||
|
@ -164,8 +160,8 @@ class KeyShareTests : InstrumentedTest {
|
||||||
// Re request
|
// Re request
|
||||||
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
|
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
|
||||||
|
|
||||||
mTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
||||||
Log.v("TEST", "Incoming request Session 1")
|
Log.v("TEST", "Incoming request Session 1")
|
||||||
Log.v("TEST", "=========================")
|
Log.v("TEST", "=========================")
|
||||||
|
@ -180,8 +176,8 @@ class KeyShareTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(6_000)
|
Thread.sleep(6_000)
|
||||||
mTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
aliceSession2.cryptoService().getOutgoingRoomKeyRequests().let {
|
aliceSession2.cryptoService().getOutgoingRoomKeyRequests().let {
|
||||||
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED }
|
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED }
|
||||||
}
|
}
|
||||||
|
@ -194,15 +190,15 @@ class KeyShareTests : InstrumentedTest {
|
||||||
fail("should have been able to decrypt")
|
fail("should have been able to decrypt")
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
commonTestHelper.signOutAndClose(aliceSession)
|
||||||
mTestHelper.signOutAndClose(aliceSession2)
|
commonTestHelper.signOutAndClose(aliceSession2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_ShareSSSSSecret() {
|
fun test_ShareSSSSSecret() {
|
||||||
val aliceSession1 = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession1 = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
commonTestHelper.doSync<Unit> {
|
||||||
aliceSession1.cryptoService().crossSigningService()
|
aliceSession1.cryptoService().crossSigningService()
|
||||||
.initializeCrossSigning(
|
.initializeCrossSigning(
|
||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
|
@ -218,25 +214,25 @@ class KeyShareTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also bootstrap keybackup on first session
|
// Also bootstrap keybackup on first session
|
||||||
val creationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> {
|
val creationInfo = commonTestHelper.doSync<MegolmBackupCreationInfo> {
|
||||||
aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
|
aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
|
||||||
}
|
}
|
||||||
val version = mTestHelper.doSync<KeysVersion> {
|
val version = commonTestHelper.doSync<KeysVersion> {
|
||||||
aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it)
|
aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it)
|
||||||
}
|
}
|
||||||
// Save it for gossiping
|
// Save it for gossiping
|
||||||
aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
|
aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
|
||||||
|
|
||||||
val aliceSession2 = mTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true))
|
val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true))
|
||||||
|
|
||||||
val aliceVerificationService1 = aliceSession1.cryptoService().verificationService()
|
val aliceVerificationService1 = aliceSession1.cryptoService().verificationService()
|
||||||
val aliceVerificationService2 = aliceSession2.cryptoService().verificationService()
|
val aliceVerificationService2 = aliceSession2.cryptoService().verificationService()
|
||||||
|
|
||||||
// force keys download
|
// force keys download
|
||||||
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
commonTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||||
aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it)
|
aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it)
|
||||||
}
|
}
|
||||||
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
commonTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||||
aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it)
|
aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,8 +272,8 @@ class KeyShareTests : InstrumentedTest {
|
||||||
aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId
|
aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId
|
||||||
?: "", txId)
|
?: "", txId)
|
||||||
|
|
||||||
mTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true
|
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,31 +286,31 @@ class KeyShareTests : InstrumentedTest {
|
||||||
|
|
||||||
// SSK and USK private keys should have been shared
|
// SSK and USK private keys should have been shared
|
||||||
|
|
||||||
mTestHelper.waitWithLatch(60_000) { latch ->
|
commonTestHelper.waitWithLatch(60_000) { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}")
|
Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}")
|
||||||
aliceSession2.cryptoService().crossSigningService().canCrossSign()
|
aliceSession2.cryptoService().crossSigningService().canCrossSign()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that key backup key has been shared to
|
// Test that key backup key has been shared to
|
||||||
mTestHelper.waitWithLatch(60_000) { latch ->
|
commonTestHelper.waitWithLatch(60_000) { latch ->
|
||||||
val keysBackupService = aliceSession2.cryptoService().keysBackupService()
|
val keysBackupService = aliceSession2.cryptoService().keysBackupService()
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}")
|
Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}")
|
||||||
keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey
|
keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession1)
|
commonTestHelper.signOutAndClose(aliceSession1)
|
||||||
mTestHelper.signOutAndClose(aliceSession2)
|
commonTestHelper.signOutAndClose(aliceSession2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_ImproperKeyShareBug() {
|
fun test_ImproperKeyShareBug() {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
commonTestHelper.doSync<Unit> {
|
||||||
aliceSession.cryptoService().crossSigningService()
|
aliceSession.cryptoService().crossSigningService()
|
||||||
.initializeCrossSigning(
|
.initializeCrossSigning(
|
||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
|
@ -331,7 +327,7 @@ class KeyShareTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an encrypted room and send a couple of messages
|
// Create an encrypted room and send a couple of messages
|
||||||
val roomId = mTestHelper.runBlockingTest {
|
val roomId = commonTestHelper.runBlockingTest {
|
||||||
aliceSession.createRoom(
|
aliceSession.createRoom(
|
||||||
CreateRoomParams().apply {
|
CreateRoomParams().apply {
|
||||||
visibility = RoomDirectoryVisibility.PRIVATE
|
visibility = RoomDirectoryVisibility.PRIVATE
|
||||||
|
@ -343,12 +339,12 @@ class KeyShareTests : InstrumentedTest {
|
||||||
assertNotNull(roomAlicePov)
|
assertNotNull(roomAlicePov)
|
||||||
Thread.sleep(1_000)
|
Thread.sleep(1_000)
|
||||||
assertTrue(roomAlicePov?.isEncrypted() == true)
|
assertTrue(roomAlicePov?.isEncrypted() == true)
|
||||||
val secondEventId = mTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
|
val secondEventId = commonTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
|
||||||
|
|
||||||
// Create bob session
|
// Create bob session
|
||||||
|
|
||||||
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
|
val bobSession = commonTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
|
||||||
mTestHelper.doSync<Unit> {
|
commonTestHelper.doSync<Unit> {
|
||||||
bobSession.cryptoService().crossSigningService()
|
bobSession.cryptoService().crossSigningService()
|
||||||
.initializeCrossSigning(
|
.initializeCrossSigning(
|
||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
|
@ -365,11 +361,11 @@ class KeyShareTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let alice invite bob
|
// Let alice invite bob
|
||||||
mTestHelper.runBlockingTest {
|
commonTestHelper.runBlockingTest {
|
||||||
roomAlicePov.invite(bobSession.myUserId, null)
|
roomAlicePov.invite(bobSession.myUserId, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
commonTestHelper.runBlockingTest {
|
||||||
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList())
|
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +373,7 @@ class KeyShareTests : InstrumentedTest {
|
||||||
aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId)
|
aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId)
|
||||||
|
|
||||||
// and now resend a new message to reset index to 0
|
// and now resend a new message to reset index to 0
|
||||||
mTestHelper.sendTextMessage(roomAlicePov, "After", 1)
|
commonTestHelper.sendTextMessage(roomAlicePov, "After", 1)
|
||||||
|
|
||||||
val roomRoomBobPov = aliceSession.getRoom(roomId)
|
val roomRoomBobPov = aliceSession.getRoom(roomId)
|
||||||
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
|
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
|
||||||
|
|
|
@ -41,8 +41,8 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class WithHeldTests : InstrumentedTest {
|
class WithHeldTests : InstrumentedTest {
|
||||||
|
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val testHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_WithHeldUnverifiedReason() {
|
fun test_WithHeldUnverifiedReason() {
|
||||||
|
@ -50,19 +50,19 @@ class WithHeldTests : InstrumentedTest {
|
||||||
// ARRANGE
|
// ARRANGE
|
||||||
// =============================
|
// =============================
|
||||||
|
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
|
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
|
||||||
|
|
||||||
// Initialize cross signing on both
|
// Initialize cross signing on both
|
||||||
mCryptoTestHelper.initializeCrossSigning(aliceSession)
|
cryptoTestHelper.initializeCrossSigning(aliceSession)
|
||||||
mCryptoTestHelper.initializeCrossSigning(bobSession)
|
cryptoTestHelper.initializeCrossSigning(bobSession)
|
||||||
|
|
||||||
val roomId = mCryptoTestHelper.createDM(aliceSession, bobSession)
|
val roomId = cryptoTestHelper.createDM(aliceSession, bobSession)
|
||||||
mCryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId)
|
cryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId)
|
||||||
|
|
||||||
val roomAlicePOV = aliceSession.getRoom(roomId)!!
|
val roomAlicePOV = aliceSession.getRoom(roomId)!!
|
||||||
|
|
||||||
val bobUnverifiedSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
|
val bobUnverifiedSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// ACT
|
// ACT
|
||||||
|
@ -71,11 +71,11 @@ class WithHeldTests : InstrumentedTest {
|
||||||
// Alice decide to not send to unverified sessions
|
// Alice decide to not send to unverified sessions
|
||||||
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(true)
|
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(true)
|
||||||
|
|
||||||
val timelineEvent = mTestHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first()
|
val timelineEvent = testHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first()
|
||||||
|
|
||||||
// await for bob unverified session to get the message
|
// await for bob unverified session to get the message
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null
|
bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,10 +101,10 @@ class WithHeldTests : InstrumentedTest {
|
||||||
// enable back sending to unverified
|
// enable back sending to unverified
|
||||||
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(false)
|
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(false)
|
||||||
|
|
||||||
val secondEvent = mTestHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first()
|
val secondEvent = testHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first()
|
||||||
|
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId)
|
val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId)
|
||||||
// wait until it's decrypted
|
// wait until it's decrypted
|
||||||
ev?.root?.getClearType() == EventType.MESSAGE
|
ev?.root?.getClearType() == EventType.MESSAGE
|
||||||
|
@ -123,17 +123,17 @@ class WithHeldTests : InstrumentedTest {
|
||||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
|
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
mTestHelper.signOutAndClose(bobSession)
|
testHelper.signOutAndClose(bobSession)
|
||||||
mTestHelper.signOutAndClose(bobUnverifiedSession)
|
testHelper.signOutAndClose(bobUnverifiedSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_WithHeldNoOlm() {
|
fun test_WithHeldNoOlm() {
|
||||||
val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
val bobSession = testData.secondSession!!
|
val bobSession = testData.secondSession!!
|
||||||
val aliceInterceptor = mTestHelper.getTestInterceptor(aliceSession)
|
val aliceInterceptor = testHelper.getTestInterceptor(aliceSession)
|
||||||
|
|
||||||
// Simulate no OTK
|
// Simulate no OTK
|
||||||
aliceInterceptor!!.addRule(MockOkHttpInterceptor.SimpleRule(
|
aliceInterceptor!!.addRule(MockOkHttpInterceptor.SimpleRule(
|
||||||
|
@ -147,11 +147,11 @@ class WithHeldTests : InstrumentedTest {
|
||||||
|
|
||||||
val roomAlicePov = aliceSession.getRoom(testData.roomId)!!
|
val roomAlicePov = aliceSession.getRoom(testData.roomId)!!
|
||||||
|
|
||||||
val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
|
val eventId = testHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
|
||||||
|
|
||||||
// await for bob session to get the message
|
// await for bob session to get the message
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null
|
bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,14 +177,14 @@ class WithHeldTests : InstrumentedTest {
|
||||||
// Add a new device for bob
|
// Add a new device for bob
|
||||||
|
|
||||||
aliceInterceptor.clearRules()
|
aliceInterceptor.clearRules()
|
||||||
val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true))
|
val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true))
|
||||||
// send a second message
|
// send a second message
|
||||||
val secondMessageId = mTestHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId
|
val secondMessageId = testHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId
|
||||||
|
|
||||||
// Check that the
|
// Check that the
|
||||||
// await for bob SecondSession session to get the message
|
// await for bob SecondSession session to get the message
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null
|
bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,27 +194,27 @@ class WithHeldTests : InstrumentedTest {
|
||||||
Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2)
|
Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2)
|
||||||
|
|
||||||
aliceInterceptor.clearRules()
|
aliceInterceptor.clearRules()
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
mTestHelper.signOutAndClose(bobSecondSession)
|
testHelper.signOutAndClose(bobSecondSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_WithHeldKeyRequest() {
|
fun test_WithHeldKeyRequest() {
|
||||||
val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
val bobSession = testData.secondSession!!
|
val bobSession = testData.secondSession!!
|
||||||
|
|
||||||
val roomAlicePov = aliceSession.getRoom(testData.roomId)!!
|
val roomAlicePov = aliceSession.getRoom(testData.roomId)!!
|
||||||
|
|
||||||
val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
|
val eventId = testHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(bobSession)
|
testHelper.signOutAndClose(bobSession)
|
||||||
|
|
||||||
// Create a new session for bob
|
// Create a new session for bob
|
||||||
|
|
||||||
val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
|
val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
|
||||||
// initialize to force request keys if missing
|
// initialize to force request keys if missing
|
||||||
mCryptoTestHelper.initializeCrossSigning(bobSecondSession)
|
cryptoTestHelper.initializeCrossSigning(bobSecondSession)
|
||||||
|
|
||||||
// Trust bob second device from Alice POV
|
// Trust bob second device from Alice POV
|
||||||
aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId!!, NoOpMatrixCallback())
|
aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId!!, NoOpMatrixCallback())
|
||||||
|
@ -223,8 +223,8 @@ class WithHeldTests : InstrumentedTest {
|
||||||
var sessionId: String? = null
|
var sessionId: String? = null
|
||||||
// Check that the
|
// Check that the
|
||||||
// await for bob SecondSession session to get the message
|
// await for bob SecondSession session to get the message
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also {
|
val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also {
|
||||||
// try to decrypt and force key request
|
// try to decrypt and force key request
|
||||||
tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
|
tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
|
||||||
|
@ -235,8 +235,8 @@ class WithHeldTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that bob second session requested the key
|
// Check that bob second session requested the key
|
||||||
mTestHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!)
|
val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!)
|
||||||
wc?.code == WithHeldCode.UNAUTHORISED
|
wc?.code == WithHeldCode.UNAUTHORISED
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.junit.Assert.assertFalse
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Assert.fail
|
|
||||||
import org.junit.FixMethodOrder
|
import org.junit.FixMethodOrder
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
@ -43,7 +42,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreat
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
|
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@ -51,9 +49,9 @@ import java.util.concurrent.CountDownLatch
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class KeysBackupTest : InstrumentedTest {
|
class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val testHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
private val mKeysBackupTestHelper = KeysBackupTestHelper(mTestHelper, mCryptoTestHelper)
|
private val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
|
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
|
||||||
|
@ -62,7 +60,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun roomKeysTest_testBackupStore_ok() {
|
fun roomKeysTest_testBackupStore_ok() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
// From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
|
// From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
|
||||||
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
|
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
|
||||||
|
@ -92,7 +90,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false))
|
assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false))
|
||||||
assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true))
|
assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true))
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,7 +98,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun prepareKeysBackupVersionTest() {
|
fun prepareKeysBackupVersionTest() {
|
||||||
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
||||||
|
|
||||||
assertNotNull(bobSession.cryptoService().keysBackupService())
|
assertNotNull(bobSession.cryptoService().keysBackupService())
|
||||||
|
|
||||||
|
@ -110,7 +108,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
val megolmBackupCreationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> {
|
val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
|
||||||
keysBackup.prepareKeysBackupVersion(null, null, it)
|
keysBackup.prepareKeysBackupVersion(null, null, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +118,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertNotNull(megolmBackupCreationInfo.recoveryKey)
|
assertNotNull(megolmBackupCreationInfo.recoveryKey)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
mTestHelper.signOutAndClose(bobSession)
|
testHelper.signOutAndClose(bobSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -128,7 +126,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun createKeysBackupVersionTest() {
|
fun createKeysBackupVersionTest() {
|
||||||
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
||||||
|
|
||||||
val keysBackup = bobSession.cryptoService().keysBackupService()
|
val keysBackup = bobSession.cryptoService().keysBackupService()
|
||||||
|
|
||||||
|
@ -136,14 +134,14 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
val megolmBackupCreationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> {
|
val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
|
||||||
keysBackup.prepareKeysBackupVersion(null, null, it)
|
keysBackup.prepareKeysBackupVersion(null, null, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
// Create the version
|
// Create the version
|
||||||
mTestHelper.doSync<KeysVersion> {
|
testHelper.doSync<KeysVersion> {
|
||||||
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +149,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertTrue(keysBackup.isEnabled)
|
assertTrue(keysBackup.isEnabled)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
mTestHelper.signOutAndClose(bobSession)
|
testHelper.signOutAndClose(bobSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,8 +158,9 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun backupAfterCreateKeysBackupVersionTest() {
|
fun backupAfterCreateKeysBackupVersionTest() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
|
keysBackupTestHelper.waitForKeybackUpBatching()
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
|
||||||
val latch = CountDownLatch(1)
|
val latch = CountDownLatch(1)
|
||||||
|
@ -171,9 +170,9 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
val stateObserver = StateObserver(keysBackup, latch, 5)
|
val stateObserver = StateObserver(keysBackup, latch, 5)
|
||||||
|
|
||||||
mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||||
|
|
||||||
mTestHelper.await(latch)
|
testHelper.await(latch)
|
||||||
|
|
||||||
val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
|
val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
|
||||||
val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)
|
val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)
|
||||||
|
@ -191,7 +190,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
KeysBackupState.ReadyToBackUp
|
KeysBackupState.ReadyToBackUp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -199,13 +198,13 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun backupAllGroupSessionsTest() {
|
fun backupAllGroupSessionsTest() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
|
||||||
val stateObserver = StateObserver(keysBackup)
|
val stateObserver = StateObserver(keysBackup)
|
||||||
|
|
||||||
mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||||
|
|
||||||
// Check that backupAllGroupSessions returns valid data
|
// Check that backupAllGroupSessions returns valid data
|
||||||
val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
|
val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
|
||||||
|
@ -214,7 +213,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
var lastBackedUpKeysProgress = 0
|
var lastBackedUpKeysProgress = 0
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
keysBackup.backupAllGroupSessions(object : ProgressListener {
|
keysBackup.backupAllGroupSessions(object : ProgressListener {
|
||||||
override fun onProgress(progress: Int, total: Int) {
|
override fun onProgress(progress: Int, total: Int) {
|
||||||
assertEquals(nbOfKeys, total)
|
assertEquals(nbOfKeys, total)
|
||||||
|
@ -230,7 +229,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
|
assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -243,7 +242,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testEncryptAndDecryptKeysBackupData() {
|
fun testEncryptAndDecryptKeysBackupData() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService
|
||||||
|
|
||||||
|
@ -252,7 +251,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
// - Pick a megolm key
|
// - Pick a megolm key
|
||||||
val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0]
|
val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0]
|
||||||
|
|
||||||
val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo
|
val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo
|
||||||
|
|
||||||
// - Check encryptGroupSession() returns stg
|
// - Check encryptGroupSession() returns stg
|
||||||
val keyBackupData = keysBackup.encryptGroupSession(session)
|
val keyBackupData = keysBackup.encryptGroupSession(session)
|
||||||
|
@ -270,10 +269,10 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
decryption!!)
|
decryption!!)
|
||||||
assertNotNull(sessionData)
|
assertNotNull(sessionData)
|
||||||
// - Compare the decrypted megolm key with the original one
|
// - Compare the decrypted megolm key with the original one
|
||||||
mKeysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
|
keysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -284,10 +283,10 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun restoreKeysBackupTest() {
|
fun restoreKeysBackupTest() {
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
|
||||||
// - Restore the e2e backup from the homeserver
|
// - Restore the e2e backup from the homeserver
|
||||||
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> {
|
val importRoomKeysResult = testHelper.doSync<ImportRoomKeysResult> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
||||||
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
|
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
|
||||||
null,
|
null,
|
||||||
|
@ -297,9 +296,9 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
||||||
|
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -369,7 +368,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
fun trustKeyBackupVersionTest() {
|
fun trustKeyBackupVersionTest() {
|
||||||
// - Do an e2e backup to the homeserver with a recovery key
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
// - And log Alice on a new device
|
// - And log Alice on a new device
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
|
||||||
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
||||||
|
|
||||||
|
@ -379,7 +378,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||||
|
|
||||||
// - Trust the backup from the new device
|
// - Trust the backup from the new device
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion(
|
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion(
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
||||||
true,
|
true,
|
||||||
|
@ -388,21 +387,21 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for backup state to be ReadyToBackUp
|
// Wait for backup state to be ReadyToBackUp
|
||||||
mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
|
keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
|
||||||
|
|
||||||
// - Backup must be enabled on the new device, on the same version
|
// - Backup must be enabled on the new device, on the same version
|
||||||
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
||||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||||
|
|
||||||
// - Retrieve the last version from the server
|
// - Retrieve the last version from the server
|
||||||
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> {
|
val keysVersionResult = testHelper.doSync<KeysVersionResult?> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
|
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// - It must be the same
|
// - It must be the same
|
||||||
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
|
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
|
||||||
|
|
||||||
val keysBackupVersionTrust = mTestHelper.doSync<KeysBackupVersionTrust> {
|
val keysBackupVersionTrust = testHelper.doSync<KeysBackupVersionTrust> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
|
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +410,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -428,7 +427,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
fun trustKeyBackupVersionWithRecoveryKeyTest() {
|
fun trustKeyBackupVersionWithRecoveryKeyTest() {
|
||||||
// - Do an e2e backup to the homeserver with a recovery key
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
// - And log Alice on a new device
|
// - And log Alice on a new device
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
|
||||||
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
||||||
|
|
||||||
|
@ -438,7 +437,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||||
|
|
||||||
// - Trust the backup from the new device with the recovery key
|
// - Trust the backup from the new device with the recovery key
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey(
|
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey(
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
||||||
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
|
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
|
||||||
|
@ -447,21 +446,21 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for backup state to be ReadyToBackUp
|
// Wait for backup state to be ReadyToBackUp
|
||||||
mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
|
keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
|
||||||
|
|
||||||
// - Backup must be enabled on the new device, on the same version
|
// - Backup must be enabled on the new device, on the same version
|
||||||
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
||||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||||
|
|
||||||
// - Retrieve the last version from the server
|
// - Retrieve the last version from the server
|
||||||
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> {
|
val keysVersionResult = testHelper.doSync<KeysVersionResult?> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
|
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// - It must be the same
|
// - It must be the same
|
||||||
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
|
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
|
||||||
|
|
||||||
val keysBackupVersionTrust = mTestHelper.doSync<KeysBackupVersionTrust> {
|
val keysBackupVersionTrust = testHelper.doSync<KeysBackupVersionTrust> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
|
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,7 +469,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -485,7 +484,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() {
|
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() {
|
||||||
// - Do an e2e backup to the homeserver with a recovery key
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
// - And log Alice on a new device
|
// - And log Alice on a new device
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
|
||||||
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
||||||
|
|
||||||
|
@ -501,7 +500,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
"Bad recovery key",
|
"Bad recovery key",
|
||||||
TestMatrixCallback(latch, false)
|
TestMatrixCallback(latch, false)
|
||||||
)
|
)
|
||||||
mTestHelper.await(latch)
|
testHelper.await(latch)
|
||||||
|
|
||||||
// - The new device must still see the previous backup as not trusted
|
// - The new device must still see the previous backup as not trusted
|
||||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||||
|
@ -509,7 +508,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -528,7 +527,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
// - Do an e2e backup to the homeserver with a password
|
// - Do an e2e backup to the homeserver with a password
|
||||||
// - And log Alice on a new device
|
// - And log Alice on a new device
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
||||||
|
|
||||||
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
||||||
|
|
||||||
|
@ -538,7 +537,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||||
|
|
||||||
// - Trust the backup from the new device with the password
|
// - Trust the backup from the new device with the password
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase(
|
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase(
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
||||||
password,
|
password,
|
||||||
|
@ -547,21 +546,21 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for backup state to be ReadyToBackUp
|
// Wait for backup state to be ReadyToBackUp
|
||||||
mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
|
keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
|
||||||
|
|
||||||
// - Backup must be enabled on the new device, on the same version
|
// - Backup must be enabled on the new device, on the same version
|
||||||
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
||||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||||
|
|
||||||
// - Retrieve the last version from the server
|
// - Retrieve the last version from the server
|
||||||
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> {
|
val keysVersionResult = testHelper.doSync<KeysVersionResult?> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
|
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// - It must be the same
|
// - It must be the same
|
||||||
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
|
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
|
||||||
|
|
||||||
val keysBackupVersionTrust = mTestHelper.doSync<KeysBackupVersionTrust> {
|
val keysBackupVersionTrust = testHelper.doSync<KeysBackupVersionTrust> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
|
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +569,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -588,7 +587,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
// - Do an e2e backup to the homeserver with a password
|
// - Do an e2e backup to the homeserver with a password
|
||||||
// - And log Alice on a new device
|
// - And log Alice on a new device
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
||||||
|
|
||||||
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
|
||||||
|
|
||||||
|
@ -604,7 +603,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
badPassword,
|
badPassword,
|
||||||
TestMatrixCallback(latch, false)
|
TestMatrixCallback(latch, false)
|
||||||
)
|
)
|
||||||
mTestHelper.await(latch)
|
testHelper.await(latch)
|
||||||
|
|
||||||
// - The new device must still see the previous backup as not trusted
|
// - The new device must still see the previous backup as not trusted
|
||||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||||
|
@ -612,7 +611,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -623,7 +622,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun restoreKeysBackupWithAWrongRecoveryKeyTest() {
|
fun restoreKeysBackupWithAWrongRecoveryKeyTest() {
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
|
||||||
// - Try to restore the e2e backup with a wrong recovery key
|
// - Try to restore the e2e backup with a wrong recovery key
|
||||||
val latch2 = CountDownLatch(1)
|
val latch2 = CountDownLatch(1)
|
||||||
|
@ -640,12 +639,12 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
mTestHelper.await(latch2)
|
testHelper.await(latch2)
|
||||||
|
|
||||||
// onSuccess may not have been called
|
// onSuccess may not have been called
|
||||||
assertNull(importRoomKeysResult)
|
assertNull(importRoomKeysResult)
|
||||||
|
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -658,12 +657,12 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
fun testBackupWithPassword() {
|
fun testBackupWithPassword() {
|
||||||
val password = "password"
|
val password = "password"
|
||||||
|
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
||||||
|
|
||||||
// - Restore the e2e backup with the password
|
// - Restore the e2e backup with the password
|
||||||
val steps = ArrayList<StepProgressListener.Step>()
|
val steps = ArrayList<StepProgressListener.Step>()
|
||||||
|
|
||||||
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> {
|
val importRoomKeysResult = testHelper.doSync<ImportRoomKeysResult> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
||||||
password,
|
password,
|
||||||
null,
|
null,
|
||||||
|
@ -698,9 +697,9 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress)
|
assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress)
|
||||||
assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress)
|
assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress)
|
||||||
|
|
||||||
mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
||||||
|
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -714,7 +713,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
val password = "password"
|
val password = "password"
|
||||||
val wrongPassword = "passw0rd"
|
val wrongPassword = "passw0rd"
|
||||||
|
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
||||||
|
|
||||||
// - Try to restore the e2e backup with a wrong password
|
// - Try to restore the e2e backup with a wrong password
|
||||||
val latch2 = CountDownLatch(1)
|
val latch2 = CountDownLatch(1)
|
||||||
|
@ -731,12 +730,12 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
mTestHelper.await(latch2)
|
testHelper.await(latch2)
|
||||||
|
|
||||||
// onSuccess may not have been called
|
// onSuccess may not have been called
|
||||||
assertNull(importRoomKeysResult)
|
assertNull(importRoomKeysResult)
|
||||||
|
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -749,10 +748,10 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() {
|
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() {
|
||||||
val password = "password"
|
val password = "password"
|
||||||
|
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
||||||
|
|
||||||
// - Restore the e2e backup with the recovery key.
|
// - Restore the e2e backup with the recovery key.
|
||||||
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> {
|
val importRoomKeysResult = testHelper.doSync<ImportRoomKeysResult> {
|
||||||
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
|
||||||
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
|
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
|
||||||
null,
|
null,
|
||||||
|
@ -762,9 +761,9 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
||||||
|
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -775,7 +774,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() {
|
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() {
|
||||||
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
|
||||||
// - Try to restore the e2e backup with a password
|
// - Try to restore the e2e backup with a password
|
||||||
val latch2 = CountDownLatch(1)
|
val latch2 = CountDownLatch(1)
|
||||||
|
@ -792,12 +791,12 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
mTestHelper.await(latch2)
|
testHelper.await(latch2)
|
||||||
|
|
||||||
// onSuccess may not have been called
|
// onSuccess may not have been called
|
||||||
assertNull(importRoomKeysResult)
|
assertNull(importRoomKeysResult)
|
||||||
|
|
||||||
testData.cleanUp(mTestHelper)
|
testData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -807,22 +806,22 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
@Test
|
@Test
|
||||||
fun testIsKeysBackupTrusted() {
|
fun testIsKeysBackupTrusted() {
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
|
||||||
val stateObserver = StateObserver(keysBackup)
|
val stateObserver = StateObserver(keysBackup)
|
||||||
|
|
||||||
// - Do an e2e backup to the homeserver
|
// - Do an e2e backup to the homeserver
|
||||||
mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||||
|
|
||||||
// Get key backup version from the homeserver
|
// Get key backup version from the homeserver
|
||||||
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> {
|
val keysVersionResult = testHelper.doSync<KeysVersionResult?> {
|
||||||
keysBackup.getCurrentVersion(it)
|
keysBackup.getCurrentVersion(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Check the returned KeyBackupVersion is trusted
|
// - Check the returned KeyBackupVersion is trusted
|
||||||
val keysBackupVersionTrust = mTestHelper.doSync<KeysBackupVersionTrust> {
|
val keysBackupVersionTrust = testHelper.doSync<KeysBackupVersionTrust> {
|
||||||
keysBackup.getKeysBackupTrust(keysVersionResult!!, it)
|
keysBackup.getKeysBackupTrust(keysVersionResult!!, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -837,7 +836,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
|
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -849,9 +848,8 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() {
|
fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() {
|
||||||
fail("This test still fail. To investigate")
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
|
||||||
|
@ -859,15 +857,15 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||||
|
|
||||||
assertTrue(keysBackup.isEnabled)
|
assertTrue(keysBackup.isEnabled)
|
||||||
|
|
||||||
// - Restart alice session
|
// - Restart alice session
|
||||||
// - Log Alice on a new device
|
// - Log Alice on a new device
|
||||||
val aliceSession2 = mTestHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
|
val aliceSession2 = testHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
|
|
||||||
val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
|
val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
|
||||||
|
|
||||||
|
@ -891,13 +889,13 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mTestHelper.await(latch)
|
testHelper.await(latch)
|
||||||
|
|
||||||
assertEquals(keyBackupCreationInfo.version, keysBackup2.currentBackupVersion)
|
assertEquals(keyBackupCreationInfo.version, keysBackup2.currentBackupVersion)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
stateObserver2.stopAndCheckStates(null)
|
stateObserver2.stopAndCheckStates(null)
|
||||||
mTestHelper.signOutAndClose(aliceSession2)
|
testHelper.signOutAndClose(aliceSession2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -911,7 +909,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
@Test
|
@Test
|
||||||
fun testBackupWhenAnotherBackupWasCreated() {
|
fun testBackupWhenAnotherBackupWasCreated() {
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
|
||||||
|
@ -939,15 +937,15 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
})
|
})
|
||||||
|
|
||||||
// - Make alice back up her keys to her homeserver
|
// - Make alice back up her keys to her homeserver
|
||||||
mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||||
|
|
||||||
assertTrue(keysBackup.isEnabled)
|
assertTrue(keysBackup.isEnabled)
|
||||||
|
|
||||||
mTestHelper.await(latch0)
|
testHelper.await(latch0)
|
||||||
|
|
||||||
// - Create a new backup with fake data on the homeserver, directly using the rest client
|
// - Create a new backup with fake data on the homeserver, directly using the rest client
|
||||||
val megolmBackupCreationInfo = mCryptoTestHelper.createFakeMegolmBackupCreationInfo()
|
val megolmBackupCreationInfo = cryptoTestHelper.createFakeMegolmBackupCreationInfo()
|
||||||
mTestHelper.doSync<KeysVersion> {
|
testHelper.doSync<KeysVersion> {
|
||||||
(keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, it)
|
(keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -957,14 +955,14 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
// - Make alice back up all her keys again
|
// - Make alice back up all her keys again
|
||||||
val latch2 = CountDownLatch(1)
|
val latch2 = CountDownLatch(1)
|
||||||
keysBackup.backupAllGroupSessions(null, TestMatrixCallback(latch2, false))
|
keysBackup.backupAllGroupSessions(null, TestMatrixCallback(latch2, false))
|
||||||
mTestHelper.await(latch2)
|
testHelper.await(latch2)
|
||||||
|
|
||||||
// -> That must fail and her backup state must be WrongBackUpVersion
|
// -> That must fail and her backup state must be WrongBackUpVersion
|
||||||
assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.state)
|
assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.state)
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -982,17 +980,17 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
@Test
|
@Test
|
||||||
fun testBackupAfterVerifyingADevice() {
|
fun testBackupAfterVerifyingADevice() {
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
|
||||||
val stateObserver = StateObserver(keysBackup)
|
val stateObserver = StateObserver(keysBackup)
|
||||||
|
|
||||||
// - Make alice back up her keys to her homeserver
|
// - Make alice back up her keys to her homeserver
|
||||||
mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||||
|
|
||||||
// Wait for keys backup to finish by asking again to backup keys.
|
// Wait for keys backup to finish by asking again to backup keys.
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
keysBackup.backupAllGroupSessions(null, it)
|
keysBackup.backupAllGroupSessions(null, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1001,14 +999,14 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
val aliceUserId = cryptoTestData.firstSession.myUserId
|
val aliceUserId = cryptoTestData.firstSession.myUserId
|
||||||
|
|
||||||
// - Log Alice on a new device
|
// - Log Alice on a new device
|
||||||
val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
|
val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
|
||||||
|
|
||||||
// - Post a message to have a new megolm session
|
// - Post a message to have a new megolm session
|
||||||
aliceSession2.cryptoService().setWarnOnUnknownDevices(false)
|
aliceSession2.cryptoService().setWarnOnUnknownDevices(false)
|
||||||
|
|
||||||
val room2 = aliceSession2.getRoom(cryptoTestData.roomId)!!
|
val room2 = aliceSession2.getRoom(cryptoTestData.roomId)!!
|
||||||
|
|
||||||
mTestHelper.sendTextMessage(room2, "New key", 1)
|
testHelper.sendTextMessage(room2, "New key", 1)
|
||||||
|
|
||||||
// - Try to backup all in aliceSession2, it must fail
|
// - Try to backup all in aliceSession2, it must fail
|
||||||
val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
|
val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
|
||||||
|
@ -1025,7 +1023,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
super.onSuccess(data)
|
super.onSuccess(data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mTestHelper.await(latch2)
|
testHelper.await(latch2)
|
||||||
|
|
||||||
assertFalse(isSuccessful)
|
assertFalse(isSuccessful)
|
||||||
|
|
||||||
|
@ -1049,12 +1047,12 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mTestHelper.await(latch4)
|
testHelper.await(latch4)
|
||||||
|
|
||||||
// -> It must use the same backup version
|
// -> It must use the same backup version
|
||||||
assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion)
|
assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion)
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it)
|
aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1063,8 +1061,8 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
stateObserver2.stopAndCheckStates(null)
|
stateObserver2.stopAndCheckStates(null)
|
||||||
mTestHelper.signOutAndClose(aliceSession2)
|
testHelper.signOutAndClose(aliceSession2)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1074,7 +1072,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
@Test
|
@Test
|
||||||
fun deleteKeysBackupTest() {
|
fun deleteKeysBackupTest() {
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
|
||||||
|
@ -1082,17 +1080,17 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||||
|
|
||||||
assertTrue(keysBackup.isEnabled)
|
assertTrue(keysBackup.isEnabled)
|
||||||
|
|
||||||
// Delete the backup
|
// Delete the backup
|
||||||
mTestHelper.doSync<Unit> { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) }
|
testHelper.doSync<Unit> { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) }
|
||||||
|
|
||||||
// Backup is now disabled
|
// Backup is now disabled
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,12 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
class KeysBackupTestHelper(
|
class KeysBackupTestHelper(
|
||||||
private val mTestHelper: CommonTestHelper,
|
private val testHelper: CommonTestHelper,
|
||||||
private val mCryptoTestHelper: CryptoTestHelper) {
|
private val cryptoTestHelper: CryptoTestHelper) {
|
||||||
|
|
||||||
|
fun waitForKeybackUpBatching() {
|
||||||
|
Thread.sleep(400)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common initial condition
|
* Common initial condition
|
||||||
|
@ -43,7 +47,9 @@ class KeysBackupTestHelper(
|
||||||
* @param password optional password
|
* @param password optional password
|
||||||
*/
|
*/
|
||||||
fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData {
|
fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
|
waitForKeybackUpBatching()
|
||||||
|
|
||||||
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
|
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
@ -57,7 +63,7 @@ class KeysBackupTestHelper(
|
||||||
|
|
||||||
var lastProgress = 0
|
var lastProgress = 0
|
||||||
var lastTotal = 0
|
var lastTotal = 0
|
||||||
mTestHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
keysBackup.backupAllGroupSessions(object : ProgressListener {
|
keysBackup.backupAllGroupSessions(object : ProgressListener {
|
||||||
override fun onProgress(progress: Int, total: Int) {
|
override fun onProgress(progress: Int, total: Int) {
|
||||||
lastProgress = progress
|
lastProgress = progress
|
||||||
|
@ -72,7 +78,7 @@ class KeysBackupTestHelper(
|
||||||
val aliceUserId = cryptoTestData.firstSession.myUserId
|
val aliceUserId = cryptoTestData.firstSession.myUserId
|
||||||
|
|
||||||
// - Log Alice on a new device
|
// - Log Alice on a new device
|
||||||
val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
|
val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
|
||||||
|
|
||||||
// Test check: aliceSession2 has no keys at login
|
// Test check: aliceSession2 has no keys at login
|
||||||
Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
|
Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
|
||||||
|
@ -92,7 +98,7 @@ class KeysBackupTestHelper(
|
||||||
password: String? = null): PrepareKeysBackupDataResult {
|
password: String? = null): PrepareKeysBackupDataResult {
|
||||||
val stateObserver = StateObserver(keysBackup)
|
val stateObserver = StateObserver(keysBackup)
|
||||||
|
|
||||||
val megolmBackupCreationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> {
|
val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
|
||||||
keysBackup.prepareKeysBackupVersion(password, null, it)
|
keysBackup.prepareKeysBackupVersion(password, null, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +107,7 @@ class KeysBackupTestHelper(
|
||||||
Assert.assertFalse(keysBackup.isEnabled)
|
Assert.assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
// Create the version
|
// Create the version
|
||||||
val keysVersion = mTestHelper.doSync<KeysVersion> {
|
val keysVersion = testHelper.doSync<KeysVersion> {
|
||||||
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +142,7 @@ class KeysBackupTestHelper(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mTestHelper.await(latch)
|
testHelper.await(latch)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) {
|
fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) {
|
||||||
|
|
|
@ -18,10 +18,6 @@ package org.matrix.android.sdk.internal.crypto.ssss
|
||||||
|
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
|
@ -36,6 +32,7 @@ import org.matrix.android.sdk.api.session.securestorage.EncryptedSecretContent
|
||||||
import org.matrix.android.sdk.api.session.securestorage.KeySigner
|
import org.matrix.android.sdk.api.session.securestorage.KeySigner
|
||||||
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
|
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
|
||||||
import org.matrix.android.sdk.api.session.securestorage.SecretStorageKeyContent
|
import org.matrix.android.sdk.api.session.securestorage.SecretStorageKeyContent
|
||||||
|
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageError
|
||||||
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
|
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
|
||||||
import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo
|
import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
@ -45,13 +42,12 @@ import org.matrix.android.sdk.common.TestConstants
|
||||||
import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
||||||
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
||||||
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class QuadSTests : InstrumentedTest {
|
class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val testHelper = CommonTestHelper(context())
|
||||||
|
|
||||||
private val emptyKeySigner = object : KeySigner {
|
private val emptyKeySigner = object : KeySigner {
|
||||||
override fun sign(canonicalJson: String): Map<String, Map<String, String>>? {
|
override fun sign(canonicalJson: String): Map<String, Map<String, String>>? {
|
||||||
|
@ -60,35 +56,29 @@ class QuadSTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun test_Generate4SKey() {
|
fun test_Generate4SKey() {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
val quadS = aliceSession.sharedSecretStorageService
|
val quadS = aliceSession.sharedSecretStorageService
|
||||||
|
|
||||||
val TEST_KEY_ID = "my.test.Key"
|
val TEST_KEY_ID = "my.test.Key"
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
|
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert Account data is updated
|
|
||||||
val accountDataLock = CountDownLatch(1)
|
|
||||||
var accountData: UserAccountDataEvent? = null
|
var accountData: UserAccountDataEvent? = null
|
||||||
|
// Assert Account data is updated
|
||||||
val liveAccountData = runBlocking(Dispatchers.Main) {
|
testHelper.waitWithLatch {
|
||||||
aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
|
val liveAccountData = aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
|
||||||
}
|
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
||||||
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
|
||||||
if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
|
accountData = t.getOrNull()
|
||||||
accountData = t.getOrNull()
|
}
|
||||||
accountDataLock.countDown()
|
it.countDown()
|
||||||
}
|
}
|
||||||
|
liveAccountData.observeForever(accountDataObserver)
|
||||||
}
|
}
|
||||||
GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
|
|
||||||
|
|
||||||
mTestHelper.await(accountDataLock)
|
|
||||||
|
|
||||||
assertNotNull("Key should be stored in account data", accountData)
|
assertNotNull("Key should be stored in account data", accountData)
|
||||||
val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
|
val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
|
||||||
assertNotNull("Key Content cannot be parsed", parsed)
|
assertNotNull("Key Content cannot be parsed", parsed)
|
||||||
|
@ -96,36 +86,29 @@ class QuadSTests : InstrumentedTest {
|
||||||
assertEquals("Unexpected key name", "Test Key", parsed.name)
|
assertEquals("Unexpected key name", "Test Key", parsed.name)
|
||||||
assertNull("Key was not generated from passphrase", parsed.passphrase)
|
assertNull("Key was not generated from passphrase", parsed.passphrase)
|
||||||
|
|
||||||
// Set as default key
|
|
||||||
GlobalScope.launch {
|
|
||||||
quadS.setDefaultKey(TEST_KEY_ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultKeyAccountData: UserAccountDataEvent? = null
|
var defaultKeyAccountData: UserAccountDataEvent? = null
|
||||||
val defaultDataLock = CountDownLatch(1)
|
// Set as default key
|
||||||
|
testHelper.waitWithLatch { latch ->
|
||||||
val liveDefAccountData = runBlocking(Dispatchers.Main) {
|
quadS.setDefaultKey(TEST_KEY_ID)
|
||||||
aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
val liveDefAccountData =
|
||||||
}
|
aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||||
val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
||||||
if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
|
if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
|
||||||
defaultKeyAccountData = t.getOrNull()!!
|
defaultKeyAccountData = t.getOrNull()!!
|
||||||
defaultDataLock.countDown()
|
latch.countDown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
liveDefAccountData.observeForever(accountDefDataObserver)
|
||||||
}
|
}
|
||||||
GlobalScope.launch(Dispatchers.Main) { liveDefAccountData.observeForever(accountDefDataObserver) }
|
|
||||||
|
|
||||||
mTestHelper.await(defaultDataLock)
|
|
||||||
|
|
||||||
assertNotNull(defaultKeyAccountData?.content)
|
assertNotNull(defaultKeyAccountData?.content)
|
||||||
assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
|
assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_StoreSecret() {
|
fun test_StoreSecret() {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
val keyId = "My.Key"
|
val keyId = "My.Key"
|
||||||
val info = generatedSecret(aliceSession, keyId, true)
|
val info = generatedSecret(aliceSession, keyId, true)
|
||||||
|
|
||||||
|
@ -133,7 +116,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
// Store a secret
|
// Store a secret
|
||||||
val clearSecret = "42".toByteArray().toBase64NoPadding()
|
val clearSecret = "42".toByteArray().toBase64NoPadding()
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"secret.of.life",
|
"secret.of.life",
|
||||||
clearSecret,
|
clearSecret,
|
||||||
|
@ -154,7 +137,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
// Try to decrypt??
|
// Try to decrypt??
|
||||||
|
|
||||||
val decryptedSecret = mTestHelper.runBlockingTest {
|
val decryptedSecret = testHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret(
|
aliceSession.sharedSecretStorageService.getSecret(
|
||||||
"secret.of.life",
|
"secret.of.life",
|
||||||
null, // default key
|
null, // default key
|
||||||
|
@ -163,32 +146,32 @@ class QuadSTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals("Secret mismatch", clearSecret, decryptedSecret)
|
assertEquals("Secret mismatch", clearSecret, decryptedSecret)
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_SetDefaultLocalEcho() {
|
fun test_SetDefaultLocalEcho() {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
val quadS = aliceSession.sharedSecretStorageService
|
val quadS = aliceSession.sharedSecretStorageService
|
||||||
|
|
||||||
val TEST_KEY_ID = "my.test.Key"
|
val TEST_KEY_ID = "my.test.Key"
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
|
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that we don't need to wait for an account data sync to access directly the keyid from DB
|
// Test that we don't need to wait for an account data sync to access directly the keyid from DB
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(TEST_KEY_ID)
|
quadS.setDefaultKey(TEST_KEY_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_StoreSecretWithMultipleKey() {
|
fun test_StoreSecretWithMultipleKey() {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
val keyId1 = "Key.1"
|
val keyId1 = "Key.1"
|
||||||
val key1Info = generatedSecret(aliceSession, keyId1, true)
|
val key1Info = generatedSecret(aliceSession, keyId1, true)
|
||||||
val keyId2 = "Key2"
|
val keyId2 = "Key2"
|
||||||
|
@ -196,7 +179,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"my.secret",
|
"my.secret",
|
||||||
mySecretText.toByteArray().toBase64NoPadding(),
|
mySecretText.toByteArray().toBase64NoPadding(),
|
||||||
|
@ -216,33 +199,33 @@ class QuadSTests : InstrumentedTest {
|
||||||
assertNotNull(encryptedContent?.get(keyId2))
|
assertNotNull(encryptedContent?.get(keyId2))
|
||||||
|
|
||||||
// Assert that can decrypt with both keys
|
// Assert that can decrypt with both keys
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId1,
|
keyId1,
|
||||||
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!
|
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId2,
|
keyId2,
|
||||||
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
|
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_GetSecretWithBadPassphrase() {
|
fun test_GetSecretWithBadPassphrase() {
|
||||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
val keyId1 = "Key.1"
|
val keyId1 = "Key.1"
|
||||||
val passphrase = "The good pass phrase"
|
val passphrase = "The good pass phrase"
|
||||||
val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true)
|
val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true)
|
||||||
|
|
||||||
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"my.secret",
|
"my.secret",
|
||||||
mySecretText.toByteArray().toBase64NoPadding(),
|
mySecretText.toByteArray().toBase64NoPadding(),
|
||||||
|
@ -250,19 +233,23 @@ class QuadSTests : InstrumentedTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
try {
|
||||||
keyId1,
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
RawBytesKeySpec.fromPassphrase(
|
keyId1,
|
||||||
"A bad passphrase",
|
RawBytesKeySpec.fromPassphrase(
|
||||||
key1Info.content?.passphrase?.salt ?: "",
|
"A bad passphrase",
|
||||||
key1Info.content?.passphrase?.iterations ?: 0,
|
key1Info.content?.passphrase?.salt ?: "",
|
||||||
null)
|
key1Info.content?.passphrase?.iterations ?: 0,
|
||||||
)
|
null)
|
||||||
|
)
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
assert(throwable is SharedSecretStorageError.BadMac)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now try with correct key
|
// Now try with correct key
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId1,
|
keyId1,
|
||||||
RawBytesKeySpec.fromPassphrase(
|
RawBytesKeySpec.fromPassphrase(
|
||||||
|
@ -273,42 +260,36 @@ class QuadSTests : InstrumentedTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
testHelper.signOutAndClose(aliceSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
|
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
|
||||||
val accountDataLock = CountDownLatch(1)
|
|
||||||
var accountData: UserAccountDataEvent? = null
|
var accountData: UserAccountDataEvent? = null
|
||||||
|
testHelper.waitWithLatch {
|
||||||
val liveAccountData = runBlocking(Dispatchers.Main) {
|
val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type)
|
||||||
session.accountDataService().getLiveUserAccountDataEvent(type)
|
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
||||||
}
|
if (t?.getOrNull()?.type == type) {
|
||||||
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
|
accountData = t.getOrNull()
|
||||||
if (t?.getOrNull()?.type == type) {
|
it.countDown()
|
||||||
accountData = t.getOrNull()
|
}
|
||||||
accountDataLock.countDown()
|
|
||||||
}
|
}
|
||||||
|
liveAccountData.observeForever(accountDataObserver)
|
||||||
}
|
}
|
||||||
GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
|
|
||||||
mTestHelper.await(accountDataLock)
|
|
||||||
|
|
||||||
assertNotNull("Account Data type:$type should be found", accountData)
|
assertNotNull("Account Data type:$type should be found", accountData)
|
||||||
|
|
||||||
return accountData!!
|
return accountData!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||||
val quadS = session.sharedSecretStorageService
|
val quadS = session.sharedSecretStorageService
|
||||||
|
|
||||||
val creationInfo = mTestHelper.runBlockingTest {
|
val creationInfo = testHelper.runBlockingTest {
|
||||||
quadS.generateKey(keyId, null, keyId, emptyKeySigner)
|
quadS.generateKey(keyId, null, keyId, emptyKeySigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||||
|
|
||||||
if (asDefault) {
|
if (asDefault) {
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(keyId)
|
quadS.setDefaultKey(keyId)
|
||||||
}
|
}
|
||||||
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||||
|
@ -320,7 +301,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||||
val quadS = session.sharedSecretStorageService
|
val quadS = session.sharedSecretStorageService
|
||||||
|
|
||||||
val creationInfo = mTestHelper.runBlockingTest {
|
val creationInfo = testHelper.runBlockingTest {
|
||||||
quadS.generateKeyWithPassphrase(
|
quadS.generateKeyWithPassphrase(
|
||||||
keyId,
|
keyId,
|
||||||
keyId,
|
keyId,
|
||||||
|
@ -331,7 +312,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||||
if (asDefault) {
|
if (asDefault) {
|
||||||
mTestHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(keyId)
|
quadS.setDefaultKey(keyId)
|
||||||
}
|
}
|
||||||
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||||
|
|
|
@ -53,12 +53,12 @@ import java.util.concurrent.CountDownLatch
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
class SASTest : InstrumentedTest {
|
class SASTest : InstrumentedTest {
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val testHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_aliceStartThenAliceCancel() {
|
fun test_aliceStartThenAliceCancel() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
@ -83,7 +83,7 @@ class SASTest : InstrumentedTest {
|
||||||
val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!)
|
val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!)
|
||||||
assertNotNull("Alice should have a started transaction", aliceKeyTx)
|
assertNotNull("Alice should have a started transaction", aliceKeyTx)
|
||||||
|
|
||||||
mTestHelper.await(bobTxCreatedLatch)
|
testHelper.await(bobTxCreatedLatch)
|
||||||
bobVerificationService.removeListener(bobListener)
|
bobVerificationService.removeListener(bobListener)
|
||||||
|
|
||||||
val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)
|
val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)
|
||||||
|
@ -116,7 +116,7 @@ class SASTest : InstrumentedTest {
|
||||||
bobVerificationService.addListener(bobListener2)
|
bobVerificationService.addListener(bobListener2)
|
||||||
|
|
||||||
aliceSasTx.cancel(CancelCode.User)
|
aliceSasTx.cancel(CancelCode.User)
|
||||||
mTestHelper.await(cancelLatch)
|
testHelper.await(cancelLatch)
|
||||||
|
|
||||||
assertTrue("Should be cancelled on alice side", aliceSasTx.state is VerificationTxState.Cancelled)
|
assertTrue("Should be cancelled on alice side", aliceSasTx.state is VerificationTxState.Cancelled)
|
||||||
assertTrue("Should be cancelled on bob side", bobSasTx.state is VerificationTxState.Cancelled)
|
assertTrue("Should be cancelled on bob side", bobSasTx.state is VerificationTxState.Cancelled)
|
||||||
|
@ -133,13 +133,13 @@ class SASTest : InstrumentedTest {
|
||||||
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
|
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
|
||||||
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
|
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_key_agreement_protocols_must_include_curve25519() {
|
fun test_key_agreement_protocols_must_include_curve25519() {
|
||||||
fail("Not passing for the moment")
|
fail("Not passing for the moment")
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val bobSession = cryptoTestData.secondSession!!
|
val bobSession = cryptoTestData.secondSession!!
|
||||||
|
|
||||||
|
@ -186,17 +186,17 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols)
|
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols)
|
||||||
|
|
||||||
mTestHelper.await(cancelLatch)
|
testHelper.await(cancelLatch)
|
||||||
|
|
||||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
|
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_key_agreement_macs_Must_include_hmac_sha256() {
|
fun test_key_agreement_macs_Must_include_hmac_sha256() {
|
||||||
fail("Not passing for the moment")
|
fail("Not passing for the moment")
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val bobSession = cryptoTestData.secondSession!!
|
val bobSession = cryptoTestData.secondSession!!
|
||||||
|
|
||||||
|
@ -223,18 +223,18 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac)
|
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac)
|
||||||
|
|
||||||
mTestHelper.await(cancelLatch)
|
testHelper.await(cancelLatch)
|
||||||
|
|
||||||
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
||||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_key_agreement_short_code_include_decimal() {
|
fun test_key_agreement_short_code_include_decimal() {
|
||||||
fail("Not passing for the moment")
|
fail("Not passing for the moment")
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val bobSession = cryptoTestData.secondSession!!
|
val bobSession = cryptoTestData.secondSession!!
|
||||||
|
|
||||||
|
@ -261,12 +261,12 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes)
|
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes)
|
||||||
|
|
||||||
mTestHelper.await(cancelLatch)
|
testHelper.await(cancelLatch)
|
||||||
|
|
||||||
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
||||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fakeBobStart(bobSession: Session,
|
private fun fakeBobStart(bobSession: Session,
|
||||||
|
@ -303,7 +303,7 @@ class SASTest : InstrumentedTest {
|
||||||
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
|
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
|
||||||
@Test
|
@Test
|
||||||
fun test_aliceStartTwoRequests() {
|
fun test_aliceStartTwoRequests() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
@ -332,10 +332,10 @@ class SASTest : InstrumentedTest {
|
||||||
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
||||||
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
||||||
|
|
||||||
mTestHelper.await(aliceCreatedLatch)
|
testHelper.await(aliceCreatedLatch)
|
||||||
mTestHelper.await(aliceCancelledLatch)
|
testHelper.await(aliceCancelledLatch)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -343,7 +343,7 @@ class SASTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun test_aliceAndBobAgreement() {
|
fun test_aliceAndBobAgreement() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
@ -383,7 +383,7 @@ class SASTest : InstrumentedTest {
|
||||||
val bobUserId = bobSession.myUserId
|
val bobUserId = bobSession.myUserId
|
||||||
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
|
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
|
||||||
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
||||||
mTestHelper.await(aliceAcceptedLatch)
|
testHelper.await(aliceAcceptedLatch)
|
||||||
|
|
||||||
assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false)
|
assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false)
|
||||||
|
|
||||||
|
@ -397,12 +397,12 @@ class SASTest : InstrumentedTest {
|
||||||
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it))
|
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it))
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_aliceAndBobSASCode() {
|
fun test_aliceAndBobSASCode() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
@ -444,8 +444,8 @@ class SASTest : InstrumentedTest {
|
||||||
val bobUserId = bobSession.myUserId
|
val bobUserId = bobSession.myUserId
|
||||||
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
|
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
|
||||||
val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
||||||
mTestHelper.await(aliceSASLatch)
|
testHelper.await(aliceSASLatch)
|
||||||
mTestHelper.await(bobSASLatch)
|
testHelper.await(bobSASLatch)
|
||||||
|
|
||||||
val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction
|
val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction
|
||||||
val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SASDefaultVerificationTransaction
|
val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SASDefaultVerificationTransaction
|
||||||
|
@ -453,12 +453,12 @@ class SASTest : InstrumentedTest {
|
||||||
assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
|
assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
|
||||||
bobTx.getShortCodeRepresentation(SasMode.DECIMAL))
|
bobTx.getShortCodeRepresentation(SasMode.DECIMAL))
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_happyPath() {
|
fun test_happyPath() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
@ -520,8 +520,8 @@ class SASTest : InstrumentedTest {
|
||||||
val bobUserId = bobSession.myUserId
|
val bobUserId = bobSession.myUserId
|
||||||
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
|
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
|
||||||
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
|
||||||
mTestHelper.await(aliceSASLatch)
|
testHelper.await(aliceSASLatch)
|
||||||
mTestHelper.await(bobSASLatch)
|
testHelper.await(bobSASLatch)
|
||||||
|
|
||||||
// Assert that devices are verified
|
// Assert that devices are verified
|
||||||
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId)
|
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId)
|
||||||
|
@ -532,12 +532,12 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified)
|
assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified)
|
||||||
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
|
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_ConcurrentStart() {
|
fun test_ConcurrentStart() {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
@ -553,8 +553,8 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
var requestID: String? = null
|
var requestID: String? = null
|
||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
||||||
requestID = prAlicePOV?.transactionId
|
requestID = prAlicePOV?.transactionId
|
||||||
Log.v("TEST", "== alicePOV is $prAlicePOV")
|
Log.v("TEST", "== alicePOV is $prAlicePOV")
|
||||||
|
@ -564,8 +564,8 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
Log.v("TEST", "== requestID is $requestID")
|
Log.v("TEST", "== requestID is $requestID")
|
||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull()
|
val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull()
|
||||||
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
||||||
prBobPOV?.transactionId == requestID
|
prBobPOV?.transactionId == requestID
|
||||||
|
@ -579,8 +579,8 @@ class SASTest : InstrumentedTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// wait for alice to get the ready
|
// wait for alice to get the ready
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
||||||
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
||||||
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
|
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
|
||||||
|
@ -606,22 +606,22 @@ class SASTest : InstrumentedTest {
|
||||||
var alicePovTx: SasVerificationTransaction?
|
var alicePovTx: SasVerificationTransaction?
|
||||||
var bobPovTx: SasVerificationTransaction?
|
var bobPovTx: SasVerificationTransaction?
|
||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID!!) as? SasVerificationTransaction
|
alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID!!) as? SasVerificationTransaction
|
||||||
Log.v("TEST", "== alicePovTx is $alicePovTx")
|
Log.v("TEST", "== alicePovTx is $alicePovTx")
|
||||||
alicePovTx?.state == VerificationTxState.ShortCodeReady
|
alicePovTx?.state == VerificationTxState.ShortCodeReady
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// wait for alice to get the ready
|
// wait for alice to get the ready
|
||||||
mTestHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID!!) as? SasVerificationTransaction
|
bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID!!) as? SasVerificationTransaction
|
||||||
Log.v("TEST", "== bobPovTx is $bobPovTx")
|
Log.v("TEST", "== bobPovTx is $bobPovTx")
|
||||||
bobPovTx?.state == VerificationTxState.ShortCodeReady
|
bobPovTx?.state == VerificationTxState.ShortCodeReady
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,8 @@ import kotlin.coroutines.resume
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class VerificationTest : InstrumentedTest {
|
class VerificationTest : InstrumentedTest {
|
||||||
private val mTestHelper = CommonTestHelper(context())
|
private val testHelper = CommonTestHelper(context())
|
||||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
|
|
||||||
data class ExpectedResult(
|
data class ExpectedResult(
|
||||||
val sasIsSupported: Boolean = false,
|
val sasIsSupported: Boolean = false,
|
||||||
|
@ -155,12 +155,12 @@ class VerificationTest : InstrumentedTest {
|
||||||
bobSupportedMethods: List<VerificationMethod>,
|
bobSupportedMethods: List<VerificationMethod>,
|
||||||
expectedResultForAlice: ExpectedResult,
|
expectedResultForAlice: ExpectedResult,
|
||||||
expectedResultForBob: ExpectedResult) {
|
expectedResultForBob: ExpectedResult) {
|
||||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession!!
|
val bobSession = cryptoTestData.secondSession!!
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> { callback ->
|
testHelper.doSync<Unit> { callback ->
|
||||||
aliceSession.cryptoService().crossSigningService()
|
aliceSession.cryptoService().crossSigningService()
|
||||||
.initializeCrossSigning(
|
.initializeCrossSigning(
|
||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
|
@ -176,7 +176,7 @@ class VerificationTest : InstrumentedTest {
|
||||||
}, callback)
|
}, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> { callback ->
|
testHelper.doSync<Unit> { callback ->
|
||||||
bobSession.cryptoService().crossSigningService()
|
bobSession.cryptoService().crossSigningService()
|
||||||
.initializeCrossSigning(
|
.initializeCrossSigning(
|
||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
|
@ -234,7 +234,7 @@ class VerificationTest : InstrumentedTest {
|
||||||
val bobUserId = bobSession.myUserId
|
val bobUserId = bobSession.myUserId
|
||||||
// Step 1: Alice starts a verification request
|
// Step 1: Alice starts a verification request
|
||||||
aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId)
|
aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId)
|
||||||
mTestHelper.await(latch)
|
testHelper.await(latch)
|
||||||
|
|
||||||
aliceReadyPendingVerificationRequest!!.let { pr ->
|
aliceReadyPendingVerificationRequest!!.let { pr ->
|
||||||
pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported
|
pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported
|
||||||
|
@ -248,6 +248,6 @@ class VerificationTest : InstrumentedTest {
|
||||||
pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode
|
pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoTestData.cleanUp(mTestHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ object RoomDataHelper {
|
||||||
content: Content? = null,
|
content: Content? = null,
|
||||||
prevContent: Content? = null,
|
prevContent: Content? = null,
|
||||||
sender: String = FAKE_TEST_SENDER,
|
sender: String = FAKE_TEST_SENDER,
|
||||||
stateKey: String = FAKE_TEST_SENDER
|
stateKey: String? = null
|
||||||
): Event {
|
): Event {
|
||||||
return Event(
|
return Event(
|
||||||
type = type,
|
type = type,
|
||||||
|
@ -64,6 +64,6 @@ object RoomDataHelper {
|
||||||
|
|
||||||
private fun createFakeRoomMemberEvent(): Event {
|
private fun createFakeRoomMemberEvent(): Event {
|
||||||
val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
|
val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
|
||||||
return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
|
return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember, stateKey = FAKE_TEST_SENDER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,14 +65,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
||||||
message,
|
message,
|
||||||
numberOfMessagesToSend)
|
numberOfMessagesToSend)
|
||||||
|
|
||||||
// Alice clear the cache
|
// Alice clear the cache and restart the sync
|
||||||
commonTestHelper.runBlockingTest {
|
commonTestHelper.clearCacheAndSync(aliceSession)
|
||||||
aliceSession.clearCache()
|
|
||||||
}
|
|
||||||
|
|
||||||
// And restarts the sync
|
|
||||||
aliceSession.startSync(true)
|
|
||||||
|
|
||||||
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30))
|
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30))
|
||||||
aliceTimeline.start()
|
aliceTimeline.start()
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,10 @@ import org.junit.runners.JUnit4
|
||||||
import org.junit.runners.MethodSorters
|
import org.junit.runners.MethodSorters
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
|
||||||
import org.matrix.android.sdk.api.session.search.SearchResult
|
import org.matrix.android.sdk.api.session.search.SearchResult
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
import org.matrix.android.sdk.common.CryptoTestData
|
import org.matrix.android.sdk.common.CryptoTestData
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
@ -84,24 +80,12 @@ class SearchMessagesTest : InstrumentedTest {
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val aliceRoomId = cryptoTestData.roomId
|
val aliceRoomId = cryptoTestData.roomId
|
||||||
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
||||||
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(10))
|
|
||||||
aliceTimeline.start()
|
|
||||||
|
|
||||||
val lock = CountDownLatch(1)
|
|
||||||
|
|
||||||
val eventListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
|
||||||
snapshot.count { it.root.content.toModel<MessageContent>()?.body?.startsWith(MESSAGE).orFalse() } == 2
|
|
||||||
}
|
|
||||||
|
|
||||||
aliceTimeline.addListener(eventListener)
|
|
||||||
|
|
||||||
commonTestHelper.sendTextMessage(
|
commonTestHelper.sendTextMessage(
|
||||||
roomFromAlicePOV,
|
roomFromAlicePOV,
|
||||||
MESSAGE,
|
MESSAGE,
|
||||||
2)
|
2)
|
||||||
|
|
||||||
commonTestHelper.await(lock)
|
|
||||||
|
|
||||||
val data = commonTestHelper.runBlockingTest {
|
val data = commonTestHelper.runBlockingTest {
|
||||||
block.invoke(cryptoTestData)
|
block.invoke(cryptoTestData)
|
||||||
}
|
}
|
||||||
|
@ -114,7 +98,6 @@ class SearchMessagesTest : InstrumentedTest {
|
||||||
}.orFalse()
|
}.orFalse()
|
||||||
)
|
)
|
||||||
|
|
||||||
aliceTimeline.removeAllListeners()
|
|
||||||
cryptoTestData.cleanUp(commonTestHelper)
|
cryptoTestData.cleanUp(commonTestHelper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,7 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.session.space
|
package org.matrix.android.sdk.session.space
|
||||||
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
|
@ -50,18 +48,15 @@ class SpaceCreationTest : InstrumentedTest {
|
||||||
private val commonTestHelper = CommonTestHelper(context())
|
private val commonTestHelper = CommonTestHelper(context())
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun createSimplePublicSpace() {
|
fun createSimplePublicSpace() {
|
||||||
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
|
||||||
val roomName = "My Space"
|
val roomName = "My Space"
|
||||||
val topic = "A public space for test"
|
val topic = "A public space for test"
|
||||||
var spaceId: String = ""
|
var spaceId: String = ""
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
spaceId = session.spaceService().createSpace(roomName, topic, null, true)
|
||||||
spaceId = session.spaceService().createSpace(roomName, topic, null, true)
|
// wait a bit to let the summary update it self :/
|
||||||
// wait a bit to let the summary update it self :/
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||||
|
@ -134,7 +129,6 @@ class SpaceCreationTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun testSimplePublicSpaceWithChildren() {
|
fun testSimplePublicSpaceWithChildren() {
|
||||||
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
|
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
|
||||||
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
|
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
|
||||||
|
@ -148,50 +142,40 @@ class SpaceCreationTest : InstrumentedTest {
|
||||||
// create a room
|
// create a room
|
||||||
var firstChild: String? = null
|
var firstChild: String? = null
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
firstChild = aliceSession.createRoom(CreateRoomParams().apply {
|
||||||
firstChild = aliceSession.createRoom(CreateRoomParams().apply {
|
this.name = "FirstRoom"
|
||||||
this.name = "FirstRoom"
|
this.topic = "Description of first room"
|
||||||
this.topic = "Description of first room"
|
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
})
|
||||||
})
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true)
|
||||||
syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var secondChild: String? = null
|
var secondChild: String? = null
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
secondChild = aliceSession.createRoom(CreateRoomParams().apply {
|
||||||
secondChild = aliceSession.createRoom(CreateRoomParams().apply {
|
this.name = "SecondRoom"
|
||||||
this.name = "SecondRoom"
|
this.topic = "Description of second room"
|
||||||
this.topic = "Description of second room"
|
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
})
|
||||||
})
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true)
|
||||||
syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to join from bob, it's a public space no need to invite
|
// Try to join from bob, it's a public space no need to invite
|
||||||
var joinResult: JoinSpaceResult? = null
|
var joinResult: JoinSpaceResult? = null
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
joinResult = bobSession.spaceService().joinSpace(spaceId)
|
||||||
joinResult = bobSession.spaceService().joinSpace(spaceId)
|
// wait a bit to let the summary update it self :/
|
||||||
// wait a bit to let the summary update it self :/
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(JoinSpaceResult.Success, joinResult)
|
assertEquals(JoinSpaceResult.Success, joinResult)
|
||||||
|
|
|
@ -18,9 +18,6 @@ package org.matrix.android.sdk.session.space
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
|
@ -56,43 +53,34 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
private val commonTestHelper = CommonTestHelper(context())
|
private val commonTestHelper = CommonTestHelper(context())
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun createCanonicalChildRelation() {
|
fun createCanonicalChildRelation() {
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
val spaceName = "My Space"
|
val spaceName = "My Space"
|
||||||
val topic = "A public space for test"
|
val topic = "A public space for test"
|
||||||
var spaceId: String = ""
|
var spaceId = ""
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
|
||||||
spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||||
|
|
||||||
var roomId: String = ""
|
var roomId = ""
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
roomId = session.createRoom(CreateRoomParams().apply { name = "General" })
|
||||||
roomId = session.createRoom(CreateRoomParams().apply { name = "General" })
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
syncedSpace!!.addChildren(roomId, viaServers, null, true)
|
||||||
syncedSpace!!.addChildren(roomId, viaServers, null, true)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers)
|
||||||
session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(9000)
|
Thread.sleep(9000)
|
||||||
|
@ -181,7 +169,6 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun testFilteringBySpace() {
|
fun testFilteringBySpace() {
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
|
|
||||||
|
@ -205,29 +192,23 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
||||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||||
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||||
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create orphan rooms
|
// Create orphan rooms
|
||||||
|
|
||||||
var orphan1 = ""
|
var orphan1 = ""
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" })
|
||||||
orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" })
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var orphan2 = ""
|
var orphan2 = ""
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" })
|
||||||
orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" })
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) })
|
val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) })
|
||||||
|
@ -250,11 +231,9 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
|
|
||||||
// Add a non canonical child and check that it does not appear as orphan
|
// Add a non canonical child and check that it does not appear as orphan
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" })
|
||||||
val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" })
|
spaceA!!.addChildren(a3, viaServers, null, false)
|
||||||
spaceA!!.addChildren(a3, viaServers, null, false)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(2_000)
|
Thread.sleep(2_000)
|
||||||
|
@ -265,7 +244,6 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun testBreakCycle() {
|
fun testBreakCycle() {
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
|
|
||||||
|
@ -283,20 +261,16 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
||||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||||
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||||
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add back A as subspace of C
|
// add back A as subspace of C
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId)
|
||||||
val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId)
|
spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true)
|
||||||
spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(1000)
|
Thread.sleep(1000)
|
||||||
|
@ -313,7 +287,6 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
fun testLiveFlatChildren() {
|
fun testLiveFlatChildren() {
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
|
|
||||||
|
@ -336,12 +309,14 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
session.spaceService().setSpaceParent(spaceBInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
session.spaceService().setSpaceParent(spaceBInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||||
}
|
}
|
||||||
|
|
||||||
val flatAChildren = runBlocking(Dispatchers.Main) {
|
val spaceCInfo = createPublicSpace(session, "SpaceC", listOf(
|
||||||
session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
|
Triple("C1", true /*auto-join*/, true/*canonical*/),
|
||||||
}
|
Triple("C2", true, true)
|
||||||
|
))
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
|
|
||||||
|
val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
|
||||||
val childObserver = object : Observer<List<RoomSummary>> {
|
val childObserver = object : Observer<List<RoomSummary>> {
|
||||||
override fun onChanged(children: List<RoomSummary>?) {
|
override fun onChanged(children: List<RoomSummary>?) {
|
||||||
// Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}")
|
// Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}")
|
||||||
|
@ -354,20 +329,13 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val spaceCInfo = createPublicSpace(session, "SpaceC", listOf(
|
|
||||||
Triple("C1", true /*auto-join*/, true/*canonical*/),
|
|
||||||
Triple("C2", true, true)
|
|
||||||
))
|
|
||||||
|
|
||||||
// add C as subspace of B
|
// add C as subspace of B
|
||||||
runBlocking {
|
val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId)
|
||||||
val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId)
|
spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||||
spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// C1 and C2 should be in flatten child of A now
|
// C1 and C2 should be in flatten child of A now
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) }
|
flatAChildren.observeForever(childObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test part one of the rooms
|
// Test part one of the rooms
|
||||||
|
@ -376,7 +344,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
val bRoom = session.getRoom(bRoomId)
|
val bRoom = session.getRoom(bRoomId)
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
|
val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
|
||||||
val childObserver = object : Observer<List<RoomSummary>> {
|
val childObserver = object : Observer<List<RoomSummary>> {
|
||||||
override fun onChanged(children: List<RoomSummary>?) {
|
override fun onChanged(children: List<RoomSummary>?) {
|
||||||
System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}")
|
System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}")
|
||||||
|
@ -389,13 +357,10 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// part from b room
|
// part from b room
|
||||||
runBlocking {
|
bRoom!!.leave(null)
|
||||||
bRoom!!.leave(null)
|
|
||||||
}
|
|
||||||
// The room should have disapear from flat children
|
// The room should have disapear from flat children
|
||||||
GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) }
|
flatAChildren.observeForever(childObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.signOutAndClose(session)
|
commonTestHelper.signOutAndClose(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,94 +369,66 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
val roomIds: List<String>
|
val roomIds: List<String>
|
||||||
)
|
)
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
private fun createPublicSpace(session: Session,
|
private fun createPublicSpace(session: Session,
|
||||||
spaceName: String,
|
spaceName: String,
|
||||||
childInfo: List<Triple<String, Boolean, Boolean?>>
|
childInfo: List<Triple<String, Boolean, Boolean?>>
|
||||||
/** Name, auto-join, canonical*/
|
/** Name, auto-join, canonical*/
|
||||||
): TestSpaceCreationResult {
|
): TestSpaceCreationResult {
|
||||||
var spaceId = ""
|
var spaceId = ""
|
||||||
commonTestHelper.waitWithLatch {
|
var roomIds: List<String> = emptyList()
|
||||||
GlobalScope.launch {
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true)
|
spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true)
|
||||||
it.countDown()
|
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||||
|
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||||
|
|
||||||
|
roomIds = childInfo.map { entry ->
|
||||||
|
session.createRoom(CreateRoomParams().apply { name = entry.first })
|
||||||
}
|
}
|
||||||
}
|
roomIds.forEachIndexed { index, roomId ->
|
||||||
|
|
||||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
|
||||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
|
||||||
|
|
||||||
val roomIds =
|
|
||||||
childInfo.map { entry ->
|
|
||||||
var roomId = ""
|
|
||||||
commonTestHelper.waitWithLatch {
|
|
||||||
GlobalScope.launch {
|
|
||||||
roomId = session.createRoom(CreateRoomParams().apply { name = entry.first })
|
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roomId
|
|
||||||
}
|
|
||||||
|
|
||||||
roomIds.forEachIndexed { index, roomId ->
|
|
||||||
runBlocking {
|
|
||||||
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
|
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
|
||||||
val canonical = childInfo[index].third
|
val canonical = childInfo[index].third
|
||||||
if (canonical != null) {
|
if (canonical != null) {
|
||||||
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
|
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
latch.countDown()
|
||||||
}
|
}
|
||||||
return TestSpaceCreationResult(spaceId, roomIds)
|
return TestSpaceCreationResult(spaceId, roomIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
private fun createPrivateSpace(session: Session,
|
private fun createPrivateSpace(session: Session,
|
||||||
spaceName: String,
|
spaceName: String,
|
||||||
childInfo: List<Triple<String, Boolean, Boolean?>>
|
childInfo: List<Triple<String, Boolean, Boolean?>>
|
||||||
/** Name, auto-join, canonical*/
|
/** Name, auto-join, canonical*/
|
||||||
): TestSpaceCreationResult {
|
): TestSpaceCreationResult {
|
||||||
var spaceId = ""
|
var spaceId = ""
|
||||||
commonTestHelper.waitWithLatch {
|
var roomIds: List<String> = emptyList()
|
||||||
GlobalScope.launch {
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
|
spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
|
||||||
it.countDown()
|
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||||
}
|
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||||
}
|
roomIds =
|
||||||
|
childInfo.map { entry ->
|
||||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
val homeServerCapabilities = session
|
||||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
.getHomeServerCapabilities()
|
||||||
|
session.createRoom(CreateRoomParams().apply {
|
||||||
val roomIds =
|
name = entry.first
|
||||||
childInfo.map { entry ->
|
this.featurePreset = RestrictedRoomPreset(
|
||||||
var roomId = ""
|
homeServerCapabilities,
|
||||||
commonTestHelper.waitWithLatch {
|
listOf(
|
||||||
GlobalScope.launch {
|
RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
|
||||||
val homeServerCapabilities = session
|
)
|
||||||
.getHomeServerCapabilities()
|
)
|
||||||
roomId = session.createRoom(CreateRoomParams().apply {
|
})
|
||||||
name = entry.first
|
|
||||||
this.featurePreset = RestrictedRoomPreset(
|
|
||||||
homeServerCapabilities,
|
|
||||||
listOf(
|
|
||||||
RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
roomId
|
roomIds.forEachIndexed { index, roomId ->
|
||||||
}
|
|
||||||
|
|
||||||
roomIds.forEachIndexed { index, roomId ->
|
|
||||||
runBlocking {
|
|
||||||
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
|
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
|
||||||
val canonical = childInfo[index].third
|
val canonical = childInfo[index].third
|
||||||
if (canonical != null) {
|
if (canonical != null) {
|
||||||
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
|
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
latch.countDown()
|
||||||
}
|
}
|
||||||
return TestSpaceCreationResult(spaceId, roomIds)
|
return TestSpaceCreationResult(spaceId, roomIds)
|
||||||
}
|
}
|
||||||
|
@ -559,11 +496,9 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
|
|
||||||
var bobRoomId = ""
|
var bobRoomId = ""
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" })
|
||||||
bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" })
|
bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId)
|
||||||
bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId)
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.runBlockingTest {
|
commonTestHelper.runBlockingTest {
|
||||||
|
@ -577,10 +512,8 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
|
||||||
bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
|
@ -600,19 +533,17 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
// Let's now try to make alice admin of the room
|
// Let's now try to make alice admin of the room
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
val room = bobSession.getRoom(bobRoomId)!!
|
||||||
val room = bobSession.getRoom(bobRoomId)!!
|
val currentPLContent = room
|
||||||
val currentPLContent = room
|
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
||||||
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
?.let { it.content.toModel<PowerLevelsContent>() }
|
||||||
?.let { it.content.toModel<PowerLevelsContent>() }
|
|
||||||
|
|
||||||
val newPowerLevelsContent = currentPLContent
|
val newPowerLevelsContent = currentPLContent
|
||||||
?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
|
?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
|
||||||
?.toContent()
|
?.toContent()
|
||||||
|
|
||||||
room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!)
|
room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!)
|
||||||
it.countDown()
|
it.countDown()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
|
@ -627,10 +558,8 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch {
|
commonTestHelper.waitWithLatch {
|
||||||
GlobalScope.launch {
|
aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
|
||||||
aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
|
it.countDown()
|
||||||
it.countDown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.content.Context
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.WorkerFactory
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import org.matrix.android.sdk.BuildConfig
|
||||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
|
@ -33,6 +34,7 @@ import org.matrix.android.sdk.internal.di.DaggerMatrixComponent
|
||||||
import org.matrix.android.sdk.internal.network.ApiInterceptor
|
import org.matrix.android.sdk.internal.network.ApiInterceptor
|
||||||
import org.matrix.android.sdk.internal.network.UserAgentHolder
|
import org.matrix.android.sdk.internal.network.UserAgentHolder
|
||||||
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
|
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
||||||
import org.matrix.olm.OlmManager
|
import org.matrix.olm.OlmManager
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
@ -53,12 +55,17 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||||
@Inject internal lateinit var sessionManager: SessionManager
|
@Inject internal lateinit var sessionManager: SessionManager
|
||||||
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
||||||
@Inject internal lateinit var apiInterceptor: ApiInterceptor
|
@Inject internal lateinit var apiInterceptor: ApiInterceptor
|
||||||
|
@Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Monarchy.init(context)
|
Monarchy.init(context)
|
||||||
DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
|
DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
|
||||||
if (context.applicationContext !is Configuration.Provider) {
|
if (context.applicationContext !is Configuration.Provider) {
|
||||||
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
|
val configuration = Configuration.Builder()
|
||||||
|
.setExecutor(Executors.newCachedThreadPool())
|
||||||
|
.setWorkerFactory(matrixWorkerFactory)
|
||||||
|
.build()
|
||||||
|
WorkManager.initialize(context, configuration)
|
||||||
}
|
}
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
|
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
|
||||||
}
|
}
|
||||||
|
@ -77,6 +84,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||||
return legacySessionImporter
|
return legacySessionImporter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun workerFactory(): WorkerFactory = matrixWorkerFactory
|
||||||
|
|
||||||
fun registerApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) {
|
fun registerApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) {
|
||||||
apiInterceptor.addListener(path, listener)
|
apiInterceptor.addListener(path, listener)
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,8 @@ object EventType {
|
||||||
|
|
||||||
// Poll
|
// Poll
|
||||||
const val POLL_START = "org.matrix.msc3381.poll.start"
|
const val POLL_START = "org.matrix.msc3381.poll.start"
|
||||||
|
const val POLL_RESPONSE = "org.matrix.msc3381.poll.response"
|
||||||
|
const val POLL_END = "org.matrix.msc3381.poll.end"
|
||||||
|
|
||||||
// Unwedging
|
// Unwedging
|
||||||
internal const val DUMMY = "m.dummy"
|
internal const val DUMMY = "m.dummy"
|
||||||
|
|
|
@ -24,25 +24,24 @@ import com.squareup.moshi.JsonClass
|
||||||
*/
|
*/
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class PollSummaryContent(
|
data class PollSummaryContent(
|
||||||
// Index of my vote
|
var myVote: String? = null,
|
||||||
var myVote: Int? = null,
|
|
||||||
// Array of VoteInfo, list is constructed so that there is only one vote by user
|
// Array of VoteInfo, list is constructed so that there is only one vote by user
|
||||||
// And that optionIndex is valid
|
// And that optionIndex is valid
|
||||||
var votes: List<VoteInfo>? = null
|
var votes: List<VoteInfo>? = null,
|
||||||
) {
|
var votesSummary: Map<String, VoteSummary>? = null,
|
||||||
|
var totalVotes: Int = 0,
|
||||||
|
var winnerVoteCount: Int = 0
|
||||||
|
)
|
||||||
|
|
||||||
fun voteCount(): Int {
|
@JsonClass(generateAdapter = true)
|
||||||
return votes?.size ?: 0
|
data class VoteSummary(
|
||||||
}
|
val total: Int = 0,
|
||||||
|
val percentage: Double = 0.0
|
||||||
fun voteCountForOption(optionIndex: Int): Int {
|
)
|
||||||
return votes?.filter { it.optionIndex == optionIndex }?.count() ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class VoteInfo(
|
data class VoteInfo(
|
||||||
val userId: String,
|
val userId: String,
|
||||||
val optionIndex: Int,
|
val option: String,
|
||||||
val voteTimestamp: Long
|
val voteTimestamp: Long
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.android.sdk.api.session.room.model.message
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing the org.matrix.msc3381.poll.end event content
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class MessageEndPollContent(
|
||||||
|
@Json(name = "m.relates_to") val relatesTo: RelationDefaultContent? = null
|
||||||
|
)
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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.android.sdk.api.session.room.model.message
|
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
|
||||||
import com.squareup.moshi.JsonClass
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
|
||||||
|
|
||||||
// Possible values for optionType
|
|
||||||
const val OPTION_TYPE_POLL = "org.matrix.poll"
|
|
||||||
const val OPTION_TYPE_BUTTONS = "org.matrix.buttons"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Polls and bot buttons are m.room.message events with a msgtype of m.options,
|
|
||||||
* Ref: https://github.com/matrix-org/matrix-doc/pull/2192
|
|
||||||
*/
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class MessageOptionsContent(
|
|
||||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_OPTIONS,
|
|
||||||
@Json(name = "type") val optionType: String? = null,
|
|
||||||
@Json(name = "body") override val body: String,
|
|
||||||
@Json(name = "label") val label: String?,
|
|
||||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
|
||||||
@Json(name = "options") val options: List<OptionItem>? = null,
|
|
||||||
@Json(name = "m.new_content") override val newContent: Content? = null
|
|
||||||
) : MessageContent
|
|
|
@ -18,8 +18,18 @@ package org.matrix.android.sdk.api.session.room.model.message
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MessagePollContent(
|
data class MessagePollContent(
|
||||||
@Json(name = "org.matrix.msc3381.poll.start") val pollCreationInfo: PollCreationInfo? = null
|
/**
|
||||||
)
|
* Local message type, not from server
|
||||||
|
*/
|
||||||
|
@Transient
|
||||||
|
override val msgType: String = MessageType.MSGTYPE_POLL_START,
|
||||||
|
@Json(name = "body") override val body: String = "",
|
||||||
|
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||||
|
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||||
|
@Json(name = "org.matrix.msc3381.poll.start") val pollCreationInfo: PollCreationInfo? = null
|
||||||
|
) : MessageContent
|
||||||
|
|
|
@ -21,13 +21,15 @@ import com.squareup.moshi.JsonClass
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||||
|
|
||||||
/**
|
|
||||||
* Ref: https://github.com/matrix-org/matrix-doc/pull/2192
|
|
||||||
*/
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MessagePollResponseContent(
|
data class MessagePollResponseContent(
|
||||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_RESPONSE,
|
/**
|
||||||
@Json(name = "body") override val body: String,
|
* Local message type, not from server
|
||||||
|
*/
|
||||||
|
@Transient
|
||||||
|
override val msgType: String = MessageType.MSGTYPE_POLL_RESPONSE,
|
||||||
|
@Json(name = "body") override val body: String = "",
|
||||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||||
@Json(name = "m.new_content") override val newContent: Content? = null
|
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||||
|
@Json(name = "org.matrix.msc3381.poll.response") val response: PollResponse? = null
|
||||||
) : MessageContent
|
) : MessageContent
|
||||||
|
|
|
@ -25,15 +25,18 @@ object MessageType {
|
||||||
const val MSGTYPE_VIDEO = "m.video"
|
const val MSGTYPE_VIDEO = "m.video"
|
||||||
const val MSGTYPE_LOCATION = "m.location"
|
const val MSGTYPE_LOCATION = "m.location"
|
||||||
const val MSGTYPE_FILE = "m.file"
|
const val MSGTYPE_FILE = "m.file"
|
||||||
const val MSGTYPE_OPTIONS = "org.matrix.options"
|
|
||||||
const val MSGTYPE_RESPONSE = "org.matrix.response"
|
|
||||||
const val MSGTYPE_POLL_CLOSED = "org.matrix.poll_closed"
|
|
||||||
const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request"
|
const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request"
|
||||||
|
|
||||||
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
|
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
|
||||||
// Because sticker isn't a message type but a event type without msgtype field
|
// Because sticker isn't a message type but a event type without msgtype field
|
||||||
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
|
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
|
||||||
|
|
||||||
|
// Fake message types for poll events to be able to inherit them from MessageContent
|
||||||
|
// Because poll events are not message events and they don't hanve msgtype field
|
||||||
|
const val MSGTYPE_POLL_START = "org.matrix.android.sdk.poll.start"
|
||||||
|
const val MSGTYPE_POLL_RESPONSE = "org.matrix.android.sdk.poll.response"
|
||||||
|
|
||||||
const val MSGTYPE_CONFETTI = "nic.custom.confetti"
|
const val MSGTYPE_CONFETTI = "nic.custom.confetti"
|
||||||
const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall"
|
const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,11 +19,7 @@ package org.matrix.android.sdk.api.session.room.model.message
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
/**
|
|
||||||
* Ref: https://github.com/matrix-org/matrix-doc/pull/2192
|
|
||||||
*/
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class OptionItem(
|
data class PollResponse(
|
||||||
@Json(name = "label") val label: String?,
|
@Json(name = "answers") val answers: List<String>? = null
|
||||||
@Json(name = "value") val value: String?
|
|
||||||
)
|
)
|
|
@ -91,11 +91,17 @@ interface SendService {
|
||||||
/**
|
/**
|
||||||
* Method to send a poll response.
|
* Method to send a poll response.
|
||||||
* @param pollEventId the poll currently replied to
|
* @param pollEventId the poll currently replied to
|
||||||
* @param optionIndex The reply index
|
* @param answerId The id of the answer
|
||||||
* @param optionValue The option value (for compatibility)
|
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendOptionsReply(pollEventId: String, optionIndex: Int, optionValue: String): Cancelable
|
fun voteToPoll(pollEventId: String, answerId: String): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End a poll in the room.
|
||||||
|
* @param pollEventId event id of the poll
|
||||||
|
* @return a [Cancelable]
|
||||||
|
*/
|
||||||
|
fun endPoll(pollEventId: String): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redact (delete) the given event.
|
* Redact (delete) the given event.
|
||||||
|
|
|
@ -32,7 +32,8 @@ object RoomSummaryConstants {
|
||||||
EventType.CALL_ANSWER,
|
EventType.CALL_ANSWER,
|
||||||
EventType.ENCRYPTED,
|
EventType.ENCRYPTED,
|
||||||
EventType.STICKER,
|
EventType.STICKER,
|
||||||
EventType.REACTION
|
EventType.REACTION,
|
||||||
|
EventType.POLL_START
|
||||||
)
|
)
|
||||||
|
|
||||||
// SC addition | this is the Element behaviour previous to Element v1.0.7
|
// SC addition | this is the Element behaviour previous to Element v1.0.7
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||||
|
@ -126,10 +127,10 @@ fun TimelineEvent.getEditedEventId(): String? {
|
||||||
* Get last MessageContent, after a possible edition
|
* Get last MessageContent, after a possible edition
|
||||||
*/
|
*/
|
||||||
fun TimelineEvent.getLastMessageContent(): MessageContent? {
|
fun TimelineEvent.getLastMessageContent(): MessageContent? {
|
||||||
return if (root.getClearType() == EventType.STICKER) {
|
return when (root.getClearType()) {
|
||||||
root.getClearContent().toModel<MessageStickerContent>()
|
EventType.STICKER -> root.getClearContent().toModel<MessageStickerContent>()
|
||||||
} else {
|
EventType.POLL_START -> root.getClearContent().toModel<MessagePollContent>()
|
||||||
(annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
|
else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,14 @@ data class SyncResponse(
|
||||||
@Json(name = "device_one_time_keys_count")
|
@Json(name = "device_one_time_keys_count")
|
||||||
val deviceOneTimeKeysCount: DeviceOneTimeKeysCountSyncResponse? = null,
|
val deviceOneTimeKeysCount: DeviceOneTimeKeysCountSyncResponse? = null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key algorithms for which the server has an unused fallback key for the device.
|
||||||
|
* If the client wants the server to have a fallback key for a given key algorithm,
|
||||||
|
* but that algorithm is not listed in device_unused_fallback_key_types, the client will upload a new key.
|
||||||
|
*/
|
||||||
|
@Json(name = "org.matrix.msc2732.device_unused_fallback_key_types")
|
||||||
|
val deviceUnusedFallbackKeyTypes: List<String>? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of groups.
|
* List of groups.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.terms
|
package org.matrix.android.sdk.api.session.terms
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.internal.session.terms.TermsResponse
|
||||||
|
|
||||||
interface TermsService {
|
interface TermsService {
|
||||||
enum class ServiceType {
|
enum class ServiceType {
|
||||||
IntegrationManager,
|
IntegrationManager,
|
||||||
|
@ -28,4 +30,10 @@ interface TermsService {
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
agreedUrls: List<String>,
|
agreedUrls: List<String>,
|
||||||
token: String?)
|
token: String?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the homeserver terms, from the register API.
|
||||||
|
* Will be updated once https://github.com/matrix-org/matrix-doc/pull/3012 will be implemented.
|
||||||
|
*/
|
||||||
|
suspend fun getHomeserverTerms(baseUrl: String): TermsResponse
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.internal.SessionManager
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation
|
import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
|
@ -34,9 +35,8 @@ import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class CancelGossipRequestWorker(context: Context,
|
internal class CancelGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
|
||||||
params: WorkerParameters) :
|
SessionSafeCoroutineWorker<CancelGossipRequestWorker.Params>(context, params, sessionManager, Params::class.java) {
|
||||||
SessionSafeCoroutineWorker<CancelGossipRequestWorker.Params>(context, params, Params::class.java) {
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
|
|
|
@ -67,6 +67,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
|
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXDeviceInfo
|
import org.matrix.android.sdk.internal.crypto.model.MXDeviceInfo
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult
|
import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.MXKey.Companion.KEY_SIGNED_CURVE_25519_TYPE
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||||
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
|
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
|
||||||
|
@ -431,6 +432,14 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
if (isStarted()) {
|
if (isStarted()) {
|
||||||
// Make sure we process to-device messages before generating new one-time-keys #2782
|
// Make sure we process to-device messages before generating new one-time-keys #2782
|
||||||
deviceListManager.refreshOutdatedDeviceLists()
|
deviceListManager.refreshOutdatedDeviceLists()
|
||||||
|
// The presence of device_unused_fallback_key_types indicates that the server supports fallback keys.
|
||||||
|
// If there's no unused signed_curve25519 fallback key we need a new one.
|
||||||
|
if (syncResponse.deviceUnusedFallbackKeyTypes != null &&
|
||||||
|
// Generate a fallback key only if the server does not already have an unused fallback key.
|
||||||
|
!syncResponse.deviceUnusedFallbackKeyTypes.contains(KEY_SIGNED_CURVE_25519_TYPE)) {
|
||||||
|
oneTimeKeysUploader.needsNewFallback()
|
||||||
|
}
|
||||||
|
|
||||||
oneTimeKeysUploader.maybeUploadOneTimeKeys()
|
oneTimeKeysUploader.maybeUploadOneTimeKeys()
|
||||||
incomingGossipingRequestManager.processReceivedGossipingRequests()
|
incomingGossipingRequestManager.processReceivedGossipingRequests()
|
||||||
}
|
}
|
||||||
|
@ -928,7 +937,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
signatures = objectSigner.signObject(canonicalJson)
|
signatures = objectSigner.signObject(canonicalJson)
|
||||||
)
|
)
|
||||||
|
|
||||||
val uploadDeviceKeysParams = UploadKeysTask.Params(rest, null)
|
val uploadDeviceKeysParams = UploadKeysTask.Params(rest, null, null)
|
||||||
uploadKeysTask.execute(uploadDeviceKeysParams)
|
uploadKeysTask.execute(uploadDeviceKeysParams)
|
||||||
|
|
||||||
cryptoStore.setDeviceKeysUploaded(true)
|
cryptoStore.setDeviceKeysUploaded(true)
|
||||||
|
|
|
@ -136,6 +136,51 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
return store.getOlmAccount().maxOneTimeKeys()
|
return store.getOlmAccount().maxOneTimeKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an unpublished fallback key
|
||||||
|
* A call to markKeysAsPublished will mark it as published and this
|
||||||
|
* call will return null (until a call to generateFallbackKey is made)
|
||||||
|
*/
|
||||||
|
fun getFallbackKey(): MutableMap<String, MutableMap<String, String>>? {
|
||||||
|
try {
|
||||||
|
return store.getOlmAccount().fallbackKey()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e("## getFallbackKey() : failed")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new fallback key if there is not already
|
||||||
|
* an unpublished one.
|
||||||
|
* @return true if a new key was generated
|
||||||
|
*/
|
||||||
|
fun generateFallbackKeyIfNeeded(): Boolean {
|
||||||
|
try {
|
||||||
|
if (!hasUnpublishedFallbackKey()) {
|
||||||
|
store.getOlmAccount().generateFallbackKey()
|
||||||
|
store.saveOlmAccount()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e("## generateFallbackKey() : failed")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun hasUnpublishedFallbackKey(): Boolean {
|
||||||
|
return getFallbackKey()?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY).orEmpty().isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forgetFallbackKey() {
|
||||||
|
try {
|
||||||
|
store.getOlmAccount().forgetFallbackKey()
|
||||||
|
store.saveOlmAccount()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e("## forgetFallbackKey() : failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release the instance
|
* Release the instance
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto
|
package org.matrix.android.sdk.internal.crypto
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXKey
|
import org.matrix.android.sdk.internal.crypto.model.MXKey
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
|
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
|
||||||
|
@ -28,11 +29,16 @@ import javax.inject.Inject
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
// The spec recommend a 5mn delay, but due to federation
|
||||||
|
// or server downtime we give it a bit more time (1 hour)
|
||||||
|
const val FALLBACK_KEY_FORGET_DELAY = 60 * 60_000L
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class OneTimeKeysUploader @Inject constructor(
|
internal class OneTimeKeysUploader @Inject constructor(
|
||||||
private val olmDevice: MXOlmDevice,
|
private val olmDevice: MXOlmDevice,
|
||||||
private val objectSigner: ObjectSigner,
|
private val objectSigner: ObjectSigner,
|
||||||
private val uploadKeysTask: UploadKeysTask
|
private val uploadKeysTask: UploadKeysTask,
|
||||||
|
context: Context
|
||||||
) {
|
) {
|
||||||
// tell if there is a OTK check in progress
|
// tell if there is a OTK check in progress
|
||||||
private var oneTimeKeyCheckInProgress = false
|
private var oneTimeKeyCheckInProgress = false
|
||||||
|
@ -41,6 +47,9 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
private var lastOneTimeKeyCheck: Long = 0
|
private var lastOneTimeKeyCheck: Long = 0
|
||||||
private var oneTimeKeyCount: Int? = null
|
private var oneTimeKeyCount: Int? = null
|
||||||
|
|
||||||
|
// Simple storage to remember when was uploaded the last fallback key
|
||||||
|
private val storage = context.getSharedPreferences("OneTimeKeysUploader_${olmDevice.deviceEd25519Key.hashCode()}", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the current one_time_key count which will be handled later (in a call of
|
* Stores the current one_time_key count which will be handled later (in a call of
|
||||||
* _onSyncCompleted). The count is e.g. coming from a /sync response.
|
* _onSyncCompleted). The count is e.g. coming from a /sync response.
|
||||||
|
@ -51,6 +60,15 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
oneTimeKeyCount = currentCount
|
oneTimeKeyCount = currentCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun needsNewFallback() {
|
||||||
|
if (olmDevice.generateFallbackKeyIfNeeded()) {
|
||||||
|
// As we generated a new one, it's already forgetting one
|
||||||
|
// so we can clear the last publish time
|
||||||
|
// (in case the network calls fails after to avoid calling forgetKey)
|
||||||
|
saveLastFallbackKeyPublishTime(0L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the OTK must be uploaded.
|
* Check if the OTK must be uploaded.
|
||||||
*/
|
*/
|
||||||
|
@ -65,9 +83,19 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lastOneTimeKeyCheck = System.currentTimeMillis()
|
|
||||||
oneTimeKeyCheckInProgress = true
|
oneTimeKeyCheckInProgress = true
|
||||||
|
|
||||||
|
val oneTimeKeyCountFromSync = oneTimeKeyCount
|
||||||
|
?: fetchOtkCount() // we don't have count from sync so get from server
|
||||||
|
?: return Unit.also {
|
||||||
|
oneTimeKeyCheckInProgress = false
|
||||||
|
Timber.w("maybeUploadOneTimeKeys: Failed to get otk count from server")
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.d("maybeUploadOneTimeKeys: otk count $oneTimeKeyCountFromSync , unpublished fallback key ${olmDevice.hasUnpublishedFallbackKey()}")
|
||||||
|
|
||||||
|
lastOneTimeKeyCheck = System.currentTimeMillis()
|
||||||
|
|
||||||
// We then check how many keys we can store in the Account object.
|
// We then check how many keys we can store in the Account object.
|
||||||
val maxOneTimeKeys = olmDevice.getMaxNumberOfOneTimeKeys()
|
val maxOneTimeKeys = olmDevice.getMaxNumberOfOneTimeKeys()
|
||||||
|
|
||||||
|
@ -78,37 +106,37 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
// discard the oldest private keys first. This will eventually clean
|
// discard the oldest private keys first. This will eventually clean
|
||||||
// out stale private keys that won't receive a message.
|
// out stale private keys that won't receive a message.
|
||||||
val keyLimit = floor(maxOneTimeKeys / 2.0).toInt()
|
val keyLimit = floor(maxOneTimeKeys / 2.0).toInt()
|
||||||
if (oneTimeKeyCount == null) {
|
|
||||||
// Ask the server how many otk he has
|
// We need to keep a pool of one time public keys on the server so that
|
||||||
oneTimeKeyCount = fetchOtkCount()
|
// other devices can start conversations with us. But we can only store
|
||||||
}
|
// a finite number of private keys in the olm Account object.
|
||||||
val oneTimeKeyCountFromSync = oneTimeKeyCount
|
// To complicate things further then can be a delay between a device
|
||||||
if (oneTimeKeyCountFromSync != null) {
|
// claiming a public one time key from the server and it sending us a
|
||||||
// We need to keep a pool of one time public keys on the server so that
|
// message. We need to keep the corresponding private key locally until
|
||||||
// other devices can start conversations with us. But we can only store
|
// we receive the message.
|
||||||
// a finite number of private keys in the olm Account object.
|
// But that message might never arrive leaving us stuck with duff
|
||||||
// To complicate things further then can be a delay between a device
|
// private keys clogging up our local storage.
|
||||||
// claiming a public one time key from the server and it sending us a
|
// So we need some kind of engineering compromise to balance all of
|
||||||
// message. We need to keep the corresponding private key locally until
|
// these factors.
|
||||||
// we receive the message.
|
tryOrNull("Unable to upload OTK") {
|
||||||
// But that message might never arrive leaving us stuck with duff
|
val uploadedKeys = uploadOTK(oneTimeKeyCountFromSync, keyLimit)
|
||||||
// private keys clogging up our local storage.
|
Timber.v("## uploadKeys() : success, $uploadedKeys key(s) sent")
|
||||||
// So we need some kind of engineering compromise to balance all of
|
|
||||||
// these factors.
|
|
||||||
tryOrNull("Unable to upload OTK") {
|
|
||||||
val uploadedKeys = uploadOTK(oneTimeKeyCountFromSync, keyLimit)
|
|
||||||
Timber.v("## uploadKeys() : success, $uploadedKeys key(s) sent")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Timber.w("maybeUploadOneTimeKeys: waiting to know the number of OTK from the sync")
|
|
||||||
lastOneTimeKeyCheck = 0
|
|
||||||
}
|
}
|
||||||
oneTimeKeyCheckInProgress = false
|
oneTimeKeyCheckInProgress = false
|
||||||
|
|
||||||
|
// Check if we need to forget a fallback key
|
||||||
|
val latestPublishedTime = getLastFallbackKeyPublishTime()
|
||||||
|
if (latestPublishedTime != 0L && System.currentTimeMillis() - latestPublishedTime > FALLBACK_KEY_FORGET_DELAY) {
|
||||||
|
// This should be called once you are reasonably certain that you will not receive any more messages
|
||||||
|
// that use the old fallback key
|
||||||
|
Timber.d("## forgetFallbackKey()")
|
||||||
|
olmDevice.forgetFallbackKey()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchOtkCount(): Int? {
|
private suspend fun fetchOtkCount(): Int? {
|
||||||
return tryOrNull("Unable to get OTK count") {
|
return tryOrNull("Unable to get OTK count") {
|
||||||
val result = uploadKeysTask.execute(UploadKeysTask.Params(null, null))
|
val result = uploadKeysTask.execute(UploadKeysTask.Params(null, null, null))
|
||||||
result.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
|
result.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,24 +149,47 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
* @return the number of uploaded keys
|
* @return the number of uploaded keys
|
||||||
*/
|
*/
|
||||||
private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Int {
|
private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Int {
|
||||||
if (keyLimit <= keyCount) {
|
if (keyLimit <= keyCount && !olmDevice.hasUnpublishedFallbackKey()) {
|
||||||
// If we don't need to generate any more keys then we are done.
|
// If we don't need to generate any more keys then we are done.
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
val keysThisLoop = min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
|
var keysThisLoop = 0
|
||||||
olmDevice.generateOneTimeKeys(keysThisLoop)
|
if (keyLimit > keyCount) {
|
||||||
|
// Creating keys can be an expensive operation so we limit the
|
||||||
|
// number we generate in one go to avoid blocking the application
|
||||||
|
// for too long.
|
||||||
|
keysThisLoop = min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
|
||||||
|
olmDevice.generateOneTimeKeys(keysThisLoop)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We check before sending if there is an unpublished key in order to saveLastFallbackKeyPublishTime if needed
|
||||||
|
val hadUnpublishedFallbackKey = olmDevice.hasUnpublishedFallbackKey()
|
||||||
val response = uploadOneTimeKeys(olmDevice.getOneTimeKeys())
|
val response = uploadOneTimeKeys(olmDevice.getOneTimeKeys())
|
||||||
olmDevice.markKeysAsPublished()
|
olmDevice.markKeysAsPublished()
|
||||||
|
if (hadUnpublishedFallbackKey) {
|
||||||
|
// It had an unpublished fallback key that was published just now
|
||||||
|
saveLastFallbackKeyPublishTime(System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
|
||||||
if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
||||||
// Maybe upload other keys
|
// Maybe upload other keys
|
||||||
return keysThisLoop + uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit)
|
return keysThisLoop +
|
||||||
|
uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) +
|
||||||
|
(if (hadUnpublishedFallbackKey) 1 else 0)
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## uploadOTK() : response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
Timber.e("## uploadOTK() : response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
||||||
throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveLastFallbackKeyPublishTime(timeMillis: Long) {
|
||||||
|
storage.edit().putLong("last_fb_key_publish", timeMillis).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLastFallbackKeyPublishTime(): Long {
|
||||||
|
return storage.getLong("last_fb_key_publish", 0)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload curve25519 one time keys.
|
* Upload curve25519 one time keys.
|
||||||
*/
|
*/
|
||||||
|
@ -159,10 +210,26 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
oneTimeJson["signed_curve25519:$key_id"] = k
|
oneTimeJson["signed_curve25519:$key_id"] = k
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val fallbackJson = mutableMapOf<String, Any>()
|
||||||
|
val fallbackCurve25519Map = olmDevice.getFallbackKey()?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY).orEmpty()
|
||||||
|
fallbackCurve25519Map.forEach { (key_id, key) ->
|
||||||
|
val k = mutableMapOf<String, Any>()
|
||||||
|
k["key"] = key
|
||||||
|
k["fallback"] = true
|
||||||
|
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, k)
|
||||||
|
k["signatures"] = objectSigner.signObject(canonicalJson)
|
||||||
|
|
||||||
|
fallbackJson["signed_curve25519:$key_id"] = k
|
||||||
|
}
|
||||||
|
|
||||||
// For now, we set the device id explicitly, as we may not be using the
|
// For now, we set the device id explicitly, as we may not be using the
|
||||||
// same one as used in login.
|
// same one as used in login.
|
||||||
val uploadParams = UploadKeysTask.Params(null, oneTimeJson)
|
val uploadParams = UploadKeysTask.Params(
|
||||||
return uploadKeysTask.execute(uploadParams)
|
deviceKeys = null,
|
||||||
|
oneTimeKeys = oneTimeJson,
|
||||||
|
fallbackKeys = fallbackJson.takeIf { fallbackJson.isNotEmpty() }
|
||||||
|
)
|
||||||
|
return uploadKeysTask.executeRetry(uploadParams, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -173,6 +240,6 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
private const val ONE_TIME_KEY_GENERATION_MAX_NUMBER = 5
|
private const val ONE_TIME_KEY_GENERATION_MAX_NUMBER = 5
|
||||||
|
|
||||||
// frequency with which to check & upload one-time keys
|
// frequency with which to check & upload one-time keys
|
||||||
private const val ONE_TIME_KEY_UPLOAD_PERIOD = (60 * 1000).toLong() // one minute
|
private const val ONE_TIME_KEY_UPLOAD_PERIOD = (60_000).toLong() // one minute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.internal.SessionManager
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
|
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest
|
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest
|
||||||
|
@ -37,9 +38,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SendGossipRequestWorker(context: Context,
|
internal class SendGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
|
||||||
params: WorkerParameters) :
|
SessionSafeCoroutineWorker<SendGossipRequestWorker.Params>(context, params, sessionManager, Params::class.java) {
|
||||||
SessionSafeCoroutineWorker<SendGossipRequestWorker.Params>(context, params, Params::class.java) {
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.internal.SessionManager
|
||||||
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
||||||
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
|
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||||
|
@ -37,9 +38,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SendGossipWorker(context: Context,
|
internal class SendGossipWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
|
||||||
params: WorkerParameters) :
|
SessionSafeCoroutineWorker<SendGossipWorker.Params>(context, params, sessionManager, Params::class.java) {
|
||||||
SessionSafeCoroutineWorker<SendGossipWorker.Params>(context, params, Params::class.java) {
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
|
|
|
@ -25,6 +25,7 @@ import io.realm.kotlin.where
|
||||||
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
|
import org.matrix.android.sdk.internal.SessionManager
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
|
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity
|
||||||
|
@ -50,9 +51,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class UpdateTrustWorker(context: Context,
|
internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
|
||||||
params: WorkerParameters) :
|
SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, sessionManager, Params::class.java) {
|
||||||
SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, Params::class.java) {
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue