Index: chrome/android/java_staging/src/org/chromium/chrome/browser/crash/MinidumpUploadCallable.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/crash/MinidumpUploadCallable.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/crash/MinidumpUploadCallable.java |
deleted file mode 100644 |
index 888d1859c01abe570b91844ac07cdfe2e84e6897..0000000000000000000000000000000000000000 |
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/crash/MinidumpUploadCallable.java |
+++ /dev/null |
@@ -1,332 +0,0 @@ |
-// Copyright 2015 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-package org.chromium.chrome.browser.crash; |
- |
-import android.content.Context; |
-import android.content.SharedPreferences; |
-import android.preference.PreferenceManager; |
-import android.util.Log; |
- |
-import org.chromium.base.VisibleForTesting; |
-import org.chromium.chrome.browser.preferences.privacy.CrashReportingPermissionManager; |
-import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager; |
-import org.chromium.chrome.browser.util.HttpURLConnectionFactory; |
-import org.chromium.chrome.browser.util.HttpURLConnectionFactoryImpl; |
-import org.chromium.chrome.browser.util.StreamUtil; |
- |
-import java.io.BufferedReader; |
-import java.io.ByteArrayOutputStream; |
-import java.io.File; |
-import java.io.FileInputStream; |
-import java.io.FileReader; |
-import java.io.FileWriter; |
-import java.io.IOException; |
-import java.io.InputStream; |
-import java.io.OutputStream; |
-import java.net.HttpURLConnection; |
-import java.util.Calendar; |
-import java.util.Locale; |
-import java.util.concurrent.Callable; |
-import java.util.zip.GZIPOutputStream; |
- |
-/** |
- * This class tries to upload a minidump to the crash server. |
- * |
- * It is implemented as a Callable<Boolean> and returns true on successful uploads, |
- * and false otherwise. |
- */ |
-public class MinidumpUploadCallable implements Callable<Boolean> { |
- private static final String TAG = "MinidumpUploadCallable"; |
- @VisibleForTesting protected static final int LOG_SIZE_LIMIT_BYTES = 1024 * 1024; // 1MB |
- @VisibleForTesting protected static final int LOG_UPLOAD_LIMIT_PER_DAY = 5; |
- |
- @VisibleForTesting |
- protected static final String PREF_LAST_UPLOAD_DAY = "crash_dump_last_upload_day"; |
- @VisibleForTesting protected static final String PREF_UPLOAD_COUNT = "crash_dump_upload_count"; |
- |
- @VisibleForTesting |
- protected static final String CRASH_URL_STRING = "https://clients2.google.com/cr/report"; |
- |
- @VisibleForTesting |
- protected static final String CONTENT_TYPE_TMPL = "multipart/form-data; boundary=%s"; |
- |
- private final File mFileToUpload; |
- private final File mLogfile; |
- private final HttpURLConnectionFactory mHttpURLConnectionFactory; |
- private final CrashReportingPermissionManager mPermManager; |
- private final SharedPreferences mSharedPreferences; |
- |
- public MinidumpUploadCallable(File fileToUpload, File logfile, Context context) { |
- this(fileToUpload, logfile, new HttpURLConnectionFactoryImpl(), |
- PrivacyPreferencesManager.getInstance(context), |
- PreferenceManager.getDefaultSharedPreferences(context)); |
- } |
- |
- public MinidumpUploadCallable(File fileToUpload, File logfile, |
- HttpURLConnectionFactory httpURLConnectionFactory, |
- CrashReportingPermissionManager permManager, SharedPreferences sharedPreferences) { |
- mFileToUpload = fileToUpload; |
- mLogfile = logfile; |
- mHttpURLConnectionFactory = httpURLConnectionFactory; |
- mPermManager = permManager; |
- mSharedPreferences = sharedPreferences; |
- } |
- |
- @Override |
- public Boolean call() { |
- if (!mPermManager.isUploadPermitted()) { |
- Log.i(TAG, "Minidump upload is not permitted"); |
- return false; |
- } |
- |
- boolean isLimited = mPermManager.isUploadLimited(); |
- if (isLimited && !isUploadSizeAndFrequencyAllowed()) { |
- Log.i(TAG, "Minidump cannot currently be uploaded due to constraints"); |
- return false; |
- } |
- |
- HttpURLConnection connection = |
- mHttpURLConnectionFactory.createHttpURLConnection(CRASH_URL_STRING); |
- if (connection == null) { |
- return false; |
- } |
- |
- FileInputStream minidumpInputStream = null; |
- try { |
- if (!configureConnectionForHttpPost(connection)) { |
- return false; |
- } |
- minidumpInputStream = new FileInputStream(mFileToUpload); |
- streamCopy(minidumpInputStream, new GZIPOutputStream(connection.getOutputStream())); |
- boolean status = handleExecutionResponse(connection); |
- |
- if (isLimited) updateUploadPrefs(); |
- return status; |
- } catch (IOException e) { |
- // For now just log the stack trace. |
- Log.w(TAG, "Error while uploading " + mFileToUpload.getName(), e); |
- return false; |
- } finally { |
- connection.disconnect(); |
- |
- if (minidumpInputStream != null) { |
- StreamUtil.closeQuietly(minidumpInputStream); |
- } |
- } |
- } |
- |
- /** |
- * Configures a HttpURLConnection to send a HTTP POST request for uploading the minidump. |
- * |
- * This also reads the content-type from the minidump file. |
- * |
- * @param connection the HttpURLConnection to configure |
- * @return true if successful. |
- * @throws IOException |
- */ |
- private boolean configureConnectionForHttpPost(HttpURLConnection connection) |
- throws IOException { |
- // Read the boundary which we need for the content type. |
- String boundary = readBoundary(); |
- if (boundary == null) { |
- return false; |
- } |
- |
- connection.setDoOutput(true); |
- connection.setRequestProperty("Connection", "Keep-Alive"); |
- connection.setRequestProperty("Content-Encoding", "gzip"); |
- connection.setRequestProperty("Content-Type", String.format(CONTENT_TYPE_TMPL, boundary)); |
- return true; |
- } |
- |
- /** |
- * Reads the HTTP response and cleans up successful uploads. |
- * |
- * @param connection the connection to read the response from |
- * @return true if the upload was successful, false otherwise. |
- * @throws IOException |
- */ |
- private Boolean handleExecutionResponse(HttpURLConnection connection) throws IOException { |
- int responseCode = connection.getResponseCode(); |
- if (isSuccessful(responseCode)) { |
- String responseContent = getResponseContentAsString(connection); |
- // The crash server returns the crash ID. |
- String id = responseContent != null ? responseContent : "unknown"; |
- Log.i(TAG, "Minidump " + mFileToUpload.getName() + " uploaded successfully, id: " + id); |
- |
- // TODO(acleung): MinidumpUploadService is in charge of renaming while this class is |
- // in charge of deleting. We should move all the file system operations into |
- // MinidumpUploadService instead. |
- cleanupMinidumpFile(); |
- |
- try { |
- appendUploadedEntryToLog(id); |
- } catch (IOException ioe) { |
- Log.e(TAG, "Fail to write uploaded entry to log file"); |
- } |
- return true; |
- } else { |
- // Log the results of the upload. Note that periodic upload failures aren't bad |
- // because we will need to throttle uploads in the future anyway. |
- String msg = String.format(Locale.US, |
- "Failed to upload %s with code: %d (%s).", |
- mFileToUpload.getName(), responseCode, connection.getResponseMessage()); |
- Log.i(TAG, msg); |
- |
- // TODO(acleung): The return status informs us about why an upload might be |
- // rejected. The next logical step is to put the reasons in an UMA histogram. |
- return false; |
- } |
- } |
- |
- /** |
- * Records the upload entry to a log file |
- * similar to what is done in chrome/app/breakpad_linux.cc |
- * |
- * @param id The crash ID return from the server. |
- */ |
- private void appendUploadedEntryToLog(String id) throws IOException { |
- FileWriter writer = new FileWriter(mLogfile, /* Appending */ true); |
- |
- // The log entries are formated like so: |
- // seconds_since_epoch,crash_id |
- StringBuilder sb = new StringBuilder(); |
- sb.append(System.currentTimeMillis() / 1000); |
- sb.append(","); |
- sb.append(id); |
- sb.append('\n'); |
- |
- try { |
- // Since we are writing one line at a time, lets forget about BufferWriters. |
- writer.write(sb.toString()); |
- } finally { |
- writer.close(); |
- } |
- } |
- |
- /** |
- * Get the boundary from the file, we need it for the content-type. |
- * |
- * @return the boundary if found, else null. |
- * @throws IOException |
- */ |
- private String readBoundary() throws IOException { |
- BufferedReader reader = new BufferedReader(new FileReader(mFileToUpload)); |
- String boundary = reader.readLine(); |
- reader.close(); |
- if (boundary == null || boundary.trim().isEmpty()) { |
- Log.e(TAG, "Ignoring invalid crash dump: '" + mFileToUpload + "'"); |
- return null; |
- } |
- boundary = boundary.trim(); |
- if (!boundary.startsWith("--") || boundary.length() < 10) { |
- Log.e(TAG, "Ignoring invalidly bound crash dump: '" + mFileToUpload + "'"); |
- return null; |
- } |
- boundary = boundary.substring(2); // Remove the initial -- |
- return boundary; |
- } |
- |
- /** |
- * Mark file we just uploaded for cleanup later. |
- * |
- * We do not immediately delete the file for testing reasons, |
- * but if marking the file fails, we do delete it right away. |
- */ |
- private void cleanupMinidumpFile() { |
- if (!CrashFileManager.tryMarkAsUploaded(mFileToUpload)) { |
- Log.w(TAG, "Unable to mark " + mFileToUpload + " as uploaded."); |
- if (!mFileToUpload.delete()) { |
- Log.w(TAG, "Cannot delete " + mFileToUpload); |
- } |
- } |
- } |
- |
- /** |
- * Checks whether crash upload satisfies the size and frequency constraints. |
- * |
- * @return whether crash upload satisfies the size and frequency constraints. |
- */ |
- private boolean isUploadSizeAndFrequencyAllowed() { |
- // Check upload size constraint. |
- if (mFileToUpload.length() > LOG_SIZE_LIMIT_BYTES) return false; |
- |
- // Check upload frequency constraint. |
- // If pref doesn't exist then in both cases default value 0 will be returned and comparison |
- // always would be true. |
- if (mSharedPreferences.getInt(PREF_LAST_UPLOAD_DAY, 0) != getCurrentDay()) return true; |
- return mSharedPreferences.getInt(PREF_UPLOAD_COUNT, 0) < LOG_UPLOAD_LIMIT_PER_DAY; |
- } |
- |
- /** |
- * Updates preferences used for determining crash upload constraints. |
- */ |
- private void updateUploadPrefs() { |
- SharedPreferences.Editor editor = mSharedPreferences.edit(); |
- |
- int day = getCurrentDay(); |
- int prevCount = mSharedPreferences.getInt(PREF_UPLOAD_COUNT, 0); |
- if (mSharedPreferences.getInt(PREF_LAST_UPLOAD_DAY, 0) != day) { |
- prevCount = 0; |
- } |
- editor.putInt(PREF_LAST_UPLOAD_DAY, day).putInt(PREF_UPLOAD_COUNT, prevCount + 1).apply(); |
- } |
- |
- /** |
- * Returns number of current day in a year starting from 1. Overridden in tests. |
- */ |
- protected int getCurrentDay() { |
- return Calendar.getInstance().get(Calendar.YEAR) * 365 |
- + Calendar.getInstance().get(Calendar.DAY_OF_YEAR); |
- } |
- |
- /** |
- * Returns whether the response code indicates a successful HTTP request. |
- * |
- * @param responseCode the response code |
- * @return true if response code indicates success, false otherwise. |
- */ |
- private static boolean isSuccessful(int responseCode) { |
- return responseCode == 200 || responseCode == 201 || responseCode == 202; |
- } |
- |
- /** |
- * Reads the response from |connection| as a String. |
- * |
- * @param connection the connection to read the response from. |
- * @return the content of the response. |
- * @throws IOException |
- */ |
- private static String getResponseContentAsString(HttpURLConnection connection) |
- throws IOException { |
- String responseContent = null; |
- ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
- streamCopy(connection.getInputStream(), baos); |
- if (baos.size() > 0) { |
- responseContent = baos.toString(); |
- } |
- return responseContent; |
- } |
- |
- /** |
- * Copies all available data from |inStream| to |outStream|. Closes both |
- * streams when done. |
- * |
- * @param inStream the stream to read |
- * @param outStream the stream to write to |
- * @throws IOException |
- */ |
- private static void streamCopy(InputStream inStream, |
- OutputStream outStream) throws IOException { |
- byte[] temp = new byte[4096]; |
- int bytesRead = inStream.read(temp); |
- while (bytesRead >= 0) { |
- outStream.write(temp, 0, bytesRead); |
- bytesRead = inStream.read(temp); |
- } |
- inStream.close(); |
- outStream.close(); |
- } |
-} |