Check existance of a file to unlock app - android

I wish to create a file on my device somewhere (I don't mind where or what the file contains, my only concern is the name of the file), then have my app look for the existence of that file. The idea is to have a mechanism to secretly unlock some features of my app to anyone I tell about the file.
My only problem is that I have never written any code to read/write files outside of my own app's space before, so I'm not sure how to do it. Can someone give me some pointers.

You can use File.exists() method to determine whether the file exists or not.
File testfile = new File(Environment.getExternalStorageDirectory()
+ File.separator ,
"myfile");
if (testfile.exists()) {
//file exists. enable your features
}

Related

Deleting a Folder/Files in Android 11+

I have looked at the various different versions of this question hear, and have tried some to no success.
In the code its downloading files with
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url.toString()));
request.setDestinationInExternalPublicDir(dirtype, dirPath);
long refid = mDownloadManager.enqueue(request);
with
dirType = Environment.DIRECTORY_DOWNLOADS
dirPath = MyAppCommon.MYAPP_FOLDER + "/attachments/" + attachment.getFile().toLowerCase()
Which puts them in storage/emulated/0/Download/Myapp/attachments/myfile.pdf
I need to delete the folder and all files within when my app starts up, but it will not let me delete. When I list files I only media files (which I understand is part of permission changes)
I have tried the MANAGE_EXETERAL_STORAGE permission and allowing access to all files, and that works but also requires that the app is approved to use it in the playstore, which it probably will not be.
I saw the Storage Access Framework and that seemed to prompt the user about the deletion of a file, but I want this to happen in the background as my app is the one that downloaded and stored these files.

How to handle SAF when I can only handle File or file-path?

