In the Google Play Console there is a very helpful page “Android vitals” / “ANRs & crashes”. The information provided under “crashes” is almost helpful (e.g. NullPointerException in "filename, line number"). That is something to work with. However, the info under “ANRs” (Activity Not Responding) is mostly mysterious.
For example, I have written an app which needs read/write access to the external storage – which is not necessarily a removable SD Card. Since Android 4.2 (API 17), access to the external SD Card is not possible any more. Instead, there is a link to “/mnt/sdcard” or "/storage/emulated/0", which resides in the internal memory. (Why this is called “external storage” is Google’s secret.)
Of course I have set the required entries in AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
and of course, since Android 6 (Marshmallow), I am handling the permissions correctly with
requestPermissions(requPerms, myRequestCode);
and
onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults)
To determine the storage path for the app data files (for example "/storage/emulated/0"), I use the following code:
public static File getMountName()
{
File mount = null;
// possible ANR 1
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED))
{
// possible ANR 2
File path = Environment.getExternalStorageDirectory();
mount = new File(path.getAbsolutePath());
}
return mount;
} // getMountName
For some reason, some of the user’s smartphones are hanging with “ANR” in one of the two lines “Environment.getExternalStorage…” marked with the respective comment. (Which means, if they hang on the 2nd line, they have passed the 1st one successfully.)
The related error messages in Android vitals are shown in the following examples:
for ANR 1:
Input dispatching timed out (Waiting to send non-key event because the
touched window has not finished processing certain input events that
were delivered to it over 500.0ms ago. Wait queue length: 2. Wait
queue head age: 14805.0ms.)
and for ANR 2:
Context.startForegroundService() did not then call
Service.startForeground(): ServiceRecord{33def99 u0
net.braun_home.sensorrecording.lite/net.braun_home.sensorrecording.SensorService}
Does anybody have an idea, what these messages mean, and why they appear on some smartphones only? According to Android vitals, they all have newer Android versions 8 or 9. I myself have also two devices with these versions and the app runs perfectly there.
Related
I am trying to diagnose an issue in an app I have written. The issue is a sporadic one, and occurs only under real-world conditions: in the field, away from my PC, and when I’m in the middle of something else, with no resources to spare for immediate debugging. Therefore, my best bet is collecting and analyzing log data.
Unfortunately, by the time I realize the issue has struck again and get around to debugging it, any log data has already rotated out of the Android log as I frequently have other chatty apps running at the same time. Increasing the size of the log buffer has not helped (either Android does not honor it or other apps are still too chatty) so I have abandoned this route.
For this reason, I am now considering having my app log to a separate text file in addition to the regular log.
Now I could easily double every call like
Log.i(TAG, "something happened");
adding another call that writes the same thing to the log file—but that does not seem very elegant to me.
Another option would be to replace all calls to Log with a wrapper that writes the event both to the Android log and the log file.
Question: Does the Android API provide a built-in mechanism for this, i.e. telling Log to write its data to the default log and a text file at the same time? Or do I need to code this by myself?
Edit:
Assumptions:
I know where in my code I need to generate log output (which can happen anywhere, which may or may not involve an exception) and what I want to be written to the log.
Getting log data from the device to my PC is also not a concern (one-man show, I just plug my phone into my PC and transfer the log file).
If you know the current Android API has no built-in mechanism to achieve what I want, then ”no, Android does not support this” is a perfectly acceptable answer. In that case the solution is clear—I would fall back to the wrapper function. I am specifically not looking for a different approach to the problem.
After doing some more research, it seems the Android API does not provide a standard way to do this. There are two possible workarounds:
Mirror output at the source
System.out and System.err output, which is written to the console in desktop systems, writes to the log on Android. These two can be redirected into any PrintStream of your choice, which would give you all Java console output. You can subclass PrintStream to duplicate its input, feeding it into the default stream as well as into a file of your choice.
Create a class which exposes the same methods as android.util.Log. In each method, call through to the respective android.util.Log method and additionally log the data to a file. If you call your class Log (but with a different package name, e.g. org.example.Log), then all you need to do is replace imports of android.util.Log with an import of your class, and any Log method calls will go to your class.
Caveats: This will only give you data explicitly logged by your code (i.e. for which you have the source files), as well as anything that goes to System.out or System.err. It will not include log output from JAR libraries (if you cannot modify their source code), nor any output generated by the system (such as stack traces from default exception handlers) or by other processes (some of which may be system processes and report conditions related to your process).
Read the logs from the command line
This article explains how to read the logs from within Android. In a nutshell:
Android includes a command line utility called logcat on the device, which will give you a continuous feed of log messages until stopped. (Try it by adb shelling into your device and running it. It has a bunch of command-line options to control its behavior. Not sure if it is present on all distributions, though.)
Launch this command via Runtime.getRuntime().exec("logcat"), then obtain the input stream of the process returned. This will give you an input stream of log messages.
According to the article, your app needs the android.permission.READ_LOGS permission to read logs.
I have read statements that certain versions of Android (4.2 was mentioned) do not allow this permission to be granted to non-system apps, though. According to my own tests, behavior without this permissions differ: Anbox will return the full logcat, while LineageOS (tested on 15.1) will only show log entries from the app which called it (including previous instances, presumably everything associated with the same Linux user). This can be a limitation or a welcome filter feature. YMMV.
logcat conveniently has a command line option, -f, to specify an output file. I tried
Runtime.getRuntime().exec("logcat -f " + absolutePathToLogFile);
and logcat keeps logging as long as the app’s process runs. Killing the app (by clicking the X in the title bar on Anbox) apparently also terminated the child process.
Now you can either run this code when your app starts up, or you can turn this functionality into a separate app which starts on boot and continuously collects logs for all apps.
Caveats: This may fill up your storage space quickly if you have some chatty apps running (which is why entries rotate out of the logcat so quickly in the first place). It is recommended to make log mirroring configurable (e.g. via Preferences) and/or ensure old files are deleted regularly. Also, if you keep the logcat process running until your app terminates, you will not be able to access the file over MTP as there is no easy way to run the media scanner (if you scan the file while it is still written to, it will appear truncated over MTP until another media scan runs).
You have not specified if some exception are thrown but you don't handle.
In case, take a look at this answer:
Android Handling Unhandled Exception
If you must look at a bunch of variables and objects, I'd suggest two choices:
Write a copy of your logs on a file. When your problem occurs, just ask the user to send the file to you. This is ideal during tests with self-aware users.
Obtain statistics about usage, like commercial software do. Just log user operations and send the data to your server (you would need one for this). This is the most transparent way to do remote logging.
In the case of writing log to a file, you can read and write what you want in internal memory (inside the app's sandbox) or external memory (in this case, write permission is required and explicit permission must have been granted at runtime if you are targeting Android 6 and above).
I've checked all the documents and threads and can't seem to find any solution to this. I've read threads with similar issues but when trying to apply their solutions I can't seem to get it working.
I'm trying to enable uploading photo/video from storage/camera to my app. When opening the RN imagePicker to begin the upload, the "Allow"-alert comes up: "Allow myApp to access photos, media and files on your device?".
The problem is that after allowing access the ImagePicker won't move on with its' process - the "Allow"-question dissapears and the app behaves as if nothing has happend. When trying to open the imagePicker again, the same process repeats itself, despite the fact that when running Permissions.openSettings() (opening the apps permission settings) i can note that the permissions has been changed in accordance with the choice made from the "Allow"-question. In other words, moment 22.
Im running Nexus 5, Android 6.0 with targetSdkVersion 23 on a Genymotion emulator.
Anyone who've had the exact same issue and solved it?
Don't think I've let any important information out, but please let me know if that's the case.
// **Permission results in the MainActivity.java-file.**
#Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
switch(permsRequestCode){
case PERMISSION_REQ_CODE:
// example how to get result of permissions requests (there can be more then one permission dialog)
// boolean readAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
// boolean writeAccepted = grantResults[1]==PackageManager.PERMISSION_GRANTED;
// checking permissions to prevent situation when user denied some permission
checkPerms();
break;
}
}
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.
This question has been asked before at How does Android enforce permissions?. While the discussions there are good, the question is still not fully answered.
In the development environment, exceptions are thrown when the app tries to do something that requires permissions not declared in AndroidManifest.xml. So how does the run-time system implement the run-time checking?
I guess it's most likely done in the core framework, which may or may not need support from native code. But I don't know what source code files in AOSP are relevant to this.
Android uses a lot of the standard Linux(-kernel?) mechanisms especially when it comes to hardware restrictions.
Every app gets assigned a new unique (Linux-)user id and whenever the app process is created the system creates it with that user id. The id will never change unless you remove the app. That means for accessing the lower system levels your app will appear as a certain user and every (Linux-)permission system that works with users will also apply to your app.
If you request WRITE_EXTERNAL_STORAGE in the manifest your app will also become member of the (Linux-)group (called sdcard_rw) that has permissions to write to that storage. Permissions on the filesystem are enforced to only allow writing to the system user (=owner) and the sdcard_rw group, anyone else (=other) may only read. See also Is Google blocking apps writing to SD cards
By doing that Android has to do pretty much nothing except for setting the correct UID/GIDs of the processes it spawns once the app starts and the rest is handled at lower levels. Apps that are not member of a certain group simply don't get access to certain hardware.
List of permission <> group mappings: platform.xml
There are also some (Android software) restrictions that are based on either the signature of your app and / or simply by looking up the permissions your app requested: e.g. ContextImpl#checkPermission() - but those permissions have to be checked at every entrypoint to code that allows restricted actions.
From time to time people discover ways to e.g. turn on GPS programmatically because a check like that is missing somewhere.
With regard to your second paragraph, "exceptions" are runtime faults. Permissions are not enforced at build time, only at run time.
Accessing hardware, low level operating system resources, and system files generally requires the app userid to be a member of an appropriate group which it may be assigned by the package manager as a result of having a corresponding android permission. (Familiar examples of that would be network sockets, and the sdcard write which zapl mentioned, but also system-only things like talking directly to the GSM modem or reading the raw touchscreen coordinates).
For the majority of android operations that are done by way of calling library functions which are stubs for interprocess communication to services running in a different process, the platform code running in the more privileged process on the receiving end of the ipc request checks with the package manager to find out if the calling application has the necessary android permission.
Many special permissions are only available to apps signed with the system signature - even if another app claims those in its manifest, they will not be applied by the package manager.
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)