ClassLoading under android ics - android

Are there any restrictions to ClassLoaders under android ICS?
I´m not getting a single example working at all, and I think I´m doing everything right.
For example, this code
DexFile df = new DexFile("/sdcard/test.apk");
ClassLoader cl = context.getClassLoader();
Class clazz = df.loadClass("com/test/LibraryClass", cl);
Produces:
E/dalvikvm﹕ Dex cache directory isn't writable: /data/dalvik-cache
I/dalvikvm﹕ Unable to open or create cache for /sdcard/test.apk (/data/dalvik-cache/sdcard#test.apk#classes.dex)
W/System.err﹕ java.io.IOException: unable to open DEX file
The location is correct, the dex file exists.

/data/dalivk-cache has permission of 775. It is not writable directory for others. It is done so for security purpose, so that applications don't modify other applications. It is meant for system installer which unpacks and unzip the dex file contained in apk.
For applications to load external classes, use DexClassLoader
DexClassLoader loader = new DexClassLoader("/sdcard/com.example.test.apk", getApplicationInfo().dataDir, null, getClassLoader());
try {
loader.loadClass("com.example.test.MainActivity");
} catch (ClassNotFoundException e) {
Log.e(TAG, "Could not load class");
e.printStackTrace();
}

If you are reading from the internal storage area, then for API 18 and earlier, you'll need this permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
You don't need the above permission starting from API 19 onwards. See here for more info.
Also, instead of hardcoding the path (i.e. "/sdcard/") in your class, I would use this instead:
String path = Environment.getExternalStorageDirectory().getAbsolutePath();

Related

How to create a folder under /Android/obb?

I'm trying to use expansion files and I found an issue that I'm not able to resolve.
It seems that you can access the /Android/obb folder and eventually delete the /Android/obb/my.package.name directory (tried with some file manager), but I cannot handle this situation whithin the app.
If I just let the Google Downloader library try to download the expansion file it will hang and error (for the missing folder), but it will start if I recreate the folder.
The strange thing is that I'm able to create the folder from other apps (the same file manager that I've used to see the directory) but not from mine!
I tried with
File f = new File(Environment.getExternalStorageDirectory(), "Android/obb/my.package.name");
f.mkdirs();
but it doesn't work.
I've the permissions in the manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
and I'm targeting api 23 and compiling with the 25.
OP dropped the Target SDK to 22 and was able to avoid Dangerous permission work flow which was causing the issue.
I had this same problem, but lowering the Target SDK was not an option.
The Context::getObbDir() method allows access to the expansion folder without the usual security rules. I found that, if the folder doesn't exist, getObbDir() creates it too (You can also double check and create it manually with mkdir()).
Excerpt from the documentation linked above:
Return the primary shared/external storage directory where this application's OBB files (if there are any) can be found. Note if the application does not have any OBB files, this directory may not exist.
This is like getFilesDir() in that these files will be deleted when the application is uninstalled, however there are some important differences:
... Starting in Build.VERSION_CODES.KITKAT, no permissions are required to read or write to the path that this method returns. ...
Starting from Build.VERSION_CODES.N, Manifest.permission.READ_EXTERNAL_STORAGE permission is not required, so don’t ask for this permission at runtime. ...
So you can do something like:
File obbDir = getObbDir();
if (null == obbDir) {
// Storage is not available
} else if (!obbDir.exists() && !obbDir.mkdir()) {
// Failed to create directory. Shouldn't happen but you never know.
}
NOTE: You may need the read/write permissions to access the expansion files within the folder.

What are differences between DexClassLoader and PathClassLoader?

I put a .jar file containing .dex file to directory "/sdcard", then I try to load the class in the .jar file using DexClassLoader and PathClassLoader respectively. Both of them can load the class successfully. What are differences between them?
Here is my code:
String dexPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.jar";
PathClassLoader classLoader1 = new PathClassLoader(dexPath, getClassLoader());
DexClassLoader classLoader2 = new DexClassLoader(dexPath, getDir("dex", 0).getAbsolutePath(), null, getClassLoader());
try {
Class clazz1 = classLoader1.loadClass("com.focans.loader.Peter");
Class clazz2 = classLoader2.loadClass("com.focans.loader.Peter");
} catch (Exception e) {
e.printStackTrace();
}
You should read official Guideline about
DexClassLoader
A class loader that loads classes from .jar and .apk files containing
a classes.dex entry. This can be used to execute code not installed as
part of an application.
PathClassLoader
Provides a simple ClassLoader implementation that operates on a list
of files and directories in the local file system, but does not
attempt to load classes from the network. Android uses this class for
its system class loader and for its application class loader(s).
DexClassLoader is instantiated to load the library from the extracted secondary dex file.
PathClassLoader Used to load classes within ant with a different classpath from that used to start ant. Note that it is possible to force a class into this loader even when that class is on the system classpath by using the forceLoadClass method. Any subsequent classes loaded by that class will then use this loader rather than the system class loader.
For Android 8.1 (API 27) and up, DexClassLoader and PathClassLoader are essentially identical. They both extend BaseDexClassLoader, and immediately call super() when constructed. There are no implementation differences or side effects (at least in the AOSP versions I've referenced here).
In prior versions (8.0 and earlier) DexClassLoader accepted an argument for String optimizedDirectory, which allowed the caller to specify the directory to store optimized Dex code (ODEX files), for the Dex that was loaded by the class loader. This argument still exists in newer versions of Android, but it has no effect.

Android mkdir() returns false, doesn't create folder

This is driving me crazy. I have tried all kinds of syntaxes but both mkdir() and mkdirs() return false.
My code:
String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
File folder = new File(extStorageDirectory, "myportal");
boolean bool = folder.mkdir();
File pdfFile = new File(folder, fileName);
try{
pdfFile.createNewFile();
}catch (IOException e){
e.printStackTrace();
}
FileDownloader.downloadFile(fileUrl, pdfFile);
I was getting and IOException: No such file or directory when trying to create the file. The logcat part showed me that no directory was created:
Log.d("BG", "URL: " + fileUrl);
Log.d("BG", "pdfFile: " + pdfFile);
Log.d("BG", "Ext Storage: " + extStorageDirectory);
Log.d("BG", "Ext storage state: " + Environment.getExternalStorageState().toString());
Log.d("BG", "Mkdir return: " + bool);
Log.d("BG", "IsDirectory: " + folder.isDirectory());
And what is printed:
05-26 22:43:03.797 19364-30646/com.kristmiha.myportal2 D/BG: URL: http://192.168.100.65:80/myportal/upload/orari.pdf
05-26 22:43:03.798 19364-30646/com.kristmiha.myportal2 D/BG: pdfFile: /storage/emulated/0/myportal/orari.pdf
05-26 22:43:03.798 19364-30646/com.kristmiha.myportal2 D/BG: Ext Storage: /storage/emulated/0
05-26 22:43:03.804 19364-30646/com.kristmiha.myportal2 D/BG: Ext storage state: mounted
05-26 22:43:03.805 19364-30646/com.kristmiha.myportal2 D/BG: Mkdir return: false
05-26 22:43:03.805 19364-30646/com.kristmiha.myportal2 D/BG: IsDirectory: false
I've double checked permissions and I've put them in the right place. I think I read somewhere that after KitKat we are not allowed to write in the external storage, but have found no solution yet.
Applicable only if your targetSdkVersion 29
Environment.getExternalStorageDirectory() is deprecated in API level 29.
To get the same functionality use this line
File mediaStorageDir = context.getExternalFilesDir(null);
If you have checked all the possible error then try this fix.
Creating external storage directories requires the WRITE_EXTERNAL_STORAGE permission; without this permission, attempts to write external storage will fail. Also, not all directories are necessarily writable; use getExternalStoragePublicDirectory() to get a directory that has shared write access (other external directories may be read-only). However, if the purpose of writing to external storage is to share the file with other applications, you should strongly consider using the FileProvider API, instead (see also Setting Up File Sharing); with this strategy, your app stores files in its own, internal app-specific directories, but then enables selective sharing of these files to other apps through a content provider. This strategy provides greater security for the files and also makes it possible for you to provide greater access control over reading/writing of the files.
Since you say that you already have the required permission*, most likely where you are getting things wrong is in the call to toString(). There is no guarantee that the toString() method on a file returns its full path. Use getPath() or getAbsolutePath() when concatenating these. It is also advisable, when choosing to write to external storage, that you first check its state; the external storage can in some cases be ejected/unmounted and not available.
*You should verify this with ContextCompat.checkSelfPermission(). You should add a call to requestPermissions() if the permissions are not present.
try this:
String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
File folder = new File(extStorageDirectory, "pdf");
folder.mkdir();
in manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com....">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
also if you are testing on android 6.0 + api 23 there is changes in rights, you must request some of them on runtime, manifest declaration is ignored )
One more possible reason: Environment.getExternalStorageDirectory() is deprecated in API level 29
if you already allowed R/W permission(Runtime Permission too) and still doesn't work add this below mentioned line in your AndroidManifest.xml
<application ........ ........ android:requestLegacyExternalStorage="true"> Note: this must required if you'r targeting Android 10+
If you're target Android api 29 add this line in the AndroidManifest
<application android:requestLegacyExternalStorage="true">
Also Environment.getExternalStorageDirectory() is deprecated in api 29 use this instead
File mediaStorageDir = context.getExternalFilesDir(null);
I had same issue, tried many things but finally fixed with this:
add android:requestLegacyExternalStorage="true" into your Manifest file inside Application tag.
<application
.
.
.
android:requestLegacyExternalStorage="true">
just remove .toString() like:
String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
to
String extStorageDirectory = Environment.getExternalStorageDirectory();

