I'd like to know if it possible to read the content of file from SDCARD using JAVA API in Android.
I can do it using NDK C code by
int fd = open(str, O_RDWR | O_NONBLOCK | O_DIRECT);
readCount = read(fd, pBuffer, size);
when pBuffer is aligned buffer but when I read it using directly from Java using
bytesRead = fin.read(originalBuffer)
It brings file data from cache
So after long investigation and test I understand there is no way to open file directly from Java.
The only way to do it is to use NDK and open the file using O_DIRECT parameter as explained in the body of the question.
Note that in order to compile the code -D_GNU_SOURCE directive should be used.
I think that the main problem is to get a page aligned memory buffer in Java, in order to read the file, because passing the file descriptor with O_DIRECT is still possible through JNI ( http://www.kfu.com/~nsayer/Java/jni-filedesc.html - I did use this technique to be able to open file named with UTF-8 names on the Oracle Java VM for ARM which can't do that and this works).
But to fulfill your needs we still have to provide to the system a destination buffer for the read operation which is page aligned, because without it the underlying linux system gives you an EINVAL error after the first try to read data.
I'm not aware of any Java code which can provide page aligned read buffer.
Related
I have application that i am testing and i want to push file into my android device (real device)
This is what i have try:
self.driver.push_file('/mnt/sdcard/Pictures/photo.png', r'C:\photo.png')
So this operation is pass and i can see the file on my device but its size is 1kb and when i try to open it i have this message:
Its looks like we dont support this file format
What i am doing wrong ?
Pay attention that using Appium with the Python language, when you call the self.driver.push_file() method, the second parameter is the Contents of file in base64 (and not the path of the file on your machine).
That means that you first have to read the file, convert it to base64 (and decode it using utf-8) and only then pass it to this method:
import base64
...
with open(r'C:\photo.png','rb') as file:
driver.push_file('/mnt/sdcard/Pictures/photo.png',
base64.b64encode(file.read()).decode('utf-8'))
Alternatively, you can simply use the following command (adding just source_path= to your snippet):
self.driver.push_file('/mnt/sdcard/Pictures/photo.png', source_path=r'C:\photo.png')
..since the push_file() method was recently updated to support also source path (see Pull #270):
def push_file(self, destination_path, base64data=None, source_path=None)
I would recommend on the source_path parameter of course :)
Why can't I open files in writing mode in C? I surely have all permisions, and the path is correct because it can be open in read mode. For example:
file = fopen(path, "r"); returns a correct file, but
file = fopen(path, "w"); always return null
In the Java part I can write the file, so, the solution I found is write the data in a C array and return it to Java
I see that in other questions or programs people can write in C... is a problem of new Android versions?
I have two parts to this question: 1) what is the best solution to my need, and 2) how do I do this?
1) I have a client app which sends bundles to a service app. the bundles can break the limit on bundle size, so I need to write the actual request out and read it in on the service side. Because of this, I can't write to my private internal storage. I've used these pages heavily, and haven't had luck: http://developer.android.com/guide/topics/data/data-storage.html
http://developer.android.com/training/basics/data-storage/files.html
My current understanding is that my best path is to use this to get a public dir:
File innerDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
I then add in my filename:
String fileName = String.valueOf(request.timestamp + "_avoidRoute"+count+++".ggr");
Combing these two results in the full file path:
/storage/emulated/0/Download/GroundGuidance/Route/1425579692169_avoidRoute1.ggr
Which I write to disk like this:
fos = context.openFileOutput(fullPath, Context.MODE_WORLD_WRITEABLE);
fos.write(routeString.getBytes());
fos.close();
When I try to write this to disk I get the error
java.lang.IllegalArgumentException: File /storage/emulated/0/Download/GroundGuidance/Route/1425579692169_avoidRoute1.ggr contains a path separator
Of course it does - I need it to have a path. I've searched online for solutions to this error which tell me to us FileOutputStream to write a full path. I did, but while my app doesn't error and appears to create the file, I'm also not able to view it on my phone in Windows Explorer, leading me to believe that it is creating a file with private permissions. So this brings me to my post and two questions:
1) Is there a different approach I should be trying to take to share large amounts of data between my client and service apps?
2) If not, what am I missing?
Thanks all for reading and trying to help!
Combing these two results in the full file path:
/storage/emulated/0/Download/GroundGuidance/Route/1425579692169_avoidRoute1.ggr
Which I write to disk like this:
fos = context.openFileOutput(fullPath, Context.MODE_WORLD_WRITEABLE);
This is not an appropriate use of Context's openFileOutput() method as that does not take a full path, but rather a filename within an app's private storage area.
If you are going to develop a full path yourself, as you have, then use
fos = new FileOutputStream(fullPath)
The Sharing permission setting is not applicable to the External Storage, though you will need a manifest permission to write (and implicitly read) on your creator, and the one for reading on your consumer.
Or, instead of constructing a full path, you could use your private storage with a filename and Context.MODE_WORLD_WRITEABLE (despite the being deprecated as an advisory) and pass the absolute path of the result to the other app to use with new FileInputStream(path).
Or you could use any of the other data interchange methods - content providers, local sockets, etc.
I am trying to read an image in my C++ code
LOGD("Loading image '%s' ...\n", (*inFile).c_str());;
Mat img = imread(*inFile, CV_LOAD_IMAGE_GRAYSCALE);
CV_Assert(img.data != 0);
and get the following output:
09-25 17:08:24.798: D/IRISREC(12120): Loading image '/data/data/com.example.irisrec/files/input/osoba1.jpg' ...
09-25 17:08:24.798: E/cv::error()(12120): OpenCV Error: Assertion failed (img.data != 0) in int wahet_main(int, char**), file jni/wahet.cpp, line 4208
The file exists. But strange is, that if I try to preview the image using Root File Browser it is just black. I copied the files there manually.
EDIT:
The code works fine under Windows with .png and .jpg format. I am just trying to port an existing C++ project for Iris Recognition to Android.
imread() determines the type of file based on its content not by the file extension. If the header of the file is corrupted, it makes sense that the method fails.
Here are a few things you could try:
Copy those images back to the computer and see if they can be opened by other apps. There's a chance that they are corrupted in the device;
Make sure there is a file at that location and that your user has permission to read it;
Test with types of images (jpg, png, tiff, bmp, ...);
For testing purposes it's always better to be more direct. Get rid of inFile:
Example:
Mat img = imread("/data/data/com.example.irisrec/files/input/osoba1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
if (!img.data) {
// Print error message and quit
}
When debugging, first try to get more data on the problem.
It's an unfortunate design that imread() doesn't provide any error info. The docs just say that it'll fail "because of missing file, improper permissions, unsupported or invalid format".
Use the debugger to step into the code if you can. Can you tell where it fails?
Search for known problems, stackoverflow.com/search?q=imread, e.g. imread not working in OpenCV.
Then generate as many hypotheses as you can. For each one, think of a way to test it. E.g.
The image file is malformed (as #karlphillip offered). -- See if other software can open the file.
The image file is not a supported format. -- Verify the file format on your desktop. Test that desktop OpenCV can read it. Check the docs to verify the image formats that AndroidCV can read.
The image file is not at the expected path. -- Write code to test if there's a file at that path, and verify its length.
The image file does not have read permission. -- Write code to open the file for reading.
A problem with the imread() arguments. -- Try defaulting the second argument.
I was able to solve this issue only by copying the image files in code.I stored them in my asset folder first and copied them to internal storage following this example.
If someone can explain this to me please do this.
It could be a permission issue.You would have to request the permission from Java code in your Activity class like this in Android 6.0 or above. Also make sure that in your AndroidManifest.xml, you have the the following line :
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
In your activity file add this:
if (PermissionUtils.requestPermission(
this,
HOME_SCREEN_ACTIVITY,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
Mat image = Imgcodecs.imread(filePath,Imgcodecs.IMREAD_COLOR);
}
I struggled a long time to find this and I was getting Mat object null for all the time before.
In my app I have to pass a file from assets folder to shared library.
I cannot do it with use of jni right now.
I'm using precompiled shared library in my project, in which I have hardcoded path to my file, but I'm getting error "No such file or directory".
So in my .apk file I have .so file in libs/armeabi-v7a folder and my file in /assets folder.
I have tried to do it like this:
char *cert_file = "/assets/cacert.cert";
av_strdup(cert_file);
And some other paths, but it doesn't work.
Is it possible at all?
You can simply use the AAssetManager class in C++.
Basically you need to:
During the init of you library get a pointer on: AAssetManager* assetManager
Use it to read your file:
// Open your file
AAsset* file = AAssetManager_open(assetManager, filePath, AASSET_MODE_BUFFER);
// Get the file length
size_t fileLength = AAsset_getLength(file);
// Allocate memory to read your file
char* fileContent = new char[fileLength+1];
// Read your file
AAsset_read(file, fileContent, fileLength);
// For safety you can add a 0 terminating character at the end of your file ...
fileContent[fileLength] = '\0';
// Do whatever you want with the content of the file
// Free the memoery you allocated earlier
delete [] fileContent;
You can find the official ndk documentation here.
Edit:
To get the AAssetManager object:
In a native activity, you main function as a paramater android_app* app, you just need to get it here: app->activity->assetManager
If you have a Java activity, you need so send throught JNI an instance of the java object AssetManager and then use the function AAssetManager_fromJava()
Your assets are packaged into your apk, so you can't refer to them directly during runtime like in the sample code you provided. You have 2 options here:
Process the assets as an input stream, using
Context.getAssets().open('cacert.cert')
Copy out your asset to a local file in your files dir, and then reference the filename of the copied file.
Building off of the answer by #Sistr , I used getBuffer along with AASSET_MODE_BUFFER (instead of AAsset_read).
However, I was trying to write the buffer to an ofstream. I got signal crashes using the <<operator:
ofstream myStream(<some file>, ios_base::binary);
auto buffer = AAsset_getBuffer(asset);
myStream << buffer;
I presume this is because the buffer pointer points to the asset without a NULL character at the end (I was reading a text asset; Android source code). Using the write function worked:
ofstream myStream(<some file>, ios_base::binary);
auto buffer = AAsset_getBuffer(asset);
myStream.write((char *)buffer, AAsset_getLength(asset));
This is because the <<operator does not have to call strlen to find out how many characters to write since it is explicitly given by AAsset_getLength.