When Android's StrictMode detects a leaked object (e.g. activity) violation, it would be helpful if I could capture a heap dump at that moment in time. There's no obvious way, however, to configure it to do this. Does anyone know of some trick that can be used to achieve it, e.g. a way to convince the system to run a particular piece of code just prior to the death penalty being invoked? I don't think StrictMode throws an exception, so I can't use the trick described here: Is there a way to have an Android process produce a heap dump on an OutOfMemoryError?
No exception, but StrictMode does print a message to System.err just before it terminates. So, this is a hack, but it works, and as it's only going to be enabled on debug builds I figure it's fine... :)
in onCreate():
//monitor System.err for messages that indicate the process is about to be killed by
//StrictMode and cause a heap dump when one is caught
System.setErr (new HProfDumpingStderrPrintStream (System.err));
and the class referred to:
private static class HProfDumpingStderrPrintStream extends PrintStream
{
public HProfDumpingStderrPrintStream (OutputStream destination)
{
super (destination);
}
#Override
public synchronized void println (String str)
{
super.println (str);
if (str.equals ("StrictMode VmPolicy violation with POLICY_DEATH; shutting down."))
{
// StrictMode is about to terminate us... don't let it!
super.println ("Trapped StrictMode shutdown notice: logging heap data");
try {
android.os.Debug.dumpHprofData(app.getDir ("hprof", MODE_WORLD_READABLE) + "/strictmode-death-penalty.hprof");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
(where app is a static field in the outer class containing a reference to the application context, for ease of reference)
The string it matches has survived unchanged from gingerbread release all the way up to jelly bean, but it could theoretically change in future versions, so it's worth checking new releases to ensure they still use the same message.
Related
I'm working on an app that uses Android's MediaMuxer for recording the screen. Using Crashlytics, a significant number of users have the "Failed to stop the muxer" crash, but I can't reproduce it locally on any of my devices. According to another question, the MPEG4Writer logs generated while MediaMuxer is running should indicate what the source of the problem is, but since I'm unable to reproduce it locally, I need to collect those logs remotely and pass them over to Crashlytics.
So here's my problem: MediaMuxer and MPEG4Writer are system classes, so obviously I can't edit them to add Crashlytics.log() lines. I've thought of having the app read the Logcat output and storing all entries containing MPEG4Writer, which are then sent to Crashlytics if the muxer crashes, using this implementation as a base. Here's my code:
public class LogRetriever extends Thread {
private static final String TAG = LogRetriever.class.getCanonicalName();
public static ArrayList<String> logStorage = new ArrayList<>();
private AtomicBoolean mLoggingActive = new AtomicBoolean(true);
#Override
public void run() {
try {
String[] command = new String[] { "logcat" };
Process process = Runtime.getRuntime().exec(command);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while (mLoggingActive.get() && ((line = bufferedReader.readLine()) != null)){
if(line.contains("MPEG4Writer")) {
logStorage.add(line);
}
}
}
catch (IOException ex) {
Log.e(TAG, "start failed", ex);
}
}
public void stopLogging() {
mLoggingActive.set(false);
}
}
Using the above method, I only seem to get the first four log lines generated by MPEG4Writer. The rest are visible through Android Studio's logcat, but aren't collected by my code. I've also tried this library which seems to do the same thing, but again, same problem, only the first 4 lines are collected. I suspect that MediaMuxer is creating its own process after those 4 lines, at which point I can no longer read its logcat output because my LogRetriever class is now in a different process. So how am I supposed to collect those logs? Am I taking the wrong approach here?
So how am I supposed to collect those logs?
Generally, unless you are working for a device manufacturer, you don't collect those logs.
First, accessing LogCat at runtime has never been officially supported; hence, the clunky "fork logcat" approach that you have to take.
Beyond that, you need the READ_LOGS permission to get more than what you are. That permission has signature|privileged|development for the protectionLevel, meaning that ordinary apps cannot hold that permission.
This is for privacy reasons. READ_LOGS gives you access to all of LogCat, and lots of apps (and some system processes) log information that may be sensitive.
I am using OkHttp (first the original verison, then I upgraded to OkHttp3), some users of my App have been reporting significant battery life loss when the App isn't running.
I ran a profiler and this is the result:
As you can see, Okio Watchdog is running the whole time. At roughly the halfway point, my App is fully in the background. There are no HTTP tasks taking place at this point in time. I started profiling after the last HTTP task ended.
Is it normal that the Watchdog runs throughout like that? If so, am I right in assuming this thread is causing a lot of battery waste? If it isn't normal, could something like a leaked Context keep the Watchdog running?
The Watchdog code runs here, it seems like to runs without a termination condition:
private static final class Watchdog extends Thread {
public Watchdog() {
super("Okio Watchdog");
setDaemon(true);
}
public void run() {
while (true) {
try {
AsyncTimeout timedOut = awaitTimeout();
// Didn't find a node to interrupt. Try again.
if (timedOut == null) continue;
// Close the timed out node.
timedOut.timedOut();
} catch (InterruptedException ignored) {
}
}
}
}
Looks like a severe & unexpected bug in Okio. I'll try to reproduce & fix. If you're able to produce this consistently, please comment on this bug!
https://github.com/square/okio/issues/185
For me it was caused by proguard's optimization. After some investigation - see the okio issue linked above - a workaround (if not final fix?) is to disable optimization or add this to your proguard-rules.pro:
-optimizations !method/marking/static,!method/removal/parameter,!code/removal/advanced
I find a NOTE in this manual
Note: the configuration specifies that none of the methods of class '...' have any side effects
Your configuration contains an option -assumenosideeffects to indicate that the specified methods don't have any side effects. However, the configuration tries to match all methods, by using a wildcard like "*;". This includes methods from java.lang.Object, such as wait() and notify(). Removing invocations of those methods will most likely break your application. You should list the methods without side effects more conservatively. You can switch off these notes by specifying the -dontnote option.
You should specify the method name in the -assumenosideeffects block.
I add this comment at https://github.com/square/okio/issues/185#issuecomment-220520926
I have an app which uses Google Maps (v1) and from the crash reports, I am seeing this exception from time to time:
java.lang.NoClassDefFoundError: android.security.MessageDigest
at com.google.android.maps.KeyHelper.getSignatureFingerprint(KeyHelper.java:60)
at com.google.android.maps.MapActivity.createMap(MapActivity.java:513)
at com.google.android.maps.MapActivity.onCreate(MapActivity.java:409)
I have defined
<uses-library
android:name="com.google.android.maps"
android:required="true" />
inside the application tag and I am extending MapActivity as well. The application works fine on most devices but there are some uncommon ones that report this exception, usually on Android 4.0.4 like Woxter Tablet PC 90BL, TAB9008GBBK and other generic names.
From what I read in Stackoverflow, it is a problem in the ROM and it can be solved by the user doing some advanced tricks but what I want is to prevent this crash, as I don't think it can be solved, I just want to inform the user (and thell him to buy a better device :) and disable maps functionality instead of crashing. But I can't find a way to handle this error or test it with the devices I have.
Also my main activity is based on MapActivity so I don't know how can I handle this exception before opening it.
Disclaimer: I've not come across this error on any of my apps / devices but I solved a similar problem. May be that same technique can help you.
Given that the class is either unavailable or an exception occurrs while loading the class, why not try to force load it when your application starts ? Class.forName("android.security.MessageDigest") should load the class and you can catch the Error thrown from that call. I know its dirty, but it should work. You can declare a custom Application class on the manifest to make this check.
Class loading test
try
{
Class.forName("android.security.MessageDigest");
}
catch (Throwable e1)
{
e1.printStackTrace();
//Bad device
}
You can also perform a litmus test and check the functionality of the class should the class loading succeed by digesting a simple String.
Functional test
try
{
MessageDigest digester = MessageDigest.getInstance("MD5");
digester.update("test".getBytes("UTF-8"));
byte[] digest = digester.digest();
}
catch (Throwable e1)
{
e1.printStackTrace();
// Class available but not functional
}
If the class loading / litmus test fails, update a shared preference flag and let the user know that his device sucks :)
Try to change the import android.security.MessageDigest to java.security.MessageDigest
by the look at this link:
What is 'android.security.MessageDigest''?
It looks that the android.security.MessageDigest was remove from Honeycomb so change it to the java one. and check this link as well:
http://productforums.google.com/forum/#!category-topic/maps/google-maps-for-mobile/KinrGn9DcIE
As been suggested there by #XGouchet:
Try downloading the latest version of the Google Maps API and rebuild your application with targetSDK set to the highest available (as of today it should be 17 / Jelly Bean).
The class android.security.MessageDigest is an abstract class (see MessageDigest API) what means that it can't be instantiated right away. So what happens is, that any time a device/app can't find an implementation of this class you will get the exception above, namely
java.lang.NoClassDefFoundError: android.security.MessageDigest
It's a good question why this happens. May be some phone vendors didn't ship their phone with the required library that actually implements this abstract class. I faced a similar issue with the TUN.ko module in the past.
Approach 1
What should help is, if you provide your own (empty) implementation of this class that "implements" the abstract classes and methods like this:
public class MessageDigestSpi extends Object {
byte[] engineDigest() { return new byte[0]; }
void engineReset() { }
void engineUpdate(byte[] input, int offset, int len) { }
}
public class MessageDigest extends MessageDigestSpi {
}
... and put those classes into the folder <src>/java/security/. So this way you provide your own implementation that is always found and might contain some code in order to inform the user or provide an alternative implementation.
So the remaining questions are: what does the app do, if the implementation is provided by the system, too and how to control that the system implementation is the first choice?
The answer: which implementation is chosen depends on the import order. Looking at Eclipse you can define the order in the project properties, Java build path, tab order and export. Be sure that you have any system libraries on top that might include the system implementation (most likely the Android libraries). This way the system searches in those libraries first. If nothing is found your classes get loaded and executed.
Approach 2
As an alternative to the implementation in an own abstract class you could of course simply instantiate the MessageDigest class, catch the NoClassDefFoundError exception and store the result for later evaluation:
import android.security.MessageDigest;
public class MessageDigestTester {
private static Boolean messageDigestAvailable = null;
public static Boolean isLibraryAvailable() {
if (messageDigestAvailable == null) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
messageDigestAvailable = true;
} catch (NoClassDefFoundError e) {
messageDigestAvailable = false;
}
}
return messageDigestAvailable;
}
}
Then use if (MessageDigestTester.isLibraryAvailable()) { } else { } in your code in order to encapsulate the usage of this library and to provide an alternative.
Approach two is easier to implement whereas approach one is the more sophisticated solution.
Hope this was helpful ... Cheers!
I just released a new version of my application to the Android market, and my new version has a GLSurfaceView in the activity. Even though I'm not doing anything fancy, I have a large user base, there's a lot of substandard Android phones out there, and I'm invariably getting exceptions in GLThread.run().
What is the recommended way to catch/handle these exceptions without crashing the entire app? Ideally I'd like to be able to catch the error, remove the surface view from the activity and switch off the component that uses OpenGL. I did a bit of searching but mostly found exception reports for Firefox on Android and stuff like that. :)
I'm thinking of just using an uncaught exception handler, switching a shared preferences flag to false, and letting it crash; the next run I won't try to add that GLSurfaceView.
I ended up working around the problem with the following code:
final UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
if (thread.getName().startsWith("GLThread")) {
disableOpenGLStuff();
}
// You could wrap this in an else, but I'm not sure how good of an idea it is to leave the application running when a thread has crashed.
defaultHandler.uncaughtException(thread, ex);
});
I do have an old app that refuses to work on Android 4.1 devices. It's the NetworkOnMainThreadException that jumps in here.
So I tried to permit this with the following steps - but these don't work. I tested that with the 4.1 emulator. What is really needed to come around that error - app rewrite is no option. Currently I exclude 4.1 devices from my apps.
A class file ...
public class StrictModeWrapper {
static {
try {
Class.forName("android.os.StrictMode");
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
public static void checkAvailable() {
}
#SuppressLint("NewApi")
public static void setThreadPolicy() {
StrictMode.ThreadPolicy strictModeThreadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(strictModeThreadPolicy);
}
}
... called in an extended Application class:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
try {
StrictModeWrapper.checkAvailable();
StrictModeWrapper.setThreadPolicy();
} catch (Throwable throwable) {
}
}
}
The extended Application class is registered in the Manifest and working.
Nothing seems to have changed in StrictMode since api 11. It must be the changes in some other android classes you used, that caused a StrictMode policy violation.
The Android documentation itself says
"But don't feel compelled to fix everything that StrictMode finds. "
But since its a NetworkOnMainThreadException you must do a thorough check. See all network communications in your app, and ensure that they are not blocking your main thread.
And make sure you remove/disable the StrictMode code in your release build, as it is only a developer tool to identify accidental mistakes.
Update:
Your app crashed because :
You had not blocked the execution of StrictMode policy setting code in your release build. It should be executed only while testing.
Something changed in the StrictMode class that caused the strict mode policy to reset after onCreate.
I have 2 questions :
Doesnt the crash indicate that the StrictMode policy was working? There was a policy violation and hence it crashed.
Doesnt it indicate that there is some network code in your app that blocks the main thread?
StrictMode behaves different on Android version >= 16 than prior releases. The docs suggest to issue StrictMode calls in onCreate() of an extended Application, Activity, etc.. At least onCreate() in an extended Application works different now and proofes the docs wrong (as of today).
Here's the StrictMode doc that describes how to add StrictMode calls to an extended application for example (that's wrong as of today):
StrictMode
Here's a Google Code issue that describes the problem and gives a workaround:
Google Code Issue 35298