Search in sources :

Example 1 with CrashReportData

use of org.acra.collector.CrashReportData in project acra by ACRA.

the class ReportExecutor method execute.

/**
     * Try to send a report, if an error occurs stores a report file for a later attempt.
     *
     * @param reportBuilder The report builder used to assemble the report
     */
public void execute(@NonNull final ReportBuilder reportBuilder) {
    if (!enabled) {
        ACRA.log.v(LOG_TAG, "ACRA is disabled. Report not sent.");
        return;
    }
    // Prime this crash report with any extra data.
    reportPrimer.primeReport(context, reportBuilder);
    boolean sendOnlySilentReports = false;
    final ReportingInteractionMode reportingInteractionMode;
    if (!reportBuilder.isSendSilently()) {
        // No interaction mode defined in the ReportBuilder, we assume it has been set during ACRA.initACRA()
        reportingInteractionMode = config.mode();
    } else {
        reportingInteractionMode = ReportingInteractionMode.SILENT;
        // explicitly declared as silent via handleSilentException().
        if (config.mode() != ReportingInteractionMode.SILENT) {
            sendOnlySilentReports = true;
        }
    }
    final boolean shouldDisplayToast = reportingInteractionMode == ReportingInteractionMode.TOAST || (config.resToastText() != 0 && (reportingInteractionMode == ReportingInteractionMode.NOTIFICATION || reportingInteractionMode == ReportingInteractionMode.DIALOG));
    final TimeHelper sentToastTimeMillis = new TimeHelper();
    if (shouldDisplayToast) {
        new Thread() {

            /*
                 * (non-Javadoc)
                 *
                 * @see java.lang.Thread#run()
                 */
            @Override
            public void run() {
                Looper.prepare();
                ToastSender.sendToast(context, config.resToastText(), Toast.LENGTH_LONG);
                sentToastTimeMillis.setInitialTimeMillis(System.currentTimeMillis());
                Looper.loop();
            }
        }.start();
    // We will wait a few seconds at the end of the method to be sure
    // that the Toast can be read by the user.
    }
    final CrashReportData crashReportData = crashReportDataFactory.createCrashData(reportBuilder);
    // Always write the report file
    final File reportFile = getReportFileName(crashReportData);
    saveCrashReportFile(reportFile, crashReportData);
    final SharedPreferences prefs = new SharedPreferencesFactory(context, config).create();
    if (reportingInteractionMode == ReportingInteractionMode.SILENT || reportingInteractionMode == ReportingInteractionMode.TOAST || prefs.getBoolean(ACRA.PREF_ALWAYS_ACCEPT, false)) {
        // Approve and then send reports now
        startSendingReports(sendOnlySilentReports);
        if ((reportingInteractionMode == ReportingInteractionMode.SILENT) && !reportBuilder.isEndApplication()) {
            // So no need to wait around for the sender to complete.
            return;
        }
    } else if (reportingInteractionMode == ReportingInteractionMode.NOTIFICATION) {
        if (ACRA.DEV_LOGGING)
            ACRA.log.d(LOG_TAG, "Creating Notification.");
        createNotification(reportFile, reportBuilder);
    }
    final boolean showDirectDialog = (reportingInteractionMode == ReportingInteractionMode.DIALOG) && !prefs.getBoolean(ACRA.PREF_ALWAYS_ACCEPT, false);
    if (shouldDisplayToast) {
        // A toast is being displayed, we have to wait for its end before doing anything else.
        new Thread() {

            @Override
            public void run() {
                if (ACRA.DEV_LOGGING)
                    ACRA.log.d(LOG_TAG, "Waiting for " + ACRAConstants.TOAST_WAIT_DURATION + " millis from " + sentToastTimeMillis.initialTimeMillis + " currentMillis=" + System.currentTimeMillis());
                final long sleep = ACRAConstants.TOAST_WAIT_DURATION - sentToastTimeMillis.getElapsedTime();
                try {
                    // Wait a bit to let the user read the toast
                    if (sleep > 0L)
                        Thread.sleep(sleep);
                } catch (InterruptedException e1) {
                    if (ACRA.DEV_LOGGING)
                        ACRA.log.d(LOG_TAG, "Interrupted while waiting for Toast to end.", e1);
                }
                if (ACRA.DEV_LOGGING)
                    ACRA.log.d(LOG_TAG, "Finished waiting for Toast");
                dialogAndEnd(reportBuilder, reportFile, showDirectDialog);
            }
        }.start();
    } else {
        dialogAndEnd(reportBuilder, reportFile, showDirectDialog);
    }
}
Also used : ReportingInteractionMode(org.acra.ReportingInteractionMode) CrashReportData(org.acra.collector.CrashReportData) SharedPreferences(android.content.SharedPreferences) SharedPreferencesFactory(org.acra.prefs.SharedPreferencesFactory) File(java.io.File)

