mobisocial.metrics.MusubiExceptionHandler.java Source code

Java tutorial

Introduction

Here is the source code for mobisocial.metrics.MusubiExceptionHandler.java

Source

/*
 * Copyright 2012 The Stanford MobiSocial Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package mobisocial.metrics;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import mobisocial.musubi.App;
import mobisocial.musubi.BootstrapActivity;
import mobisocial.musubi.model.MIdentity;
import mobisocial.musubi.model.helpers.IdentitiesManager;
import mobisocial.musubi.ui.MusubiBaseActivity;
import mobisocial.musubi.ui.SettingsActivity;
import mobisocial.musubi.ui.util.UiUtil;
import mobisocial.musubi.util.CertifiedHttpClient;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.util.Log;

public class MusubiExceptionHandler implements UncaughtExceptionHandler {
    private final String TAG = getClass().getSimpleName();
    private final Context mContext;
    private final UncaughtExceptionHandler mDefaultHandler;

    public MusubiExceptionHandler(Context context, UncaughtExceptionHandler defaultHandler) {
        mContext = context;
        mDefaultHandler = defaultHandler;
    }

    @Override
    public void uncaughtException(final Thread thread, final Throwable ex) {
        new Thread() {
            @Override
            public void run() {
                try {
                    // force upgrade check on next boot.
                    SharedPreferences p = mContext.getSharedPreferences(SettingsActivity.PREFS_NAME, 0);
                    p.edit().putLong(BootstrapActivity.PREF_LAST_UPDATE_CHECK, 0).commit();

                    // submit to chirp.
                    submitException(ex);
                } catch (IOException e) {
                    // TODO: put in UserMetrics database for later submission
                    Log.e(TAG, "failed to post message", e);
                }
            }
        }.start();

        Log.e(TAG, "Uncaught exception", ex);
        if (mDefaultHandler != null) {
            mDefaultHandler.uncaughtException(thread, ex);
        }
    }

    private void submitException(Throwable ex) throws IOException {
        if (UsageMetrics.CHIRP_REPORTING_ENDPOINT == null) {
            return;
        }
        try {
            HttpClient http = getHttpClient(mContext);
            HttpPost post = new HttpPost(UsageMetrics.CHIRP_REPORTING_ENDPOINT);
            List<NameValuePair> data = new ArrayList<NameValuePair>();
            JSONObject json = jsonForException(mContext, ex, false);
            data.add(new BasicNameValuePair("json", json.toString()));
            post.setEntity(new UrlEncodedFormEntity(data, HTTP.UTF_8));

            HttpResponse response = http.execute(post);
            response.getEntity();
            int status = response.getStatusLine().getStatusCode();
            if (status != HttpStatus.SC_OK) {
                throw new IOException("Failed to post message to server. Http code: " + status);
            }
        } catch (ClientProtocolException e) {
            throw new IOException("Protocol exception while posting to server", e);
        }
    }

    static JSONObject jsonForException(Context context, Throwable ex, boolean caught) {
        JSONObject json = new JSONObject();
        try {
            Writer traceWriter = new StringWriter();
            PrintWriter printer = new PrintWriter(traceWriter);
            ex.printStackTrace(printer);
            json.put("type", "exception");
            json.put("caught", caught);
            json.put("app", context.getPackageName());
            json.put("message", ex.getMessage());
            json.put("trace", traceWriter.toString());
            json.put("timestamp", Long.toString(new Date().getTime()));

            boolean devmode = MusubiBaseActivity.isDeveloperModeEnabled(context);
            json.put("musubi_devmode", Boolean.toString(devmode));
            if (devmode) {
                IdentitiesManager im = new IdentitiesManager(App.getDatabaseSource(context));
                MIdentity id = im.getMyDefaultIdentity();
                String user = "Unknown";
                if (id != null) {
                    user = UiUtil.safeNameForIdentity(id);
                }
                json.put("musubi_devmode_user_id", user);
                user = App.getMusubi(context).userForLocalDevice(null).getName();
                json.put("musubi_devmode_user_name", user);
            }
            try {
                PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
                json.put("musubi_version_name", info.versionName);
                json.put("musubi_version_code", Integer.toString(info.versionCode));
            } catch (NameNotFoundException e) {
                // shouldn't happen, but not fatal.
            }
            json.put("android_api", Integer.toString(Build.VERSION.SDK_INT));
            json.put("android_release", Build.VERSION.RELEASE);
            json.put("android_model", Build.MODEL);
            json.put("android_make", Build.MANUFACTURER);
        } catch (JSONException e) {
        }
        return json;
    }

    static final HttpClient getHttpClient(Context context) {
        HttpClient http = new CertifiedHttpClient(context);
        HttpParams params = http.getParams();
        HttpProtocolParams.setUseExpectContinue(params, false);
        HttpConnectionParams.setConnectionTimeout(params, 6000);
        HttpConnectionParams.setSoTimeout(params, 6000);
        return http;
    }

    public static void installHandler(Context context) {
        UncaughtExceptionHandler currentHandler = Thread.getDefaultUncaughtExceptionHandler();
        if (!(currentHandler instanceof MusubiExceptionHandler)) {
            Thread.setDefaultUncaughtExceptionHandler(new MusubiExceptionHandler(context, currentHandler));
        }
    }
}