I am fairly new with cpp or jni in android. I have few lines of code in kotlin which I want to move it to cpp, reason being want to make the validation in bit more secure way.
Kotlin code:
val entries = ZipFile(context.packageCodePath).entries().toList()
var totalCrc = 0L
for (entry in entries) {
if (entry.name.startsWith(DEX_FILENAME)) totalCrc += entry.crc
}
What I could achieve in cpp:
jclass zipFileClass = env->FindClass("java/util/zip/ZipFile");
jmethodID zipFileConstructor = env->GetMethodID(zipFileClass, "<init>", "(Ljava/lang/String;)V");
jobject zipFileObject = env->NewObject(zipFileClass, zipFileConstructor, packageCodePath);
jmethodID methodId = env->GetMethodID(zipFileClass, "entries", "()Ljava/util/Enumeration;");
jobject zipFileList = env->CallObjectMethod(zipFileObject, methodId);
jclass ZipEntryIteratorClass = env->FindClass("java/util/zip/ZipFile$ZipEntryIterator");
** // This line doesnt have any error from the compiler. **
jmethodID mid_Iterator_hasNext = env->GetMethodID(ZipEntryIteratorClass, "hasMoreElements", "()Z");
** // I get error at this line, for some reason jni layer is not able to find the nextElement method**
jmethodID mid_Iterator_next = env->GetMethodID(ZipEntryIteratorClass, "nextElement", "()Ljava/util/zip/ZipEntry;");
** // This line also is able to run without any exception. **
jboolean hasMoreElements = env->CallBooleanMethod(zipFileList, mid_Iterator_hasNext);
**// Of course below code doesnt work, due to the exception**
//jobject v = env->CallObjectMethod(zipFileList, mid_Iterator_next);
//env->DeleteLocalRef(v);
**// This is the logic which I would eventually have to iterate through the items.**
//while (hasMoreElements) {
//jobject v = env->CallObjectMethod(zipFileList, mid_Iterator_next);
// Do something with `v`
// Avoid overflowing the local reference table if legList gets large
//env->DeleteLocalRef(v);
//}
The exception I get is:
runtime.cc:677] JNI DETECTED ERROR IN APPLICATION: JNI CallBooleanMethodV called with pending exception java.lang.NoSuchMethodError: no non-static method "Ljava/util/zip/ZipFile$ZipEntryIterator;.nextElement()Ljava/util/zip/ZipEntry;"
runtime.cc:677] at boolean com.something.else.RandomScreenActivity.validateChecksum(java.lang.String) (RandomScreenActivity.kt:-2)
runtime.cc:677] at void com.something.else.RandomScreenActivity.onCreate(android.os.Bundle) (RandomScreenActivity.kt:72)
runtime.cc:677] at void android.app.Activity.performCreate(android.os.Bundle, android.os.PersistableBundle) (Activity.java:8051)
runtime.cc:677] at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:8031)
runtime.cc:677] at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1329)
runtime.cc:677] at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:3608)
runtime.cc:677] at android.app.Activity android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.app.servertransaction.PendingTransactionActions, android.content.Intent) (ActivityThread.java:3792)
runtime.cc:677] at void android.app.servertransaction.LaunchActivityItem.execute(android.app.ClientTransactionHandler, android.os.IBinder, android.app.servertransaction.PendingTransactionActions) (LaunchActivityItem.java:103)
runtime.cc:677] at void android.app.servertransaction.TransactionExecutor.executeCallbacks(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:135)
runtime.cc:677] at void android.app.servertransaction.TransactionExecutor.execute(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:95)
runtime.cc:677] at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:2210)
runtime.cc:677] at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:106)
runtime.cc:677] at boolean android.os.Looper.loopOnce(android.os.Looper, long, int) (Looper.java:201)
runtime.cc:677] at void android.os.Looper.loop() (Looper.java:288)
runtime.cc:677] at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:7839)
runtime.cc:677] at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
runtime.cc:677] at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:548)
runtime.cc:677] at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:1003)
runtime.cc:677]
runtime.cc:677] in call to CallBooleanMethodV
runtime.cc:677] from boolean RandomScreenActivity.validateChecksum(java.lang.String)
Additionally ZipEntryIterator class from the java.util.ZipFIle class has following methods which can be used to iterate through the items:
private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {
private int i = 0;
public ZipEntryIterator() {
ensureOpen();
}
public boolean hasMoreElements() {
return hasNext();
}
public boolean hasNext() {
synchronized (ZipFile.this) {
ensureOpen();
return i < total;
}
}
public ZipEntry nextElement() {
return next();
}
public ZipEntry next() {
synchronized (ZipFile.this) {
ensureOpen();
if (i >= total) {
throw new NoSuchElementException();
}
long jzentry = getNextEntry(jzfile, i++);
if (jzentry == 0) {
String message;
if (closeRequested) {
message = "ZipFile concurrently closed";
} else {
message = getZipMessage(ZipFile.this.jzfile);
}
throw new ZipError("jzentry == 0" +
",\n jzfile = " + ZipFile.this.jzfile +
",\n total = " + ZipFile.this.total +
",\n name = " + ZipFile.this.name +
",\n i = " + i +
",\n message = " + message
);
}
ZipEntry ze = getZipEntry(null, jzentry);
freeEntry(jzfile, jzentry);
return ze;
}
}
}
Any suggestion how to iterate through the ZipEntryIterator list?
On my virtual Android device, I get the following logcat entry:
Accessing hidden method
Ljava/util/zip/ZipFile$ZipEntryIterator;nextElement()Ljava/util/zip/ZipEntry;
(max-target-r, JNI, denied)
I also found this line in the Android codebase, which confirms the warning message above.
which makes me think you should stick to the generic Enumeration
interface.
The ZipFile
class returns a generic Enumeration
interface, which only returns Object
s. So:
jmethodID mid_Iterator_next = env->GetMethodID(ZipEntryIteratorClass, "nextElement", "()Ljava/lang/Object;");