Example 2 with CrashReportData

use of org.acra.collector.CrashReportData in project acra by ACRA.

the class BaseCrashReportDialog method sendCrash.

/**
     * Send crash report given user's comment and email address. If none should be empty strings
     *
     * @param comment   Comment (may be null) provided by the user.
     * @param userEmail Email address (may be null) provided by the client.
     */
protected final void sendCrash(@Nullable String comment, @Nullable String userEmail) {
    final CrashReportPersister persister = new CrashReportPersister();
    try {
        if (ACRA.DEV_LOGGING)
            ACRA.log.d(LOG_TAG, "Add user comment to " + reportFile);
        final CrashReportData crashData = persister.load(reportFile);
        crashData.putString(USER_COMMENT, comment == null ? "" : comment);
        crashData.putString(USER_EMAIL, userEmail == null ? "" : userEmail);
        persister.store(crashData, reportFile);
    } catch (IOException e) {
        ACRA.log.w(LOG_TAG, "User comment not added: ", e);
    } catch (JSONException e) {
        ACRA.log.w(LOG_TAG, "User comment not added: ", e);
    }
    // Start the report sending task
    final SenderServiceStarter starter = new SenderServiceStarter(getApplicationContext(), config);
    starter.startService(false, true);
    // Optional Toast to thank the user
    final int toastId = config.resDialogOkToast();
    if (toastId != 0) {
        ToastSender.sendToast(getApplicationContext(), toastId, Toast.LENGTH_LONG);
    }
}
Also used : CrashReportData(org.acra.collector.CrashReportData) SenderServiceStarter(org.acra.sender.SenderServiceStarter) JSONException(org.json.JSONException) CrashReportPersister(org.acra.file.CrashReportPersister) IOException(java.io.IOException)

Example 3 with CrashReportData

use of org.acra.collector.CrashReportData in project acra by ACRA.

the class ReportConverter method legacyLoad.

/**
     * Loads properties from the specified InputStream. The properties are of
     * the form <code>key=value</code>, one property per line. It may be not
     * encode as 'ISO-8859-1'.The {@code Properties} file is interpreted
     * according to the following rules:
     * <ul>
     * <li>Empty lines are ignored.</li>
     * <li>Lines starting with either a "#" or a "!" are comment lines and are
     * ignored.</li>
     * <li>A backslash at the end of the line escapes the following newline
     * character ("\r", "\n", "\r\n"). If there's a whitespace after the
     * backslash it will just escape that whitespace instead of concatenating
     * the lines. This does not apply to comment lines.</li>
     * <li>A property line consists of the key, the space between the key and
     * the value, and the value. The key goes up to the first whitespace, "=" or
     * ":" that is not escaped. The space between the key and the value contains
     * either one whitespace, one "=" or one ":" and any number of additional
     * whitespaces before and after that character. The value starts with the
     * first character after the space between the key and the value.</li>
     * <li>Following escape sequences are recognized: "\ ", "\\", "\r", "\n",
     * "\!", "\#", "\t", "\b", "\f", and "&#92;uXXXX" (unicode character).</li>
     * </ul>
     *
     * @param reader Reader from which to read the properties of this CrashReportData.
     * @return CrashReportData read from the supplied Reader.
     * @throws java.io.IOException if the properties could not be read.
     * @since 1.6
     */
