From 51e952cc9efb213b3d62455093d12a7059ba0c34 Mon Sep 17 00:00:00 2001 From: Jonny Andrew Date: Wed, 15 Feb 2023 17:51:16 +0000 Subject: [PATCH] Add remote wipe --- .../matrix/android/sdk/api/session/Session.kt | 6 ++ .../api/session/cleardata/ClearDataService.kt | 25 +++++++ .../sdk/internal/session/DefaultSession.kt | 3 + .../sdk/internal/session/SessionComponent.kt | 2 + .../session/cleanup/CleanupSession.kt | 10 +++ .../session/cleardata/ClearDataModule.kt | 31 ++++++++ .../session/cleardata/ClearDataTask.kt | 35 +++++++++ .../cleardata/DefaultClearDataService.kt | 29 +++++++ vector-app/src/debug/AndroidManifest.xml | 14 +++- .../im/vector/app/core/pushers/PushParser.kt | 1 + .../app/core/pushers/VectorPushHandler.kt | 75 +++++++++++-------- .../vector/app/core/pushers/model/PushData.kt | 32 +++++--- .../app/core/pushers/model/PushDataFcm.kt | 19 +++-- .../core/pushers/model/PushDataUnifiedPush.kt | 3 +- 14 files changed, 236 insertions(+), 49 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/cleardata/ClearDataService.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/ClearDataModule.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/ClearDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/DefaultClearDataService.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt index cf0f4bdce0..2805b9a3cf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt @@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.federation.FederationService import org.matrix.android.sdk.api.session.account.AccountService import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService import org.matrix.android.sdk.api.session.call.CallSignalingService +import org.matrix.android.sdk.api.session.cleardata.ClearDataService import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.contentscanner.ContentScannerService @@ -162,6 +163,11 @@ interface Session { */ fun signOutService(): SignOutService + /** + * Returns the ClearDataService associated with the session. + */ + fun clearDataService(): ClearDataService + /** * Returns the PushRuleService associated with the session. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/cleardata/ClearDataService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/cleardata/ClearDataService.kt new file mode 100644 index 0000000000..9ed1326d16 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/cleardata/ClearDataService.kt @@ -0,0 +1,25 @@ +/* + * 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.cleardata + +interface ClearDataService { + + /** + * Clear all data except for basic session data such as credentials + */ + suspend fun clearData() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index 1af904bbc7..1c0292702c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.account.AccountService import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService import org.matrix.android.sdk.api.session.cache.CacheService import org.matrix.android.sdk.api.session.call.CallSignalingService +import org.matrix.android.sdk.api.session.cleardata.ClearDataService import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.contentscanner.ContentScannerService @@ -99,6 +100,7 @@ internal class DefaultSession @Inject constructor( private val federationService: Lazy, private val cacheService: Lazy, private val signOutService: Lazy, + private val clearDataService: Lazy, private val pushRuleService: Lazy, private val pushersService: Lazy, private val termsService: Lazy, @@ -207,6 +209,7 @@ internal class DefaultSession @Inject constructor( override fun roomDirectoryService(): RoomDirectoryService = roomDirectoryService.get() override fun userService(): UserService = userService.get() override fun signOutService(): SignOutService = signOutService.get() + override fun clearDataService(): ClearDataService = clearDataService.get() override fun pushRuleService(): PushRuleService = pushRuleService.get() override fun pushersService(): PushersService = pushersService.get() override fun eventService(): EventService = eventService.get() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index a7572035df..62aac79e80 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.network.RequestModule import org.matrix.android.sdk.internal.session.account.AccountModule import org.matrix.android.sdk.internal.session.cache.CacheModule import org.matrix.android.sdk.internal.session.call.CallModule +import org.matrix.android.sdk.internal.session.cleardata.ClearDataModule import org.matrix.android.sdk.internal.session.content.ContentModule import org.matrix.android.sdk.internal.session.content.UploadContentWorker import org.matrix.android.sdk.internal.session.contentscanner.ContentScannerModule @@ -73,6 +74,7 @@ import org.matrix.android.sdk.internal.util.system.SystemModule SyncModule::class, HomeServerCapabilitiesModule::class, SignOutModule::class, + ClearDataModule::class, UserModule::class, FilterModule::class, ContentModule::class, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt index 44fff45917..fc643d3634 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt @@ -89,6 +89,16 @@ internal class CleanupSession @Inject constructor( sessionCache.deleteRecursively() } + suspend fun clearSessionData() { + Timber.d("Clear data: clear session data...") + clearSessionDataTask.execute(Unit) + + waitRealmRelease() + + Timber.d("Clear data: clear session cache") + sessionCache.deleteRecursively() + } + private suspend fun waitRealmRelease() { var timeToWaitMillis = MAX_TIME_TO_WAIT_MILLIS do { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/ClearDataModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/ClearDataModule.kt new file mode 100644 index 0000000000..10f3157288 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/ClearDataModule.kt @@ -0,0 +1,31 @@ +/* + * 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.internal.session.cleardata + +import dagger.Binds +import dagger.Module +import org.matrix.android.sdk.api.session.cleardata.ClearDataService + +@Module +internal abstract class ClearDataModule { + + @Binds + abstract fun bindClearDataTask(task: DefaultClearDataTask): ClearDataTask + + @Binds + abstract fun bindClearDataService(service: DefaultClearDataService): ClearDataService +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/ClearDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/ClearDataTask.kt new file mode 100644 index 0000000000..370fd22c89 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/ClearDataTask.kt @@ -0,0 +1,35 @@ +/* + * 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.internal.session.cleardata + +import org.matrix.android.sdk.internal.session.cleanup.CleanupSession +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface ClearDataTask : Task { + object Params +} + +internal class DefaultClearDataTask @Inject constructor( + private val cleanupSession: CleanupSession +) : ClearDataTask { + + override suspend fun execute(params: ClearDataTask.Params) { + cleanupSession.stopActiveTasks() + cleanupSession.clearSessionData() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/DefaultClearDataService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/DefaultClearDataService.kt new file mode 100644 index 0000000000..9692b015f1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleardata/DefaultClearDataService.kt @@ -0,0 +1,29 @@ +/* + * 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.internal.session.cleardata + +import org.matrix.android.sdk.api.session.cleardata.ClearDataService +import javax.inject.Inject + +internal class DefaultClearDataService @Inject constructor( + private val clearDataTask: ClearDataTask, +) : ClearDataService { + + override suspend fun clearData() { + return clearDataTask.execute(ClearDataTask.Params) + } +} diff --git a/vector-app/src/debug/AndroidManifest.xml b/vector-app/src/debug/AndroidManifest.xml index be2aadbeaf..af4c9aae39 100644 --- a/vector-app/src/debug/AndroidManifest.xml +++ b/vector-app/src/debug/AndroidManifest.xml @@ -1,5 +1,6 @@ - + @@ -16,6 +17,17 @@ android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity" android:exported="true" /> + + + + + + + diff --git a/vector/src/main/java/im/vector/app/core/pushers/PushParser.kt b/vector/src/main/java/im/vector/app/core/pushers/PushParser.kt index 8c18295c45..b0b14a061b 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/PushParser.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/PushParser.kt @@ -51,6 +51,7 @@ class PushParser @Inject constructor() { eventId = message["event_id"], roomId = message["room_id"], unread = message["unread"]?.let { tryOrNull { Integer.parseInt(it) } }, + remoteWipeNonce = message["io.element.remote_wipe_nonce"] ) return pushDataFcm.toPushData() } diff --git a/vector/src/main/java/im/vector/app/core/pushers/VectorPushHandler.kt b/vector/src/main/java/im/vector/app/core/pushers/VectorPushHandler.kt index 04c71e0412..816f74fbf1 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/VectorPushHandler.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/VectorPushHandler.kt @@ -49,15 +49,15 @@ import javax.inject.Inject private val loggerTag = LoggerTag("Push", LoggerTag.SYNC) class VectorPushHandler @Inject constructor( - private val notificationDrawerManager: NotificationDrawerManager, - private val notifiableEventResolver: NotifiableEventResolver, - private val activeSessionHolder: ActiveSessionHolder, - private val vectorPreferences: VectorPreferences, - private val vectorDataStore: VectorDataStore, - private val wifiDetector: WifiDetector, - private val actionIds: NotificationActionIds, - private val context: Context, - private val buildMeta: BuildMeta + private val notificationDrawerManager: NotificationDrawerManager, + private val notifiableEventResolver: NotifiableEventResolver, + private val activeSessionHolder: ActiveSessionHolder, + private val vectorPreferences: VectorPreferences, + private val vectorDataStore: VectorDataStore, + private val wifiDetector: WifiDetector, + private val actionIds: NotificationActionIds, + private val context: Context, + private val buildMeta: BuildMeta ) { private val coroutineScope = CoroutineScope(SupervisorJob()) @@ -83,24 +83,30 @@ class VectorPushHandler @Inject constructor( vectorDataStore.incrementPushCounter() } - // Diagnostic Push - if (pushData.eventId == PushersManager.TEST_EVENT_ID) { - val intent = Intent(actionIds.push) - LocalBroadcastManager.getInstance(context).sendBroadcast(intent) - return - } - - if (!vectorPreferences.areNotificationEnabledForDevice()) { - Timber.tag(loggerTag.value).i("Notification are disabled for this device") - return - } - - mUIHandler.post { - if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { - // we are in foreground, let the sync do the things? - Timber.tag(loggerTag.value).d("PUSH received in a foreground state, ignore") - } else { + when (pushData) { + PushData.Diagnostic -> { + val intent = Intent(actionIds.push) + LocalBroadcastManager.getInstance(context).sendBroadcast(intent) + return + } + is PushData.RemoteWipe -> { coroutineScope.launch(Dispatchers.IO) { handleInternal(pushData) } + return + } + is PushData.Event -> { + if (!vectorPreferences.areNotificationEnabledForDevice()) { + Timber.tag(loggerTag.value).i("Notification are disabled for this device") + return + } + + mUIHandler.post { + if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { + // we are in foreground, let the sync do the things? + Timber.tag(loggerTag.value).d("PUSH received in a foreground state, ignore") + } else { + coroutineScope.launch(Dispatchers.IO) { handleInternal(pushData) } + } + } } } } @@ -122,8 +128,11 @@ class VectorPushHandler @Inject constructor( if (session == null) { Timber.tag(loggerTag.value).w("## Can't sync from push, no current session") - } else { - if (isEventAlreadyKnown(pushData)) { + return + } + when (pushData) { + PushData.Diagnostic -> {} + is PushData.Event -> if (isEventAlreadyKnown(pushData)) { Timber.tag(loggerTag.value).d("Ignoring push, event already known") } else { // Try to get the Event content faster @@ -133,13 +142,19 @@ class VectorPushHandler @Inject constructor( Timber.tag(loggerTag.value).d("Requesting background sync") session.syncService().requireBackgroundSync() } + is PushData.RemoteWipe -> { + Timber.tag(loggerTag.value).d("## Remote wipe: received") + + Timber.tag(loggerTag.value).d("## Remote wipe: clearing data") + session.clearDataService().clearData() + } } } catch (e: Exception) { Timber.tag(loggerTag.value).e(e, "## handleInternal() failed") } } - private suspend fun getEventFastLane(session: Session, pushData: PushData) { + private suspend fun getEventFastLane(session: Session, pushData: PushData.Event) { pushData.roomId ?: return pushData.eventId ?: return @@ -169,7 +184,7 @@ class VectorPushHandler @Inject constructor( // check if the event was not yet received // a previous catchup might have already retrieved the notified event - private fun isEventAlreadyKnown(pushData: PushData): Boolean { + private fun isEventAlreadyKnown(pushData: PushData.Event): Boolean { if (pushData.eventId != null && pushData.roomId != null) { try { val session = activeSessionHolder.getSafeActiveSession() ?: return false diff --git a/vector/src/main/java/im/vector/app/core/pushers/model/PushData.kt b/vector/src/main/java/im/vector/app/core/pushers/model/PushData.kt index d1d095a6fa..732867310d 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/model/PushData.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/model/PushData.kt @@ -16,15 +16,23 @@ package im.vector.app.core.pushers.model -/** - * Represent parsed data that the app has received from a Push content. - * - * @property eventId The Event ID. If not null, it will not be empty, and will have a valid format. - * @property roomId The Room ID. If not null, it will not be empty, and will have a valid format. - * @property unread Number of unread message. - */ -data class PushData( - val eventId: String?, - val roomId: String?, - val unread: Int?, -) +sealed class PushData { + /** + * Represent parsed data that the app has received from a Push content. + * + * @property eventId The Event ID. If not null, it will not be empty, and will have a valid format. + * @property roomId The Room ID. If not null, it will not be empty, and will have a valid format. + * @property unread Number of unread message. + */ + data class Event( + val eventId: String?, + val roomId: String?, + val unread: Int?, + ) : PushData() + + data class RemoteWipe( + val nonce: String + ) : PushData() + + object Diagnostic : PushData() +} diff --git a/vector/src/main/java/im/vector/app/core/pushers/model/PushDataFcm.kt b/vector/src/main/java/im/vector/app/core/pushers/model/PushDataFcm.kt index e78cdf5ff8..3a6d0463fd 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/model/PushDataFcm.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/model/PushDataFcm.kt @@ -16,6 +16,7 @@ package im.vector.app.core.pushers.model +import im.vector.app.core.pushers.PushersManager import org.matrix.android.sdk.api.MatrixPatterns /** @@ -34,10 +35,18 @@ data class PushDataFcm( val eventId: String?, val roomId: String?, var unread: Int?, + val remoteWipeNonce: String?, ) -fun PushDataFcm.toPushData() = PushData( - eventId = eventId?.takeIf { MatrixPatterns.isEventId(it) }, - roomId = roomId?.takeIf { MatrixPatterns.isRoomId(it) }, - unread = unread -) +fun PushDataFcm.toPushData(): PushData = + if (eventId == PushersManager.TEST_EVENT_ID) { + PushData.Diagnostic + } else if (remoteWipeNonce != null) { + PushData.RemoteWipe(nonce = remoteWipeNonce) + } else { + PushData.Event( + eventId = eventId?.takeIf { MatrixPatterns.isEventId(it) }, + roomId = roomId?.takeIf { MatrixPatterns.isRoomId(it) }, + unread = unread, + ) + } diff --git a/vector/src/main/java/im/vector/app/core/pushers/model/PushDataUnifiedPush.kt b/vector/src/main/java/im/vector/app/core/pushers/model/PushDataUnifiedPush.kt index 3dbd44f8ae..98d872722c 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/model/PushDataUnifiedPush.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/model/PushDataUnifiedPush.kt @@ -53,7 +53,8 @@ data class PushDataUnifiedPushCounts( @Json(name = "unread") val unread: Int? ) -fun PushDataUnifiedPush.toPushData() = PushData( +// TODO +fun PushDataUnifiedPush.toPushData() = PushData.Event( eventId = notification?.eventId?.takeIf { MatrixPatterns.isEventId(it) }, roomId = notification?.roomId?.takeIf { MatrixPatterns.isRoomId(it) }, unread = notification?.counts?.unread