Skip to content

Commit 84b1362

Browse files
committed
To resolve unhandled exception
'JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal continuation byte 0' that can be produced by invalid filename (broken sdcard, etc) or 'JNI WARNING: input is not valid Modified UTF-8: illegal start byte 0xf0' that can be generated if 4-byte UTF-8 sequence found in the filename on Android older than 6.0 (API 23), we implement own directory listing method instead of File.listFiles().
1 parent 37a4b0f commit 84b1362

File tree

6 files changed

+69
-5
lines changed

6 files changed

+69
-5
lines changed

android/jni/cr3engine.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,45 @@ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_checkFontLanguage
709709
return res;
710710
}
711711

712+
/*
713+
* Class: org_coolreader_crengine_Engine
714+
* Method: listFilesInternal
715+
* Signature: (Ljava/io/File;)[Ljava/io/File;
716+
*/
717+
JNIEXPORT jobjectArray JNICALL Java_org_coolreader_crengine_Engine_listFilesInternal
718+
(JNIEnv *env, jclass, jobject jdir)
719+
{
720+
jclass fileClass = env->FindClass("java/io/File");
721+
if (NULL == fileClass)
722+
return NULL;
723+
jmethodID mFileGetAbsolutePath = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;");
724+
if (NULL == mFileGetAbsolutePath)
725+
return NULL;
726+
jmethodID fileCtor = env->GetMethodID(fileClass, "<init>", "(Ljava/lang/String;)V");
727+
if (NULL == fileCtor)
728+
return NULL;
729+
jstring jpathname = (jstring)env->CallObjectMethod(jdir, mFileGetAbsolutePath);
730+
jboolean iscopy;
731+
const char * s = env->GetStringUTFChars(jpathname, &iscopy);
732+
lString16 path = (CRJNIEnv::sdk_int >= ANDROID_SDK_M) ? Utf8ToUnicode(s) : Wtf8ToUnicode(s);
733+
LVContainerRef dir = LVOpenDirectory(path);
734+
jobjectArray jarray = NULL;
735+
if ( !dir.isNull() ) {
736+
jstring emptyString = env->NewStringUTF("");
737+
jobject emptyFile = env->NewObject(fileClass, fileCtor, emptyString);
738+
jarray = env->NewObjectArray(dir->GetObjectCount(), fileClass, emptyFile);
739+
for (int i = 0; i < dir->GetObjectCount(); i++) {
740+
const LVContainerItemInfo *item = dir->GetObjectInfo(i);
741+
lString16 fileName = path + "/" + item->GetName();
742+
jstring jfilename = env->NewStringUTF((CRJNIEnv::sdk_int >= ANDROID_SDK_M) ? UnicodeToUtf8(fileName).c_str() : UnicodeToWtf8(fileName).c_str());
743+
jobject jfile = env->NewObject(fileClass, fileCtor, jfilename);
744+
env->SetObjectArrayElement(jarray, i, jfile);
745+
}
746+
}
747+
env->ReleaseStringUTFChars(jpathname, s);
748+
return jarray;
749+
}
750+
712751
/*
713752
* Class: org_coolreader_crengine_Engine
714753
* Method: isLink
@@ -789,6 +828,9 @@ static JNINativeMethod sEngineMethods[] = {
789828
{"setKeyBacklightInternal", "(I)Z", (void*)Java_org_coolreader_crengine_Engine_setKeyBacklightInternal},
790829
{"scanBookCoverInternal", "(Ljava/lang/String;)[B", (void*)Java_org_coolreader_crengine_Engine_scanBookCoverInternal},
791830
{"drawBookCoverInternal", "(Landroid/graphics/Bitmap;[BLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V", (void*)Java_org_coolreader_crengine_Engine_drawBookCoverInternal},
831+
{"haveFcLangCodeInternal", "(Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_Engine_haveFcLangCodeInternal},
832+
{"checkFontLanguageCompatibilityInternal", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_Engine_checkFontLanguageCompatibilityInternal},
833+
{"listFilesInternal", "(Ljava/io/File;)[Ljava/io/File;", (void*)Java_org_coolreader_crengine_Engine_listFilesInternal}
792834
};
793835

794836

android/jni/cr3java.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33

44
#include <dlfcn.h>
55

6-
// M is for Marshmallow!
7-
#define ANDROID_SDK_M 23
8-
96
uint8_t CRJNIEnv::sdk_int = 0;
107

118
lString16 CRJNIEnv::fromJavaString( jstring str )

android/jni/cr3java.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
#include "../../crengine/include/props.h"
2121
#include "../../crengine/include/lvtinydom.h"
2222

23+
// M is for Marshmallow!
24+
#define ANDROID_SDK_M 23
25+
2326
//====================================================================
2427
// libjnigraphics replacement for pre-2.2 SDKs
2528
enum AndroidBitmapFormat {

android/jni/org_coolreader_crengine_Engine.h

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

android/src/org/coolreader/crengine/Engine.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,8 @@ public void initAgain() {
640640
*/
641641
private native static boolean checkFontLanguageCompatibilityInternal(String fontFace, String langCode);
642642

643+
private native static File[] listFilesInternal(File dir);
644+
643645
public static void suspendLongOperation() {
644646
suspendLongOperationInternal();
645647
}
@@ -648,6 +650,10 @@ public synchronized static boolean checkFontLanguageCompatibility(String fontFac
648650
return checkFontLanguageCompatibilityInternal(fontFace, langCode);
649651
}
650652

653+
public static synchronized File[] listFiles(File dir) {
654+
return listFilesInternal(dir);
655+
}
656+
651657
/**
652658
* Finds the corresponding language code in embedded FontConfig language orthography catalog.
653659
*

android/src/org/coolreader/crengine/Scanner.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,15 @@ public boolean listDirectory(FileInfo baseDir)
111111
}
112112
try {
113113
File dir = new File(baseDir.pathname);
114-
File[] items = dir.listFiles();
114+
//File[] items = dir.listFiles();
115+
// To resolve unhandled exception
116+
// 'JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal continuation byte 0'
117+
// that can be produced by invalid filename (broken sdcard, etc)
118+
// or 'JNI WARNING: input is not valid Modified UTF-8: illegal start byte 0xf0'
119+
// that can be generated if 4-byte UTF-8 sequence found in the filename,
120+
// we implement own directory listing method instead of File.listFiles().
121+
// TODO: replace other occurrences of the method File.listFiles().
122+
File[] items = Engine.listFiles(dir);
115123
// process normal files
116124
if ( items!=null ) {
117125
for ( File f : items ) {

0 commit comments

Comments
 (0)