Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0630fa93 authored by Jacky Wang's avatar Jacky Wang Committed by Android (Google) Code Review
Browse files

Merge "[DataStore] Reduce memory footprint for BackupRestoreStorage" into main

parents 96fd8a65 14b87a51
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -62,7 +62,7 @@ internal class BackupRestoreFileArchiver(
            }
            }
        Log.i(LOG_TAG, "[$name] Restore ${data.size()} bytes for $key to $file")
        Log.i(LOG_TAG, "[$name] Restore ${data.size()} bytes for $key to $file")
        val inputStream = LimitedNoCloseInputStream(data)
        val inputStream = LimitedNoCloseInputStream(data)
        checksum.reset()
        val checksum = createChecksum()
        val checkedInputStream = CheckedInputStream(inputStream, checksum)
        val checkedInputStream = CheckedInputStream(inputStream, checksum)
        try {
        try {
            val codec = BackupCodec.fromId(checkedInputStream.read().toByte())
            val codec = BackupCodec.fromId(checkedInputStream.read().toByte())
+27 −23
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@ import java.io.OutputStream
import java.util.zip.CRC32
import java.util.zip.CRC32
import java.util.zip.CheckedInputStream
import java.util.zip.CheckedInputStream
import java.util.zip.CheckedOutputStream
import java.util.zip.CheckedOutputStream
import java.util.zip.Checksum


internal const val LOG_TAG = "BackupRestoreStorage"
internal const val LOG_TAG = "BackupRestoreStorage"


@@ -54,15 +55,6 @@ abstract class BackupRestoreStorage : BackupHelper {
     */
     */
    abstract val name: String
    abstract val name: String


    private val entities: List<BackupRestoreEntity> by lazy { createBackupRestoreEntities() }

    /**
     * Checksum of the data.
     *
     * Always call [java.util.zip.Checksum.reset] before using it.
     */
    protected val checksum = CRC32()

    /**
    /**
     * Entity states represented by checksum.
     * Entity states represented by checksum.
     *
     *
@@ -70,13 +62,16 @@ abstract class BackupRestoreStorage : BackupHelper {
     */
     */
    protected val entityStates = MutableScatterMap<String, Long>()
    protected val entityStates = MutableScatterMap<String, Long>()


    /** Entities created by [createBackupRestoreEntities]. This field is for restore only. */
    private var entities: List<BackupRestoreEntity>? = null

    /** Entities to back up and restore. */
    /** Entities to back up and restore. */
    abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>
    abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>


    /** Default codec used to encode/decode the entity data. */
    /** Default codec used to encode/decode the entity data. */
    open fun defaultCodec(): BackupCodec = BackupZipCodec.BEST_COMPRESSION
    open fun defaultCodec(): BackupCodec = BackupZipCodec.BEST_COMPRESSION


    override fun performBackup(
    final override fun performBackup(
        oldState: ParcelFileDescriptor?,
        oldState: ParcelFileDescriptor?,
        data: BackupDataOutput,
        data: BackupDataOutput,
        newState: ParcelFileDescriptor,
        newState: ParcelFileDescriptor,
@@ -88,6 +83,9 @@ abstract class BackupRestoreStorage : BackupHelper {
            return
            return
        }
        }
        Log.i(LOG_TAG, "[$name] Backup start")
        Log.i(LOG_TAG, "[$name] Backup start")
        val checksum = createChecksum()
        // recreate entities for backup to avoid stale states
        val entities = createBackupRestoreEntities()
        for (entity in entities) {
        for (entity in entities) {
            val key = entity.key
            val key = entity.key
            val outputStream = ByteArrayOutputStream()
            val outputStream = ByteArrayOutputStream()
@@ -103,7 +101,8 @@ abstract class BackupRestoreStorage : BackupHelper {
                }
                }
            when (result) {
            when (result) {
                EntityBackupResult.UPDATE -> {
                EntityBackupResult.UPDATE -> {
                    if (updateEntityState(key)) {
                    val value = checksum.value
                    if (entityStates.put(key, value) != value) {
                        val payload = outputStream.toByteArray()
                        val payload = outputStream.toByteArray()
                        val size = payload.size
                        val size = payload.size
                        data.writeEntityHeader(key, size)
                        data.writeEntityHeader(key, size)
@@ -126,15 +125,10 @@ abstract class BackupRestoreStorage : BackupHelper {
                }
                }
            }
            }
        }
        }
        newState.writeEntityStates(entityStates)
        newState.writeAndClearEntityStates()
        Log.i(LOG_TAG, "[$name] Backup end")
        Log.i(LOG_TAG, "[$name] Backup end")
    }
    }


    private fun updateEntityState(key: String): Boolean {
        val value = checksum.value
        return entityStates.put(key, value) != value
    }

    /** Returns if backup is enabled. */
    /** Returns if backup is enabled. */
    open fun enableBackup(backupContext: BackupContext): Boolean = true
    open fun enableBackup(backupContext: BackupContext): Boolean = true


@@ -144,13 +138,14 @@ abstract class BackupRestoreStorage : BackupHelper {
        return codec.encode(outputStream)
        return codec.encode(outputStream)
    }
    }


    /** This callback is invoked for every backed up entity. */
    override fun restoreEntity(data: BackupDataInputStream) {
    override fun restoreEntity(data: BackupDataInputStream) {
        val key = data.key
        val key = data.key
        if (!enableRestore()) {
        if (!enableRestore()) {
            Log.i(LOG_TAG, "[$name] Restore disabled, ignore entity $key")
            Log.i(LOG_TAG, "[$name] Restore disabled, ignore entity $key")
            return
            return
        }
        }
        val entity = entities.firstOrNull { it.key == key }
        val entity = ensureEntities().firstOrNull { it.key == key }
        if (entity == null) {
        if (entity == null) {
            Log.w(LOG_TAG, "[$name] Cannot find handler for entity $key")
            Log.w(LOG_TAG, "[$name] Cannot find handler for entity $key")
            return
            return
@@ -159,7 +154,7 @@ abstract class BackupRestoreStorage : BackupHelper {
        val restoreContext = RestoreContext(key)
        val restoreContext = RestoreContext(key)
        val codec = entity.codec() ?: defaultCodec()
        val codec = entity.codec() ?: defaultCodec()
        val inputStream = LimitedNoCloseInputStream(data)
        val inputStream = LimitedNoCloseInputStream(data)
        checksum.reset()
        val checksum = createChecksum()
        val checkedInputStream = CheckedInputStream(inputStream, checksum)
        val checkedInputStream = CheckedInputStream(inputStream, checksum)
        try {
        try {
            entity.restore(restoreContext, wrapRestoreInputStream(codec, checkedInputStream))
            entity.restore(restoreContext, wrapRestoreInputStream(codec, checkedInputStream))
@@ -169,6 +164,9 @@ abstract class BackupRestoreStorage : BackupHelper {
        }
        }
    }
    }


    private fun ensureEntities(): List<BackupRestoreEntity> =
        entities ?: createBackupRestoreEntities().also { entities = it }

    /** Returns if restore is enabled. */
    /** Returns if restore is enabled. */
    open fun enableRestore(): Boolean = true
    open fun enableRestore(): Boolean = true


@@ -185,7 +183,8 @@ abstract class BackupRestoreStorage : BackupHelper {
    }
    }


    final override fun writeNewStateDescription(newState: ParcelFileDescriptor) {
    final override fun writeNewStateDescription(newState: ParcelFileDescriptor) {
        newState.writeEntityStates(entityStates)
        entities = null // clear to reduce memory footprint
        newState.writeAndClearEntityStates()
        onRestoreFinished()
        onRestoreFinished()
    }
    }


@@ -223,24 +222,29 @@ abstract class BackupRestoreStorage : BackupHelper {
        }
        }
    }
    }


    private fun ParcelFileDescriptor.writeEntityStates(state: MutableScatterMap<String, Long>) {
    private fun ParcelFileDescriptor.writeAndClearEntityStates() {
        // do not close the streams
        // do not close the streams
        val fileOutputStream = FileOutputStream(fileDescriptor)
        val fileOutputStream = FileOutputStream(fileDescriptor)
        val dataOutputStream = DataOutputStream(fileOutputStream)
        val dataOutputStream = DataOutputStream(fileOutputStream)
        try {
        try {
            dataOutputStream.writeByte(STATE_VERSION.toInt())
            dataOutputStream.writeByte(STATE_VERSION.toInt())
            dataOutputStream.writeInt(state.size)
            dataOutputStream.writeInt(entityStates.size)
            state.forEach { key, value ->
            entityStates.forEach { key, value ->
                dataOutputStream.writeUTF(key)
                dataOutputStream.writeUTF(key)
                dataOutputStream.writeLong(value)
                dataOutputStream.writeLong(value)
            }
            }
        } catch (exception: Exception) {
        } catch (exception: Exception) {
            Log.e(LOG_TAG, "[$name] Fail to write state file", exception)
            Log.e(LOG_TAG, "[$name] Fail to write state file", exception)
        }
        }
        entityStates.clear()
        entityStates.trim() // trim to reduce memory footprint
    }
    }


    companion object {
    companion object {
        private const val STATE_VERSION: Byte = 0
        private const val STATE_VERSION: Byte = 0

        /** Checksum for entity backup data. */
        fun createChecksum(): Checksum = CRC32()
    }
    }
}
}