Why do some Android phones cause our app to throw an java.lang.UnsatisfiedLinkError? -


we're experiencing java.lang.unsatisfiedlinkerror on of android phones using our app in market.

problem description:

static {     system.loadlibrary("stlport_shared"); // c++ stl             system.loadlibrary("lib2");      system.loadlibrary("lib3");  } 

crashes app in on of system.loadlibrary() lines java.lang.unsatisfiedlinkerror. java.lang.unsatisfiedlinkerror: couldn't load stlport_shared loader dalvik.system.pathclassloader[dexpath=/data/app/app_id-2.apk,librarypath=/data/app-lib/app_id-2]: findlibrary returned null

solution approach

we started running custom diagnostics on our installs check if every lib unpacked in /data/data/app_id/lib folder.

packagemanager m = context.getpackagemanager(); string s = context.getpackagename(); packageinfo p; p = m.getpackageinfo(s, 0); s = p.applicationinfo.datadir;  file appdir = new file(s); long freespace = appdir.getfreespace();  file[] appdirlist = appdir.listfiles(); int numberoflibfiles = 0; boolean subfileslarger0 = true; (int = 0; < appdirlist.length; i++) {      if(appdirlist[i].getname().startswith("lib")) {         file[] subfile = appdirlist[i].listfiles(filefilters.filterdirs);            numberoflibfiles = subfile.length;         (int j = 0; j < subfile.length; j++) {             if(subfile[j].length() <= 0) {                 subfileslarger0 = false;                 break;             }         }     } } 

on every test phone have numberoflibfiles == 3 , subfileslarger0 == true. wanted test if libs unpacked , larger 0 byte. in addition we're looking @ freespace see how disk space available. freespace matches amount of memory can find in settings --> applications @ bottom of screen. thinking behind approach when there not enough space on disk available installer might have problems unpacking apk.

real world scenario

looking @ diagnostics, of devices out there not have 3 libs in /data/data/app_id/lib folder have plenty of free space. i'm wondering why error message looking /data/app-lib/app_id-2. our phones store libs in /data/data/app_id/lib. system.loadlibrary() should use consistent path across installation , loading libs? how can know os looking libs?

question

anyone experiencing problems installing native libs? work arounds have been successful? experience downloading native libs on internet when not existent , storing them manually? cause problem in first place?

edit

i have user runs problem after application update. previous version worked fine on phone, after update native libs seem missing. copying libs manually seem cause trouble well. on android 4.x non rooted phone without custom rom.

edit 2 - solution

after 2 years of spending time on problem. came solution works now. open sourced it: https://github.com/keepsafe/relinker

i have same trouble, , unsatisfiedlinkerrors comes on versions of android - on past 6 months, app has on 90000 active installs, had:

android 4.2     36  57.1% android 4.1     11  17.5% android 4.3     8   12.7% android 2.3.x   6   9.5% android 4.4     1   1.6% android 4.0.x   1   1.6% 

and users report happens after app update. app gets around 200 - 500 new users per day.

i think came simpler work-around. can find out original apk of app simple call:

    string apkfilename = context.getapplicationinfo().sourcedir; 

this returns "/data/app/com.example.pkgname-3.apk", exact file name of app's apk file. file regular zip file , readable without root. therefore, if catch java.lang.unsatisfiedlinkerror, can extract , copy native library, inside of .apk (zip) lib/armeabi-v7a folder (or whatever architecture i'm on), directory can read/write/execute, , load system.load(full_path).

edit: seems work

update july 1, 2014 since releasing version of product code similar listed below, on june 23, 2014, did not have unsatisfied link errors native library.

here code used:

