blob: 5616a00592f23ed97724627a6c3ca279fb3d7c13 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.backup
import android.app.backup.BackupAgentHelper
import android.app.backup.BackupDataInputStream
import android.app.backup.BackupDataOutput
import android.app.backup.FileBackupHelper
import android.app.job.JobScheduler
import android.content.Context
import android.content.Intent
import android.os.Environment
import android.os.ParcelFileDescriptor
import android.os.UserHandle
import android.util.Log
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
import com.android.systemui.people.widget.PeopleBackupHelper
/**
* Helper for backing up elements in SystemUI
*
* This helper is invoked by BackupManager whenever a backup or restore is required in SystemUI.
* The helper can be used to back up any element that is stored in [Context.getFilesDir].
*
* After restoring is done, a [ACTION_RESTORE_FINISHED] intent will be send to SystemUI user 0,
* indicating that restoring is finished for a given user.
*/
open class BackupHelper : BackupAgentHelper() {
companion object {
const val TAG = "BackupHelper"
internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME
private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite"
private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
val controlsDataLock = Any()
const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
private const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
}
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
}
val keys = PeopleBackupHelper.getFilesToBackup()
addHelper(PEOPLE_TILES_BACKUP_KEY, PeopleBackupHelper(
this, userHandle, keys.toTypedArray()))
}
override fun onRestoreFinished() {
super.onRestoreFinished()
val intent = Intent(ACTION_RESTORE_FINISHED).apply {
`package` = packageName
putExtra(Intent.EXTRA_USER_ID, userId)
flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
}
sendBroadcastAsUser(intent, UserHandle.SYSTEM, PERMISSION_SELF)
}
/**
* Helper class for restoring files ONLY if they are not present.
*
* A [Map] between filenames and actions (functions) is passed to indicate post processing
* actions to be taken after each file is restored.
*
* @property lock a lock to hold while backing up and restoring the files.
* @property context the context of the [BackupAgent]
* @property fileNamesAndPostProcess a map from the filenames to back up and the post processing
* actions to take
*/
private class NoOverwriteFileBackupHelper(
val lock: Any,
val context: Context,
val fileNamesAndPostProcess: Map<String, () -> Unit>
) : FileBackupHelper(context, *fileNamesAndPostProcess.keys.toTypedArray()) {
override fun restoreEntity(data: BackupDataInputStream) {
val file = Environment.buildPath(context.filesDir, data.key)
if (file.exists()) {
Log.w(TAG, "File " + data.key + " already exists. Skipping restore.")
return
}
synchronized(lock) {
super.restoreEntity(data)
fileNamesAndPostProcess.get(data.key)?.invoke()
}
}
override fun performBackup(
oldState: ParcelFileDescriptor?,
data: BackupDataOutput?,
newState: ParcelFileDescriptor?
) {
synchronized(lock) {
super.performBackup(oldState, data, newState)
}
}
}
}
private fun getPPControlsFile(context: Context): () -> Unit {
return {
val filesDir = context.filesDir
val file = Environment.buildPath(filesDir, BackupHelper.CONTROLS)
if (file.exists()) {
val dest = Environment.buildPath(filesDir,
AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
file.copyTo(dest)
val jobScheduler = context.getSystemService(JobScheduler::class.java)
jobScheduler?.schedule(
AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context))
}
}
}