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.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper 33 import com.android.systemui.people.widget.PeopleBackupHelper 34 import com.android.systemui.settings.UserFileManagerImpl 35 36 /** 37 * Helper for backing up elements in SystemUI 38 * 39 * This helper is invoked by BackupManager whenever a backup or restore is required in SystemUI. The 40 * helper can be used to back up any element that is stored in [Context.getFilesDir] or 41 * [Context.getSharedPreferences]. 42 * 43 * After restoring is done, a [ACTION_RESTORE_FINISHED] intent will be send to SystemUI user 0, 44 * indicating that restoring is finished for a given user. 45 */ 46 open class BackupHelper : BackupAgentHelper() { 47 48 companion object { 49 const val TAG = "BackupHelper" 50 internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME 51 private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite" 52 private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences" 53 private const val KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY = 54 "systemui.keyguard.quickaffordance.shared_preferences" 55 val controlsDataLock = Any() 56 const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED" 57 const val PERMISSION_SELF = "com.android.systemui.permission.SELF" 58 } 59 60 override fun onCreate(userHandle: UserHandle, operationType: Int) { 61 super.onCreate() 62 63 addControlsHelper(userHandle.identifier) 64 65 val keys = PeopleBackupHelper.getFilesToBackup() 66 addHelper( 67 PEOPLE_TILES_BACKUP_KEY, 68 PeopleBackupHelper(this, userHandle, keys.toTypedArray()) 69 ) 70 addHelper( 71 KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY, 72 KeyguardQuickAffordanceBackupHelper( 73 context = this, 74 userId = userHandle.identifier, 75 ), 76 ) 77 } 78 79 override fun onRestoreFinished() { 80 super.onRestoreFinished() 81 val intent = 82 Intent(ACTION_RESTORE_FINISHED).apply { 83 `package` = packageName 84 putExtra(Intent.EXTRA_USER_ID, userId) 85 flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY 86 } 87 sendBroadcastAsUser(intent, UserHandle.SYSTEM, PERMISSION_SELF) 88 } 89 90 private fun addControlsHelper(userId: Int) { 91 val file = UserFileManagerImpl.createFile( 92 userId = userId, 93 fileName = CONTROLS, 94 ) 95 // The map in mapOf is guaranteed to be order preserving 96 val controlsMap = mapOf(file.getPath() to getPPControlsFile(this, userId)) 97 NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also { 98 addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it) 99 } 100 } 101 102 /** 103 * Helper class for restoring files ONLY if they are not present. 104 * 105 * A [Map] between filenames and actions (functions) is passed to indicate post processing 106 * actions to be taken after each file is restored. 107 * 108 * @property lock a lock to hold while backing up and restoring the files. 109 * @property context the context of the [BackupAgent] 110 * @property fileNamesAndPostProcess a map from the filenames to back up and the post processing 111 * ``` 112 * actions to take 113 * ``` 114 */ 115 private class NoOverwriteFileBackupHelper( 116 val lock: Any, 117 val context: Context, 118 val fileNamesAndPostProcess: Map<String, () -> Unit> 119 ) : FileBackupHelper(context, *fileNamesAndPostProcess.keys.toTypedArray()) { 120 121 override fun restoreEntity(data: BackupDataInputStream) { 122 val file = Environment.buildPath(context.filesDir, data.key) 123 if (file.exists()) { 124 Log.w(TAG, "File " + data.key + " already exists. Skipping restore.") 125 return 126 } 127 synchronized(lock) { 128 super.restoreEntity(data) 129 fileNamesAndPostProcess.get(data.key)?.invoke() 130 } 131 } 132 133 override fun performBackup( 134 oldState: ParcelFileDescriptor?, 135 data: BackupDataOutput?, 136 newState: ParcelFileDescriptor? 137 ) { 138 synchronized(lock) { super.performBackup(oldState, data, newState) } 139 } 140 } 141 } 142 143 private fun getPPControlsFile(context: Context, userId: Int): () -> Unit { 144 return { 145 val file = UserFileManagerImpl.createFile( 146 userId = userId, 147 fileName = BackupHelper.CONTROLS, 148 ) 149 if (file.exists()) { 150 val dest = UserFileManagerImpl.createFile( 151 userId = userId, 152 fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME, 153 ) 154 file.copyTo(dest) 155 val jobScheduler = context.getSystemService(JobScheduler::class.java) 156 jobScheduler?.schedule( 157 AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context, userId) 158 ) 159 } 160 } 161 } 162