Pull out code in ApkBackup and ApkRestore into own methods
This commit is contained in:
parent
741e5ef1a0
commit
46e8a46c63
2 changed files with 48 additions and 39 deletions
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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."
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue