Capture application-wide exceptions and save to a file/send to internet - android

I've started to work in a company which has a system with a lot of legacy code. Sometimes we have problems like the application randomly exiting, uncaught exceptions and so on. The compilation works fine and it's hard to know where exactly the error is coming from (either from legacy code or new code), so I'd like to capture exceptions globally in the application and either send them to our servers (best option) or write them to a local file in the device.
Is it possible in Xamarin.Android to write such an interface or method that catches any exception that ever occur in the application and log it to a file? If so, how?

Yes, you can able to handle error globally, by adding some line code in ApplicationStart class, something like below:
[Application(Label = "#string/app_name", Icon = "#drawable/ic_launcher")]
public class ApplicationStart : Application
{
public ApplicationStart(IntPtr handle,
global::Android.Runtime.JniHandleOwnership transfer)
: base(handle, transfer)
{
}
public override void OnCreate()
{
AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironmentOnUnhandledExceptionRaiser;
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += CurrentDomainOnUnhandledException;
base.OnCreate();
}
private void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
//you can able to write a code here to write Exeption e to file
Toast.MakeText(ApplicationContext, "Application crashed", ToastLength.Short).Show();
}
private void AndroidEnvironmentOnUnhandledExceptionRaiser(object sender, RaiseThrowableEventArgs e)
{
//if you have set e.Handled = true here then when application crashed at any point at that time device not stoped your app to go more
e.Handled = true;
//you can able to write a code here to write Exeption e to file
Toast.MakeText(ApplicationContext, "Application crashed", ToastLength.Short).Show();
}
}

Related

How do I save app logs locally on Android?

I want to save the logs generated by my application locally on the android device and view them in an instance of a crash.
Using the "Take Bug Report" under the developer options gives the entire system logs which are irrelevant to me. I am looking only for those logs created by my application when it runs.
Is there any application that does this? Or are there any libraries I could include in my application code to satisfy my requirement?
You may just add firebase to your project, and everything will be done automatically.
Or if need it to be "locally", can use the Thread.UncaughtExceptionHandler to save crash log. Register it when your application onCreate.
private static UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
public static void registerUncaughtExceptionHandler() {
mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
public void uncaughtException(Thread thread, Throwable ex) {
// Save Log
saveLog(ex);
// Throw system
mDefaultUncaughtExceptionHandler.uncaughtException(thread, ex);
}
});
}
private static void saveLog(Throwable exception) {
try {
String stackTrace = Log.getStackTraceString(exception);
// Save it to SharedPreferences or DB as you like
} catch (Exception e) {
}
}
Then can extract the last crash log, submit to your server or display in logcat when app starts.
It is much better to use Third Party libraries such as Firebase Crashlytics or Sentry Crash Report or AppMetrica for crash reports.
just add these libraries and make an account on one of these sites, then you can have a full report of crashes if happen.
but if you want to save the logs on the device, you can refer to this question :
Saving Logcat to a text file in Android Device
You can try this
fun writeLog(context: Context) {
try {
val path = File(context.filesDir, "log_files")
if (!path.exists()) {
path.mkdir()
}
val fileName = "your_filename.txt"
Runtime.getRuntime().exec("logcat -v time -f $fileName")
} catch (e: IOException) {
}
}
Or you can change logcat command based on your requirements: refer to this https://developer.android.com/studio/command-line/logcat
You can check it at data/data/{applicationId}/files/log_files/

How to change crash message in android(if possible)

I am wondering if it is possible to change the crash message for android?("unfortunately app has stopped") I haven't found anything that says you can(which I don't think you can), I am just making sure by asking on here.
Thanks
You cannot change the system message as stated by #Nuno Gomes but you can suppress the original message and display a message on your own or start some activity.
You can define an exceptionhandler that catches all uncaught exceptions in app class and show a message box from there
public class MyApp extends Application implements Thread.UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler mPreviousUncaughtExceptionHandler;
#Override public void onCreate() {
super.onCreate();
mPreviousUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
#Override
public void uncaughtException(Thread thread, Throwable ex) {
try {
// Do your stuff with the exception
Log.e(Global.LOG_CONTEXT,"LogCat.uncaughtException " + ex, ex);
// show user defined messagebox
} catch (Exception e) {
/* Ignore */
} finally {
// uncomment this to let Android show the default error dialog
// mPreviousUncaughtExceptionHandler.uncaughtException(thread, ex);
}
}
}
the app must be declared in the manifest
<manifest ...>
...
<application
android:name=".MyApp" ...>
</application>
</manifest>
On my android-4.4 i use this code to write a chrash log file
That message is a system message, it's outside the app scope, so no you cannot change it

How to call an Android app's method remotely?

