Skip to content

Commit 7076d1b

Browse files
authored
Fix Android loadJar (#10867)
* Fix Android loadJar * Using another way * Code formating * Fix mod update
1 parent 18d852b commit 7076d1b

File tree

3 files changed

+70
-24
lines changed

3 files changed

+70
-24
lines changed

android/src/mindustry/android/AndroidLauncher.java

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,28 +73,57 @@ public void shareFile(Fi file){
7373
@Override
7474
public ClassLoader loadJar(Fi jar, ClassLoader parent) throws Exception{
7575
//Required to load jar files in Android 14: https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading
76-
jar.file().setReadOnly();
77-
return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, parent){
78-
@Override
79-
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
80-
//check for loaded state
81-
Class<?> loadedClass = findLoadedClass(name);
82-
if(loadedClass == null){
83-
try{
84-
//try to load own class first
85-
loadedClass = findClass(name);
86-
}catch(ClassNotFoundException | NoClassDefFoundError e){
87-
//use parent if not found
88-
return parent.loadClass(name);
76+
try{
77+
jar.file().setReadOnly();
78+
return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, parent){
79+
@Override
80+
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
81+
//check for loaded state
82+
Class<?> loadedClass = findLoadedClass(name);
83+
if(loadedClass == null){
84+
try{
85+
//try to load own class first
86+
loadedClass = findClass(name);
87+
}catch(ClassNotFoundException | NoClassDefFoundError e){
88+
//use parent if not found
89+
return parent.loadClass(name);
90+
}
8991
}
90-
}
9192

92-
if(resolve){
93-
resolveClass(loadedClass);
93+
if(resolve){
94+
resolveClass(loadedClass);
95+
}
96+
return loadedClass;
9497
}
95-
return loadedClass;
98+
};
99+
}catch(SecurityException e){
100+
//`setReadOnly` to jar file in `/sdcard/Android/data/...` does not work on some Android 14 device
101+
//But in `/data/...` it works
102+
103+
if(Build.VERSION.SDK_INT < VERSION_CODES.O_MR1){
104+
throw e;
105+
}
106+
107+
Fi cacheDir = new Fi(getCacheDir()).child("mods");
108+
cacheDir.mkdirs();
109+
110+
//long file name support
111+
Fi modCacheDir = cacheDir.child(jar.nameWithoutExtension());
112+
Fi modCache = modCacheDir.child(Long.toHexString(jar.lastModified()) + ".zip");
113+
114+
if(modCacheDir.equals(jar.parent())){
115+
//should not reach here, just in case
116+
throw e;
96117
}
97-
};
118+
119+
//Cache will be deleted when mod is removed
120+
if(!modCache.exists() || jar.length() != modCache.length()){
121+
modCacheDir.mkdirs();
122+
jar.copyTo(modCache);
123+
}
124+
modCache.file().setReadOnly();
125+
return loadJar(modCache, parent);
126+
}
98127
}
99128

100129
@Override

core/assets/contributors

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,4 @@ MonoChronos
182182
RushieWashie
183183
ITY
184184
Iniquit
185+
DSFdsfWxp

core/src/mindustry/mod/Mods.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -413,19 +413,30 @@ private PageType getPage(Fi file){
413413

414414
/** Removes a mod file and marks it for requiring a restart. */
415415
public void removeMod(LoadedMod mod){
416-
if(!android && mod.loader != null){
417-
try{
418-
ClassLoaderCloser.close(mod.loader);
419-
}catch(Exception e){
420-
Log.err(e);
416+
boolean deleted = true;
417+
418+
if(mod.loader != null){
419+
if(android){
420+
//Try to remove cache for Android 14 security problem
421+
Fi cacheDir = new Fi(Core.files.getCachePath()).child("mods");
422+
Fi modCacheDir = cacheDir.child(mod.file.nameWithoutExtension());
423+
if(modCacheDir.exists()){
424+
deleted = modCacheDir.deleteDirectory();
425+
}
426+
}else{
427+
try{
428+
ClassLoaderCloser.close(mod.loader);
429+
}catch(Exception e){
430+
Log.err(e);
431+
}
421432
}
422433
}
423434

424435
if(mod.root instanceof ZipFi){
425436
mod.root.delete();
426437
}
427438

428-
boolean deleted = mod.file.isDirectory() ? mod.file.deleteDirectory() : mod.file.delete();
439+
deleted &= mod.file.isDirectory() ? mod.file.deleteDirectory() : mod.file.delete();
429440

430441
if(!deleted){
431442
ui.showErrorMessage("@mod.delete.error");
@@ -1112,6 +1123,11 @@ private LoadedMod loadMod(Fi sourceFile, boolean overwrite, boolean initialize)
11121123
//close the classloader for jar mods
11131124
if(!android){
11141125
ClassLoaderCloser.close(other.loader);
1126+
}else if(other.loader != null){
1127+
//Try to remove cache for Android 14 security problem
1128+
Fi cacheDir = new Fi(Core.files.getCachePath()).child("mods");
1129+
Fi modCacheDir = cacheDir.child(other.file.nameWithoutExtension());
1130+
modCacheDir.deleteDirectory();
11151131
}
11161132

11171133
//close zip file

0 commit comments

Comments
 (0)