@@ -8,8 +8,10 @@ import androidx.core.content.contentValuesOf
88import net.zetetic.database.sqlcipher.SQLiteDatabase
99import net.zetetic.database.sqlcipher.SQLiteOpenHelper
1010import org.signal.core.util.CursorUtil
11+ import org.signal.core.util.SqlUtil
1112import org.signal.core.util.concurrent.SignalExecutors
1213import org.signal.core.util.delete
14+ import org.signal.core.util.forEach
1315import org.signal.core.util.insertInto
1416import org.signal.core.util.logging.Log
1517import org.signal.core.util.readToList
@@ -31,6 +33,7 @@ import org.thoughtcrime.securesms.jobmanager.persistence.DependencySpec
3133import org.thoughtcrime.securesms.jobmanager.persistence.FullSpec
3234import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec
3335import org.thoughtcrime.securesms.jobs.MinimalJobSpec
36+ import java.util.function.Predicate
3437
3538class JobDatabase (
3639 application : Application ,
@@ -184,28 +187,32 @@ class JobDatabase(
184187 }
185188
186189 @Synchronized
187- fun getAllJobSpecs ( ): List <JobSpec > {
190+ fun getJobSpecs ( limit : Int ): List <JobSpec > {
188191 return readableDatabase
189192 .select()
190193 .from(Jobs .TABLE_NAME )
191194 .orderBy(" ${Jobs .CREATE_TIME } , ${Jobs .ID } ASC" )
195+ .limit(limit)
192196 .run ()
193- .readToList { cursor ->
194- jobSpecFromCursor(cursor)
195- }
197+ .readToList { it.toJobSpec() }
196198 }
197199
198200 @Synchronized
199- fun getOldestJobSpecs (limit : Int ): List <JobSpec > {
200- return readableDatabase
201+ fun getAllMatchingFilter (predicate : Predicate <JobSpec >): List <JobSpec > {
202+ val output: MutableList <JobSpec > = mutableListOf ()
203+
204+ readableDatabase
201205 .select()
202206 .from(Jobs .TABLE_NAME )
203- .orderBy(" ${Jobs .CREATE_TIME } , ${Jobs .ID } ASC" )
204- .limit(limit)
205207 .run ()
206208 .readToList { cursor ->
207- jobSpecFromCursor(cursor)
209+ val jobSpec = cursor.toJobSpec()
210+ if (predicate.test(jobSpec)) {
211+ output + = jobSpec
212+ }
208213 }
214+
215+ return output
209216 }
210217
211218 @Synchronized
@@ -215,9 +222,7 @@ class JobDatabase(
215222 .from(Jobs .TABLE_NAME )
216223 .where(" ${Jobs .JOB_SPEC_ID } = ?" , id)
217224 .run ()
218- .readToSingleObject {
219- jobSpecFromCursor(it)
220- }
225+ .readToSingleObject { it.toJobSpec() }
221226 }
222227
223228 @Synchronized
@@ -296,26 +301,41 @@ class JobDatabase(
296301 .filterNot { it.isMemoryOnly }
297302 .forEach { job ->
298303 db.update(Jobs .TABLE_NAME )
299- .values(
300- Jobs .JOB_SPEC_ID to job.id,
301- Jobs .FACTORY_KEY to job.factoryKey,
302- Jobs .QUEUE_KEY to job.queueKey,
303- Jobs .CREATE_TIME to job.createTime,
304- Jobs .LAST_RUN_ATTEMPT_TIME to job.lastRunAttemptTime,
305- Jobs .NEXT_BACKOFF_INTERVAL to job.nextBackoffInterval,
306- Jobs .RUN_ATTEMPT to job.runAttempt,
307- Jobs .MAX_ATTEMPTS to job.maxAttempts,
308- Jobs .LIFESPAN to job.lifespan,
309- Jobs .SERIALIZED_DATA to job.serializedData,
310- Jobs .SERIALIZED_INPUT_DATA to job.serializedInputData,
311- Jobs .IS_RUNNING to if (job.isRunning) 1 else 0
312- )
304+ .values(job.toContentValues())
313305 .where(" ${Jobs .JOB_SPEC_ID } = ?" , job.id)
314306 .run ()
315307 }
316308 }
317309 }
318310
311+ @Synchronized
312+ fun transformJobs (transformer : (JobSpec ) -> JobSpec ): List <JobSpec > {
313+ val transformed: MutableList <JobSpec > = mutableListOf ()
314+
315+ writableDatabase.withinTransaction { db ->
316+ readableDatabase
317+ .select()
318+ .from(Jobs .TABLE_NAME )
319+ .run ()
320+ .forEach { cursor ->
321+ val jobSpec = cursor.toJobSpec()
322+ val updated = transformer(jobSpec)
323+ if (updated != jobSpec) {
324+ transformed + = updated
325+ }
326+ }
327+
328+ for (job in transformed) {
329+ db.update(Jobs .TABLE_NAME )
330+ .values(job.toContentValues())
331+ .where(" ${Jobs .JOB_SPEC_ID } = ?" , job.id)
332+ .run ()
333+ }
334+ }
335+
336+ return transformed
337+ }
338+
319339 @Synchronized
320340 fun deleteJobs (jobIds : List <String >) {
321341 writableDatabase.withinTransaction { db ->
@@ -340,14 +360,30 @@ class JobDatabase(
340360 }
341361
342362 @Synchronized
343- fun getAllConstraintSpecs ( ): List <ConstraintSpec > {
363+ fun getConstraintSpecs ( limit : Int ): List <ConstraintSpec > {
344364 return readableDatabase
345365 .select()
346366 .from(Constraints .TABLE_NAME )
367+ .limit(limit)
347368 .run ()
348- .readToList { cursor ->
349- constraintSpecFromCursor(cursor)
350- }
369+ .readToList { it.toConstraintSpec() }
370+ }
371+
372+ fun getConstraintSpecsForJobs (jobIds : Collection <String >): List <ConstraintSpec > {
373+ val output: MutableList <ConstraintSpec > = mutableListOf ()
374+
375+ for (query in SqlUtil .buildCollectionQuery(Constraints .JOB_SPEC_ID , jobIds)) {
376+ readableDatabase
377+ .select()
378+ .from(Constraints .TABLE_NAME )
379+ .where(query.where, query.whereArgs)
380+ .run ()
381+ .forEach {
382+ output + = it.toConstraintSpec()
383+ }
384+ }
385+
386+ return output
351387 }
352388
353389 @Synchronized
@@ -356,9 +392,7 @@ class JobDatabase(
356392 .select()
357393 .from(Dependencies .TABLE_NAME )
358394 .run ()
359- .readToList { cursor ->
360- dependencySpecFromCursor(cursor)
361- }
395+ .readToList { it.toDependencySpec() }
362396 }
363397
364398 private fun insertJobSpec (db : SQLiteDatabase , job : JobSpec ) {
@@ -369,21 +403,7 @@ class JobDatabase(
369403 check(db.inTransaction())
370404
371405 db.insertInto(Jobs .TABLE_NAME )
372- .values(
373- Jobs .JOB_SPEC_ID to job.id,
374- Jobs .FACTORY_KEY to job.factoryKey,
375- Jobs .QUEUE_KEY to job.queueKey,
376- Jobs .CREATE_TIME to job.createTime,
377- Jobs .LAST_RUN_ATTEMPT_TIME to job.lastRunAttemptTime,
378- Jobs .NEXT_BACKOFF_INTERVAL to job.nextBackoffInterval,
379- Jobs .RUN_ATTEMPT to job.runAttempt,
380- Jobs .MAX_ATTEMPTS to job.maxAttempts,
381- Jobs .LIFESPAN to job.lifespan,
382- Jobs .SERIALIZED_DATA to job.serializedData,
383- Jobs .SERIALIZED_INPUT_DATA to job.serializedInputData,
384- Jobs .IS_RUNNING to if (job.isRunning) 1 else 0 ,
385- Jobs .PRIORITY to job.priority
386- )
406+ .values(job.toContentValues())
387407 .run (SQLiteDatabase .CONFLICT_IGNORE )
388408 }
389409
@@ -417,37 +437,37 @@ class JobDatabase(
417437 }
418438 }
419439
420- private fun jobSpecFromCursor ( cursor : Cursor ): JobSpec {
440+ private fun Cursor. toJobSpec ( ): JobSpec {
421441 return JobSpec (
422- id = cursor .requireNonNullString(Jobs .JOB_SPEC_ID ),
423- factoryKey = cursor .requireNonNullString(Jobs .FACTORY_KEY ),
424- queueKey = cursor .requireString(Jobs .QUEUE_KEY ),
425- createTime = cursor .requireLong(Jobs .CREATE_TIME ),
426- lastRunAttemptTime = cursor .requireLong(Jobs .LAST_RUN_ATTEMPT_TIME ),
427- nextBackoffInterval = cursor .requireLong(Jobs .NEXT_BACKOFF_INTERVAL ),
428- runAttempt = cursor .requireInt(Jobs .RUN_ATTEMPT ),
429- maxAttempts = cursor .requireInt(Jobs .MAX_ATTEMPTS ),
430- lifespan = cursor .requireLong(Jobs .LIFESPAN ),
431- serializedData = cursor .requireBlob(Jobs .SERIALIZED_DATA ),
432- serializedInputData = cursor .requireBlob(Jobs .SERIALIZED_INPUT_DATA ),
433- isRunning = cursor .requireBoolean(Jobs .IS_RUNNING ),
442+ id = this .requireNonNullString(Jobs .JOB_SPEC_ID ),
443+ factoryKey = this .requireNonNullString(Jobs .FACTORY_KEY ),
444+ queueKey = this .requireString(Jobs .QUEUE_KEY ),
445+ createTime = this .requireLong(Jobs .CREATE_TIME ),
446+ lastRunAttemptTime = this .requireLong(Jobs .LAST_RUN_ATTEMPT_TIME ),
447+ nextBackoffInterval = this .requireLong(Jobs .NEXT_BACKOFF_INTERVAL ),
448+ runAttempt = this .requireInt(Jobs .RUN_ATTEMPT ),
449+ maxAttempts = this .requireInt(Jobs .MAX_ATTEMPTS ),
450+ lifespan = this .requireLong(Jobs .LIFESPAN ),
451+ serializedData = this .requireBlob(Jobs .SERIALIZED_DATA ),
452+ serializedInputData = this .requireBlob(Jobs .SERIALIZED_INPUT_DATA ),
453+ isRunning = this .requireBoolean(Jobs .IS_RUNNING ),
434454 isMemoryOnly = false ,
435- priority = cursor .requireInt(Jobs .PRIORITY )
455+ priority = this .requireInt(Jobs .PRIORITY )
436456 )
437457 }
438458
439- private fun constraintSpecFromCursor ( cursor : Cursor ): ConstraintSpec {
459+ private fun Cursor. toConstraintSpec ( ): ConstraintSpec {
440460 return ConstraintSpec (
441- jobSpecId = cursor .requireNonNullString(Constraints .JOB_SPEC_ID ),
442- factoryKey = cursor .requireNonNullString(Constraints .FACTORY_KEY ),
461+ jobSpecId = this .requireNonNullString(Constraints .JOB_SPEC_ID ),
462+ factoryKey = this .requireNonNullString(Constraints .FACTORY_KEY ),
443463 isMemoryOnly = false
444464 )
445465 }
446466
447- private fun dependencySpecFromCursor ( cursor : Cursor ): DependencySpec {
467+ private fun Cursor. toDependencySpec ( ): DependencySpec {
448468 return DependencySpec (
449- jobId = cursor .requireNonNullString(Dependencies .JOB_SPEC_ID ),
450- dependsOnJobId = cursor .requireNonNullString(Dependencies .DEPENDS_ON_JOB_SPEC_ID ),
469+ jobId = this .requireNonNullString(Dependencies .JOB_SPEC_ID ),
470+ dependsOnJobId = this .requireNonNullString(Dependencies .DEPENDS_ON_JOB_SPEC_ID ),
451471 isMemoryOnly = false
452472 )
453473 }
@@ -468,6 +488,24 @@ class JobDatabase(
468488 writableDatabase.update(Jobs .TABLE_NAME , contentValuesOf(Jobs .NEXT_BACKOFF_INTERVAL to 0 ), null , null )
469489 }
470490
491+ private fun JobSpec.toContentValues (): ContentValues {
492+ return contentValuesOf(
493+ Jobs .JOB_SPEC_ID to this .id,
494+ Jobs .FACTORY_KEY to this .factoryKey,
495+ Jobs .QUEUE_KEY to this .queueKey,
496+ Jobs .CREATE_TIME to this .createTime,
497+ Jobs .LAST_RUN_ATTEMPT_TIME to this .lastRunAttemptTime,
498+ Jobs .NEXT_BACKOFF_INTERVAL to this .nextBackoffInterval,
499+ Jobs .RUN_ATTEMPT to this .runAttempt,
500+ Jobs .MAX_ATTEMPTS to this .maxAttempts,
501+ Jobs .LIFESPAN to this .lifespan,
502+ Jobs .SERIALIZED_DATA to this .serializedData,
503+ Jobs .SERIALIZED_INPUT_DATA to this .serializedInputData,
504+ Jobs .IS_RUNNING to if (this .isRunning) 1 else 0 ,
505+ Jobs .PRIORITY to this .priority
506+ )
507+ }
508+
471509 companion object {
472510 private val TAG = Log .tag(JobDatabase ::class .java)
473511 private const val DATABASE_VERSION = 3
0 commit comments