Can't locate application data on Android phone

I have spent many hours trying to find/create files for an app I am writing. When I pull the application directory name I get: /data/data/com.example.android.[myapp]/files. I am using File(getFilesDir():
File fileDir = new File(getFilesDir() + File.separator);
Log.i(TAG, "File directory: "+fileDir);
When I try to find this path I find many application folders here: Android/data/com but no /data/data folder under Android. There are many other application folders there but not mine. I see the same results whether I use Android Files app or Windows Explorer over USB. I've also tried to look using Eclipse DDMS tab. I see a data folder with a (+) to the left but when I click, it does not expand.
I have also tried creating the directory and file manually with Windows explorer and my app still can't find neither the Android/data/com.example... nor the Android/data/data/com.example... paths.
Also puzzling to me is when the app creates the path and file and write to it (using MODE_WORLD_WRITEABLE) I get no exceptions thrown but then I am unable to read it back or see it with either of the tools mentioned above. I have set the manifest permissions to WRITE_INTERNAL_STORAGE and WRITE_INTERNAL_STORAGE for the app.
Obviously, I am making a very basic mistake.
I am on Android 4.1.2 (API 16).
Sincerely,
ScratchingMyHead
To get the path of my application directory, Try this code sample
PackageManager m = getPackageManager();
String s = getPackageName();
try {
PackageInfo p = m.getPackageInfo(s, 0);
s = p.applicationInfo.dataDir;
} catch (NameNotFoundException e) {
Log.w("yourtag", "Error Package name not found ", e);
}
When I try to find this path I find many application folders here: Android/data/com but no /data/data folder under Android.
That is because you are looking on external storage, not internal storage where your files are. Use DDMS on an emulator to examine internal storage.
I've also tried to look using Eclipse DDMS tab. I see a data folder with a (+) to the left but when I click, it does not expand.
That would sound like what you will get when testing on hardware, as neither you nor DDMS have access to the contents of /data on production hardware.

Android exception 'open failed: EACCES (Permission denied)' - not due to the SD card

I am having a problem copying files (.so binaries) from my assets directory to the app installation "/data/data/name.ofmy.app/lib" directory.
When I try to I get the following error:
07-09 11:00:48.902: W/System.err(25122): java.io.FileNotFoundException: /data/data/name.ofmy.app/lib/libOlsrdPudWireFormat.so: open failed: EACCES (Permission denied)
This happens when the following code is being ececuted:
if ((so = new File(dir, fileName)).exists()) {
/*
* Check to see if this timestamp pre-dates the current package
*/
if ((pkg = new File(ctx.getPackageResourcePath())).exists()) {
if (pkg.lastModified() < so.lastModified()) {
Log.v(TAG, "extractFile: File up to date");
return;
}
}
Log.v(TAG, "extractFile: library present but out of date");
so.delete();
}
Log.v(TAG, "extractFile: copying library");
InputStream in = ctx.getAssets().open(name);
FileOutputStream out = new FileOutputStream(so);
It breaks right at InputStream in = ctx.getAssets().open(name); when there is not an already existing file with that name...
I have been reading similar questions here on Stack Overflow, but unfortunately that did not help. Most are copying to the SD card, but I am not.
Also the strange part is that a previous directory (/bin instead of /lib), which is also supplied within my assets, does get copied without any problem in the same /dat/data... directory!
I tried adding the <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> permission to no avail.
I even tried adding Runtime.getRuntime().exec("su"); just before I try to copy anything, also to no avail.
It's a rooted phone BTW!
I also double checked if the file is really there in the assets, and it is!
Just for the sake of trying I changed the directory name from lib to libs (because as mentioned in my question, I also copied another directory named /bin without any issues) and guess what: now it works!
It seems you cannot use the /lib directoryname whilst copying. I have no idea why, though.
Definitely you forgot to put in the manifest:
<manifest
.
.
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

Categories

Resources