1 /* <lambda>null2 * Copyright (C) 2024 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.communal.data.backup 18 19 import android.content.Context 20 import androidx.annotation.WorkerThread 21 import com.android.systemui.communal.data.db.CommunalDatabase 22 import com.android.systemui.communal.nano.CommunalHubState 23 import java.io.File 24 import java.io.FileInputStream 25 import java.io.FileNotFoundException 26 import java.io.FileOutputStream 27 import java.io.IOException 28 import kotlinx.coroutines.flow.first 29 import kotlinx.coroutines.runBlocking 30 31 /** Utilities for communal backup and restore. */ 32 class CommunalBackupUtils(private val context: Context) { 33 34 /** 35 * Retrieves a communal hub state protobuf that represents the current state of the communal 36 * database. 37 */ 38 @WorkerThread 39 fun getCommunalHubState(): CommunalHubState { 40 val database = CommunalDatabase.getInstance(context) 41 val widgetsFromDb = runBlocking { database.communalWidgetDao().getWidgets().first() } 42 val widgetsState = mutableListOf<CommunalHubState.CommunalWidgetItem>() 43 widgetsFromDb.keys.forEach { rankItem -> 44 val widget = widgetsFromDb[rankItem]!! 45 widgetsState.add( 46 CommunalHubState.CommunalWidgetItem().apply { 47 rank = rankItem.rank 48 widgetId = widget.widgetId 49 componentName = widget.componentName 50 userSerialNumber = widget.userSerialNumber 51 spanY = widget.spanY 52 spanYNew = widget.spanYNew 53 } 54 ) 55 } 56 return CommunalHubState().apply { widgets = widgetsState.toTypedArray() } 57 } 58 59 /** 60 * Writes [data] to disk as a file as [FILE_NAME], overwriting existing content if any. 61 * 62 * @throws FileNotFoundException if the file exists but is a directory rather than a regular 63 * file, does not exist but cannot be created, or cannot be opened for any other reason. 64 * @throws SecurityException if write access is denied. 65 * @throws IOException if writing fails. 66 */ 67 @WorkerThread 68 fun writeBytesToDisk(data: ByteArray) { 69 val output = FileOutputStream(getFile()) 70 output.write(data) 71 output.close() 72 } 73 74 /** 75 * Reads bytes from [FILE_NAME], and throws if file does not exist. 76 * 77 * @throws FileNotFoundException if file does not exist. 78 * @throws SecurityException if read access is denied. 79 * @throws IOException if reading fails. 80 */ 81 @WorkerThread 82 fun readBytesFromDisk(): ByteArray { 83 val input = FileInputStream(getFile()) 84 val bytes = input.readAllBytes() 85 input.close() 86 87 return bytes 88 } 89 90 /** 91 * Removes the bytes written to disk at [FILE_NAME]. 92 * 93 * @return True if and only if the file is successfully deleted 94 * @throws SecurityException if permission is denied 95 */ 96 @WorkerThread 97 fun clear(): Boolean { 98 return getFile().delete() 99 } 100 101 /** Whether [FILE_NAME] exists. */ 102 @WorkerThread 103 fun fileExists(): Boolean { 104 return getFile().exists() 105 } 106 107 @WorkerThread 108 private fun getFile(): File { 109 return File(context.filesDir, FILE_NAME) 110 } 111 112 companion object { 113 private const val FILE_NAME = "communal_restore" 114 } 115 } 116