1 /*
<lambda>null2  * Copyright 2019 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
17 
18 import android.content.Context
19 import android.os.Build
20 import androidx.annotation.RequiresApi
21 import androidx.annotation.RestrictTo
22 import androidx.work.Logger
23 import java.io.File
24 
25 private val TAG = Logger.tagWithPrefix("WrkDbPathHelper")
26 
27 /** @return The name of the database. */
28 internal const val WORK_DATABASE_NAME = "androidx.work.workdb"
29 
30 // Supporting files for a SQLite database
31 private val DATABASE_EXTRA_FILES = arrayOf("-journal", "-shm", "-wal")
32 
33 /** Keeps track of {@link WorkDatabase} paths. */
34 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
35 object WorkDatabasePathHelper {
36     /**
37      * Migrates [WorkDatabase] to the no-backup directory.
38      *
39      * @param context The application context.
40      */
41     @JvmStatic
42     fun migrateDatabase(context: Context) {
43         val defaultDatabasePath = getDefaultDatabasePath(context)
44         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && defaultDatabasePath.exists()) {
45             Logger.get().debug(TAG, "Migrating WorkDatabase to the no-backup directory")
46             migrationPaths(context).forEach { (source, destination) ->
47                 if (source.exists()) {
48                     if (destination.exists()) {
49                         Logger.get().warning(TAG, "Over-writing contents of $destination")
50                     }
51                     val renamed = source.renameTo(destination)
52                     val message =
53                         if (renamed) {
54                             "Migrated ${source}to $destination"
55                         } else {
56                             "Renaming $source to $destination failed"
57                         }
58                     Logger.get().debug(TAG, message)
59                 }
60             }
61         }
62     }
63 
64     /**
65      * Returns a [Map] of all paths which need to be migrated to the no-backup directory.
66      *
67      * @param context The application [Context]
68      * @return a [Map] of paths to be migrated from source -> destination
69      */
70     fun migrationPaths(context: Context): Map<File, File> {
71         return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
72             val databasePath = getDefaultDatabasePath(context)
73             val migratedPath = getDatabasePath(context)
74             val map =
75                 DATABASE_EXTRA_FILES.associate { extra ->
76                     File(databasePath.path + extra) to File(migratedPath.path + extra)
77                 }
78             map + (databasePath to migratedPath)
79         } else emptyMap()
80     }
81 
82     /**
83      * @param context The application [Context]
84      * @return The database path before migration to the no-backup directory.
85      */
86     fun getDefaultDatabasePath(context: Context): File {
87         return context.getDatabasePath(WORK_DATABASE_NAME)
88     }
89 
90     /**
91      * @param context The application [Context]
92      * @return The the migrated database path.
93      */
94     fun getDatabasePath(context: Context): File {
95         return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
96             // No notion of a backup directory exists.
97             getDefaultDatabasePath(context)
98         } else {
99             getNoBackupPath(context)
100         }
101     }
102 
103     /**
104      * Return the path for a [File] path in the [Context.getNoBackupFilesDir] identified by the
105      * [String] fragment.
106      *
107      * @param context The application [Context]
108      * @return the [File]
109      */
110     @RequiresApi(23)
111     private fun getNoBackupPath(context: Context): File {
112         return File(Api21Impl.getNoBackupFilesDir(context), WORK_DATABASE_NAME)
113     }
114 }
115 
116 @RequiresApi(21)
117 internal object Api21Impl {
getNoBackupFilesDirnull118     fun getNoBackupFilesDir(context: Context): File {
119         return context.noBackupFilesDir
120     }
121 }
122