Background
Up until Android Q, if we wanted to get information about an APK file, we could use WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE to get access to the storage, and then use PackageManager.getPackageArchiveInfo function on the file-path.
Similar cases exist, such as using ZipFile class on a compressed file, and probably countless framework APIs and third party libraries.
The problem
Google announced a huge amount of restrictions recently on Android Q.
One of them is called Scoped Storage, which ruins storage permission when it comes to accessing all files the device has. It lets you either handle media files, or use the very restricted Storage-Access-Framework (SAF) which can't allow apps to reach and use files using File API and file-paths.
When Android Q Beta 2 was published, it broke a lot of apps because of it, including of Google. The reason was that it was turned on by default, affecting all apps, whether they target Android Q or not.
The reason is that many apps, SDKs and Android framework itself - all use File API quite often. On many cases, they also don't support InputStream or SAF-related solutions. An example for this is exactly the APK parsing example I wrote about (PackageManager.getPackageArchiveInfo).
On Q beta 3, however, things changed a bit, so that app that target Q will have the scoped storage, and there is a flag to disable it and still use the normal storage permissions and File API as usual. Sadly the flag is only temporary (read here), so it's delaying the inevitable .
What I've tried
I've tried and found the next things:
Using the storage permission indeed didn't let me read any file that's not media file (I wanted to find APK files). It's as if the files don't exist.
Using SAF, I could find the APK file, and with some workaround to find its real path (link here), I've noticed that File API can tell me that indeed the file exist, but it couldn't get its size, and the framework failed to use its path using getPackageArchiveInfo . Wrote about this here
I tried to make a symlink to the file (link here), and then read from the symlink. It didn't help.
For the case of parsing APK files, I tried to search for alternative solutions. I've found 2 github repositories that handle the APK using a File class (here and here), and one that uses InputStream instead ( here). Sadly the one that uses InputStream is very old, missing various features (such as getting the app's name and icon) and isn't going to be updated anytime soon. Besides, having a library requires maintenance to keep up with future versions of Android, otherwise it might have issues in the future, or even crash.
The questions
Generally, is there a way to still use File API when using SAF ? I'm not talking about root solutions or just copying the file to somewhere else. I'm talking about a more solid solution.
For the case of APK parsing, is there a way to overcome this issue that the framework only provides file-path as a parameter? Any workaround or a way to use InputStream perhaps?
How to handle SAF when I can only handle File or file-path? It is possible, even if you can send only a Java File object, or path string to a library function which you cannot modify:
First, obtain a Uri to a file you need to handle (in String form it would be like "content://..."), then:
try {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r"); // may get FileNotFoundException here
// Obtain file descriptor:
int fd = parcelFileDescriptor.getFd(); // or detachFd() if we want to close file in native code
String linkFileName = "/proc/self/fd/" + fd;
// Call library function with path/file string:
someFunc(/*file name*/ linkFileName);
// or with File parameter
otherFunc(new File(linkFileName));
// Finally, if you did not call detachFd() to obtain the file descriptor, call:
parcelFileDescriptor.close();
// Otherwise your library function should close file/stream...
} catch (FileNotFoundException fnf) {
fnf.printStackTrace(); // or whatever
}
Posting another answer just to have more room and let me insert code snipes. Given the file descriptor as explained in my previous answer, I tried using net.dongliu:apk-parser package mentioned by #androiddeveloper in the original question, as follows (Lt.d is my shorthand to using Log.d(SOME_TAG, string...)):
String linkFileName = "/proc/self/fd/" + fd;
try (ApkFile apkFile = new ApkFile(new File(linkFileName))) {
ApkMeta apkMeta = apkFile.getApkMeta();
Lt.d("ApkFile Label: ", apkMeta.getLabel());
Lt.d("ApkFile pkg name: ", apkMeta.getPackageName());
Lt.d("ApkFile version code: ", apkMeta.getVersionCode());
String iconStr = apkMeta.getIcon();
Lt.d("ApkFile icon str: ", iconStr);
for (UseFeature feature : apkMeta.getUsesFeatures()) {
Lt.d(feature.getName());
}
}
catch (Exception ex) {
Lt.e("Exception in ApkFile code: ", ex);
ex.printStackTrace();
}
}
It gives me the correct app label, for the icon it gives me only a string to the resource directory (like "res/drawable-mdpi-v4/fex.png"), so again raw ZIP reading functions would have to be applied to read the actual icon bits. Specifically I was testing ES File Explorer Pro APK (bought this product and saved APK for my own backup, got the following output:
I/StorageTest: ApkFile Label: ES File Explorer Pro
I/StorageTest: ApkFile pkg name: com.estrongs.android.pop.pro
I/StorageTest: ApkFile version code: 1010
I/StorageTest: ApkFile icon str: res/drawable-mdpi-v4/fex.png
I/StorageTest: android.hardware.bluetooth
I/StorageTest: android.hardware.touchscreen
I/StorageTest: android.hardware.wifi
I/StorageTest: android.software.leanback
I/StorageTest: android.hardware.screen.portrait

how do I open a newly written file from application storage directory in as3 / flex mobile?

Im working on an app (flex 4.12 sdk, using flashbuilder 4.5, creating an app for ios and android, testing on an android htc one primarily)... and am using the camera to capture a file... Im then saving that image to the application storage directory, and I want to open the image in the default web browser or trigger a native dialog (android users) to choose the web browser of their choice... how it opens isnt really important right now -- Im mainly trying to just 'access' it with the device and 'load' it outside my air app...
heres the code I have:
var fs2 : FileStream = new FileStream();
fs2.addEventListener(Event.CLOSE, fileCompleteHandler);
var targetFile : File = File.applicationStorageDirectory.resolvePath("test.jpg");
fs2.openAsync(targetFile, FileMode.WRITE);
fs2.writeBytes(myBMDByteArray,0,myBMDByteArray.length);
fs2.close();
and for the event listener that detects the close of the newly created file:
function fileCompleteHandler(e:Event):void {
trace('File saved.');
trace('exists? ' + targetFile.exists);
trace('the url: ' + targetFile.url);
trace('path: ' + targetFile.nativePath);
navigateToURL(new URLRequest(targetFile.url));
}
I get the following info back from this listener
File saved.
exists? true
the url: app-storage:/test.jpg
path: /data/data/air.com.xxxxx.apptesting.debug/com.xxxxx.apptesting.debug/Local Store/test.jpg
... and problem is that navigateToURL cant access the location where the file is stored (the protocol shows in browser as file:///data/data/air.com/xxx... )
how can I use navigateToURL to get access to this newly created file in the web browser or whatever native application the device associates with the file (its a .JPG file)? I also have had success in adding the newly created image to the camera roll but couldnt figure out how to then open that newly saved image in the native camera roll or whatever app the device chooses or presents to the user for the .jpg format.
I can show the user the image INSIDE my app by referencing the bitmap data fine, I just want to give the user access to the physical file that Im creating on their device.
I even have had success in posting (via urlLoader) the bitmap data as base64 encoding and then creating a file on the server side and loading that url but the encoding and trip to and from the server to give the user the image adds a lot of overhead and it takes a little too long and I'd like to avoid that elongated process.
Thanks for any help anyone can provide - let me know if I need to be more specific in any of this.
Solved the issue... I was able to store / write my file in the documentsDirectory using:
var targetFile : File = File.documentsDirectory.resolvePath('test.jpg');
and then
navigateToURL(new URLRequest(targetFile.url));
And this works fine now. Hopefully it helps someone else! Seems that the storage directory SHOULD work but up until now I've only written to and read files stored there... maybe to open the files one HAS to copy it to a 'safe' location in the filesystem (i.e. sd card?)... will move on to test in ios Now - hope all works well in that OS. Thanks all who chimed in on this.
My first hunch is that you need to specify the proper user-permissions in your application descriptor so you can use the openWith functionality with content from your application.
Remember that you need to specify this for IOS and Android specifically.
On your application.xml you need this permissions set inside android > manifestAdditions > manifest:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
With this permissions you can save files to applicationStorageDirectory:
const FILE_LOADER:URLLoader = new URLLoader();
FILE_LOADER.addEventListener(Event.COMPLETE, onTempFileComplete);
FILE_LOADER.dataFormat = URLLoaderDataFormat.BINARY;
FILE_LOADER.load(new URLRequest(BASE_URL + filePath));
The applicationStorageDirectory can only be accessed by the application it belongs too when using Android or iOS. navigateToURL() hands over your request to the default browser, which cannot access said directory.
documentsDirectory is a public directory in Android, but not in iOS. So it cannot be used for both platforms. Unfortunately none of the pre-compiled file paths File has point to a public directory in iOS. You can find a table explaining all the pre-compiled paths here

Read file made by another app

An application makes a file in data/data/package.name/file.txt. Is there a way for me to open that file from another application (with different package)?
I tried reading the file the standard way (FileInputStream), but I get EACCES error (no permission).
Can I maybe copy the file (withouth opening it) to root directory and then open it?
EDIT: What about changing permission of the file withouth root access?
By default, every Android application is able to access the files belonging to its own package only. This means your applications can only read files underneath the package with your package.name. If you try to read other files, you will get an access denied exception. This is Android's security model.
One exception is when another application stored its files using MODE_WORLD_READABLE or MODE_WORLD_WRITABLE flags, then every other application will be able to access (read or read & write) them. This option is deprecated in API level 17 and higher though.
Your code is probably correct, you just need to add an extra / (or File.separator) before the file name as follows:
//Set desired file path. For example:-
File newxmlfile = new File(Environment.getExternalStorageDirectory() + "/new.xml");
try {
//Read file
} catch (Exception e) {
Log.e("IOException", "exception while reading file",
e);
}
Also the other applications must have set its data as readable with the appropriate permission.

Android Shared Internal Storage

Is there such thing on any Android based device as shared internal storage? I know you can use the SDCard as a shared file location between applications, but for a project I'm working on we dont want the ability for a SD Card to go missing (sensitive material).
The problem is as follows. App1 allows a user to browse (for example) some word documents and download them to the proposed shared storage, the user can then pass this file to Documents 2 Go editing them and saving the change. App 1 then allows an upload of the changed file.
I don't fancy building a document editor word/excel directly into app, unless thats easy?
EDIT:
The second app is "Documents 2 Go" I won't be able to edit its AndroidManifest
I faced a similar situation now for txt files and did this.
File downloadedFile= new File( context.getFilesDir(), "simple.txt" );
downloadedFile.setReadable( true, false );
downloadedFile.setWritable( true, false ); //set read/write for others
Uri downloadFileUri = Uri.fromFile( downloadedFile );
Intent intentToEditFile = new Intent( Intent.ACTION_EDIT );
intentToEditFile.setDataAndType( downloadFileUri, "text/plain" );
context.startActivity( intentToEditFile );
Now the 'Document 2 Go' editor will be launched to edit the file and
will be able to edit simple.txt
Note 1: Uri should be created from same file object that was set with
setReadable()/setWritable.
Note 2: Read/Write permission for other users might not be reflected in file
system. Some times I cannot see rw-rw-rw- in adb shell
I believe ContentProviders is your solution. Its the Android recommended method for sharing application data between different apps.
https://stackoverflow.com/a/6708681/804447
Sharing data between apps is what ContentProviders are for. Assuming that you know how to write a ContentProvider and access it, you can access files via ParcelFileDescriptor, which includes constants for the mode in which you create the files.
What you need now is to limit access so that not everybody can read the files through the content provider, and you do that via android permissions. In the manifest of one your apps, the one that will host the files and the content provider, write something like this:
<permission android:name="com.example.android.provider.ACCESS" android:protectionLevel="signature"/>
and in both apps add this:
<uses-permission android:name="com.example.android.provider.ACCESS" />
by using protectionLevel="signature", only apps signed by you can access your content provider, and thus your files.

Categories

Resources