Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Forward Wear Crashed to Smartphone/Tablet for reporting via Crashlytics (or other Crash Analytics solutions)

Send uncaught Exceptions from Android Wear to Android

This is a short Gist showing how I transmit any uncaught exceptions happening in the Wearable part of my App to the connected Smartphone/Tablet. This is necessary because Android Wear devices are not directly connected to the Internet themselves.

##Wear

  • WearApp.java
  • AndroidManifest.xml
  • ErrorService.java

##Smartphone

  • WearDataListenerService.java
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="at.maui.sample.wear" >
<uses-feature android:name="android.hardware.type.watch" />
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<application
android:name=".WearableApp"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<service android:name=".wear.service.ErrorService" android:process=":error"></service>
</application>
</manifest>
package at.maui.sample.wear.service;
import android.app.IntentService;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.DataMap;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Created by maui on 06.07.2014.
*/
public class ErrorService extends IntentService {
public ErrorService() {
super("ErrorService");
}
private List<String> getNodes(GoogleApiClient mGoogleApiClient) {
ArrayList<String> results= new ArrayList<String>();
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
for (Node node : nodes.getNodes()) {
results.add(node.getId());
}
return results;
}
@Override
protected void onHandleIntent(Intent intent) {
GoogleApiClient mGoogleAppiClient = new GoogleApiClient.Builder(ErrorService.this)
.addApi(Wearable.API)
.build();
mGoogleAppiClient.blockingConnect();
List<String> nodes = getNodes(mGoogleAppiClient);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(bos);
oos.writeObject(intent.getSerializableExtra("exception"));
byte[] exceptionData = bos.toByteArray();
DataMap dataMap = new DataMap();
// Add a bit of information on the Wear Device to pass a long with the exception
dataMap.putString("board", Build.BOARD);
dataMap.putString("fingerprint", Build.FINGERPRINT);
dataMap.putString("model", Build.MODEL);
dataMap.putString("manufacturer", Build.MANUFACTURER);
dataMap.putString("product", Build.PRODUCT);
dataMap.putByteArray("exception", exceptionData);
// "Fire and forget" send to connected Smartphone/Tablet using the Wearable Message API
Wearable.MessageApi.sendMessage(mGoogleAppiClient, nodes.get(0), "/wear/wear_error", dataMap.toByteArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null)
oos.close();
} catch (IOException exx) {
// ignore close exception
}
try {
bos.close();
} catch (IOException exx) {
// ignore close exception
}
}
}
}
package at.maui.sample.wear;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
public class WearableApp extends Application {
// Android Wear's default UncaughtExceptionHandler
private Thread.UncaughtExceptionHandler mDefaultUEH;
private Thread.UncaughtExceptionHandler mWearUEH = new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread thread, final Throwable ex) {
// Pass the exception to a Service which will send the data upstream to your Smartphone/Tablet
Intent errorIntent = new Intent(WearableApp.this, ErrorService.class);
errorIntent.putExtra("exception", ex);
startService(errorIntent);
// Let the default UncaughtExceptionHandler take it from here
mDefaultUEH.uncaughtException(thread, ex);
}
};
@Override public void onCreate() {
super.onCreate();
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(mWearUEH);
}
}
package at.maui.bunting.data;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import com.crashlytics.android.Crashlytics;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.data.FreezableUtils;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.DataEvent;
import com.google.android.gms.wearable.DataEventBuffer;
import com.google.android.gms.wearable.DataMap;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;
import com.google.android.gms.wearable.WearableListenerService;
import com.squareup.picasso.Picasso;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Created by maui on 04.07.2014.
*/
public class WearDataListenerService extends WearableListenerService {
private GoogleApiClient mGoogleApiClient;
@Override
public void onCreate() {
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.build();
}
@Override
public void onMessageReceived(MessageEvent messageEvent) {
if(!mGoogleApiClient.isConnected())
mGoogleApiClient.blockingConnect();
final Uri url = Uri.parse(messageEvent.getPath());
if(messageEvent.getPath().equals("/wear/wear_error")) {
DataMap map = DataMap.fromByteArray(messageEvent.getData());
ByteArrayInputStream bis = new ByteArrayInputStream(map.getByteArray("exception"));
try {
ObjectInputStream ois = new ObjectInputStream(bis);
Throwable ex = (Throwable) ois.readObject();
Crashlytics.setBool("wear_exception", true);
Crashlytics.setString("board", map.getString("board"));
Crashlytics.setString("fingerprint", map.getString("fingerprint"));
Crashlytics.setString("model", map.getString("model"));
Crashlytics.setString("manufacturer", map.getString("manufacturer"));
Crashlytics.setString("product", map.getString("product"));
Crashlytics.logException(ex);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} else if(messageEvent.getPath().equals("/some-other-path") {
// Handle all the other events synced using the Messaging API
}
}
}
@FoulCloud

This comment has been minimized.

Copy link

@FoulCloud FoulCloud commented Apr 20, 2016

Thank you so much for this. I had an unreproducible crash on watch device that would have been impossible to fix.

@LouisCAD

This comment has been minimized.

Copy link

@LouisCAD LouisCAD commented Feb 9, 2017

I'd add setIntentRedelivery(true); in the ErrorService constructor so the user clicking "ok" or the system giving up on the process which causes the process to be killed doesn't discards the non finished running IntentService which needs to report the error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.