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() {
|
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)
|
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_ARRAY, initialFilters.toTypedArray())
|
||||||
.putExtra(MainActivity.EXTRA_KEY_FILTERS_STRING, initialFilters.joinToString(MainActivity.EXTRA_STRING_ARRAY_SEPARATOR))
|
.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>()
|
private val LOG_TAG = LogUtils.createTag<SafePngMetadataReader>()
|
||||||
|
|
||||||
// arbitrary size to detect chunks that may yield an OOM
|
// 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 latin1Encoding = Charsets.ISO_8859_1
|
||||||
private val desiredChunkTypes: Set<PngChunkType> = hashSetOf(
|
private val desiredChunkTypes: Set<PngChunkType> = hashSetOf(
|
||||||
|
|
|
@ -48,7 +48,7 @@ class SafeXmpReader : XmpReader() {
|
||||||
|
|
||||||
extendedXMPBuffer?.let { xmpBytes ->
|
extendedXMPBuffer?.let { xmpBytes ->
|
||||||
val totalSize = xmpBytes.size
|
val totalSize = xmpBytes.size
|
||||||
if (totalSize > segmentTypeSizeDangerThreshold) {
|
if (totalSize > SEGMENT_TYPE_SIZE_DANGER_THRESHOLD) {
|
||||||
logError(metadata, totalSize)
|
logError(metadata, totalSize)
|
||||||
} else {
|
} else {
|
||||||
extract(xmpBytes, metadata)
|
extract(xmpBytes, metadata)
|
||||||
|
@ -111,7 +111,7 @@ class SafeXmpReader : XmpReader() {
|
||||||
val chunkOffset = reader.uInt32.toInt()
|
val chunkOffset = reader.uInt32.toInt()
|
||||||
if (extendedXMPBuffer == null) {
|
if (extendedXMPBuffer == null) {
|
||||||
// TLAD insert start
|
// TLAD insert start
|
||||||
if (fullLength > segmentTypeSizeDangerThreshold) {
|
if (fullLength > SEGMENT_TYPE_SIZE_DANGER_THRESHOLD) {
|
||||||
logError(metadata, fullLength)
|
logError(metadata, fullLength)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ class SafeXmpReader : XmpReader() {
|
||||||
private val LOG_TAG = LogUtils.createTag<SafeXmpReader>()
|
private val LOG_TAG = LogUtils.createTag<SafeXmpReader>()
|
||||||
|
|
||||||
// arbitrary size to detect extended XMP that may yield an OOM
|
// 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
|
// tighter node limits for faster loading
|
||||||
val PARSE_OPTIONS: ParseOptions = ParseOptions().setXMPNodesToLimit(
|
val PARSE_OPTIONS: ParseOptions = ParseOptions().setXMPNodesToLimit(
|
||||||
|
|
|
@ -14,6 +14,9 @@ object BitmapUtils {
|
||||||
private val LOG_TAG = LogUtils.createTag<BitmapUtils>()
|
private val LOG_TAG = LogUtils.createTag<BitmapUtils>()
|
||||||
private const val INITIAL_BUFFER_SIZE = 2 shl 17 // 256kB
|
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 freeBaos = ArrayList<ByteArrayOutputStream>()
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
|
||||||
|
@ -39,6 +42,15 @@ object BitmapUtils {
|
||||||
this.compress(Bitmap.CompressFormat.JPEG, quality, stream)
|
this.compress(Bitmap.CompressFormat.JPEG, quality, stream)
|
||||||
}
|
}
|
||||||
if (recycle) this.recycle()
|
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()
|
val byteArray = stream.toByteArray()
|
||||||
stream.reset()
|
stream.reset()
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
|
@ -59,7 +71,7 @@ object BitmapUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun centerSquareCrop(context: Context, bitmap: Bitmap?, size: Int): Bitmap? {
|
fun centerSquareCrop(context: Context, bitmap: Bitmap?, size: Int): Bitmap? {
|
||||||
bitmap ?: return bitmap
|
bitmap ?: return null
|
||||||
return TransformationUtils.centerCrop(getBitmapPool(context), bitmap, size, size)
|
return TransformationUtils.centerCrop(getBitmapPool(context), bitmap, size, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ object ContextUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.isMyServiceRunning(serviceClass: Class<out Service>): Boolean {
|
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
|
am ?: return false
|
||||||
@Suppress("deprecation")
|
@Suppress("deprecation")
|
||||||
return am.getRunningServices(Integer.MAX_VALUE).any { it.service.className == serviceClass.name }
|
return am.getRunningServices(Integer.MAX_VALUE).any { it.service.className == serviceClass.name }
|
||||||
|
|
Loading…
Reference in a new issue