1 /*
2 * Copyright (C) 2020 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
17 package com.android.systemui.backup
18
19 import android.app.backup.BackupAgentHelper
20 import android.app.backup.BackupDataInputStream
21 import android.app.backup.BackupDataOutput
22 import android.app.backup.FileBackupHelper
23 import android.app.job.JobScheduler
24 import android.content.Context
25 import android.content.Intent
26 import android.os.Environment
27 import android.os.ParcelFileDescriptor
28 import android.os.UserHandle
29 import android.util.Log
30 import com.android.app.tracing.traceSection
31 import com.android.systemui.backup.BackupHelper.Companion.ACTION_RESTORE_FINISHED
32 import com.android.systemui.communal.data.backup.CommunalBackupHelper
33 import com.android.systemui.communal.data.backup.CommunalBackupUtils
34 import com.android.systemui.communal.domain.backup.CommunalPrefsBackupHelper
35 import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
36 import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
37 import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper
38 import com.android.systemui.people.widget.PeopleBackupHelper
39 import com.android.systemui.qs.panels.domain.backup.QSPreferencesBackupHelper
40 import com.android.systemui.res.R
41 import com.android.systemui.settings.UserFileManagerImpl
42
43 /**
44 * Helper for backing up elements in SystemUI
45 *
46 * This helper is invoked by BackupManager whenever a backup or restore is required in SystemUI. The
47 * helper can be used to back up any element that is stored in [Context.getFilesDir] or
48 * [Context.getSharedPreferences].
49 *
50 * After restoring is done, a [ACTION_RESTORE_FINISHED] intent will be send to SystemUI user 0,
51 * indicating that restoring is finished for a given user.
52 */
53 open class BackupHelper : BackupAgentHelper() {
54
55 companion object {
56 const val TAG = "BackupHelper"
57 internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME
58 private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite"
59 private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
60 private const val KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY =
61 "systemui.keyguard.quickaffordance.shared_preferences"
62 private const val COMMUNAL_PREFS_BACKUP_KEY = "systemui.communal.shared_preferences"
63 private const val COMMUNAL_STATE_BACKUP_KEY = "systemui.communal_state"
64 private const val QS_PREFERENCES_BACKUP_KEY = "systemui.qs.shared_preferences"
65 val controlsDataLock = Any()
66 const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
67 const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
68 }
69
onCreatenull70 override fun onCreate(userHandle: UserHandle) {
71 super.onCreate(userHandle)
72
73 addControlsHelper(userHandle.identifier)
74
75 val keys = PeopleBackupHelper.getFilesToBackup()
76 addHelper(
77 PEOPLE_TILES_BACKUP_KEY,
78 PeopleBackupHelper(this, userHandle, keys.toTypedArray()),
79 )
80 addHelper(
81 KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY,
82 KeyguardQuickAffordanceBackupHelper(context = this, userId = userHandle.identifier),
83 )
84 addHelper(
85 QS_PREFERENCES_BACKUP_KEY,
86 QSPreferencesBackupHelper(context = this, userId = userHandle.identifier),
87 )
88 if (communalEnabled()) {
89 addHelper(
90 COMMUNAL_PREFS_BACKUP_KEY,
91 CommunalPrefsBackupHelper(context = this, userId = userHandle.identifier),
92 )
93 addHelper(
94 COMMUNAL_STATE_BACKUP_KEY,
95 CommunalBackupHelper(userHandle, CommunalBackupUtils(context = this)),
96 )
97 }
98 }
99
onRestoreFinishednull100 override fun onRestoreFinished() {
101 super.onRestoreFinished()
102 val intent =
103 Intent(ACTION_RESTORE_FINISHED).apply {
104 `package` = packageName
105 putExtra(Intent.EXTRA_USER_ID, userId)
106 flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
107 }
108 sendBroadcastAsUser(intent, UserHandle.SYSTEM, PERMISSION_SELF)
109 }
110
addControlsHelpernull111 private fun addControlsHelper(userId: Int) {
112 val file = UserFileManagerImpl.createFile(userId = userId, fileName = CONTROLS)
113 // The map in mapOf is guaranteed to be order preserving
114 val controlsMap = mapOf(file.getPath() to getPPControlsFile(this, userId))
115 NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
116 addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it)
117 }
118 }
119
communalEnablednull120 private fun communalEnabled(): Boolean {
121 return resources.getBoolean(R.bool.config_communalServiceEnabled)
122 }
123
124 /**
125 * Helper class for restoring files ONLY if they are not present.
126 *
127 * A [Map] between filenames and actions (functions) is passed to indicate post processing
128 * actions to be taken after each file is restored.
129 *
130 * @property lock a lock to hold while backing up and restoring the files.
131 * @property context the context of the [BackupAgent]
132 * @property fileNamesAndPostProcess a map from the filenames to back up and the post processing
133 *
134 * ```
135 * actions to take
136 * ```
137 */
138 private class NoOverwriteFileBackupHelper(
139 val lock: Any,
140 val context: Context,
141 val fileNamesAndPostProcess: Map<String, () -> Unit>,
142 ) : FileBackupHelper(context, *fileNamesAndPostProcess.keys.toTypedArray()) {
143
restoreEntitynull144 override fun restoreEntity(data: BackupDataInputStream) {
145 Log.d(TAG, "Starting restore for ${data.key} for user ${context.userId}")
146 val file = Environment.buildPath(context.filesDir, data.key)
147 if (file.exists()) {
148 Log.w(TAG, "File " + data.key + " already exists. Skipping restore.")
149 return
150 }
151 synchronized(lock) {
152 traceSection("File restore: ${data.key}") { super.restoreEntity(data) }
153 Log.d(
154 TAG,
155 "Finishing restore for ${data.key} for user ${context.userId}. " +
156 "Starting postProcess.",
157 )
158 traceSection("Postprocess: ${data.key}") {
159 fileNamesAndPostProcess.get(data.key)?.invoke()
160 }
161 Log.d(TAG, "Finishing postprocess for ${data.key} for user ${context.userId}.")
162 }
163 }
164
performBackupnull165 override fun performBackup(
166 oldState: ParcelFileDescriptor?,
167 data: BackupDataOutput?,
168 newState: ParcelFileDescriptor?,
169 ) {
170 synchronized(lock) { super.performBackup(oldState, data, newState) }
171 }
172 }
173 }
174
getPPControlsFilenull175 private fun getPPControlsFile(context: Context, userId: Int): () -> Unit {
176 return {
177 val file = UserFileManagerImpl.createFile(userId = userId, fileName = BackupHelper.CONTROLS)
178 if (file.exists()) {
179 val dest =
180 UserFileManagerImpl.createFile(
181 userId = userId,
182 fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME,
183 )
184 file.copyTo(dest)
185 val jobScheduler = context.getSystemService(JobScheduler::class.java)
186 jobScheduler?.schedule(
187 AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context, userId)
188 )
189 }
190 }
191 }
192