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.settings.SettingsManager
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.security.MessageDigest
private val TAG = ApkBackup::class.java.simpleName
@Suppress("BlockingMethodInNonBlockingContext")
class ApkBackup(
private val pm: PackageManager,
private val settingsManager: SettingsManager,
@ -87,31 +90,10 @@ class ApkBackup(
}
// get an InputStream for the APK
val apk = File(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)
}
val inputStream = getApkInputStream(packageInfo.applicationInfo.sourceDir)
// copy the APK to the storage's output and calculate SHA-256 hash while at it
val messageDigest = MessageDigest.getInstance("SHA-256")
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()
val sha256 = copyStreamsAndGetHash(inputStream, streamGetter())
Log.d(TAG, "Backed up new APK of $packageName with version $version.")
// return updated metadata
@ -134,6 +116,45 @@ class ApkBackup(
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.graphics.drawable.Drawable
import android.util.Log
import com.stevesoltys.seedvault.encodeBase64
import com.stevesoltys.seedvault.metadata.PackageMetadata
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.isSystemApp
import com.stevesoltys.seedvault.transport.restore.ApkRestoreStatus.FAILED
@ -20,7 +20,6 @@ import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import java.io.File
import java.io.IOException
import java.security.MessageDigest
import java.util.concurrent.ConcurrentHashMap
private val TAG = ApkRestore::class.java.simpleName
@ -80,21 +79,10 @@ internal class ApkRestore(
// create a cache file to write the APK into
val cachedApk = File.createTempFile(packageName, ".apk", context.cacheDir)
// copy APK to cache file and calculate SHA-256 hash while we are at it
val messageDigest = MessageDigest.getInstance("SHA-256")
restorePlugin.getApkInputStream(token, packageName).use { inputStream ->
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)
}
}
}
val inputStream = restorePlugin.getApkInputStream(token, packageName)
val sha256 = copyStreamsAndGetHash(inputStream, cachedApk.outputStream())
// check APK's SHA-256 hash
val sha256 = messageDigest.digest().encodeBase64()
if (metadata.sha256 != sha256) throw SecurityException(
"Package $packageName has sha256 '$sha256', but '${metadata.sha256}' expected."
)