public static void initnativelib(context context) {     try {         // try loading our native lib, see if works...         system.loadlibrary("mynativelibname");     } catch (unsatisfiedlinkerror er) {         applicationinfo appinfo = context.getapplicationinfo();         string libname = "libmynativelibname.so";         string destpath = context.getfilesdir().tostring();         try {             string soname = destpath + file.separator + libname;             new file(soname).delete();             unziputil.extractfile(appinfo.sourcedir, "lib/" + build.cpu_abi + "/" + libname, destpath);             system.load(soname);         } catch (ioexception e) {             // extractfile app files dir did not work. not enough space? try elsewhere...             destpath = context.getexternalcachedir().tostring();             // note: location on external memory not secure, can read/write it...             // extract "secure" place (our apk) , instantly load it,             // on each start of app, should make safer.             string soname = destpath + file.separator + libname;             new file(soname).delete(); // copy old, or altered attack             try {                 unziputil.extractfile(appinfo.sourcedir, "lib/" + build.cpu_abi + "/" + libname, destpath);                 system.load(soname);             } catch (ioexception e2) {                 log.e(tag "exception in installinfo.init(): " + e);                 e.printstacktrace();             }         }     } } 

unfortunately, if bad app update leaves old version of native library, or copy somehow damaged, loaded system.loadlibrary("mynativelibname"), there no way unload it. upon finding out such remnant defunct library lingering in standard app native lib folder, e.g. calling 1 of our native methods , finding out it's not there (unsatisfiedlinkerror again), store preference avoid calling standard system.loadlibrary() altogether , relying on our own extraction , loading code upon next app startups.

for completeness, here unziputil class, copied , modified codejava unziputility article:

import java.io.*; import java.util.zip.zipentry; import java.util.zip.zipinputstream;  public class unziputil {     /**      * size of buffer read/write data      */      private static final int buffer_size = 4096;     /**      * extracts zip file specified zipfilepath directory specified      * destdirectory (will created if not exists)      * @param zipfilepath      * @param destdirectory      * @throws java.io.ioexception      */     public static void unzip(string zipfilepath, string destdirectory) throws ioexception {         file destdir = new file(destdirectory);         if (!destdir.exists()) {             destdir.mkdir();         }         zipinputstream zipin = new zipinputstream(new fileinputstream(zipfilepath));         zipentry entry = zipin.getnextentry();         // iterates on entries in zip file         while (entry != null) {             string filepath = destdirectory + file.separator + entry.getname();             if (!entry.isdirectory()) {                 // if entry file, extracts                 extractfile(zipin, filepath);             } else {                 // if entry directory, make directory                 file dir = new file(filepath);                 dir.mkdir();             }             zipin.closeentry();             entry = zipin.getnextentry();         }         zipin.close();     }      /**      * extracts file zip specified destination directory.      * path of file inside zip discarded, file      * copied directly destdirectory.      * @param zipfilepath - path , file name of zip file      * @param inzipfilepath - path , file name inside zip      * @param destdirectory - directory file zip should extracted, path part discarded.      * @throws java.io.ioexception      */     public static void extractfile(string zipfilepath, string inzipfilepath, string destdirectory) throws ioexception  {         zipinputstream zipin = new zipinputstream(new fileinputstream(zipfilepath));         zipentry entry = zipin.getnextentry();         // iterates on entries in zip file         while (entry != null) {             if (!entry.isdirectory() && inzipfilepath.equals(entry.getname())) {                 string filepath = entry.getname();                 int separatorindex = filepath.lastindexof(file.separator);                 if (separatorindex > -1)                     filepath = filepath.substring(separatorindex + 1, filepath.length());                 filepath = destdirectory + file.separator + filepath;                 extractfile(zipin, filepath);                 break;             }             zipin.closeentry();             entry = zipin.getnextentry();         }         zipin.close();     }      /**      * extracts zip entry (file entry)      * @param zipin      * @param filepath      * @throws java.io.ioexception      */     private static void extractfile(zipinputstream zipin, string filepath) throws ioexception {         bufferedoutputstream bos = new bufferedoutputstream(new fileoutputstream(filepath));         byte[] bytesin = new byte[buffer_size];         int read = 0;         while ((read = zipin.read(bytesin)) != -1) {             bos.write(bytesin, 0, read);         }         bos.close();     } } 

greg


Comments

Popular posts from this blog

image - ClassNotFoundException when add a prebuilt apk into system.img in android -

I need to import mysql 5.1 to 5.5? -

Java, Hibernate, MySQL - store UTC date-time -