summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Matt Pietal <mpietal@google.com> 2023-02-15 15:06:08 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-02-15 15:06:08 +0000
commitcb5228cf3fc7c6b439222e825c57c265553bd8b8 (patch)
tree6c49c55875026b06e569cb1f06851f579783d11c
parente2a2e37c5e049cbe2a55162a287fd642f3639f93 (diff)
parent7a8c125e88f7a251a1ee2eb0af77803a00362977 (diff)
Merge "Backup and restore - Fix non-system users" into tm-qpr-dev
-rw-r--r--packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt185
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt125
6 files changed, 246 insertions, 199 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index 621b99d6804a..6721c5d5e413 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -31,6 +31,7 @@ import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper
import com.android.systemui.people.widget.PeopleBackupHelper
+import com.android.systemui.settings.UserFileManagerImpl
/**
* Helper for backing up elements in SystemUI
@@ -58,17 +59,8 @@ open class BackupHelper : BackupAgentHelper() {
override fun onCreate(userHandle: UserHandle, operationType: Int) {
super.onCreate()
- // The map in mapOf is guaranteed to be order preserving
- val controlsMap = mapOf(CONTROLS to getPPControlsFile(this))
- NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
- addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it)
- }
- // Conversations widgets backup only works for system user, because widgets' information is
- // stored in system user's SharedPreferences files and we can't open those from other users.
- if (!userHandle.isSystem) {
- return
- }
+ addControlsHelper(userHandle.identifier)
val keys = PeopleBackupHelper.getFilesToBackup()
addHelper(
@@ -95,6 +87,18 @@ open class BackupHelper : BackupAgentHelper() {
sendBroadcastAsUser(intent, UserHandle.SYSTEM, PERMISSION_SELF)
}
+ private fun addControlsHelper(userId: Int) {
+ val file = UserFileManagerImpl.createFile(
+ userId = userId,
+ fileName = CONTROLS,
+ )
+ // The map in mapOf is guaranteed to be order preserving
+ val controlsMap = mapOf(file.getPath() to getPPControlsFile(this, userId))
+ NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
+ addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it)
+ }
+ }
+
/**
* Helper class for restoring files ONLY if they are not present.
*
@@ -136,17 +140,21 @@ open class BackupHelper : BackupAgentHelper() {
}
}
-private fun getPPControlsFile(context: Context): () -> Unit {
+private fun getPPControlsFile(context: Context, userId: Int): () -> Unit {
return {
- val filesDir = context.filesDir
- val file = Environment.buildPath(filesDir, BackupHelper.CONTROLS)
+ val file = UserFileManagerImpl.createFile(
+ userId = userId,
+ fileName = BackupHelper.CONTROLS,
+ )
if (file.exists()) {
- val dest =
- Environment.buildPath(filesDir, AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
+ val dest = UserFileManagerImpl.createFile(
+ userId = userId,
+ fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME,
+ )
file.copyTo(dest)
val jobScheduler = context.getSystemService(JobScheduler::class.java)
jobScheduler?.schedule(
- AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context)
+ AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context, userId)
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
index 0a6335e01f9f..b3c18fb3cd98 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
@@ -21,8 +21,10 @@ import android.app.job.JobParameters
import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
+import android.os.PersistableBundle
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.backup.BackupHelper
+import com.android.systemui.settings.UserFileManagerImpl
import java.io.File
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
@@ -33,14 +35,14 @@ import java.util.concurrent.TimeUnit
* This file is a copy of the `controls_favorites.xml` file restored from a back up. It is used to
* keep track of controls that were restored but its corresponding app has not been installed yet.
*/
-class AuxiliaryPersistenceWrapper @VisibleForTesting internal constructor(
- wrapper: ControlsFavoritePersistenceWrapper
-) {
+class AuxiliaryPersistenceWrapper
+@VisibleForTesting
+internal constructor(wrapper: ControlsFavoritePersistenceWrapper) {
constructor(
file: File,
executor: Executor
- ): this(ControlsFavoritePersistenceWrapper(file, executor))
+ ) : this(ControlsFavoritePersistenceWrapper(file, executor))
companion object {
const val AUXILIARY_FILE_NAME = "aux_controls_favorites.xml"
@@ -48,9 +50,7 @@ class AuxiliaryPersistenceWrapper @VisibleForTesting internal constructor(
private var persistenceWrapper: ControlsFavoritePersistenceWrapper = wrapper
- /**
- * Access the current list of favorites as tracked by the auxiliary file
- */
+ /** Access the current list of favorites as tracked by the auxiliary file */
var favorites: List<StructureInfo> = emptyList()
private set
@@ -73,18 +73,19 @@ class AuxiliaryPersistenceWrapper @VisibleForTesting internal constructor(
* exist, it will be initialized to an empty list.
*/
fun initialize() {
- favorites = if (persistenceWrapper.fileExists) {
- persistenceWrapper.readFavorites()
- } else {
- emptyList()
- }
+ favorites =
+ if (persistenceWrapper.fileExists) {
+ persistenceWrapper.readFavorites()
+ } else {
+ emptyList()
+ }
}
/**
* Gets the list of favorite controls as persisted in the auxiliary file for a given component.
*
- * When the favorites for that application are returned, they will be removed from the
- * auxiliary file immediately, so they won't be retrieved again.
+ * When the favorites for that application are returned, they will be removed from the auxiliary
+ * file immediately, so they won't be retrieved again.
* @param componentName the name of the service that provided the controls
* @return a list of structures with favorites
*/
@@ -103,20 +104,20 @@ class AuxiliaryPersistenceWrapper @VisibleForTesting internal constructor(
}
}
- /**
- * [JobService] to delete the auxiliary file after a week.
- */
+ /** [JobService] to delete the auxiliary file after a week. */
class DeletionJobService : JobService() {
companion object {
- @VisibleForTesting
- internal val DELETE_FILE_JOB_ID = 1000
+ @VisibleForTesting internal val DELETE_FILE_JOB_ID = 1000
+ @VisibleForTesting internal val USER = "USER"
private val WEEK_IN_MILLIS = TimeUnit.DAYS.toMillis(7)
- fun getJobForContext(context: Context): JobInfo {
+ fun getJobForContext(context: Context, targetUserId: Int): JobInfo {
val jobId = DELETE_FILE_JOB_ID + context.userId
val componentName = ComponentName(context, DeletionJobService::class.java)
+ val bundle = PersistableBundle().also { it.putInt(USER, targetUserId) }
return JobInfo.Builder(jobId, componentName)
.setMinimumLatency(WEEK_IN_MILLIS)
.setPersisted(true)
+ .setExtras(bundle)
.build()
}
}
@@ -127,8 +128,14 @@ class AuxiliaryPersistenceWrapper @VisibleForTesting internal constructor(
}
override fun onStartJob(params: JobParameters): Boolean {
+ val userId = params.getExtras()?.getInt(USER, 0) ?: 0
synchronized(BackupHelper.controlsDataLock) {
- baseContext.deleteFile(AUXILIARY_FILE_NAME)
+ val file =
+ UserFileManagerImpl.createFile(
+ userId = userId,
+ fileName = AUXILIARY_FILE_NAME,
+ )
+ baseContext.deleteFile(file.getPath())
}
return false
}
@@ -137,4 +144,4 @@ class AuxiliaryPersistenceWrapper @VisibleForTesting internal constructor(
return true // reschedule and try again if the job was stopped without completing
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
index 0e865cee0b76..fa6efa504623 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
@@ -29,16 +29,9 @@ class KeyguardQuickAffordanceBackupHelper(
) :
SharedPreferencesBackupHelper(
context,
- if (UserFileManagerImpl.isPrimaryUser(userId)) {
- KeyguardQuickAffordanceSelectionManager.FILE_NAME
- } else {
- UserFileManagerImpl.secondaryUserFile(
- context = context,
- fileName = KeyguardQuickAffordanceSelectionManager.FILE_NAME,
- directoryName = UserFileManagerImpl.SHARED_PREFS,
- userId = userId,
- )
- .also { UserFileManagerImpl.ensureParentDirExists(it) }
- .toString()
- }
+ UserFileManagerImpl.createFile(
+ userId = userId,
+ fileName = KeyguardQuickAffordanceSelectionManager.FILE_NAME,
+ )
+ .getPath()
)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
index bfba6dfddfac..bb637dcd860e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -32,74 +32,62 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.File
+import java.io.FilenameFilter
import javax.inject.Inject
/**
- * Implementation for retrieving file paths for file storage of system and secondary users. Files
- * lie in {File Directory}/UserFileManager/{User Id} for secondary user. For system user, we use the
- * conventional {File Directory}
+ * Implementation for retrieving file paths for file storage of system and secondary users. For
+ * non-system users, files will be prepended by a special prefix containing the user id.
*/
@SysUISingleton
class UserFileManagerImpl
@Inject
constructor(
- // Context of system process and system user.
private val context: Context,
val userManager: UserManager,
val broadcastDispatcher: BroadcastDispatcher,
@Background val backgroundExecutor: DelayableExecutor
) : UserFileManager, CoreStartable {
companion object {
- private const val FILES = "files"
+ private const val PREFIX = "__USER_"
+ private const val TAG = "UserFileManagerImpl"
+ const val ROOT_DIR = "UserFileManager"
+ const val FILES = "files"
const val SHARED_PREFS = "shared_prefs"
- @VisibleForTesting internal const val ID = "UserFileManager"
-
- /** Returns `true` if the given user ID is that for the primary/system user. */
- fun isPrimaryUser(userId: Int): Boolean {
- return UserHandle(userId).isSystem
- }
/**
- * Returns a [File] pointing to the correct path for a secondary user ID.
- *
- * Note that there is no check for the type of user. This should only be called for
- * secondary users, never for the system user. For that, make sure to call [isPrimaryUser].
- *
- * Note also that there is no guarantee that the parent directory structure for the file
- * exists on disk. For that, call [ensureParentDirExists].
- *
- * @param context The context
- * @param fileName The name of the file
- * @param directoryName The name of the directory that would contain the file
- * @param userId The ID of the user to build a file path for
+ * Returns a File object with a relative path, built from the userId for non-system users
*/
- fun secondaryUserFile(
- context: Context,
- fileName: String,
- directoryName: String,
- userId: Int,
- ): File {
- return Environment.buildPath(
- context.filesDir,
- ID,
- userId.toString(),
- directoryName,
- fileName,
- )
+ fun createFile(fileName: String, userId: Int): File {
+ return if (isSystemUser(userId)) {
+ File(fileName)
+ } else {
+ File(getFilePrefix(userId) + fileName)
+ }
}
- /**
- * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs
- * recursively.
- */
- fun ensureParentDirExists(file: File) {
- val parent = file.parentFile
- if (!parent.exists()) {
- if (!parent.mkdirs()) {
- Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}")
- }
+ fun createLegacyFile(context: Context, dir: String, fileName: String, userId: Int): File? {
+ return if (isSystemUser(userId)) {
+ null
+ } else {
+ return Environment.buildPath(
+ context.filesDir,
+ ROOT_DIR,
+ userId.toString(),
+ dir,
+ fileName
+ )
}
}
+
+ fun getFilePrefix(userId: Int): String {
+ return PREFIX + userId.toString() + "_"
+ }
+
+ /** Returns `true` if the given user ID is that for the system user. */
+ private fun isSystemUser(userId: Int): Boolean {
+ return UserHandle(userId).isSystem
+ }
}
private val broadcastReceiver =
@@ -119,64 +107,87 @@ constructor(
broadcastDispatcher.registerReceiver(broadcastReceiver, filter, backgroundExecutor)
}
- /** Return the file based on current user. */
+ /**
+ * Return the file based on current user. Files for all users will exist in [context.filesDir],
+ * but non system user files will be prepended with [getFilePrefix].
+ */
override fun getFile(fileName: String, userId: Int): File {
- return if (isPrimaryUser(userId)) {
- Environment.buildPath(context.filesDir, fileName)
- } else {
- val secondaryFile =
- secondaryUserFile(
- context = context,
- userId = userId,
- directoryName = FILES,
- fileName = fileName,
- )
- ensureParentDirExists(secondaryFile)
- secondaryFile
- }
+ val file = File(context.filesDir, createFile(fileName, userId).path)
+ createLegacyFile(context, FILES, fileName, userId)?.run { migrate(file, this) }
+ return file
}
- /** Get shared preferences from user. */
+ /**
+ * Get shared preferences from user. Files for all users will exist in the shared_prefs dir, but
+ * non system user files will be prepended with [getFilePrefix].
+ */
override fun getSharedPreferences(
fileName: String,
@Context.PreferencesMode mode: Int,
userId: Int
): SharedPreferences {
- if (isPrimaryUser(userId)) {
- return context.getSharedPreferences(fileName, mode)
+ val file = createFile(fileName, userId)
+ createLegacyFile(context, SHARED_PREFS, "$fileName.xml", userId)?.run {
+ val path = Environment.buildPath(context.dataDir, SHARED_PREFS, "${file.path}.xml")
+ migrate(path, this)
}
-
- val secondaryUserDir =
- secondaryUserFile(
- context = context,
- fileName = fileName,
- directoryName = SHARED_PREFS,
- userId = userId,
- )
-
- ensureParentDirExists(secondaryUserDir)
- return context.getSharedPreferences(secondaryUserDir, mode)
+ return context.getSharedPreferences(file.path, mode)
}
- /** Remove dirs for deleted users. */
+ /** Remove files for deleted users. */
@VisibleForTesting
internal fun clearDeletedUserData() {
backgroundExecutor.execute {
- val file = Environment.buildPath(context.filesDir, ID)
- if (!file.exists()) return@execute
- val aliveUsers = userManager.aliveUsers.map { it.id.toString() }
- val dirsToDelete = file.list().filter { !aliveUsers.contains(it) }
+ deleteFiles(context.filesDir)
+ deleteFiles(File(context.dataDir, SHARED_PREFS))
+ }
+ }
+
+ private fun migrate(dest: File, source: File) {
+ if (source.exists()) {
+ try {
+ val parent = source.getParentFile()
+ source.renameTo(dest)
+
+ deleteParentDirsIfEmpty(parent)
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to rename and delete ${source.path}", e)
+ }
+ }
+ }
- dirsToDelete.forEach { dir ->
+ private fun deleteParentDirsIfEmpty(dir: File?) {
+ if (dir != null && dir.listFiles().size == 0) {
+ val priorParent = dir.parentFile
+ val isRoot = dir.name == ROOT_DIR
+ dir.delete()
+
+ if (!isRoot) {
+ deleteParentDirsIfEmpty(priorParent)
+ }
+ }
+ }
+
+ private fun deleteFiles(parent: File) {
+ val aliveUserFilePrefix = userManager.aliveUsers.map { getFilePrefix(it.id) }
+ val filesToDelete =
+ parent.listFiles(
+ FilenameFilter { _, name ->
+ name.startsWith(PREFIX) &&
+ aliveUserFilePrefix.filter { name.startsWith(it) }.isEmpty()
+ }
+ )
+
+ // This can happen in test environments
+ if (filesToDelete == null) {
+ Log.i(TAG, "Empty directory: ${parent.path}")
+ } else {
+ filesToDelete.forEach { file ->
+ Log.i(TAG, "Deleting file: ${file.path}")
try {
- val dirToDelete =
- Environment.buildPath(
- file,
- dir,
- )
- dirToDelete.deleteRecursively()
+ file.delete()
} catch (e: Exception) {
- Log.e(ID, "Deletion failed.", e)
+ Log.e(TAG, "Deletion failed.", e)
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
index 4439586497ff..228374671ad4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
@@ -18,9 +18,13 @@ package com.android.systemui.controls.controller
import android.app.job.JobParameters
import android.content.Context
+import android.os.PersistableBundle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper.DeletionJobService.Companion.USER
+import com.android.systemui.util.mockito.whenever
+import java.util.concurrent.TimeUnit
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -28,18 +32,15 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import java.util.concurrent.TimeUnit
@SmallTest
@RunWith(AndroidTestingRunner::class)
class DeletionJobServiceTest : SysuiTestCase() {
- @Mock
- private lateinit var context: Context
+ @Mock private lateinit var context: Context
private lateinit var service: AuxiliaryPersistenceWrapper.DeletionJobService
@@ -53,6 +54,10 @@ class DeletionJobServiceTest : SysuiTestCase() {
@Test
fun testOnStartJob() {
+ val bundle = PersistableBundle().also { it.putInt(USER, 0) }
+ val params = mock(JobParameters::class.java)
+ whenever(params.getExtras()).thenReturn(bundle)
+
// false means job is terminated
assertFalse(service.onStartJob(mock(JobParameters::class.java)))
verify(context).deleteFile(AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
@@ -67,13 +72,17 @@ class DeletionJobServiceTest : SysuiTestCase() {
@Test
fun testJobHasRightParameters() {
val userId = 10
- `when`(context.userId).thenReturn(userId)
- `when`(context.packageName).thenReturn(mContext.packageName)
+ whenever(context.userId).thenReturn(userId)
+ whenever(context.packageName).thenReturn(mContext.packageName)
- val jobInfo = AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context)
+ val jobInfo =
+ AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context, userId)
assertEquals(
- AuxiliaryPersistenceWrapper.DeletionJobService.DELETE_FILE_JOB_ID + userId, jobInfo.id)
+ AuxiliaryPersistenceWrapper.DeletionJobService.DELETE_FILE_JOB_ID + userId,
+ jobInfo.id
+ )
assertTrue(jobInfo.isPersisted)
+ assertEquals(userId, jobInfo.getExtras().getInt(USER))
assertEquals(TimeUnit.DAYS.toMillis(7), jobInfo.minLatencyMillis)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
index 020a86611552..3fd19ff1827b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -30,12 +30,14 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.nio.file.Files
import java.util.concurrent.Executor
-import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.isNull
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
@@ -61,36 +63,83 @@ class UserFileManagerImplTest : SysuiTestCase() {
UserFileManagerImpl(context, userManager, broadcastDispatcher, backgroundExecutor)
}
- @After
- fun end() {
- val dir = Environment.buildPath(context.filesDir, UserFileManagerImpl.ID)
- dir.deleteRecursively()
- }
-
@Test
fun testGetFile() {
assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path)
.isEqualTo("${context.filesDir}/$TEST_FILE_NAME")
assertThat(userFileManager.getFile(TEST_FILE_NAME, 11).path)
- .isEqualTo("${context.filesDir}/${UserFileManagerImpl.ID}/11/files/$TEST_FILE_NAME")
+ .isEqualTo("${context.filesDir}/__USER_11_$TEST_FILE_NAME")
}
@Test
fun testGetSharedPreferences() {
+ val primarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0)
val secondarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)
- val secondaryUserDir =
- Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
+
+ assertThat(primarySharedPref).isNotEqualTo(secondarySharedPref)
+
+ // Make sure these are different files
+ primarySharedPref.edit().putString("TEST", "ABC").commit()
+ assertThat(secondarySharedPref.getString("TEST", null)).isNull()
+
+ context.deleteSharedPreferences("TEST")
+ context.deleteSharedPreferences("__USER_11_TEST")
+ }
+
+ @Test
+ fun testMigrateFile() {
+ val userId = 12
+ val fileName = "myFile.txt"
+ val fileContents = "TestingFile"
+ val legacyFile =
+ UserFileManagerImpl.createLegacyFile(
+ context,
+ UserFileManagerImpl.FILES,
+ fileName,
+ userId
+ )!!
+
+ // Write file to legacy area
+ Files.createDirectories(legacyFile.getParentFile().toPath())
+ Files.write(legacyFile.toPath(), fileContents.toByteArray())
+ assertThat(legacyFile.exists()).isTrue()
+
+ // getFile() should migrate the legacy file to the new location
+ val file = userFileManager.getFile(fileName, userId)
+ val newContents = String(Files.readAllBytes(file.toPath()))
+
+ assertThat(newContents).isEqualTo(fileContents)
+ assertThat(legacyFile.exists()).isFalse()
+ assertThat(File(context.filesDir, UserFileManagerImpl.ROOT_DIR).exists()).isFalse()
+ }
+
+ @Test
+ fun testMigrateSharedPrefs() {
+ val userId = 13
+ val fileName = "myFile"
+ val contents = "TestingSharedPrefs"
+ val legacyFile =
+ UserFileManagerImpl.createLegacyFile(
+ context,
UserFileManagerImpl.SHARED_PREFS,
- TEST_FILE_NAME
- )
+ "$fileName.xml",
+ userId
+ )!!
- assertThat(secondarySharedPref).isNotNull()
- assertThat(secondaryUserDir.exists())
- assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0))
- .isNotEqualTo(secondarySharedPref)
+ // Write a valid shared prefs xml file to legacy area
+ val tmpPrefs = context.getSharedPreferences("tmp", Context.MODE_PRIVATE)
+ tmpPrefs.edit().putString(contents, contents).commit()
+ Files.createDirectories(legacyFile.getParentFile().toPath())
+ val tmpFile =
+ Environment.buildPath(context.dataDir, UserFileManagerImpl.SHARED_PREFS, "tmp.xml")
+ tmpFile.renameTo(legacyFile)
+ assertThat(legacyFile.exists()).isTrue()
+
+ // getSharedpreferences() should migrate the legacy file to the new location
+ val prefs = userFileManager.getSharedPreferences(fileName, Context.MODE_PRIVATE, userId)
+ assertThat(prefs.getString(contents, "")).isEqualTo(contents)
+ assertThat(legacyFile.exists()).isFalse()
+ assertThat(File(context.filesDir, UserFileManagerImpl.ROOT_DIR).exists()).isFalse()
}
@Test
@@ -111,44 +160,14 @@ class UserFileManagerImplTest : SysuiTestCase() {
@Test
fun testClearDeletedUserData() {
- val dir = Environment.buildPath(context.filesDir, UserFileManagerImpl.ID, "11", "files")
- dir.mkdirs()
- val file =
- Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
- "files",
- TEST_FILE_NAME
- )
- val secondaryUserDir =
- Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
- )
+ val file = userFileManager.getFile(TEST_FILE_NAME, 11)
file.createNewFile()
- assertThat(secondaryUserDir.exists()).isTrue()
+
assertThat(file.exists()).isTrue()
userFileManager.clearDeletedUserData()
assertThat(backgroundExecutor.runAllReady()).isGreaterThan(0)
- verify(userManager).aliveUsers
- assertThat(secondaryUserDir.exists()).isFalse()
- assertThat(file.exists()).isFalse()
- }
+ verify(userManager, atLeastOnce()).aliveUsers
- @Test
- fun testEnsureParentDirExists() {
- val file =
- Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
- "files",
- TEST_FILE_NAME
- )
- assertThat(file.parentFile.exists()).isFalse()
- UserFileManagerImpl.ensureParentDirExists(file)
- assertThat(file.parentFile.exists()).isTrue()
+ assertThat(file.exists()).isFalse()
}
}