I want my app to automatically load data from a file at startup & then display a summary of this data for the user. I thought this would be simple, but...
I almost had it working, but found out that it would be tricky putting the data file on the emulated Android provided by Android Studio. So I put a basic version of the file in the package as an asset. My code checked for the file, and if not found, copies the file from assets to the phone storage. So far so good, but then I realized that this didn't really meet the requirements, because the user couldn't customize the file (through another app or by putting a new version of the file on her phone).
I found I needed to use "external" storage (which isn't really external, it's merely shared / public storage). So I used getExternalStoragePublicDirectory() to get access to the Documents folder. In the part of my code which copies the asset file to the Documents folder, I get "java.io.IOException: No such file or directory" when I try to create the target file.
This really threw me for a while, since I had preceded it with mkdirs() to ensure the folder(s) existed and I was trying to create the file in the first place.
After lots of poking around on S/O, it seems my problem may be that I don't have permissions to read / write in the Documents folder. So this is where I can't get my head around how this is supposed to work.
In my Activity's OnCreate(), I want to load the data from my file. But I need to check the permissions first. This is guaranteed to fail the first time, because the user has never granted my app this permission before.
So then I need to request the permission. But this means I can no longer load my data in the OnCreate() method, as I will get the response from the user asynchronously, in a callback method (onRequestPermissionsResult).
If the user says 'No' to my request, I need to make sure the app quits gracefully. If she says 'Yes', then... what? At this point I'm in a callback and my OnCreate() method is no longer running. How can I get back to setting up my Activity?
If I load the data file in the callback method, that only works for the initial case where the user must give permission. But after that, my app (most likely) will not lose permission, so my app will not need to ask for it. The callback will never be executed in this case. So my data file must be loaded... where?
But all of this only happens if the user is running Android 6.0 or higher. If it's an earlier version of Android, then my app can load the data in my Activity's OnCreate() method.
Oh... my head hurts!
How is all of this supposed to work? I don't need a link to the relevant methods. I need a conceptual model - a diagram would really help!
Here's the solution in a diagram: Permissions Conceptual Diagram
How can I get back to setting up my Activity?
Move the "setting up my Activity" code that depends on file I/O to a separate method. In onCreate(), check to see if you have the permission, and if you do, call that method to set up your activity. If not, call requestPermissions(). In onRequestPermissionsResult(), if you now have the permission, call that method to set up your activity.
See this sample app, which performs disk I/O from external storage using runtime permissions. The permission-handling logic is isolated in an AbstractPermissionsActivity, leaving the main activity class to handle the real business logic.
If it's an earlier version of Android, then my app can load the data in my Activity's OnCreate() method.
Well, you should be doing this disk I/O on a background thread. You can kick off that I/O from onCreate().
If you are using ContextCompat and ActivityCompat for checking and requesting permissions, respectively, your code will "do the right thing" on older devices, as those classes do the version checks for you.
Related
Other questions on this topic have been asked here:
Classes for Permission Checks Android
how are android security permissions checked at run-time?
How does Android enforce permissions?
But none of them answer what I am trying to figure out. I want to know where exactly I can find the functions or methods that literally check the permissions I'm requesting to see if I'm allowed to have that permission. More specifically, I want to find out what happens with Android Instant Apps permissions, since IA allows only a fraction of all Android permissions (the list can be found here https://developer.android.com/topic/google-play-instant/faqs).
For them there has to be a check somewhere, a whitelisting method that takes the permissions I'm requesting, understands that my application is an Instant App (rather than a normal one) and so it knows to check what I requested against that limited list only. That way it ensures I cannot ask permissions that are not even supposed to be allowed.
I want to understand and see where this happens, source code of these checks, especially for Instant Apps. I have started from the checkSelfPermissions() function used when implementing the Android Runtime Permissions. Through the function call trace feature in Android Studio and the xref (http://androidxref.com)
I went back as much as possible until I found the Context.java file (http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/content/Context.java) which has the prototype declarations with comments for each function.
public abstract int checkPermission(#NonNull String permission, int pid, int uid);
I just don't know where to find the definitions with the actual function body and code for it though. Context.java doesn't have them. And I think I am going more and more down a rabbit hole and a bit too low-level with these:
http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/content/pm/PackageManager.java#532
http://androidxref.com/8.1.0_r33/xref/frameworks/native/libs/binder/IPermissionController.cpp#39
http://androidxref.com/8.1.0_r33/xref/frameworks/native/libs/binder/IPermissionController.cpp#39
especially the last two, which not only do I not know if I'm on the right path with them, but I'm trying to figure out that remote()->transact function now and where it's defined, but we're in android native c++ territory now...
Any help or pointers would be immensely appreciated, it shouldn't be that hard to just go through the AOSP source code, right?
For future reference, in the end I have managed to solve it. It took way longer than I thought, but as it happens with most things, the answer was a lot simplier than thought.Turns out that yes, I was definitely going down lots of rabbit holes, but only up to a point. This whole process of search was actually useful in realizing where the answer would lie.
So the first step was successfully finding the actual proper, "low-level" implementations of the permission check or the permission granting action, and the functions are grantRuntimePermission and checkUidPermission, both in the PackageManagerService.java class. Here are the interesting bits, of each function:
http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#5655
http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#5335
That is where the checks are being performed, but the system somehow already "knows" about the permission and that it is runtime or not, instant or not. The crucial bit (which also kinda hinted to me what eventually I found the answer to be) is to look at the BasePermission objects created.
I found that the Android OS treats permissions in a more modular way, where each permission, rather than left as a simple string to do a check against, gets transformed into an independent BasePermission object. This gives it a more defined importance, as these objects now contain, besides the permission name, attributes such as the sourcePackage name, permission type, UID that owns the permission, and most importantly, the protectionLevel, amongst others. Here is the BasePermission.java class:
http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/pm/BasePermission.java#23
It seemed that the BasePermission objects get created within each application, based on that application. So in the context of an instant app, the Android OS attributes the permissionLevel to each individual permission object accordingly as it learns the application is, in this case, Instant. Here is the important bit of code:
if (isUidInstantApp) {
BasePermission bp = mSettings.mPermissions.get(permName);
if (bp != null && bp.isInstant()) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
return PackageManager.PERMISSION_GRANTED;
}
So it's possible to see how it creates the BasePermission object, based on permName, from a list/array of pre-created permission objects (mSettings.mPermissions) that the System somehow builds for the current application only. This only answers the "where is the permission check made?" question, but now the problem is "how does the system know how to create the BasePermission objects and assign to each of them the correct protectionLevel?". Initially I was completely unable to find where the "mPermissions" list gets populated. Then it hit me: I didn't actually need to know that.
All Android permissions and their names are defined in the global AndroidManifest.xml file. Whenever you use permissions in your application code, you call the Manifest.permission.PERMISSION_NAME string, right? I thought that the manifest would only contains the names of each permission, declared as a string. What I didn't expect was that the only other tiny piece of information the manifest declares is (you guessed it)... a per-permission protectionLevel value. For each permission it would state if it is a "normal" permission as opposed to the "dangerous" ones, as well as if it can be used for instant apps or not. And as expected, only the 10 (at the time of this writing) defined permissions for instant apps from the official docs had the "instant" attribute beside the other available protection levels. Here's one of them:
http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/res/AndroidManifest.xml#773
That is how the system knows to build the BasePermission objects, wherever it does that. And there was the whitelisting that I was looking for.
Even if I did look at the manifest as first step in my research, I probably wouldn't have known that the answer was right there in front of me. The knowledge of BasePermission objects was crucial to understand how the permission model and checks are implemented.
Next step now would be to actually find where exactly the system creates the base permission object based on these protection level strings in the manifest. I am guessing there should be an association of those strings to a binary integer such as "normal" or "normal|instant", number which is then used by the BasePermission class to construct the objects' protection level and ultimately define if they're instant.
At some point in my app, I have to save some temporary image files. I've tried using file.deleteOnExit(), after some research I found out it does not ensure the file will be actually deleted. So I tried to follow one of the alternatives listed on deleteOnExit() documentation that says:
• Maintain your own set of files to delete, and process it at an appropriate point in your application's lifecycle.
I am wondering, what is this appropriate point? I've tried to delete a set of files on application.onTerminate(), but the result is the same, if the app process is killed, it'll never reach onTerminate() method.
In order to keep performance and delete temporary files safely, when (where) should I place my code to delete them?
As soon as you are sure you won't use them, but no sooner.
More seriously, you should store them in the cache directory. You can get that with Context.getCacheDir(). That way if your cleanup methods don't end up getting called, the user can delete them from the Applications menu, or the system may delete them if space is required.
You can (and should) try to delete them in onDestroy() or even onPause(), depending on your implementation, but those are also not guaranteed to be called.
You might also put a timestamp in the filename, and run a cleanup periodically with AlarmManager and BroadcastReceiver by comparing the filename (which should reflect the last time that file was accessed, ideally) with the current time, and deleting it if the difference is greater than some constant. For performance sake I would probably run this daily or a couple times a day, and also (in a background thread) when the app opens or the relevant activity starts.
Accordingly to http://developer.android.com/google/play/expansion-files.html,
The main activity in your application (the one started by your launcher icon) is responsible for verifying whether the expansion files are already on the device and initiating the download if they are not.
And they really check existance of OBB in onCreate method of main activity.
I wonder how do I handle situation when OBB is present at application launch, but afterwards, during gameplay, user erases it and returns to the app. And do I need to take care at all?
I've only started with android development, but ...
You should be able to implement a http://developer.android.com/reference/android/os/FileObserver.html to watch for deletion or movement.
However as Kai stated, most people don't delete things they don't know about. So I wouldn't worry about it.
This might be as simple as checking if the file exists, you could easily do a File mFile = new File(pathToFile); then do a check if (!f.Exists()) { //do something }. You could do this every time content is accessed.
Another option is to use getSystemService(STORAGE_SERVICE); and pass commands to see if the OBB is mounted or not. StorageManager android
I want to determine whether my application being run for the first time, and so it will be hard to pass around it.
The preferences or empty file approaches are not going to work because you can simply clear the application data or delete the empty file.
Also I want to do it offline so no server checking is going to work either.
Maybe it's possible to add some code to the installation of the apk so it will only run once.
So my best bet is spawning a lot of empty files all around and hope the user won't find them, but this is pretty messy.
Has anyone ever done this somehow before? Any suggestions?
The preferences or empty file approaches are not going to work because you can simply clear the application data or delete the empty file.
One possibility is to have a BroadcastReceiver, registered in your manifest, that you do not actually use for anything. On first run of your app, disable that receiver via PackageManager and setComponentEnabledSetting(). On future runs, you can determine if that BroadcastReceiver is already disabled via PackageManager and getReceiverInfo().
If the user uninstalls and reinstalls the app, the app will return to its original state. Rooted device users can also get past this. Otherwise, this should be reasonably solid.
I know you said you dont want files, but if you create them as local internal storage then the user can not delete them as they wont have access unless it is rooted phone. Clearing data "may" delete them.
There is another solution but it is lengthy one. Use SQLite database.IT is local and I dont think it can be deleted otherwise the whole application will crash
I know there are various version of this question asked, but I was having trouble getting a clear answer on what the best approach is for my problem.
What I am doing is I am creating a SQLite database on the SD card. I want to be able to query from it and write to it.
The question I have is what is the best way to manage when the SD card is unmounted. I am totally fine with my application closing like the stock MP3 player does. However, I want to make sure any write action to the db do not get partially done.
One thought I had is the use beginTransaction, mark it as successful, and then call end transaction. The question I have is what happens if end of transaction does not get called. Can that potentially lead to data corruption? Also, I need a little help understanding what to listen to or hook into to get notification of the sd card being unmounted.
Thanks
That is the great thing about transactions in databases - you almost never need to worry:
"All changes within a single transaction in SQLite either occur
completely or not at all, even if the act of writing the change out to
the disk is interrupted by
a program crash, an operating system crash, or a power failure."
Taken from http://www.sqlite.org/transactional.html
The disk being removed on which the database resides should (in the worst case) behave like a power failure while writing the data to the disk. The database will discard that data on next startup.
Thus, as soon as your transaction is committed using commit or end transaction and the method call executing your statement returns all data has been stored. Otherwise NO data from your transaction will have been stored - both cases leave you in a consistent state.
Beware of the only catch: You will need to make sure that all statements you need to execute together (i.e. atomically) must be within one transaction!