@NonNull
private synchronized CrashReportData legacyLoad(@NonNull Reader reader) throws IOException {
    int mode = NONE, unicode = 0, count = 0;
    char nextChar;
    char[] buf = new char[40];
    int offset = 0, keyLength = -1, intVal;
    boolean firstChar = true;
    final CrashReportData crashData = new CrashReportData();
    final BufferedReader br = new BufferedReader(reader, ACRAConstants.DEFAULT_BUFFER_SIZE_IN_BYTES);
    try {
        while (true) {
            intVal = br.read();
            if (intVal == -1) {
                break;
            }
            nextChar = (char) intVal;
            if (offset == buf.length) {
                final char[] newBuf = new char[buf.length * 2];
                System.arraycopy(buf, 0, newBuf, 0, offset);
                buf = newBuf;
            }
            if (mode == UNICODE) {
                final int digit = Character.digit(nextChar, 16);
                if (digit >= 0) {
                    unicode = (unicode << 4) + digit;
                    if (++count < 4) {
                        continue;
                    }
                } else if (count <= 4) {
                    // luni.09=Invalid Unicode sequence: illegal character
                    throw new IllegalArgumentException("luni.09");
                }
                mode = NONE;
                buf[offset++] = (char) unicode;
                if (nextChar != '\n' && nextChar != '…') {
                    continue;
                }
            }
            if (mode == SLASH) {
                mode = NONE;
                switch(nextChar) {
                    case '\r':
                        // Look for a following \n
                        mode = CONTINUE;
                        continue;
                    case '…':
                    case '\n':
                        // Ignore whitespace on the next line
                        mode = IGNORE;
                        continue;
                    case 'b':
                        nextChar = '\b';
                        break;
                    case 'f':
                        nextChar = '\f';
                        break;
                    case 'n':
                        nextChar = '\n';
                        break;
                    case 'r':
                        nextChar = '\r';
                        break;
                    case 't':
                        nextChar = '\t';
                        break;
                    case 'u':
                        mode = UNICODE;
                        unicode = count = 0;
                        continue;
                }
            } else {
                switch(nextChar) {
                    case '#':
                    case '!':
                        if (firstChar) {
                            while (true) {
                                intVal = br.read();
                                if (intVal == -1) {
                                    break;
                                }
                                // & 0xff
                                nextChar = (char) intVal;
                                // required
                                if (nextChar == '\r' || nextChar == '\n' || nextChar == '…') {
                                    break;
                                }
                            }
                            continue;
                        }
                        break;
                    case '\n':
                        if (mode == CONTINUE) {
                            // Part of a \r\n sequence
                            // Ignore whitespace on the next line
                            mode = IGNORE;
                            continue;
                        }
                    // fall into the next case
                    case '…':
                    case '\r':
                        mode = NONE;
                        firstChar = true;
                        if (offset > 0 || (offset == 0 && keyLength == 0)) {
                            if (keyLength == -1) {
                                keyLength = offset;
                            }
                            final String temp = new String(buf, 0, offset);
                            final String elementString = temp.substring(keyLength);
                            Element element;
                            try {
                                element = new ComplexElement(elementString);
                            } catch (JSONException e1) {
                                try {
                                    element = new NumberElement(Double.valueOf(elementString));
                                } catch (NumberFormatException e2) {
                                    if (elementString.equals("true")) {
                                        element = new BooleanElement(true);
                                    } else if (elementString.equals("false")) {
                                        element = new BooleanElement(false);
                                    } else {
                                        element = new StringElement(elementString);
                                    }
                                }
                            }
                            crashData.put(Enum.valueOf(ReportField.class, temp.substring(0, keyLength)), element);
                        }
                        keyLength = -1;
                        offset = 0;
                        continue;
                    case '\\':
                        if (mode == KEY_DONE) {
                            keyLength = offset;
                        }
                        mode = SLASH;
                        continue;
                    case ':':
                    case '=':
                        if (keyLength == -1) {
                            // if parsing the key
                            mode = NONE;
                            keyLength = offset;
                            continue;
                        }
                        break;
                }
                if (Character.isWhitespace(nextChar)) {
                    if (mode == CONTINUE) {
                        mode = IGNORE;
                    }
                    // if key length == 0 or value length == 0
                    if (offset == 0 || offset == keyLength || mode == IGNORE) {
                        continue;
                    }
                    if (keyLength == -1) {
                        // if parsing the key
                        mode = KEY_DONE;
                        continue;
                    }
                }
                if (mode == IGNORE || mode == CONTINUE) {
                    mode = NONE;
                }
            }
            firstChar = false;
            if (mode == KEY_DONE) {
                keyLength = offset;
                mode = NONE;
            }
            buf[offset++] = nextChar;
        }
        if (mode == UNICODE && count <= 4) {
            // luni.08=Invalid Unicode sequence: expected format \\uxxxx
            throw new IllegalArgumentException("luni.08");
        }
        if (keyLength == -1 && offset > 0) {
            keyLength = offset;
        }
        if (keyLength >= 0) {
            final String temp = new String(buf, 0, offset);
            final ReportField key = Enum.valueOf(ReportField.class, temp.substring(0, keyLength));
            String value = temp.substring(keyLength);
            if (mode == SLASH) {
                value += "";
            }
            Element element;
            try {
                element = new ComplexElement(value);
            } catch (JSONException e1) {
                try {
                    element = new NumberElement(Double.valueOf(value));
                } catch (NumberFormatException e2) {
                    if (value.equals("true")) {
                        element = new BooleanElement(true);
                    } else if (value.equals("false")) {
                        element = new BooleanElement(false);
                    } else {
                        element = new StringElement(value);
                    }
                }
            }
            crashData.put(key, element);
        }
        IOUtils.safeClose(reader);
        return crashData;
    } finally {
        IOUtils.safeClose(br);
    }
}
Also used : StringElement(org.acra.model.StringElement) Element(org.acra.model.Element) StringElement(org.acra.model.StringElement) ComplexElement(org.acra.model.ComplexElement) BooleanElement(org.acra.model.BooleanElement) NumberElement(org.acra.model.NumberElement) NumberElement(org.acra.model.NumberElement) JSONException(org.json.JSONException) BooleanElement(org.acra.model.BooleanElement) CrashReportData(org.acra.collector.CrashReportData) ReportField(org.acra.ReportField) BufferedReader(java.io.BufferedReader) ComplexElement(org.acra.model.ComplexElement) NonNull(android.support.annotation.NonNull)

