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