Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(115)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java

Issue 2737263006: [Android Crash Reporting] Allow uploading minidumps via the JobScheduler (Closed)
Patch Set: Use shared prefs Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.crash; 5 package org.chromium.chrome.browser.crash;
6 6
7 import android.annotation.SuppressLint;
7 import android.app.IntentService; 8 import android.app.IntentService;
9 import android.app.job.JobInfo;
10 import android.content.ComponentName;
8 import android.content.Context; 11 import android.content.Context;
9 import android.content.Intent; 12 import android.content.Intent;
13 import android.os.Build;
14 import android.os.PersistableBundle;
10 import android.support.annotation.StringDef; 15 import android.support.annotation.StringDef;
11 16
12 import org.chromium.base.Log; 17 import org.chromium.base.Log;
13 import org.chromium.base.StreamUtil; 18 import org.chromium.base.StreamUtil;
14 import org.chromium.base.VisibleForTesting; 19 import org.chromium.base.VisibleForTesting;
15 import org.chromium.base.annotations.CalledByNative; 20 import org.chromium.base.annotations.CalledByNative;
16 import org.chromium.base.metrics.RecordHistogram; 21 import org.chromium.base.metrics.RecordHistogram;
22 import org.chromium.chrome.browser.ChromeFeatureList;
17 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; 23 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
18 import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager ; 24 import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager ;
19 import org.chromium.components.minidump_uploader.CrashFileManager; 25 import org.chromium.components.minidump_uploader.CrashFileManager;
20 import org.chromium.components.minidump_uploader.MinidumpUploadCallable; 26 import org.chromium.components.minidump_uploader.MinidumpUploadCallable;
27 import org.chromium.components.minidump_uploader.MinidumpUploadJobService;
21 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionMa nager; 28 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionMa nager;
22 29
23 import java.io.BufferedReader; 30 import java.io.BufferedReader;
24 import java.io.File; 31 import java.io.File;
25 import java.io.FileReader; 32 import java.io.FileReader;
26 import java.io.IOException; 33 import java.io.IOException;
27 34
28 /** 35 /**
29 * Service that is responsible for uploading crash minidumps to the Google crash server. 36 * Service that is responsible for uploading crash minidumps to the Google crash server.
30 */ 37 */
31 public class MinidumpUploadService extends IntentService { 38 public class MinidumpUploadService extends IntentService {
32 private static final String TAG = "MinidmpUploadService"; 39 private static final String TAG = "MinidmpUploadService";
33 40
34 // Intent actions 41 // Intent actions
35 @VisibleForTesting 42 @VisibleForTesting
36 static final String ACTION_UPLOAD = "com.google.android.apps.chrome.crash.AC TION_UPLOAD"; 43 static final String ACTION_UPLOAD = "com.google.android.apps.chrome.crash.AC TION_UPLOAD";
37 44
38 // Intent bundle keys 45 // Intent bundle keys
39 @VisibleForTesting 46 @VisibleForTesting
40 static final String FILE_TO_UPLOAD_KEY = "minidump_file"; 47 static final String FILE_TO_UPLOAD_KEY = "minidump_file";
41 static final String UPLOAD_LOG_KEY = "upload_log"; 48 static final String UPLOAD_LOG_KEY = "upload_log";
42 49
43 /** 50 /**
51 * The job id for uploading minidumps. For more info on this constant, see
52 * https://developer.android.com/reference/android/app/job/JobInfo.Builder.h tml#JobInfo.Builder(int,%20android.content.ComponentName)
53 */
54 private static final int MINIDUMP_UPLOADING_JOB_ID = 142;
gsennton 2017/03/14 18:17:28 I just realized it might be good to look for the I
Ilya Sherman 2017/03/15 02:13:34 Good call! Moved the constants into that componen
gsennton 2017/03/15 12:57:43 Indeed, I wonder if there will be anything stoppin
55
56 /**
44 * The number of times we will try to upload a crash. 57 * The number of times we will try to upload a crash.
45 */ 58 */
46 public static final int MAX_TRIES_ALLOWED = 3; 59 public static final int MAX_TRIES_ALLOWED = 3;
47 60
48 /** 61 /**
49 * Histogram related constants. 62 * Histogram related constants.
50 */ 63 */
51 private static final String HISTOGRAM_NAME_PREFIX = "Tab.AndroidCrashUpload_ "; 64 private static final String HISTOGRAM_NAME_PREFIX = "Tab.AndroidCrashUpload_ ";
52 private static final int HISTOGRAM_MAX = 2; 65 private static final int HISTOGRAM_MAX = 2;
53 private static final int FAILURE = 0; 66 private static final int FAILURE = 0;
54 private static final int SUCCESS = 1; 67 private static final int SUCCESS = 1;
55 68
56 @StringDef({BROWSER, RENDERER, GPU, OTHER}) 69 @StringDef({BROWSER, RENDERER, GPU, OTHER})
57 public @interface ProcessType {} 70 public @interface ProcessType {}
58 static final String BROWSER = "Browser"; 71 static final String BROWSER = "Browser";
59 static final String RENDERER = "Renderer"; 72 static final String RENDERER = "Renderer";
60 static final String GPU = "GPU"; 73 static final String GPU = "GPU";
61 static final String OTHER = "Other"; 74 static final String OTHER = "Other";
62 75
63 static final String[] TYPES = {BROWSER, RENDERER, GPU, OTHER}; 76 static final String[] TYPES = {BROWSER, RENDERER, GPU, OTHER};
64 77
65 public MinidumpUploadService() { 78 public MinidumpUploadService() {
66 super(TAG); 79 super(TAG);
67 setIntentRedelivery(true); 80 setIntentRedelivery(true);
68 } 81 }
69 82
70 /** 83 /**
84 * @return Whether to use the JobSchduler API to upload crash reports, rathe r than directly
85 * creating a service for uploading.
86 */
87 public static boolean shouldUseJobSchedulerForUploads() {
88 // The JobScheduler API is only available as of Android L.
89 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
90 return false;
91 }
92
93 return ChromeFeatureList.isEnabled(
94 ChromeFeatureList.UPLOAD_CRASH_REPORTS_USING_JOB_SCHEDULER);
95 }
96
97 /**
98 * Schedules uploading of all pending minidumps, using the JobScheduler API.
99 */
100 @SuppressLint("NewApi")
101 public static void scheduleUploadJob(Context context) {
102 assert shouldUseJobSchedulerForUploads();
103
104 CrashReportingPermissionManager permissionManager = PrivacyPreferencesMa nager.getInstance();
105 PersistableBundle permissions = new PersistableBundle();
106 // Note: putBoolean is only available in API level 22, so massage the da ta into ints
107 // instead.
108 permissions.putInt(ChromeMinidumpUploaderDelegate.IS_CLIENT_IN_METRICS_S AMPLE,
109 permissionManager.isClientInMetricsSample() ? 1 : 0);
110 permissions.putInt(ChromeMinidumpUploaderDelegate.IS_CRASH_UPLOAD_DISABL ED_BY_COMMAND_LINE,
111 permissionManager.isCrashUploadDisabledByCommandLine() ? 1 : 0);
112 permissions.putInt(ChromeMinidumpUploaderDelegate.IS_UPLOAD_ENABLED_FOR_ TESTS,
113 permissionManager.isUploadEnabledForTests() ? 1 : 0);
114
115 JobInfo.Builder builder =
116 new JobInfo
117 .Builder(MINIDUMP_UPLOADING_JOB_ID,
118 new ComponentName(context, ChromeMinidumpUploadJ obService.class))
119 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
120 .setExtras(permissions);
121 MinidumpUploadJobService.scheduleUpload(context, builder);
122 }
123
124 /**
71 * Stores the successes and failures from uploading crash to UMA, 125 * Stores the successes and failures from uploading crash to UMA,
72 */ 126 */
73 public static void storeBreakpadUploadStatsInUma(ChromePreferenceManager pre f) { 127 public static void storeBreakpadUploadStatsInUma(ChromePreferenceManager pre f) {
74 for (String type : TYPES) { 128 for (String type : TYPES) {
75 for (int success = pref.getCrashSuccessUploadCount(type); success > 0; success--) { 129 for (int success = pref.getCrashSuccessUploadCount(type); success > 0; success--) {
76 RecordHistogram.recordEnumeratedHistogram( 130 RecordHistogram.recordEnumeratedHistogram(
77 HISTOGRAM_NAME_PREFIX + type, SUCCESS, HISTOGRAM_MAX); 131 HISTOGRAM_NAME_PREFIX + type, SUCCESS, HISTOGRAM_MAX);
78 } 132 }
79 for (int fail = pref.getCrashFailureUploadCount(type); fail > 0; fai l--) { 133 for (int fail = pref.getCrashFailureUploadCount(type); fail > 0; fai l--) {
80 RecordHistogram.recordEnumeratedHistogram( 134 RecordHistogram.recordEnumeratedHistogram(
(...skipping 18 matching lines...) Expand all
99 Log.w(TAG, "Cannot upload crash data since minidump is absent."); 153 Log.w(TAG, "Cannot upload crash data since minidump is absent.");
100 return; 154 return;
101 } 155 }
102 File minidumpFile = new File(minidumpFileName); 156 File minidumpFile = new File(minidumpFileName);
103 if (!minidumpFile.isFile()) { 157 if (!minidumpFile.isFile()) {
104 Log.w(TAG, "Cannot upload crash data since specified minidump " 158 Log.w(TAG, "Cannot upload crash data since specified minidump "
105 + minidumpFileName + " is not present."); 159 + minidumpFileName + " is not present.");
106 return; 160 return;
107 } 161 }
108 int tries = CrashFileManager.readAttemptNumber(minidumpFileName); 162 int tries = CrashFileManager.readAttemptNumber(minidumpFileName);
109 // -1 means no attempt number was read.
110 if (tries == -1) {
111 tries = 0;
112 }
113 163
114 // Since we do not rename a file after reaching max number of tries, 164 // Since we do not rename a file after reaching max number of tries,
115 // files that have maxed out tries will NOT reach this. 165 // files that have maxed out tries will NOT reach this.
116 if (tries >= MAX_TRIES_ALLOWED || tries < 0) { 166 if (tries >= MAX_TRIES_ALLOWED || tries < 0) {
117 // Reachable only if the file naming is incorrect by current standar d. 167 // Reachable only if the file naming is incorrect by current standar d.
118 // Thus we log an error instead of recording failure to UMA. 168 // Thus we log an error instead of recording failure to UMA.
119 Log.e(TAG, "Giving up on trying to upload " + minidumpFileName 169 Log.e(TAG, "Giving up on trying to upload " + minidumpFileName
120 + " after failing to read a valid attempt number."); 170 + " after failing to read a valid attempt number.");
121 return; 171 return;
122 } 172 }
123 173
124 String logfileName = intent.getStringExtra(UPLOAD_LOG_KEY); 174 String logfileName = intent.getStringExtra(UPLOAD_LOG_KEY);
125 File logfile = new File(logfileName); 175 File logfile = new File(logfileName);
126 176
127 // Try to upload minidump 177 // Try to upload minidump
128 MinidumpUploadCallable minidumpUploadCallable = 178 MinidumpUploadCallable minidumpUploadCallable =
129 createMinidumpUploadCallable(minidumpFile, logfile); 179 createMinidumpUploadCallable(minidumpFile, logfile);
130 @MinidumpUploadCallable.MinidumpUploadStatus int uploadStatus = 180 @MinidumpUploadCallable.MinidumpUploadStatus int uploadStatus =
131 minidumpUploadCallable.call(); 181 minidumpUploadCallable.call();
132 182
133 if (uploadStatus == MinidumpUploadCallable.UPLOAD_SUCCESS) { 183 if (uploadStatus == MinidumpUploadCallable.UPLOAD_SUCCESS) {
134 // Only update UMA stats if an intended and successful upload. 184 // Only update UMA stats if an intended and successful upload.
135 incrementCrashSuccessUploadCount(getNewNameAfterSuccessfulUpload(min idumpFileName)); 185 incrementCrashSuccessUploadCount(minidumpFileName);
136 } else if (uploadStatus == MinidumpUploadCallable.UPLOAD_FAILURE) { 186 } else if (uploadStatus == MinidumpUploadCallable.UPLOAD_FAILURE) {
137 // Unable to upload minidump. Incrementing try number and restarting . 187 // Unable to upload minidump. Incrementing try number and restarting .
138 188
139 // Only create another attempt if we have successfully renamed 189 // Only create another attempt if we have successfully renamed
140 // the file. 190 // the file.
141 String newName = CrashFileManager.tryIncrementAttemptNumber(minidump File); 191 String newName = CrashFileManager.tryIncrementAttemptNumber(minidump File);
142 if (newName != null) { 192 if (newName != null) {
143 if (++tries < MAX_TRIES_ALLOWED) { 193 if (++tries < MAX_TRIES_ALLOWED) {
144 // TODO(nyquist): Do this as an exponential backoff. 194 // TODO(nyquist): Do this as an exponential backoff.
145 MinidumpUploadRetry.scheduleRetry( 195 MinidumpUploadRetry.scheduleRetry(
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 StreamUtil.closeQuietly(fileReader); 255 StreamUtil.closeQuietly(fileReader);
206 } 256 }
207 return OTHER; 257 return OTHER;
208 } 258 }
209 259
210 /** 260 /**
211 * Increment the count of success/failure by 1 and distinguish between diffe rent types of 261 * Increment the count of success/failure by 1 and distinguish between diffe rent types of
212 * crashes by looking into the file. 262 * crashes by looking into the file.
213 * @param fileName is the name of a minidump file that contains the type of crash. 263 * @param fileName is the name of a minidump file that contains the type of crash.
214 */ 264 */
215 private void incrementCrashSuccessUploadCount(String fileName) { 265 public static void incrementCrashSuccessUploadCount(String fileName) {
216 ChromePreferenceManager.getInstance().incrementCrashSuccessUploadCount( 266 ChromePreferenceManager.getInstance().incrementCrashSuccessUploadCount(
217 getCrashType(fileName)); 267 getCrashType(getNewNameAfterSuccessfulUpload(fileName)));
gsennton 2017/03/14 18:17:28 Hiding this call in here seems like an implementat
Ilya Sherman 2017/03/15 02:13:34 That's a very good point, and I actually had a bug
gsennton 2017/03/15 12:57:43 Yeah, that looks fine ;).
218 } 268 }
219 269
220 private void incrementCrashFailureUploadCount(String fileName) { 270 public static void incrementCrashFailureUploadCount(String fileName) {
221 ChromePreferenceManager.getInstance().incrementCrashFailureUploadCount( 271 ChromePreferenceManager.getInstance().incrementCrashFailureUploadCount(
222 getCrashType(fileName)); 272 getCrashType(fileName));
223 } 273 }
224 274
225 /** 275 /**
226 * Factory method for creating minidump callables. 276 * Factory method for creating minidump callables.
227 * 277 *
228 * This may be overridden for tests. 278 * This may be overridden for tests.
229 * 279 *
230 * @param minidumpFile the File to upload. 280 * @param minidumpFile the File to upload.
(...skipping 11 matching lines...) Expand all
242 * 292 *
243 * Note that this method is asynchronous. All that is guaranteed is that an upload attempt will 293 * Note that this method is asynchronous. All that is guaranteed is that an upload attempt will
244 * be enqueued. 294 * be enqueued.
245 * 295 *
246 * @param context The application context, in which to initiate the crash re port upload. 296 * @param context The application context, in which to initiate the crash re port upload.
247 * @throws A security excpetion if the caller doesn't have permission to sta rt the upload 297 * @throws A security excpetion if the caller doesn't have permission to sta rt the upload
248 * service. This can only happen on KitKat and below, due to a frame work bug. 298 * service. This can only happen on KitKat and below, due to a frame work bug.
249 */ 299 */
250 public static void tryUploadCrashDump(Context context, File minidumpFile) 300 public static void tryUploadCrashDump(Context context, File minidumpFile)
251 throws SecurityException { 301 throws SecurityException {
302 assert !shouldUseJobSchedulerForUploads();
252 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( )); 303 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( ));
253 Intent intent = new Intent(context, MinidumpUploadService.class); 304 Intent intent = new Intent(context, MinidumpUploadService.class);
254 intent.setAction(ACTION_UPLOAD); 305 intent.setAction(ACTION_UPLOAD);
255 intent.putExtra(FILE_TO_UPLOAD_KEY, minidumpFile.getAbsolutePath()); 306 intent.putExtra(FILE_TO_UPLOAD_KEY, minidumpFile.getAbsolutePath());
256 intent.putExtra(UPLOAD_LOG_KEY, fileManager.getCrashUploadLogFile().getA bsolutePath()); 307 intent.putExtra(UPLOAD_LOG_KEY, fileManager.getCrashUploadLogFile().getA bsolutePath());
257 context.startService(intent); 308 context.startService(intent);
258 } 309 }
259 310
260 /** 311 /**
261 * Attempts to upload all minidump files using the given {@link android.cont ent.Context}. 312 * Attempts to upload all minidump files using the given {@link android.cont ent.Context}.
262 * 313 *
263 * Note that this method is asynchronous. All that is guaranteed is that 314 * Note that this method is asynchronous. All that is guaranteed is that
264 * upload attempts will be enqueued. 315 * upload attempts will be enqueued.
265 * 316 *
266 * This method is safe to call from the UI thread. 317 * This method is safe to call from the UI thread.
267 * 318 *
268 * @param context Context of the application. 319 * @param context Context of the application.
269 */ 320 */
270 public static void tryUploadAllCrashDumps(Context context) { 321 public static void tryUploadAllCrashDumps(Context context) {
322 assert !shouldUseJobSchedulerForUploads();
271 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( )); 323 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( ));
272 File[] minidumps = fileManager.getAllMinidumpFiles(MAX_TRIES_ALLOWED); 324 File[] minidumps = fileManager.getAllMinidumpFiles(MAX_TRIES_ALLOWED);
273 Log.i(TAG, "Attempting to upload accumulated crash dumps."); 325 Log.i(TAG, "Attempting to upload accumulated crash dumps.");
274 for (File minidump : minidumps) { 326 for (File minidump : minidumps) {
275 MinidumpUploadService.tryUploadCrashDump(context, minidump); 327 tryUploadCrashDump(context, minidump);
276 } 328 }
277 } 329 }
278 330
279 /** 331 /**
280 * Attempts to upload the crash report with the given local ID. 332 * Attempts to upload the crash report with the given local ID.
281 * 333 *
282 * Note that this method is asynchronous. All that is guaranteed is that 334 * Note that this method is asynchronous. All that is guaranteed is that
283 * upload attempts will be enqueued. 335 * upload attempts will be enqueued.
284 * 336 *
285 * This method is safe to call from the UI thread. 337 * This method is safe to call from the UI thread.
(...skipping 12 matching lines...) Expand all
298 File minidumpFile = fileManager.getCrashFileWithLocalId(localId); 350 File minidumpFile = fileManager.getCrashFileWithLocalId(localId);
299 if (minidumpFile == null) { 351 if (minidumpFile == null) {
300 Log.w(TAG, "Could not find a crash dump with local ID " + localId); 352 Log.w(TAG, "Could not find a crash dump with local ID " + localId);
301 return; 353 return;
302 } 354 }
303 File renamedMinidumpFile = CrashFileManager.trySetForcedUpload(minidumpF ile); 355 File renamedMinidumpFile = CrashFileManager.trySetForcedUpload(minidumpF ile);
304 if (renamedMinidumpFile == null) { 356 if (renamedMinidumpFile == null) {
305 Log.w(TAG, "Could not rename the file " + minidumpFile.getName() + " for re-upload"); 357 Log.w(TAG, "Could not rename the file " + minidumpFile.getName() + " for re-upload");
306 return; 358 return;
307 } 359 }
308 MinidumpUploadService.tryUploadCrashDump(context, renamedMinidumpFile); 360
361 if (shouldUseJobSchedulerForUploads()) {
362 scheduleUploadJob(context);
363 } else {
364 tryUploadCrashDump(context, renamedMinidumpFile);
365 }
309 } 366 }
310 } 367 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698