Example 4 with CrashReportData

use of org.acra.collector.CrashReportData in project acra by ACRA.

the class ReportConverter method convert.

void convert() {
    ACRA.log.i(LOG_TAG, "Converting unsent ACRA reports to json");
    final ReportLocator locator = new ReportLocator(context);
    final CrashReportPersister persister = new CrashReportPersister();
    final List<File> reportFiles = new ArrayList<File>();
    reportFiles.addAll(Arrays.asList(locator.getUnapprovedReports()));
    reportFiles.addAll(Arrays.asList(locator.getApprovedReports()));
    int converted = 0;
    for (File report : reportFiles) {
        InputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(report), ACRAConstants.DEFAULT_BUFFER_SIZE_IN_BYTES);
            //$NON-NLS-1$
            CrashReportData data = legacyLoad(new InputStreamReader(in, "ISO8859-1"));
            if (data.containsKey(ReportField.REPORT_ID) && data.containsKey(ReportField.USER_CRASH_DATE)) {
                persister.store(data, report);
                converted++;
            } else {
                //reports without these keys are probably invalid
                IOUtils.deleteReport(report);
            }
        } catch (Throwable e) {
            try {
                //If this succeeds the report has already been converted, happens e.g. on preference clear.
                persister.load(report);
                if (ACRA.DEV_LOGGING)
                    ACRA.log.d(LOG_TAG, "Tried to convert already converted report file " + report.getPath() + ". Ignoring");
            } catch (Throwable t) {
                //File matches neither of the known formats, remove it.
                ACRA.log.w(LOG_TAG, "Unable to read report file " + report.getPath() + ". Deleting", e);
                IOUtils.deleteReport(report);
            }
        } finally {
            IOUtils.safeClose(in);
        }
    }
    ACRA.log.i(LOG_TAG, "Converted " + converted + " unsent reports");
}
Also used : CrashReportData(org.acra.collector.CrashReportData) InputStreamReader(java.io.InputStreamReader) BufferedInputStream(java.io.BufferedInputStream) BufferedInputStream(java.io.BufferedInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) ReportLocator(org.acra.file.ReportLocator) ArrayList(java.util.ArrayList) CrashReportPersister(org.acra.file.CrashReportPersister) File(java.io.File) FileInputStream(java.io.FileInputStream)

