DownloadManager runs MediaScanner after download but shouldn't - android

it seems that MediaScanner wants to scan files that I told it not to. Now I wonder why.
My app downloads several media files from my server and shows them later with a playlist.
For that, the app gets the media files with the Android system's DownloadManager.
Using Request.setDestinationUri(), the download will be saved to a subdirectory of getExternalCacheDir() named "pending".
When the download is finished, the Android DownloadManager sends ACTION_DOWNLOAD_COMPLETE broadcast. My app's broadcast listener will then take that finished download and move it from the "pending" folder to a different folder named "media".
All this works as intended.
However, the system log is full of messages like these:
E/BitmapFactory(23779): Unable to decode stream: java.io.FileNotFoundException: /path/to/pending/image.jpg: open failed: ENOENT (No such file or directory)
E/JHEAD (23779): can't open '/path/to/pending/image.jpg'
E/StagefrightMetadataRetriever(25911): Unable to create data source for '/path/to/pending/video.mp4'.
E/MediaScannerJNI(23779): An error occurred while scanning file '/path/to/pending/video.mp4'.
So apparently, my app tells the DownloadManager to download an image / a video to the "pending" directory. It does as it's told and sends a "I completed the download" broadcast. My app receives the broadcast and moves the complete file to the "media" directory. Some moments later, the MediaScanner (or something else) tries to scan the completed file in the "pending" folder and barfs into the system log.
Now I'm wondering: Why is MediaScanner trying to read these files, anyway?
According to the Android API doc for setDestinationUri: "The downloaded file is not scanned by MediaScanner. But it can be made scannable by calling allowScanningByMediaScanner()." I don't call that method, so the downloaded file should not be scanned.
Next, I tried to put an empty .ignore in the app's cache directory and reminded the MediaScanner of the .ignore-file's existence through ACTION_MEDIA_SCANNER_SCAN_FILE, but the error messages remain.
To add to the mystique, the files do not show up in the system's gallery or video apps, so yes, the media scanner ignores them. But still: Why does it try to read them when it doesn't have to? Is it the MediaScanner at all or is it some other system service?

