Problem
I want to read some files (in binary format is ok – i do not need to decode them, just read and copy) created by the DJI Fly software. I have tried to create an app that runs as a background service in Kotlin.
My code
private fun initFileObserver() {
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.SIZE
)
val selection = "${MediaStore.Files.FileColumns.DATA} LIKE ?"
val selectionArgs = arrayOf("$flightRecordsPath/%")
val sortOrder = "${MediaStore.Files.FileColumns.DATE_ADDED} DESC"
val queryUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
val contentResolver = context.contentResolver
val cursor = contentResolver.query(
queryUri,
projection,
selection,
selectionArgs,
sortOrder
)
cursor?.use {
val idColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
val nameColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
val sizeColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE)
var ic = it.count
Log.d("monsoonParasiteRDR", "cursor $it, $ic")
while (it.moveToNext()) {
Log.d("monsoonParasiteRDR", "$it")
val id = it.getLong(idColumn)
val name = it.getString(nameColumn)
val size = it.getLong(sizeColumn)
if (name.endsWith(".txt")) {
val uri = ContentUris.withAppendedId(queryUri, id)
val inputStream = contentResolver.openInputStream(uri)
val text = inputStream?.bufferedReader().use { it?.readText() }
// Do something with the text
Log.d("monsoonParasiteRDR", "File: $name, Size: $size, Content: $text")
}
}
}
val directory = File(Environment.getExternalStorageDirectory(), flightRecordsPath)
if (directory.exists()) {
Log.d("monsoonParasiteRDR", "Directory successfully located: $directory")
} else {
Log.d("monsoonParasiteRDR", "Directory does not exist: $directory")
}
val files = directory.listFiles()
Log.d("monsoonParasiteRDR", "got files $files")
if (files != null && files.isNotEmpty()) {
files.forEach { file ->
Log.d("monsoonParasiteRDR", "File: ${file.name}, Size: ${file.length()} bytes")
}
} else {
Log.d("monsoonParasiteRDR", "No files found in the directory.")
}
Log.d("monsoonParasiteRDR", "got it")
fileObserver = object : FileObserver(flightRecordsPath, CREATE) {
override fun onEvent(event: Int, path: String?) {
Log.d("monsoonParasiteRDR","running: $event")
if (event == CREATE && path != null) {
val file = File("$flightRecordsPath/$path")
val content = file.readText()
Log.d("monsoonParasiteRDR","new file found")
sendContentViaWebSocket(content)
}
}
}
fileObserver.startWatching()
}
Now, The app runs in background, via a service. The app is able to run as a background service. The output in logcat is:
2024-08-08 14:05:21.075 8264-8264 monsoonParasite org...sky.monsoon.parasitetelemetry D Starting operation
2024-08-08 14:05:21.076 8264-8264 monsoonParasite org...sky.monsoon.parasitetelemetry D Starting service
2024-08-08 14:05:21.076 8264-8264 monsoonParasite org...sky.monsoon.parasitetelemetry D Starting reader service
2024-08-08 14:05:21.180 8264-8264 monsoonParasiteRDR org...sky.monsoon.parasitetelemetry D cursor android.content.ContentResolver$CursorWrapperInner@54733e7, 0
Break intentional. Note this line:
2024-08-08 14:05:21.181 8264-8264 monsoonParasiteRDR org...sky.monsoon.parasitetelemetry D Directory successfully located: /storage/emulated/0/Download
Break intentional, note the subsequent output:
2024-08-08 14:05:21.181 8264-8264 monsoonParasiteRDR org...sky.monsoon.parasitetelemetry D got files null
2024-08-08 14:05:21.181 8264-8264 monsoonParasiteRDR org...sky.monsoon.parasitetelemetry D No files found in the directory.
2024-08-08 14:05:21.181 8264-8264 monsoonParasiteRDR org...sky.monsoon.parasitetelemetry D got it
So I am able to open the directory. If I check the directory via adb shell, I see the files.
DJIFlightRecord_2023-11-05_[16-44-49].txt DJIFlightRecord_2023-11-29_[13-13-16].txt DJIFlightRecord_2023-12-07_[12-03-57].txt DJIFlightRecord_2023-12-13_[13-10-43].txt DJIFlightRecord_2024-03-28_[16-12-03].txt
DJIFlightRecord_2023-11-12_[14-55-26].txt DJIFlightRecord_2023-11-29_[13-19-17].txt DJIFlightRecord_2023-12-07_[12-14-12].txt DJIFlightRecord_2023-12-17_[13-44-36].txt DJIFlightRecord_2024-03-28_[16-54-45].txt
DJIFlightRecord_2023-11-13_[11-29-02].txt DJIFlightRecord_2023-11-29_[13-29-33].txt DJIFlightRecord_2023-12-07_[12-19-24].txt DJIFlightRecord_2023-12-17_[13-50-12].txt DJIFlightRecord_2024-03-31_[12-57-35].txt
DJIFlightRecord_2023-11-29_[12-22-15].txt DJIFlightRecord_2023-12-07_[11-40-37].txt DJIFlightRecord_2023-12-07_[12-32-25].txt DJIFlightRecord_2023-12-17_[13-57-33].txt DJIFlightRecord_2024-03-31_[13-07-54].txt
DJIFlightRecord_2023-11-29_[13-04-43].txt DJIFlightRecord_2023-12-07_[11-48-21].txt DJIFlightRecord_2023-12-07_[12-50-51].txt DJIFlightRecord_2023-12-17_[15-34-16].txt MCDatFlightRecords
DJIFlightRecord_2023-11-29_[13-11-08].txt DJIFlightRecord_2023-12-07_[11-54-59].txt DJIFlightRecord_2023-12-07_[13-04-31].txt DJIFlightRecord_2023-12-18_[11-35-20].txt dgdhgdh.txt
DJIFlightRecord_2023-11-29_[13-12-17].txt DJIFlightRecord_2023-12-07_[12-00-26].txt DJIFlightRecord_2023-12-12_[12-52-52].txt DJIFlightRecord_2024-01-01_[15-46-59].txt test.txt
The last two files are manually created by me.
Problem
Unfortunately, all these files have permission -rw-rw----
, and belong to the group sdcard_rw
. So as seen in the last part of my output, the file iterator is null.
Question
What can I do? I have tried to use direct reading (not just mediastore).
DJI is not going to give me intent or fileprovider. What other option do I have?
Thank you
Additional
We have Android 10. Here is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" package="org.opensky.monsoon.parasitetelemetry">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".DJIFileReaderService"
android:enabled="true"
android:exported="false" />
</application>
</manifest>