safer compressed bitmap allocation
This commit is contained in:
parent
e8b46b02d8
commit
eb3acaa307
5 changed files with 19 additions and 7 deletions
|
@ -188,7 +188,7 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any
|
|||
}
|
||||
|
||||
private fun pickCollectionFilters() {
|
||||
val initialFilters = (args["initialFilters"] as List<*>?)?.mapNotNull { if (it is String) it else null } ?: listOf()
|
||||
val initialFilters = (args["initialFilters"] as? List<*>)?.mapNotNull { if (it is String) it else null } ?: listOf()
|
||||
val intent = Intent(MainActivity.INTENT_ACTION_PICK_COLLECTION_FILTERS, null, activity, MainActivity::class.java)
|
||||
.putExtra(MainActivity.EXTRA_KEY_FILTERS_ARRAY, initialFilters.toTypedArray())
|
||||
.putExtra(MainActivity.EXTRA_KEY_FILTERS_STRING, initialFilters.joinToString(MainActivity.EXTRA_STRING_ARRAY_SEPARATOR))
|
||||
|
|
|
@ -28,7 +28,7 @@ object SafePngMetadataReader {
|
|||
private val LOG_TAG = LogUtils.createTag<SafePngMetadataReader>()
|
||||
|
||||
// arbitrary size to detect chunks that may yield an OOM
|
||||
private const val chunkSizeDangerThreshold = SafeXmpReader.segmentTypeSizeDangerThreshold
|
||||
private const val chunkSizeDangerThreshold = SafeXmpReader.SEGMENT_TYPE_SIZE_DANGER_THRESHOLD
|
||||
|
||||
private val latin1Encoding = Charsets.ISO_8859_1
|
||||
private val desiredChunkTypes: Set<PngChunkType> = hashSetOf(
|
||||
|
|
|
@ -48,7 +48,7 @@ class SafeXmpReader : XmpReader() {
|
|||
|
||||
extendedXMPBuffer?.let { xmpBytes ->
|
||||
val totalSize = xmpBytes.size
|
||||
if (totalSize > segmentTypeSizeDangerThreshold) {
|
||||
if (totalSize > SEGMENT_TYPE_SIZE_DANGER_THRESHOLD) {
|
||||
logError(metadata, totalSize)
|
||||
} else {
|
||||
extract(xmpBytes, metadata)
|
||||
|
@ -111,7 +111,7 @@ class SafeXmpReader : XmpReader() {
|
|||
val chunkOffset = reader.uInt32.toInt()
|
||||
if (extendedXMPBuffer == null) {
|
||||
// TLAD insert start
|
||||
if (fullLength > segmentTypeSizeDangerThreshold) {
|
||||
if (fullLength > SEGMENT_TYPE_SIZE_DANGER_THRESHOLD) {
|
||||
logError(metadata, fullLength)
|
||||
return null
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ class SafeXmpReader : XmpReader() {
|
|||
private val LOG_TAG = LogUtils.createTag<SafeXmpReader>()
|
||||
|
||||
// arbitrary size to detect extended XMP that may yield an OOM
|
||||
const val segmentTypeSizeDangerThreshold = 3 * (1 shl 20) // MB
|
||||
const val SEGMENT_TYPE_SIZE_DANGER_THRESHOLD = 3 * (1 shl 20) // MB
|
||||
|
||||
// tighter node limits for faster loading
|
||||
val PARSE_OPTIONS: ParseOptions = ParseOptions().setXMPNodesToLimit(
|
||||
|
|
|
@ -14,6 +14,9 @@ object BitmapUtils {
|
|||
private val LOG_TAG = LogUtils.createTag<BitmapUtils>()
|
||||
private const val INITIAL_BUFFER_SIZE = 2 shl 17 // 256kB
|
||||
|
||||
// arbitrary size to detect buffer that may yield an OOM
|
||||
private const val BUFFER_SIZE_DANGER_THRESHOLD = 10 * (1 shl 20) // MB
|
||||
|
||||
private val freeBaos = ArrayList<ByteArrayOutputStream>()
|
||||
private val mutex = Mutex()
|
||||
|
||||
|
@ -39,6 +42,15 @@ object BitmapUtils {
|
|||
this.compress(Bitmap.CompressFormat.JPEG, quality, stream)
|
||||
}
|
||||
if (recycle) this.recycle()
|
||||
|
||||
val bufferSize = stream.size()
|
||||
if (bufferSize > BUFFER_SIZE_DANGER_THRESHOLD) {
|
||||
val availHeapSize = Runtime.getRuntime().let { it.maxMemory() - (it.totalMemory() - it.freeMemory()) }
|
||||
if (bufferSize > availHeapSize) {
|
||||
throw Exception("compressed bitmap to $bufferSize bytes, which cannot be allocated to a new byte array, with only $availHeapSize free bytes")
|
||||
}
|
||||
}
|
||||
|
||||
val byteArray = stream.toByteArray()
|
||||
stream.reset()
|
||||
mutex.withLock {
|
||||
|
@ -59,7 +71,7 @@ object BitmapUtils {
|
|||
}
|
||||
|
||||
fun centerSquareCrop(context: Context, bitmap: Bitmap?, size: Int): Bitmap? {
|
||||
bitmap ?: return bitmap
|
||||
bitmap ?: return null
|
||||
return TransformationUtils.centerCrop(getBitmapPool(context), bitmap, size, size)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ object ContextUtils {
|
|||
}
|
||||
|
||||
fun Context.isMyServiceRunning(serviceClass: Class<out Service>): Boolean {
|
||||
val am = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager?
|
||||
val am = this.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
|
||||
am ?: return false
|
||||
@Suppress("deprecation")
|
||||
return am.getRunningServices(Integer.MAX_VALUE).any { it.service.className == serviceClass.name }
|
||||
|
|
Loading…
Reference in a new issue