• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.systemui.controls.controller.AuxiliaryPersistenceWrapper
31 import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
32 import com.android.systemui.people.widget.PeopleBackupHelper
33 
34 /**
35  * Helper for backing up elements in SystemUI
36  *
37  * This helper is invoked by BackupManager whenever a backup or restore is required in SystemUI.
38  * The helper can be used to back up any element that is stored in [Context.getFilesDir].
39  *
40  * After restoring is done, a [ACTION_RESTORE_FINISHED] intent will be send to SystemUI user 0,
41  * indicating that restoring is finished for a given user.
42  */
43 class BackupHelper : BackupAgentHelper() {
44 
45     companion object {
46         private const val TAG = "BackupHelper"
47         internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME
48         private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite"
49         private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
50         val controlsDataLock = Any()
51         const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
52         private const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
53     }
54 
onCreatenull55     override fun onCreate(userHandle: UserHandle, operationType: Int) {
56         super.onCreate()
57         // The map in mapOf is guaranteed to be order preserving
58         val controlsMap = mapOf(CONTROLS to getPPControlsFile(this))
59         NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
60             addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it)
61         }
62 
63         // Conversations widgets backup only works for system user, because widgets' information is
64         // stored in system user's SharedPreferences files and we can't open those from other users.
65         if (!userHandle.isSystem) {
66             return
67         }
68 
69         val keys = PeopleBackupHelper.getFilesToBackup()
70         addHelper(PEOPLE_TILES_BACKUP_KEY, PeopleBackupHelper(
71                 this, userHandle, keys.toTypedArray()))
72     }
73 
onRestoreFinishednull74     override fun onRestoreFinished() {
75         super.onRestoreFinished()
76         val intent = Intent(ACTION_RESTORE_FINISHED).apply {
77             `package` = packageName
78             putExtra(Intent.EXTRA_USER_ID, userId)
79             flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
80         }
81         sendBroadcastAsUser(intent, UserHandle.SYSTEM, PERMISSION_SELF)
82     }
83 
84     /**
85      * Helper class for restoring files ONLY if they are not present.
86      *
87      * A [Map] between filenames and actions (functions) is passed to indicate post processing
88      * actions to be taken after each file is restored.
89      *
90      * @property lock a lock to hold while backing up and restoring the files.
91      * @property context the context of the [BackupAgent]
92      * @property fileNamesAndPostProcess a map from the filenames to back up and the post processing
93      *                                   actions to take
94      */
95     private class NoOverwriteFileBackupHelper(
96         val lock: Any,
97         val context: Context,
98         val fileNamesAndPostProcess: Map<String, () -> Unit>
99     ) : FileBackupHelper(context, *fileNamesAndPostProcess.keys.toTypedArray()) {
100 
restoreEntitynull101         override fun restoreEntity(data: BackupDataInputStream) {
102             val file = Environment.buildPath(context.filesDir, data.key)
103             if (file.exists()) {
104                 Log.w(TAG, "File " + data.key + " already exists. Skipping restore.")
105                 return
106             }
107             synchronized(lock) {
108                 super.restoreEntity(data)
109                 fileNamesAndPostProcess.get(data.key)?.invoke()
110             }
111         }
112 
performBackupnull113         override fun performBackup(
114             oldState: ParcelFileDescriptor?,
115             data: BackupDataOutput?,
116             newState: ParcelFileDescriptor?
117         ) {
118             synchronized(lock) {
119                 super.performBackup(oldState, data, newState)
120             }
121         }
122     }
123 }
getPPControlsFilenull124 private fun getPPControlsFile(context: Context): () -> Unit {
125     return {
126         val filesDir = context.filesDir
127         val file = Environment.buildPath(filesDir, BackupHelper.CONTROLS)
128         if (file.exists()) {
129             val dest = Environment.buildPath(filesDir,
130                 AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
131             file.copyTo(dest)
132             val jobScheduler = context.getSystemService(JobScheduler::class.java)
133             jobScheduler?.schedule(
134                 AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context))
135         }
136     }
137 }