Skip to content

Commit

Permalink
App/Activity: Let the FileProvider load bundled model files
Browse files Browse the repository at this point in the history
This patch makes the ModelFileProvider pre-load the model files, which
are packaged into the APK file.

Signed-off-by: Wook Song <[email protected]>
  • Loading branch information
wooksong committed Jul 4, 2024
1 parent 8b6dddb commit ebed8a5
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 46 deletions.
2 changes: 1 addition & 1 deletion ml_inference_offloading/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
android:name="androidx.core.content.FileProvider">
android:name=".providers.ModelFileProvider">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
Expand All @@ -22,7 +21,6 @@ import androidx.compose.ui.Modifier
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import java.io.File
import java.io.IOException

// todo: Define DTO with generality and extensibility
data class ModelInfo(
Expand Down Expand Up @@ -89,29 +87,6 @@ class MainActivity : ComponentActivity() {
}
}

private fun copyFilesToExternalDir() {
val am = resources.assets

am.list("models/")?.let { fileArray ->
// Copy files into app-specific directory.
fileArray.forEach { fileName ->
try {
val inFile = am.open("models/$fileName")
inFile.use { stream ->
val outDir = getExternalFilesDir(null).toString()
File(outDir, fileName).outputStream().use {
stream.copyTo(it)
}
}
} catch (e: IOException) {
Log.e(TAG, "Failed to copy file: $fileName")
e.printStackTrace()
return
}
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Expand All @@ -124,31 +99,40 @@ class MainActivity : ComponentActivity() {
}
}
setContentView(R.layout.activity_main)

copyFilesToExternalDir()
startForegroundService(Intent(this, MainService::class.java))

// todo: Use database instead of just ArrayList
val modelList = ArrayList<ModelInfo>()
val path = getExternalFilesDir(null)!!.absolutePath

val mobileNet = File("$path/mobilenet_v1_1.0_224_quant.tflite")
val mobileNetFilter =
"other/tensor,format=static,dimension=(string)3:224:224:1,type=uint8,framerate=0/1 ! " +
"tensor_filter framework=tensorflow-lite model=" + mobileNet.absolutePath + " ! " +
"other/tensor,format=static,dimension=(string)1001:1,type=uint8,framerate=0/1"
val mobileNetInfo = ModelInfo("MobileNet", mobileNetFilter)
modelList.add(mobileNetInfo)

val yolov8 = File("$path/yolov8s_float32.tflite")
val yolov8Filter =
"other/tensors,num_tensors=1,format=static,dimensions=3:224:224:1,types=float32,framerate=0/1 ! " +
"tensor_filter framework=tensorflow-lite model=" + yolov8.absolutePath + " ! " +
"other/tensors,num_tensors=1,types=float32,format=static,dimensions=1029:84:1,framerate=0/1"

val yolov8Info = ModelInfo("Yolov8", yolov8Filter)
modelList.add(yolov8Info)
val privateExternalRoot = getExternalFilesDir(null)
val privateExternalModels = privateExternalRoot?.resolve("models")

if (privateExternalModels?.isDirectory == true) {
privateExternalModels.list()?.onEach {
when (it.toString()) {
// todo: Changes below are temporarily
"mobilenet_v1_1.0_224_quant.tflite" -> {
val mobileNet =
File("$privateExternalModels/$it")
val mobileNetFilter =
"other/tensor,format=static,dimension=(string)3:224:224:1,type=uint8,framerate=0/1 ! " +
"tensor_filter framework=tensorflow-lite model=" + mobileNet.absolutePath + " ! " +
"other/tensor,format=static,dimension=(string)1001:1,type=uint8,framerate=0/1"
val mobileNetInfo = ModelInfo("MobileNet", mobileNetFilter)
modelList.add(mobileNetInfo)
}

"yolov8s_float32.tflite" -> {
val yolov8 = File("$privateExternalModels/$it")
val yolov8Filter =
"other/tensors,num_tensors=1,format=static,dimensions=3:224:224:1,types=float32,framerate=0/1 ! " +
"tensor_filter framework=tensorflow-lite model=" + yolov8.absolutePath + " ! " +
"other/tensors,num_tensors=1,types=float32,format=static,dimensions=1029:84:1,framerate=0/1"
val yolov8Info = ModelInfo("Yolov8", yolov8Filter)
modelList.add(yolov8Info)
}
}
}
}
val recyclerView = findViewById<RecyclerView>(R.id.model_list)
recyclerView.adapter = ModelAdapter(modelList)
recyclerView.layoutManager = LinearLayoutManager(this)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ai.nnstreamer.ml.inference.offloading.providers

import ai.nnstreamer.ml.inference.offloading.R
import android.content.Context
import androidx.core.content.FileProvider
import java.io.FileOutputStream
import java.io.IOException

class ModelFileProvider : FileProvider(R.xml.file_paths) {
companion object {
val assetPaths = listOf("models")
}

private fun copyAssetsToExternal(ctx: Context, path: String) {
val resAssets = ctx.resources.assets
val externalDir = ctx.getExternalFilesDir(null)?.resolve(path)
?: throw IOException("Failed to resolve $path in the App's private external storage")

try {
if (!externalDir.isDirectory && !externalDir.mkdir()) {
throw IOException("Failed to create $externalDir")
}
} catch (e: SecurityException) {
val msg = e.message ?: "none"

throw IOException("Failed to access $externalDir for the security reason (details: $msg)")
}


resAssets.list(path)?.run {
filterNotNull().onEach { file ->
val os = FileOutputStream(externalDir.resolve(file))

resAssets.open("$path/$file").use { stream ->
stream.copyTo(os)
}
}
}
}

@Override
override fun onCreate(): Boolean {
context?.run {
assetPaths.onEach { path ->
try {
copyAssetsToExternal(this, path)
} catch (e: Exception) {
throw RuntimeException("Failed to preload $path in the APK's assets directory")
}
}
}

return super.onCreate()
}

}

0 comments on commit ebed8a5

Please sign in to comment.