Next, I tried to put an empty .ignore in the app's cache directory and reminded the MediaScanner of the .ignore-file's existence through ACTION_MEDIA_SCANNER_SCAN_FILE, but the error messages remain.
The magic file to stop the Mediascanner is not ".ignore" but ".nomedia".
From MediaScanner.java:
File file = new File(path.substring(0, slashIndex) + ".nomedia");
if (file.exists()) {
// we have a .nomedia in one of the parent directories
return true;
}
And the reason why it did not appear in the system's gallery or video apps is maybe, because the scan crashed (as indicated in the log).
However, I have bad feelings about the media scanner too. For example, why doesn't it stop scanning in a more straight way. For example, in MediaScanner.java, instead of
public void scanDirectories(String[] directories, String volumeName) {
[...]
for (int i = 0; i < directories.length; i++) {
processDirectory(directories[i], mClient);
}
it could be
public void scanDirectories(String[] directories, String volumeName) {
[...]
for (int i = 0; i < directories.length; i++) {
if (! isNoMediaPath(directories[i]))
processDirectory(directories[i], mClient);
}
But instead, it goes forth and back between java code and cpp code again and again. What is the reason for that?

It really looks like the ".nomedia" won't give even half the effect some might expect. After further investigating in MediaScanner.java I would say, that this file does not stop the MediaScanner from scanning the whole tree at all. It still adds entries to MediaStore for each file, never mind of noMedia being set or not.
It just marks those entries in the MediaStore as "must not show up". On each file it does an "beginFile" and an "endFile". In the endFile it always does a mMediaInserter.insert, the one way or the other.
What bothers me so much about this is the fact, that it scans through all the files in e.g. a mounted stick, hereby taking the risk of trapping into a virus (specially designed for that scan process) and no .nomedia file can stop it from doing so.

Related

Downloaded files get deleted automatically

In my app, there are a few files that users can download. The files get downloaded via the android download manager. But, since a few weeks now, hundreds of users have been complaining that their files automatically keep deleting every 8-12 days, without them even uninstalling the app. (There might be many more users who haven't bothered to complain about the same.)
Now, there could be a number of user-specific reasons why that would happen on a few devices. But considering the huge number of users, it seems that I might have been doing something wrong.
Why would the system/download manager delete the files automatically? Is there a way to inform the system or the download manager to not delete certain files? Or should I just settle with renaming the files after downloading, so as to unlink them from the download manager, and hope that the problem gets solved with just that?
Edit:
Here's the code that I use to download the files:
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(trackLink));
request.setTitle(trackTitle);
request.setDestinationInExternalPublicDir("Tracks", trackTitle + ".mp3");
request.setVisibleInDownloadsUi(false);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
I also came across this issue. Looking at the source for DownloadIdleService it appears that if the download are set to not be visible in the UI they're deleted after 7 days as they're considered "stale".
Here's the javadoc fromDownloadIdleService:
/**
* Remove stale downloads that third-party apps probably forgot about. We
* only consider non-visible downloads that haven't been touched in over a
* week.
*/
https://android.googlesource.com/platform/packages/providers/DownloadProvider/+/master/src/com/android/providers/downloads/DownloadIdleService.java#110
There is a bigger problem starting in Android Q where value set by setVisibleInDownloadsUi is ignored and it is false by default. The files will be deleted after a week. You can test it by: download file, change date in calendar by 2 months for example, call this in terminal:
adb shell cmd jobscheduler run -f com.android.providers.downloads -100
It starts job from DownloadIdleService where old and forbidden files are deleted. You can see in logcat (set no filter) that DownloadManager try to delete your files.
To prevent from that you can:
download files to Environment#getExternalStoragePublicDirectory(String) with Environment#DIRECTORY_DOWNLOADS) - this is the only path starting in Android Q where downloads are visible and should not be deleted
change file name after downloading (in DownloadedReceiver for example)
It was too big for a comment to I'm just putting it as an answer.
I don't have any clue yet why the files are missing or getting deleted over a period of time - which a bit strange. But I had to do something similar and never experienced something like this. So I though I might share what I have done in that case.
I preferred to keep the downloaded files in the internal storage which the application holds rather than keeping it in the external storage. For example, the file path where I used to save the downloaded files is something like...
/data/data/" + "com.example.myapplication" + "/musicfiles/
So what I did right after downloading is telling the media scanner about the new file so that it is immediately available to the user. So as I was downloading the file using an AsyncTask I had to run the scanner in the onPostExecute method like this after a successful download.
protected void onPostExecute() {
if(downloadCompletedSuccessfully) {
MediaScannerConnection.scanFile(context,
new String[]{file.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
}
}
So in your case, I think its not might be the issue that the Download button is not visible due to the unavailability of the file downloaded. It might happen if the media scanner fails to find the file in the file system as well. So you might consider giving it a try.
I don't know if clean master or some softwares like that cleans junk files every now and then if they are in external storage. You might consider investigate those as well.

Android - how to read screenshot without gallery intent

I am interested in writing a screenshot app and want to learn the technique from this app.
After user takes a screenshot using power and volume buttons, the app opens up the screenshot without the user needing to pick an image from the gallery. I want to do something similar (save the user the step to navigate the gallery to get screenshots).
Does anyone how can an app read a screenshot as this app did ? (In the app's demo video this step is shown at time 0:30)
Edit
I've tried testing it on my nexus 5. I can see the screen shots are in folder /sdcard/Pictures/Screenshots. The directory permissions are:
drwxrwx--x root sdcard_rw 2015-08-30 01:42 Screenshots
I gave my app storage permissions. I used the following code in a service, but it didn't work:
FileObserver fileObserver = new FileObserver("/sdcard/Pictures/Screenshots") {
#Override
public void onEvent(int event, String path) {
Log.d("Test", "FileObserver event");
}
};
fileObserver.startWatching();
It should be running as a service in the background, which register a FileObserver, and perform action upon file added.
Or the service simply check the folder manually.
Edit:
Warning: If a FileObserver is garbage collected, it will stop sending events. To ensure you keep receiving events, you must keep a reference to the FileObserver instance from some other live object.
It means that local variable is definitely not working, put it into a field AND make sure your class will not be garbage collected, e.g. even Activity can be killed, and garbage collected afterwards.

Deleting files from applications data folder

I have a tablet where I need to execute a service from a binary file which will continually receive data from a chip and store the various data files in the "/data/data/com.example.binary/received/" folder. I also have a FileObserver set up on that folder to perform an action on the files that are created and once the Close event is given by the FileObserver I an trying to delete the file. This is what I have an issue with. I can delete the files if it is in the "/data/data/com.example.binary/" folder but if its in the "/data/data/com.example.binary/received/" folder I cant delete it. Seems to be some android permission issue. Is there any way around this?.
So far I have tried and failed by
1) trying to execute the "rm /data/data/com.example.binary/received/file1.xy"
2) trying file.delete()
FYI - I have the Read and Write Permissions. And all file handles to the file are closed, that's when the file's Close event is fired by the FileObserver.
I can delete the files from ADB using the "rm file1.xy"
And the binary file is from our client and i cannot change the location where it creates the output files.xy . . it will always be in the received folder. So any suggestions?
PS: /data/data folder is the location where all the installed apps get stored in my tablet.

Not able to access a file on reboot in Android

I am developing an app where I load my settings of the app by reading an xml file stored in the external storage. But whenever I reboot the system, I am not able to access the xml for the first time, my app crashes because of this and restarts. from then it can access the xml file. Any clue on whats happening, or what I am doing wrong?
My sample code to open the file:
filepath= Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(filepath+"/xy.xml");
Please check if the External Storage is available. Use a broadcast receiver, listen for Intent.ACTION_MEDIA_MOUNTED : Broadcast Action: External media is present and mounted at its mount point.

Android: Problem with Media Scanner not Running

I have an app that user's can draw with, and then 'export' that drawing as a .png file to external storage, if present. Generating the PNG, copying the file to external all work like a charm, but a rather unique problem happens; after the export, if the user navigates to the image via My files (Samsung Tab running 2.2 in this case), they can see the .png file, but when they open it, the screen is black for about 10 seconds... then they see the image, Additionally, the images don't show up in the user's 'Gallery' app either.
Now, if the user connects the device to the computer via USB, or reboots the device, they can access the images no problem from My files, and they appear in 'Gallery' from that point forward, but again, any newly esported files experience the same problems until they cycle/connect the device again.
My thinking was that this had to be related to the Media Scanner (at least in the case of the 'Gallery' problem, it most certainly is).
So, as I am targetting Api 8+, I am trying to use the static MediaScannerConnection.scanFile() method to have the OS re-scan and add my images into the Gallery, etc. Also hoping this solves the issue of the strange delay in opening the images. Here is my code:
MediaScannerConnection.scanFile(
context,
new String[] { "/mnt/sdcard/MyApp" },
null,
null
);
LogCat gives me the following entries when I export an image, and thus run the above call:
DEBUG/MediaScannerService(2567): IMediaScannerService.scanFile: /mnt/sdcard/MyApp mimeType: null
DEBUG/MediaScannerService(2567): onStartCommand : intent - Intent { cmp=com.android.providers.media/.MediaScannerService (has extras) }
DEBUG/MediaScannerService(2567): onStartCommand : flags [0], startId [1]
DEBUG/MediaScannerService(2567): ServiceHandler:handleMessage volume[null], filePath[/mnt/sdcard/MyApp]
DEBUG/MediaProvider(2567): getSdSerial() sd state = removed
INFO/Database(2567): sqlite returned: error code = 17, msg = prepared statement aborts at 43: [SELECT DISTINCT sd_serial FROM images WHERE sd_serial LIKE 'external_0x%']
ERROR/MediaProvider(2567): removeMediaDBData called
DEBUG/MediaScanner(2567): prescan enter: path - /mnt/sdcard/MyApp
DEBUG/MediaScanner(2567): prescan return
So, it looks like the MediaScanner is getting the correct location, but is failing to find the SD card, which is correct, and failing. The Samsung Tab has built-in non-SD external storage, which Android gives access to via Environment.getExternalStorageDirectory(). How do I tell it to scan the non-SD storage?
Any ideas how to proceed?
Paul
Found the solution here, which involves sending a broadcast request to the media scanner via an Intent:
How to update the Android media database
Never did figure out the issue with MediaScannerConnection.scanFile.
Whenever you add a file, let MediaStore Content Provider knows about it using
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(imageAddedOrDeleted)));
Main advantage: work with any mime type supported by MediaStore
For deletion: just use getContentResolver().delete(uri, null, null)

Categories

Resources