I'm working on a project that improves Automation Test for Android's App. What I want to do is very "easy": I have this very simple SIP Client with a basic UI and developed just reading the API guides on the android developer website (https://developer.android.com/guide/topics/connectivity/sip.html) that receives and makes SIP calls.
I need to control remotely this app from my PC, connected at the same local network or the same wifi, by sending commands or similar (without interact with the phone) to the app itslef running normally on my phone.For a specific example I posted the method initiateCall() that calls sipAddress(in the app, sipAddress is taken from a Text Box), what I want to do is:
Starting the app on my phone
calling the method initiateCall() from my pc giving a sipAddress as a parameter (I must not use the UI from the app running, that's why I need to give the sipAddress)
check if an outgoing call starts from the app running on my phone
I thought that the solution must be something about web-services,but I don't have any better ideas and i don't know how to start and where to start solving this problem,that's why i need you help.
public void initiateCall() {
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
// set up the listener for outgoing calls
#Override
public void onCallEstablished(SipAudioCall call) {
call.startAudio();
call.setSpeakerMode(true);
updateStatus(call, 2);
}
#Override
public void onCallEnded(SipAudioCall call) {
updateStatus("Call End");
}
};
call = manager.makeAudioCall(me.getUriString(), sipAddress,
listener, 30);
} catch (Exception e) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", e);
if (me != null) {
try {
manager.close(me.getUriString());
} catch (Exception ee) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", ee);
ee.printStackTrace();
}
}
if (call != null) {
call.close();
}
}
}
You could do it REST API style. You would need to set up a minimalistic webserver.
If you access for example the url phoneip/ctrl/makecall?number=yournumber a serverside method us called if set up correctly. Then you can call you method and use the GET or POST variables as arguments.
You would have to look into Java Webserver Libraries/Frameworks. You can pick a lightweight one for that purpose. For example this one.
You could then also add security features (authentification to protect it) quite easily.
Example with sparkjava
import static spark.Spark.*;
....
get("/ctrl/makecall", (request, response) -> {
String phonenum = request.queryParams("number"); //may not be accurate; you have to determine the GET variable called "number" in that case; you can rename it; see docs!!!
//call your method with proper arguments
});

Injecting key events from android service

I'm making a custom navigation bar for android. I want to inject KEYCODE_BACK event from any part of application whether I'm on menu, home screen or any application. Basically, any part of android. How can I do that using services. I found one piece of code but it doesn't work coz I have to install application as system.
new Thread() { // requires to use INJECT_EVENTS permission in android
#Override
public void run() {
try {
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
} catch (Exception e) {
Log.e("Exception when sendKeyDownUpSync", e.toString());
}
}
}.start();
For this to work, I use uses-permission android:name="android.permission.INJECT_EVENTS" but it throws error saying INJECTING INTO ANOTHER APPLICATION REQUIRES INJECT_EVENT PERMISSION.
Is there anyway to implement back button through service.
Thanks

Should I remove e.printStackTrace() from my code before publishing

I was reading the the Android Publishing docs and they said to remove all Log calls from my code. I have some calls to e.printStackTrace() in my code that can be printed as part of the normal running of my program (ie. if a file does not exist yet).
Should I also remove these calls?
You shouldn't be using e.printStackTrace() directly anyway — doing so will send the info to the Android log without displaying which application (log tag) it came from.
As others have mentioned, continue to catch the Exception in question, but use one of the android.util.Log methods to do the logging. You could log only the message, but not the stack trace, or use verbose logging for the stack trace:
try {
Object foo = null;
foo.toString();
} catch (NullPointerException ex) {
Log.w(LOG_TAG, "Foo didn't work: "+ ex.getMessage());
Log.d(LOG_TAG, Util.stackTraceWriter(ex));
}
You should strip DEBUG or VERBOSE log messages from your production builds. The easiest way is to use ProGuard to remove Log.[dv] calls from your code.
If you allow an Exception to propagate up to the OS then the OS will log it and also pop up a Force Close window, killing your application. If you catch it, then you can prevent your application from being force closed.
If you want your users to have the ability to send you errors that they are getting, then I would log the stack trace. They can then send you the log via an app like Log Collector.
If you want to avoid the possibility of exposing your stack trace information to your users, then catch the exception and don't log it.
I would use Log class for message out put. For logs that you think are important to stay in the app - use Log.i
for errors warning - Log.e Log.w
For you debug Log.d - and that you can turnoff on base on if your application is in debug mode.
http://developer.android.com/reference/android/util/DebugUtils.html
Well printStackTrace() will log it into the OS, causing your andorid (or computer) app to terminate (force close), instead, do something like this:
public void nullPointerExceptionCauser()
{
try
{
Object example = null;
example.toString();
}
catch (Exception e)
{
Logger.log(Level.SEVERE, "Caught Exception: {0}", e.getStackTrace());
}
}
in my modest opinion (I'm not an Android developer)
It should be nice. I don't know the logging options for Android but I'm sure you have some configurable thing to output (or not) your traces.
And if you don't do printStackTrace() Android will not be doing the dirty work of ignoring it.
:)
It's only a good-feeling (style) thing.
If you want to be secure i.e. not allow anyone snooping to read exception logs you can do something like
private void hideExceptionsInReleaseMode()
{
final Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
if(!BuildConfig.DEBUG)
{
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
{
#Override
public void uncaughtException(Thread thread, Throwable ex)
{
defaultHandler.uncaughtException(thread, new RuntimeException("Something went wrong :p"));
}
});
}
}
In order to use printStackTrace in a safer way I would use StringWrite and PrintWriter:
...
catch (final Exception e)
{
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
Log.e("TAG", sw.toString());
}
Or alternatively:
catch (final Exception e)
{
Log.e(TAG, Log.getStackTraceString(e));
}
Use this to remove the logs from release apk
if (BuildConfig.DEBUG) Log.d(TAG, "your meseage");

Categories

Resources