From 2d893d441572d59e97512678bc415c05b9ab83fa Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Thu, 28 Jan 2021 16:31:37 +0900 Subject: [PATCH] safer platform calls --- .../aves/channel/calls/AppAdapterHandler.kt | 7 ++-- .../thibault/aves/channel/calls/Coresult.kt | 22 +++++++++++ .../aves/channel/calls/DebugHandler.kt | 13 ++++--- .../aves/channel/calls/ImageFileHandler.kt | 26 +++++++------ .../aves/channel/calls/MetadataHandler.kt | 17 +++++---- .../aves/channel/calls/StorageHandler.kt | 38 +++++++++---------- 6 files changed, 76 insertions(+), 47 deletions(-) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt index 6930273bc..3e2ebda05 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt @@ -12,6 +12,7 @@ import androidx.core.content.FileProvider import com.bumptech.glide.Glide import com.bumptech.glide.load.DecodeFormat import com.bumptech.glide.request.RequestOptions +import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import deckers.thibault.aves.utils.BitmapUtils.getBytes import deckers.thibault.aves.utils.LogUtils import io.flutter.plugin.common.MethodCall @@ -28,8 +29,8 @@ import kotlin.math.roundToInt class AppAdapterHandler(private val context: Context) : MethodCallHandler { override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { - "getAppIcon" -> GlobalScope.launch(Dispatchers.IO) { getAppIcon(call, Coresult(result)) } - "getAppNames" -> GlobalScope.launch(Dispatchers.IO) { getAppNames(Coresult(result)) } + "getAppNames" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getAppNames) } + "getAppIcon" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getAppIcon) } "edit" -> { val title = call.argument("title") val uri = call.argument("uri")?.let { Uri.parse(it) } @@ -61,7 +62,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler { } } - private fun getAppNames(result: MethodChannel.Result) { + private fun getAppNames(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { val nameMap = HashMap() val intent = Intent(Intent.ACTION_MAIN, null) .addCategory(Intent.CATEGORY_LAUNCHER) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/Coresult.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/Coresult.kt index d2db62561..5207bde7d 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/Coresult.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/Coresult.kt @@ -1,9 +1,11 @@ package deckers.thibault.aves.channel.calls +import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlin.reflect.KSuspendFunction2 // ensure `result` methods are called on the main looper thread class Coresult internal constructor(private val methodResult: MethodChannel.Result) : MethodChannel.Result { @@ -20,4 +22,24 @@ class Coresult internal constructor(private val methodResult: MethodChannel.Resu override fun notImplemented() { mainScope.launch { methodResult.notImplemented() } } + + companion object { + fun safe(call: MethodCall, result: MethodChannel.Result, function: (call: MethodCall, result: MethodChannel.Result) -> Unit) { + val res = Coresult(result) + try { + function(call, res) + } catch (e: Exception) { + res.error("safe-exception", e.message, e.stackTraceToString()) + } + } + + suspend fun safesus(call: MethodCall, result: MethodChannel.Result, function: KSuspendFunction2) { + val res = Coresult(result) + try { + function(call, res) + } catch (e: Exception) { + res.error("safe-exception", e.message, e.stackTraceToString()) + } + } + } } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt index 22e4c84ca..13ec93da6 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt @@ -12,6 +12,7 @@ import android.util.Log import androidx.exifinterface.media.ExifInterface import com.drew.imaging.ImageMetadataReader import com.drew.metadata.file.FileTypeDirectory +import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import deckers.thibault.aves.metadata.ExifInterfaceHelper import deckers.thibault.aves.metadata.MediaMetadataRetrieverHelper import deckers.thibault.aves.metadata.Metadata @@ -37,12 +38,12 @@ class DebugHandler(private val context: Context) : MethodCallHandler { when (call.method) { "getContextDirs" -> result.success(getContextDirs()) "getEnv" -> result.success(System.getenv()) - "getBitmapFactoryInfo" -> GlobalScope.launch(Dispatchers.IO) { getBitmapFactoryInfo(call, Coresult(result)) } - "getContentResolverMetadata" -> GlobalScope.launch(Dispatchers.IO) { getContentResolverMetadata(call, Coresult(result)) } - "getExifInterfaceMetadata" -> GlobalScope.launch(Dispatchers.IO) { getExifInterfaceMetadata(call, Coresult(result)) } - "getMediaMetadataRetrieverMetadata" -> GlobalScope.launch(Dispatchers.IO) { getMediaMetadataRetrieverMetadata(call, Coresult(result)) } - "getMetadataExtractorSummary" -> GlobalScope.launch(Dispatchers.IO) { getMetadataExtractorSummary(call, Coresult(result)) } - "getTiffStructure" -> GlobalScope.launch(Dispatchers.IO) { getTiffStructure(call, Coresult(result)) } + "getBitmapFactoryInfo" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getBitmapFactoryInfo) } + "getContentResolverMetadata" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getContentResolverMetadata) } + "getExifInterfaceMetadata" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getExifInterfaceMetadata) } + "getMediaMetadataRetrieverMetadata" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getMediaMetadataRetrieverMetadata) } + "getMetadataExtractorSummary" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getMetadataExtractorSummary) } + "getTiffStructure" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getTiffStructure) } else -> result.notImplemented() } } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/ImageFileHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/ImageFileHandler.kt index f37b4f2ce..776747900 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/ImageFileHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/ImageFileHandler.kt @@ -5,6 +5,8 @@ import android.graphics.Rect import android.net.Uri import android.util.Size import com.bumptech.glide.Glide +import deckers.thibault.aves.channel.calls.Coresult.Companion.safe +import deckers.thibault.aves.channel.calls.Coresult.Companion.safesus import deckers.thibault.aves.channel.calls.fetchers.RegionFetcher import deckers.thibault.aves.channel.calls.fetchers.ThumbnailFetcher import deckers.thibault.aves.channel.calls.fetchers.TiffRegionFetcher @@ -29,17 +31,14 @@ class ImageFileHandler(private val activity: Activity) : MethodCallHandler { override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { - "getObsoleteEntries" -> GlobalScope.launch(Dispatchers.IO) { getObsoleteEntries(call, Coresult(result)) } - "getEntry" -> GlobalScope.launch(Dispatchers.IO) { getEntry(call, Coresult(result)) } - "getThumbnail" -> GlobalScope.launch(Dispatchers.IO) { getThumbnail(call, Coresult(result)) } - "getRegion" -> GlobalScope.launch(Dispatchers.IO) { getRegion(call, Coresult(result)) } - "clearSizedThumbnailDiskCache" -> { - GlobalScope.launch(Dispatchers.IO) { Glide.get(activity).clearDiskCache() } - result.success(null) - } - "rename" -> GlobalScope.launch(Dispatchers.IO) { rename(call, Coresult(result)) } - "rotate" -> GlobalScope.launch(Dispatchers.IO) { rotate(call, Coresult(result)) } - "flip" -> GlobalScope.launch(Dispatchers.IO) { flip(call, Coresult(result)) } + "getObsoleteEntries" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getObsoleteEntries) } + "getEntry" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::getEntry) } + "getThumbnail" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getThumbnail) } + "getRegion" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getRegion) } + "clearSizedThumbnailDiskCache" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::clearSizedThumbnailDiskCache) } + "rename" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::rename) } + "rotate" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::rotate) } + "flip" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::flip) } else -> result.notImplemented() } } @@ -143,6 +142,11 @@ class ImageFileHandler(private val activity: Activity) : MethodCallHandler { }) } + private fun clearSizedThumbnailDiskCache(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { + Glide.get(activity).clearDiskCache() + result.success(null) + } + private suspend fun rename(call: MethodCall, result: MethodChannel.Result) { val entryMap = call.argument("entry") val newName = call.argument("newName") diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt index 689d79458..57fdb11b2 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt @@ -20,6 +20,7 @@ import com.drew.metadata.iptc.IptcDirectory import com.drew.metadata.mp4.media.Mp4UuidBoxDirectory import com.drew.metadata.webp.WebpDirectory import com.drew.metadata.xmp.XmpDirectory +import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import deckers.thibault.aves.metadata.* import deckers.thibault.aves.metadata.ExifInterfaceHelper.describeAll import deckers.thibault.aves.metadata.ExifInterfaceHelper.getSafeDateMillis @@ -69,14 +70,14 @@ import kotlin.math.roundToLong class MetadataHandler(private val context: Context) : MethodCallHandler { override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { - "getAllMetadata" -> GlobalScope.launch(Dispatchers.IO) { getAllMetadata(call, Coresult(result)) } - "getCatalogMetadata" -> GlobalScope.launch(Dispatchers.IO) { getCatalogMetadata(call, Coresult(result)) } - "getOverlayMetadata" -> GlobalScope.launch(Dispatchers.IO) { getOverlayMetadata(call, Coresult(result)) } - "getMultiPageInfo" -> GlobalScope.launch(Dispatchers.IO) { getMultiPageInfo(call, Coresult(result)) } - "getPanoramaInfo" -> GlobalScope.launch(Dispatchers.IO) { getPanoramaInfo(call, Coresult(result)) } - "getEmbeddedPictures" -> GlobalScope.launch(Dispatchers.IO) { getEmbeddedPictures(call, Coresult(result)) } - "getExifThumbnails" -> GlobalScope.launch(Dispatchers.IO) { getExifThumbnails(call, Coresult(result)) } - "extractXmpDataProp" -> GlobalScope.launch(Dispatchers.IO) { extractXmpDataProp(call, Coresult(result)) } + "getAllMetadata" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getAllMetadata) } + "getCatalogMetadata" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getCatalogMetadata) } + "getOverlayMetadata" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getOverlayMetadata) } + "getMultiPageInfo" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getMultiPageInfo) } + "getPanoramaInfo" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getPanoramaInfo) } + "getEmbeddedPictures" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getEmbeddedPictures) } + "getExifThumbnails" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getExifThumbnails) } + "extractXmpDataProp" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::extractXmpDataProp) } else -> result.notImplemented() } } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt index 6ff8babbb..248db4ff6 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt @@ -5,7 +5,7 @@ import android.media.MediaScannerConnection import android.net.Uri import android.os.Build import android.os.storage.StorageManager -import androidx.annotation.RequiresApi +import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import deckers.thibault.aves.utils.PermissionManager import deckers.thibault.aves.utils.StorageUtils.getVolumePaths import io.flutter.plugin.common.MethodCall @@ -20,27 +20,18 @@ import java.util.* class StorageHandler(private val context: Context) : MethodCallHandler { override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { - "getStorageVolumes" -> { - val volumes: List> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - storageVolumes - } else { - // TODO TLAD find alternative for Android getFreeSpace(call, result) - "getGrantedDirectories" -> result.success(ArrayList(PermissionManager.getGrantedDirs(context))) - "getInaccessibleDirectories" -> getInaccessibleDirectories(call, result) - "revokeDirectoryAccess" -> revokeDirectoryAccess(call, result) - "scanFile" -> GlobalScope.launch(Dispatchers.IO) { scanFile(call, Coresult(result)) } + "getStorageVolumes" -> safe(call, result, ::getStorageVolumes) + "getFreeSpace" -> safe(call, result, ::getFreeSpace) + "getGrantedDirectories" -> safe(call, result, ::getGrantedDirectories) + "getInaccessibleDirectories" -> safe(call, result, ::getInaccessibleDirectories) + "revokeDirectoryAccess" -> safe(call, result, ::revokeDirectoryAccess) + "scanFile" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::scanFile) } else -> result.notImplemented() } } - private val storageVolumes: List> - @RequiresApi(api = Build.VERSION_CODES.N) - get() { + private fun getStorageVolumes(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { + val volumes: List> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { val volumes = ArrayList>() val sm = context.getSystemService(StorageManager::class.java) if (sm != null) { @@ -61,8 +52,13 @@ class StorageHandler(private val context: Context) : MethodCallHandler { } } } - return volumes + volumes + } else { + // TODO TLAD find alternative for Android ("path") @@ -93,6 +89,10 @@ class StorageHandler(private val context: Context) : MethodCallHandler { } } + private fun getGrantedDirectories(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { + result.success(ArrayList(PermissionManager.getGrantedDirs(context))) + } + private fun getInaccessibleDirectories(call: MethodCall, result: MethodChannel.Result) { val dirPaths = call.argument>("dirPaths") if (dirPaths == null) {