Pull out code in ApkBackup and ApkRestore into own methods

This commit is contained in:
Torsten Grote 2020-10-05 15:51:30 -03:00 committed by Chirayu Desai
parent 741e5ef1a0
commit 46e8a46c63
2 changed files with 48 additions and 39 deletions

View file

@ -14,13 +14,16 @@ import com.stevesoltys.seedvault.metadata.PackageMetadata
import com.stevesoltys.seedvault.metadata.PackageState import com.stevesoltys.seedvault.metadata.PackageState
import com.stevesoltys.seedvault.settings.SettingsManager import com.stevesoltys.seedvault.settings.SettingsManager
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.IOException import java.io.IOException
import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.security.MessageDigest import java.security.MessageDigest
private val TAG = ApkBackup::class.java.simpleName private val TAG = ApkBackup::class.java.simpleName
@Suppress("BlockingMethodInNonBlockingContext")
class ApkBackup( class ApkBackup(
private val pm: PackageManager, private val pm: PackageManager,
private val settingsManager: SettingsManager, private val settingsManager: SettingsManager,
@ -87,31 +90,10 @@ class ApkBackup(
} }
// get an InputStream for the APK // get an InputStream for the APK
val apk = File(packageInfo.applicationInfo.sourceDir) val inputStream = getApkInputStream(packageInfo.applicationInfo.sourceDir)
val inputStream = try {
apk.inputStream()
} catch (e: FileNotFoundException) {
Log.e(TAG, "Error opening ${apk.absolutePath} for backup.", e)
throw IOException(e)
} catch (e: SecurityException) {
Log.e(TAG, "Error opening ${apk.absolutePath} for backup.", e)
throw IOException(e)
}
// copy the APK to the storage's output and calculate SHA-256 hash while at it // copy the APK to the storage's output and calculate SHA-256 hash while at it
val messageDigest = MessageDigest.getInstance("SHA-256") val sha256 = copyStreamsAndGetHash(inputStream, streamGetter())
streamGetter().use { outputStream ->
inputStream.use { inputStream ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = inputStream.read(buffer)
while (bytes >= 0) {
outputStream.write(buffer, 0, bytes)
messageDigest.update(buffer, 0, bytes)
bytes = inputStream.read(buffer)
}
}
}
val sha256 = messageDigest.digest().encodeBase64()
Log.d(TAG, "Backed up new APK of $packageName with version $version.") Log.d(TAG, "Backed up new APK of $packageName with version $version.")
// return updated metadata // return updated metadata
@ -134,6 +116,45 @@ class ApkBackup(
return packageMetadata.signatures.intersect(signatures).isEmpty() return packageMetadata.signatures.intersect(signatures).isEmpty()
} }
@Throws(IOException::class)
private fun getApkInputStream(apkPath: String): FileInputStream {
val apk = File(apkPath)
return try {
apk.inputStream()
} catch (e: FileNotFoundException) {
Log.e(TAG, "Error opening ${apk.absolutePath} for backup.", e)
throw IOException(e)
} catch (e: SecurityException) {
Log.e(TAG, "Error opening ${apk.absolutePath} for backup.", e)
throw IOException(e)
}
}
}
/**
* Copy the APK from the given [InputStream] to the given [OutputStream]
* and calculate the SHA-256 hash while at it.
*
* Both streams will be closed when the method returns.
*
* @return the APK's SHA-256 hash in Base64 format.
*/
@Throws(IOException::class)
fun copyStreamsAndGetHash(inputStream: InputStream, outputStream: OutputStream): String {
val messageDigest = MessageDigest.getInstance("SHA-256")
outputStream.use { oStream ->
inputStream.use { inputStream ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = inputStream.read(buffer)
while (bytes >= 0) {
oStream.write(buffer, 0, bytes)
messageDigest.update(buffer, 0, bytes)
bytes = inputStream.read(buffer)
}
}
}
return messageDigest.digest().encodeBase64()
} }
/** /**

View file

@ -6,9 +6,9 @@ import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
import android.content.pm.PackageManager.NameNotFoundException import android.content.pm.PackageManager.NameNotFoundException
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.Log import android.util.Log
import com.stevesoltys.seedvault.encodeBase64
import com.stevesoltys.seedvault.metadata.PackageMetadata import com.stevesoltys.seedvault.metadata.PackageMetadata
import com.stevesoltys.seedvault.metadata.PackageMetadataMap import com.stevesoltys.seedvault.metadata.PackageMetadataMap
import com.stevesoltys.seedvault.transport.backup.copyStreamsAndGetHash
import com.stevesoltys.seedvault.transport.backup.getSignatures import com.stevesoltys.seedvault.transport.backup.getSignatures
import com.stevesoltys.seedvault.transport.backup.isSystemApp import com.stevesoltys.seedvault.transport.backup.isSystemApp
import com.stevesoltys.seedvault.transport.restore.ApkRestoreStatus.FAILED import com.stevesoltys.seedvault.transport.restore.ApkRestoreStatus.FAILED
@ -20,7 +20,6 @@ import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.security.MessageDigest
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
private val TAG = ApkRestore::class.java.simpleName private val TAG = ApkRestore::class.java.simpleName
@ -80,21 +79,10 @@ internal class ApkRestore(
// create a cache file to write the APK into // create a cache file to write the APK into
val cachedApk = File.createTempFile(packageName, ".apk", context.cacheDir) val cachedApk = File.createTempFile(packageName, ".apk", context.cacheDir)
// copy APK to cache file and calculate SHA-256 hash while we are at it // copy APK to cache file and calculate SHA-256 hash while we are at it
val messageDigest = MessageDigest.getInstance("SHA-256") val inputStream = restorePlugin.getApkInputStream(token, packageName)
restorePlugin.getApkInputStream(token, packageName).use { inputStream -> val sha256 = copyStreamsAndGetHash(inputStream, cachedApk.outputStream())
cachedApk.outputStream().use { outputStream ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = inputStream.read(buffer)
while (bytes >= 0) {
outputStream.write(buffer, 0, bytes)
messageDigest.update(buffer, 0, bytes)
bytes = inputStream.read(buffer)
}
}
}
// check APK's SHA-256 hash // check APK's SHA-256 hash
val sha256 = messageDigest.digest().encodeBase64()
if (metadata.sha256 != sha256) throw SecurityException( if (metadata.sha256 != sha256) throw SecurityException(
"Package $packageName has sha256 '$sha256', but '${metadata.sha256}' expected." "Package $packageName has sha256 '$sha256', but '${metadata.sha256}' expected."
) )