1 /*
2 * Copyright 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package androidx.work.impl.utils
17
18 import android.content.Context
19 import android.content.SharedPreferences
20 import androidx.sqlite.db.SupportSQLiteDatabase
21 import androidx.work.impl.WorkDatabase
22 import androidx.work.impl.model.Preference
23 import androidx.work.impl.utils.PreferenceUtils.INSERT_PREFERENCE
24 import java.util.concurrent.Callable
25
26 /**
27 * Generates unique IDs that are persisted in [SharedPreferences].
28 *
29 * @param workDatabase The [WorkDatabase] where metadata is persisted.
30 */
31 class IdGenerator(private val workDatabase: WorkDatabase) {
32 /** Generates IDs for [android.app.job.JobInfo] jobs given a reserved range. */
nextJobSchedulerIdWithRangenull33 fun nextJobSchedulerIdWithRange(minInclusive: Int, maxInclusive: Int): Int {
34 return workDatabase.runInTransaction(
35 Callable {
36 var id = workDatabase.nextId(NEXT_JOB_SCHEDULER_ID_KEY)
37 if (id !in minInclusive..maxInclusive) {
38 // outside the range, re-start at minInclusive.
39 id = minInclusive
40 workDatabase.updatePreference(NEXT_JOB_SCHEDULER_ID_KEY, id + 1)
41 }
42 id
43 }
44 )
45 }
46
47 /** Generates IDs for [android.app.AlarmManager] work. */
nextAlarmManagerIdnull48 fun nextAlarmManagerId(): Int {
49 return workDatabase.runInTransaction(
50 Callable { workDatabase.nextId(NEXT_ALARM_MANAGER_ID_KEY) }
51 )
52 }
53 }
54
nextIdnull55 private fun WorkDatabase.nextId(key: String): Int {
56 val value = preferenceDao().getLongValue(key)
57 val id = value?.toInt() ?: INITIAL_ID
58 val nextId = if (id == Int.MAX_VALUE) INITIAL_ID else id + 1
59 updatePreference(key, nextId)
60 return id
61 }
62
WorkDatabasenull63 private fun WorkDatabase.updatePreference(key: String, value: Int) =
64 this.preferenceDao().insertPreference(Preference(key, value.toLong()))
65
66 /** The initial id used for JobInfos and Alarms. */
67 const val INITIAL_ID = 0
68 const val NEXT_JOB_SCHEDULER_ID_KEY = "next_job_scheduler_id"
69 const val NEXT_ALARM_MANAGER_ID_KEY = "next_alarm_manager_id"
70 const val PREFERENCE_FILE_KEY = "androidx.work.util.id"
71
72 /**
73 * Migrates [IdGenerator] from [android.content.SharedPreferences] to the [WorkDatabase].
74 *
75 * @param context The application [Context]
76 */
77 internal fun migrateLegacyIdGenerator(context: Context, sqLiteDatabase: SupportSQLiteDatabase) {
78 val sharedPreferences = context.getSharedPreferences(PREFERENCE_FILE_KEY, Context.MODE_PRIVATE)
79
80 // Check to see if we have not migrated already.
81 if (
82 sharedPreferences.contains(NEXT_JOB_SCHEDULER_ID_KEY) ||
83 sharedPreferences.contains(NEXT_JOB_SCHEDULER_ID_KEY)
84 ) {
85 val nextJobId = sharedPreferences.getInt(NEXT_JOB_SCHEDULER_ID_KEY, INITIAL_ID)
86 val nextAlarmId = sharedPreferences.getInt(NEXT_ALARM_MANAGER_ID_KEY, INITIAL_ID)
87 sqLiteDatabase.beginTransaction()
88 try {
89 sqLiteDatabase.execSQL(INSERT_PREFERENCE, arrayOf(NEXT_JOB_SCHEDULER_ID_KEY, nextJobId))
90 sqLiteDatabase.execSQL(
91 INSERT_PREFERENCE,
92 arrayOf(NEXT_ALARM_MANAGER_ID_KEY, nextAlarmId)
93 )
94 // Cleanup
95 sharedPreferences.edit().clear().apply()
96 sqLiteDatabase.setTransactionSuccessful()
97 } finally {
98 sqLiteDatabase.endTransaction()
99 }
100 }
101 }
102