Example 5 with CrashReportData

use of org.acra.collector.CrashReportData in project acra by ACRA.

the class ReportDistributor method distribute.

/**
     * Send report via all senders.
     *
     * @param reportFile    Report to send.
     */
public void distribute(@NonNull File reportFile) {
    ACRA.log.i(LOG_TAG, "Sending report " + reportFile);
    try {
        final CrashReportPersister persister = new CrashReportPersister();
        final CrashReportData previousCrashReport = persister.load(reportFile);
        sendCrashReport(previousCrashReport);
        IOUtils.deleteReport(reportFile);
    } catch (RuntimeException e) {
        ACRA.log.e(LOG_TAG, "Failed to send crash reports for " + reportFile, e);
        IOUtils.deleteReport(reportFile);
    } catch (IOException e) {
        ACRA.log.e(LOG_TAG, "Failed to load crash report for " + reportFile, e);
        IOUtils.deleteReport(reportFile);
    } catch (JSONException e) {
        ACRA.log.e(LOG_TAG, "Failed to load crash report for " + reportFile, e);
        IOUtils.deleteReport(reportFile);
    } catch (ReportSenderException e) {
        ACRA.log.e(LOG_TAG, "Failed to send crash report for " + reportFile, e);
    // An issue occurred while sending this report but we can still try to
    // send other reports. Report sending is limited by ACRAConstants.MAX_SEND_REPORTS
    // so there's not much to fear about overloading a failing server.
    }
}
Also used : CrashReportData(org.acra.collector.CrashReportData) JSONException(org.json.JSONException) CrashReportPersister(org.acra.file.CrashReportPersister) IOException(java.io.IOException)

Aggregations

CrashReportData (org.acra.collector.CrashReportData)6 JSONException (org.json.JSONException)4 CrashReportPersister (org.acra.file.CrashReportPersister)3 File (java.io.File)2 IOException (java.io.IOException)2 ReportField (org.acra.ReportField)2 ComplexElement (org.acra.model.ComplexElement)2 SharedPreferences (android.content.SharedPreferences)1 NonNull (android.support.annotation.NonNull)1 BufferedInputStream (java.io.BufferedInputStream)1 BufferedReader (java.io.BufferedReader)1 FileInputStream (java.io.FileInputStream)1 InputStream (java.io.InputStream)1 InputStreamReader (java.io.InputStreamReader)1 ArrayList (java.util.ArrayList)1 ReportingInteractionMode (org.acra.ReportingInteractionMode)1 ReportLocator (org.acra.file.ReportLocator)1 BooleanElement (org.acra.model.BooleanElement)1 Element (org.acra.model.Element)1 NumberElement (org.acra.model.NumberElement)1