Skip to content

Commit 3421bf2

Browse files
committed
check for reference in mmssmsdatabase
1 parent f5798c4 commit 3421bf2

File tree

3 files changed

+101
-19
lines changed

3 files changed

+101
-19
lines changed

app/src/main/java/org/session/libsession/avatars/AvatarCacheCleaner.kt

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import kotlinx.coroutines.CoroutineScope
66
import kotlinx.coroutines.Dispatchers
77
import kotlinx.coroutines.launch
88
import kotlinx.coroutines.withContext
9+
import org.session.libsession.utilities.Address
910
import org.session.libsession.utilities.recipients.RemoteFile
1011
import org.session.libsignal.utilities.Log
1112
import org.thoughtcrime.securesms.attachments.RemoteFileDownloadWorker
13+
import org.thoughtcrime.securesms.database.MmsSmsDatabase
14+
import org.thoughtcrime.securesms.database.RecipientSettingsDatabase
1215
import org.thoughtcrime.securesms.dependencies.ManagerScope
1316
import org.thoughtcrime.securesms.glide.RecipientAvatarDownloadManager
1417
import java.io.File
@@ -18,7 +21,9 @@ import javax.inject.Singleton
1821
@Singleton
1922
class AvatarCacheCleaner @Inject constructor(
2023
private val application: Application,
21-
private val avatarDownloadManager: RecipientAvatarDownloadManager, // we can reuse some logic here
24+
private val recipientAvatarDownloadManager: RecipientAvatarDownloadManager,
25+
private val recipientSettingsDatabase: RecipientSettingsDatabase,
26+
private val mmsSmsDatabase: MmsSmsDatabase,
2227
@param:ManagerScope private val coroutineScope: CoroutineScope
2328
) {
2429

@@ -31,26 +36,44 @@ class AvatarCacheCleaner @Inject constructor(
3136
* in the current config. Returns number of files deleted.
3237
*/
3338
suspend fun cleanUpAvatars(): Int = withContext(Dispatchers.IO) {
34-
// 1) Build the set of still-wanted RemoteFiles (contacts + groups)
35-
val wantedRemotes: Set<RemoteFile> = avatarDownloadManager.getAllAvatars()
39+
// 1) Build the set of still-wanted Avatars from:
40+
// -> Config
41+
// -> RecipientSettingsDatabase
3642

37-
// 2) Map to actual files (same hashing/location as downloader)
38-
val wantedFiles: Set<File> = wantedRemotes
43+
// config
44+
val avatarsFromConfig: Set<RemoteFile> = recipientAvatarDownloadManager.getAllAvatars()
45+
// recipient_settings
46+
val recipientAvatars : Map<Address, RemoteFile> = recipientSettingsDatabase.getAllReferencedAvatarFiles()
47+
// mms and sms used to check for active references
48+
val localActiveAddresses : Set<Address> = mmsSmsDatabase.getAllReferencedAddresses()
49+
.mapTo(mutableSetOf()) { Address.fromSerialized(it) }
50+
51+
// 2) Keep only those recipient avatars whose address exists in a community/group
52+
// Filter here since we don't delete rows from recipient_settings
53+
val keepFromRecipients: Set<RemoteFile> =
54+
recipientAvatars
55+
.asSequence()
56+
.filter { (address, _) -> address in localActiveAddresses }
57+
.map { it.value }
58+
.toSet()
59+
60+
// 3) Union of everything we want to keep
61+
val filesToKeep: Set<RemoteFile> =
62+
(avatarsFromConfig + keepFromRecipients).toSet()
63+
64+
// 4) Map to actual files (same hashing/location as downloader)
65+
val wantedFiles: Set<File> = filesToKeep
3966
.map { RemoteFileDownloadWorker.computeFileName(application, it) }
4067
.toSet()
4168

42-
// 3) Delete everything not wanted in cache/remote_files
69+
// 5) Delete everything not wanted in cache/remote_files
4370
val dir = File(application.cacheDir, "remote_files")
4471
val files = dir.listFiles().orEmpty()
4572
var deleted = 0
4673
for (file in files) {
4774
if (file !in wantedFiles && file.delete()) deleted++
4875
}
4976

50-
// 4) Clear Glide cache. Might need this now but we should remove after we fully migrate to Coil
51-
// Note to keep this off the main thread
52-
Glide.get(application).clearDiskCache()
53-
5477
deleted
5578
}
5679

app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,22 @@ public Cursor getUnreadOrUnseenReactions() {
449449
return queryTables(PROJECTION, selection, order, null);
450450
}
451451

452+
public Set<String> getAllReferencedAddresses() {
453+
final String[] projection = new String[] { "DISTINCT " + MmsSmsColumns.ADDRESS };
454+
final String selection =
455+
"NOT " + MmsSmsColumns.IS_DELETED +
456+
" AND " + MmsSmsColumns.ADDRESS + " IS NOT NULL" +
457+
" AND " + MmsSmsColumns.ADDRESS + " != ''";
458+
459+
Set<String> out = new HashSet<>();
460+
try (Cursor cursor = queryTables(projection, selection, null, null)) {
461+
while (cursor != null && cursor.moveToNext()) {
462+
out.add(cursor.getString(0));
463+
}
464+
}
465+
return out;
466+
}
467+
452468
/** Builds the comma-separated list of base types that represent
453469
* *outgoing* messages (same helper as before). */
454470
private String buildOutgoingTypesList() {

app/src/main/java/org/thoughtcrime/securesms/database/RecipientSettingsDatabase.kt

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import dagger.hilt.android.qualifiers.ApplicationContext
99
import kotlinx.coroutines.flow.MutableSharedFlow
1010
import kotlinx.coroutines.flow.SharedFlow
1111
import kotlinx.serialization.json.Json
12+
import network.loki.messenger.libsession_util.util.Bytes
1213
import network.loki.messenger.libsession_util.util.UserPic
1314
import org.session.libsession.utilities.Address
1415
import org.session.libsession.utilities.recipients.ProStatus
16+
import org.session.libsession.utilities.recipients.RemoteFile
1517
import org.session.libsignal.utilities.Base64
1618
import org.session.libsignal.utilities.Log
1719
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
@@ -41,7 +43,10 @@ class RecipientSettingsDatabase @Inject constructor(
4143

4244
// If nothing is updated, return early
4345
if (oldSettings == newSettings) {
44-
Log.d(TAG, "No changes to settings for ${address.debugString}, old: $oldSettings, new: $newSettings")
46+
Log.d(
47+
TAG,
48+
"No changes to settings for ${address.debugString}, old: $oldSettings, new: $newSettings"
49+
)
4550
return
4651
}
4752

@@ -78,10 +83,11 @@ class RecipientSettingsDatabase @Inject constructor(
7883
fun delete(address: Address) {
7984
cache.remove(address)
8085
if (writableDatabase.delete(
81-
TABLE_NAME,
82-
"$COL_ADDRESS = ?",
83-
arrayOf(address.toString())
84-
) > 0) {
86+
TABLE_NAME,
87+
"$COL_ADDRESS = ?",
88+
arrayOf(address.toString())
89+
) > 0
90+
) {
8591
mutableChangeNotification.tryEmit(address)
8692
}
8793
}
@@ -92,7 +98,10 @@ class RecipientSettingsDatabase @Inject constructor(
9298
return existing
9399
}
94100

95-
return readableDatabase.rawQuery("SELECT * FROM $TABLE_NAME WHERE $COL_ADDRESS = ?", address.address)
101+
return readableDatabase.rawQuery(
102+
"SELECT * FROM $TABLE_NAME WHERE $COL_ADDRESS = ?",
103+
address.address
104+
)
96105
.use { cursor ->
97106
// If no settings are saved in the database, return the empty settings, and cache
98107
// that as well so that we don't have to query the database again.
@@ -116,7 +125,11 @@ class RecipientSettingsDatabase @Inject constructor(
116125
keyB64 = getString(getColumnIndexOrThrow(COL_PROFILE_PIC_KEY)),
117126
url = getString(getColumnIndexOrThrow(COL_PROFILE_PIC_URL))
118127
),
119-
blocksCommunityMessagesRequests = getInt(getColumnIndexOrThrow(COL_BLOCKS_COMMUNITY_MESSAGES_REQUESTS)) == 1,
128+
blocksCommunityMessagesRequests = getInt(
129+
getColumnIndexOrThrow(
130+
COL_BLOCKS_COMMUNITY_MESSAGES_REQUESTS
131+
)
132+
) == 1,
120133
name = getString(getColumnIndexOrThrow(COL_NAME)),
121134
proStatus = getString(getColumnIndexOrThrow(COL_PRO_STATUS))
122135
?.let {
@@ -143,6 +156,34 @@ class RecipientSettingsDatabase @Inject constructor(
143156
}
144157
}
145158

159+
/**
160+
* This method returns all referenced profile picture URL.
161+
* This will be used to identify which avatars are still being used to exclude
162+
* them from the cleanup.
163+
*/
164+
fun getAllReferencedAvatarFiles(): Map<Address, RemoteFile.Encrypted> {
165+
LinkedHashSet<RemoteFile.Encrypted>()
166+
167+
val sql =
168+
"""
169+
SELECT $COL_PROFILE_PIC_URL, $COL_PROFILE_PIC_KEY
170+
FROM $TABLE_NAME
171+
WHERE $COL_PROFILE_PIC_URL IS NOT NULL AND $COL_PROFILE_PIC_URL != ''
172+
AND $COL_PROFILE_PIC_KEY IS NOT NULL AND $COL_PROFILE_PIC_KEY != ''
173+
""".trimIndent()
174+
175+
return readableDatabase.rawQuery(sql, emptyArray()).use { cursor ->
176+
buildMap {
177+
while (cursor.moveToNext()) {
178+
val address = Address.fromSerialized(cursor.getString(0))
179+
val url = cursor.getString(0)
180+
val key = Base64.decode(cursor.getString(1))
181+
put(address, RemoteFile.Encrypted(url = url, key = Bytes(key)))
182+
}
183+
}
184+
}
185+
}
186+
146187
companion object {
147188
private const val TAG = "RecipientSettingsDatabase"
148189

@@ -155,13 +196,15 @@ class RecipientSettingsDatabase @Inject constructor(
155196
private const val COL_PROFILE_PIC_KEY = "profile_pic_key_b64"
156197
private const val COL_PROFILE_PIC_URL = "profile_pic_url"
157198
private const val COL_NAME = "name"
158-
private const val COL_BLOCKS_COMMUNITY_MESSAGES_REQUESTS = "blocks_community_messages_requests"
199+
private const val COL_BLOCKS_COMMUNITY_MESSAGES_REQUESTS =
200+
"blocks_community_messages_requests"
159201
private const val COL_PRO_STATUS = "pro_status"
160202

161203
// The time when the profile pic/name/is_pro was last updated, in epoch seconds.
162204
private const val COL_PROFILE_UPDATE_TIME = "profile_update_time"
163205

164-
val MIGRATION_CREATE_TABLE = arrayOf("""
206+
val MIGRATION_CREATE_TABLE = arrayOf(
207+
"""
165208
CREATE TABLE recipient_settings (
166209
$COL_ADDRESS TEXT NOT NULL PRIMARY KEY COLLATE NOCASE,
167210
$COL_MUTE_UNTIL INTEGER NOT NULL DEFAULT 0,

0 commit comments

Comments
 (0)