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

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: MinidumpUploadService tests 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 ;
25 import org.chromium.components.background_task_scheduler.TaskIds;
19 import org.chromium.components.minidump_uploader.CrashFileManager; 26 import org.chromium.components.minidump_uploader.CrashFileManager;
20 import org.chromium.components.minidump_uploader.MinidumpUploadCallable; 27 import org.chromium.components.minidump_uploader.MinidumpUploadCallable;
28 import org.chromium.components.minidump_uploader.MinidumpUploadJobService;
21 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionMa nager; 29 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionMa nager;
22 30
23 import java.io.BufferedReader; 31 import java.io.BufferedReader;
24 import java.io.File; 32 import java.io.File;
25 import java.io.FileReader; 33 import java.io.FileReader;
26 import java.io.IOException; 34 import java.io.IOException;
27 35
28 /** 36 /**
29 * Service that is responsible for uploading crash minidumps to the Google crash server. 37 * Service that is responsible for uploading crash minidumps to the Google crash server.
30 */ 38 */
(...skipping 30 matching lines...) Expand all
61 static final String OTHER = "Other"; 69 static final String OTHER = "Other";
62 70
63 static final String[] TYPES = {BROWSER, RENDERER, GPU, OTHER}; 71 static final String[] TYPES = {BROWSER, RENDERER, GPU, OTHER};
64 72
65 public MinidumpUploadService() { 73 public MinidumpUploadService() {
66 super(TAG); 74 super(TAG);
67 setIntentRedelivery(true); 75 setIntentRedelivery(true);
68 } 76 }
69 77
70 /** 78 /**
79 * @return Whether to use the JobSchduler API to upload crash reports, rathe r than directly
80 * creating a service for uploading.
81 */
82 public static boolean shouldUseJobSchedulerForUploads() {
83 // The JobScheduler API is only available as of Android L.
84 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return false;
85
86 return ChromeFeatureList.isEnabled(
87 ChromeFeatureList.UPLOAD_CRASH_REPORTS_USING_JOB_SCHEDULER);
88 }
89
90 /**
91 * Schedules uploading of all pending minidumps, using the JobScheduler API.
92 */
93 @SuppressLint("NewApi")
94 public static void scheduleUploadJob(Context context) {
95 assert shouldUseJobSchedulerForUploads();
96
97 CrashReportingPermissionManager permissionManager = PrivacyPreferencesMa nager.getInstance();
98 PersistableBundle permissions = new PersistableBundle();
99 // Note: putBoolean is only available in API level 22, so massage the da ta into ints
100 // instead.
101 permissions.putInt(ChromeMinidumpUploaderDelegate.IS_CLIENT_IN_METRICS_S AMPLE,
102 permissionManager.isClientInMetricsSample() ? 1 : 0);
103 permissions.putInt(ChromeMinidumpUploaderDelegate.IS_CRASH_UPLOAD_DISABL ED_BY_COMMAND_LINE,
104 permissionManager.isCrashUploadDisabledByCommandLine() ? 1 : 0);
105 permissions.putInt(ChromeMinidumpUploaderDelegate.IS_UPLOAD_ENABLED_FOR_ TESTS,
106 permissionManager.isUploadEnabledForTests() ? 1 : 0);
107
108 JobInfo.Builder builder =
109 new JobInfo
110 .Builder(TaskIds.CHROME_MINIDUMP_UPLOADING_JOB_ID,
111 new ComponentName(context, ChromeMinidumpUploadJ obService.class))
112 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
113 .setExtras(permissions);
114 MinidumpUploadJobService.scheduleUpload(context, builder);
115 }
116
117 /**
71 * Stores the successes and failures from uploading crash to UMA, 118 * Stores the successes and failures from uploading crash to UMA,
72 */ 119 */
73 public static void storeBreakpadUploadStatsInUma(ChromePreferenceManager pre f) { 120 public static void storeBreakpadUploadStatsInUma(ChromePreferenceManager pre f) {
74 for (String type : TYPES) { 121 for (String type : TYPES) {
75 for (int success = pref.getCrashSuccessUploadCount(type); success > 0; success--) { 122 for (int success = pref.getCrashSuccessUploadCount(type); success > 0; success--) {
76 RecordHistogram.recordEnumeratedHistogram( 123 RecordHistogram.recordEnumeratedHistogram(
77 HISTOGRAM_NAME_PREFIX + type, SUCCESS, HISTOGRAM_MAX); 124 HISTOGRAM_NAME_PREFIX + type, SUCCESS, HISTOGRAM_MAX);
78 } 125 }
79 for (int fail = pref.getCrashFailureUploadCount(type); fail > 0; fai l--) { 126 for (int fail = pref.getCrashFailureUploadCount(type); fail > 0; fai l--) {
80 RecordHistogram.recordEnumeratedHistogram( 127 RecordHistogram.recordEnumeratedHistogram(
(...skipping 18 matching lines...) Expand all
99 Log.w(TAG, "Cannot upload crash data since minidump is absent."); 146 Log.w(TAG, "Cannot upload crash data since minidump is absent.");
100 return; 147 return;
101 } 148 }
102 File minidumpFile = new File(minidumpFileName); 149 File minidumpFile = new File(minidumpFileName);
103 if (!minidumpFile.isFile()) { 150 if (!minidumpFile.isFile()) {
104 Log.w(TAG, "Cannot upload crash data since specified minidump " 151 Log.w(TAG, "Cannot upload crash data since specified minidump "
105 + minidumpFileName + " is not present."); 152 + minidumpFileName + " is not present.");
106 return; 153 return;
107 } 154 }
108 int tries = CrashFileManager.readAttemptNumber(minidumpFileName); 155 int tries = CrashFileManager.readAttemptNumber(minidumpFileName);
109 // -1 means no attempt number was read.
110 if (tries == -1) {
111 tries = 0;
112 }
113 156
114 // Since we do not rename a file after reaching max number of tries, 157 // Since we do not rename a file after reaching max number of tries,
115 // files that have maxed out tries will NOT reach this. 158 // files that have maxed out tries will NOT reach this.
116 if (tries >= MAX_TRIES_ALLOWED || tries < 0) { 159 if (tries >= MAX_TRIES_ALLOWED || tries < 0) {
117 // Reachable only if the file naming is incorrect by current standar d. 160 // Reachable only if the file naming is incorrect by current standar d.
118 // Thus we log an error instead of recording failure to UMA. 161 // Thus we log an error instead of recording failure to UMA.
119 Log.e(TAG, "Giving up on trying to upload " + minidumpFileName 162 Log.e(TAG, "Giving up on trying to upload " + minidumpFileName
120 + " after failing to read a valid attempt number."); 163 + " after failing to read a valid attempt number.");
121 return; 164 return;
122 } 165 }
123 166
124 String logfileName = intent.getStringExtra(UPLOAD_LOG_KEY); 167 String logfileName = intent.getStringExtra(UPLOAD_LOG_KEY);
125 File logfile = new File(logfileName); 168 File logfile = new File(logfileName);
126 169
127 // Try to upload minidump 170 // Try to upload minidump
128 MinidumpUploadCallable minidumpUploadCallable = 171 MinidumpUploadCallable minidumpUploadCallable =
129 createMinidumpUploadCallable(minidumpFile, logfile); 172 createMinidumpUploadCallable(minidumpFile, logfile);
130 @MinidumpUploadCallable.MinidumpUploadStatus int uploadStatus = 173 @MinidumpUploadCallable.MinidumpUploadStatus int uploadStatus =
131 minidumpUploadCallable.call(); 174 minidumpUploadCallable.call();
132 175
133 if (uploadStatus == MinidumpUploadCallable.UPLOAD_SUCCESS) { 176 if (uploadStatus == MinidumpUploadCallable.UPLOAD_SUCCESS) {
134 // Only update UMA stats if an intended and successful upload. 177 // Only update UMA stats if an intended and successful upload.
135 incrementCrashSuccessUploadCount(getNewNameAfterSuccessfulUpload(min idumpFileName)); 178 incrementCrashSuccessUploadCount(minidumpFileName);
136 } else if (uploadStatus == MinidumpUploadCallable.UPLOAD_FAILURE) { 179 } else if (uploadStatus == MinidumpUploadCallable.UPLOAD_FAILURE) {
137 // Unable to upload minidump. Incrementing try number and restarting . 180 // Unable to upload minidump. Incrementing try number and restarting .
181 ++tries;
182 if (tries == MAX_TRIES_ALLOWED) {
183 // Only record failure to UMA after we have maxed out the allott ed tries.
184 incrementCrashFailureUploadCount(minidumpFileName);
185 }
138 186
139 // Only create another attempt if we have successfully renamed 187 // Only create another attempt if we have successfully renamed the f ile.
140 // the file.
141 String newName = CrashFileManager.tryIncrementAttemptNumber(minidump File); 188 String newName = CrashFileManager.tryIncrementAttemptNumber(minidump File);
142 if (newName != null) { 189 if (newName != null) {
143 if (++tries < MAX_TRIES_ALLOWED) { 190 if (tries < MAX_TRIES_ALLOWED) {
144 // TODO(nyquist): Do this as an exponential backoff. 191 // TODO(nyquist): Do this as an exponential backoff.
145 MinidumpUploadRetry.scheduleRetry( 192 MinidumpUploadRetry.scheduleRetry(
146 getApplicationContext(), getCrashReportingPermission Manager()); 193 getApplicationContext(), getCrashReportingPermission Manager());
147 } else { 194 } else {
148 // Only record failure to UMA after we have maxed out the al lotted tries.
149 incrementCrashFailureUploadCount(newName);
150 Log.d(TAG, "Giving up on trying to upload " + minidumpFileNa me + "after " 195 Log.d(TAG, "Giving up on trying to upload " + minidumpFileNa me + "after "
151 + tries + " number of tries."); 196 + tries + " number of tries.");
152 } 197 }
153 } else { 198 } else {
154 Log.w(TAG, "Failed to rename minidump " + minidumpFileName); 199 Log.w(TAG, "Failed to rename minidump " + minidumpFileName);
155 } 200 }
156 } 201 }
157 } 202 }
158 203
159 /** 204 /**
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 } 246 }
202 } catch (IOException e) { 247 } catch (IOException e) {
203 Log.w(TAG, "Error while reading crash file %s: %s", fileName, e.toSt ring()); 248 Log.w(TAG, "Error while reading crash file %s: %s", fileName, e.toSt ring());
204 } finally { 249 } finally {
205 StreamUtil.closeQuietly(fileReader); 250 StreamUtil.closeQuietly(fileReader);
206 } 251 }
207 return OTHER; 252 return OTHER;
208 } 253 }
209 254
210 /** 255 /**
211 * Increment the count of success/failure by 1 and distinguish between diffe rent types of 256 * Increments the count of successful uploads by 1. Distinguishes between di fferent types of
212 * crashes by looking into the file. 257 * crashes by looking into the file contents. Because this code can execute in a context when
213 * @param fileName is the name of a minidump file that contains the type of crash. 258 * the main Chrome activity is no longer running, the counts are stored in s hared preferences;
259 * they are later read and recorded as metrics by the main Chrome activity.
260 * NOTE: This method should be called *after* renaming the file, since renam ing occurs as a
261 * side-effect of a successful upload.
262 * @param originalFilename The name of the successfully uploaded minidump, * prior* to uploading.
214 */ 263 */
215 private void incrementCrashSuccessUploadCount(String fileName) { 264 public static void incrementCrashSuccessUploadCount(String originalFilename) {
216 ChromePreferenceManager.getInstance().incrementCrashSuccessUploadCount( 265 ChromePreferenceManager.getInstance().incrementCrashSuccessUploadCount(
217 getCrashType(fileName)); 266 getCrashType(getNewNameAfterSuccessfulUpload(originalFilename))) ;
218 }
219
220 private void incrementCrashFailureUploadCount(String fileName) {
221 ChromePreferenceManager.getInstance().incrementCrashFailureUploadCount(
222 getCrashType(fileName));
223 } 267 }
224 268
225 /** 269 /**
270 * Increments the count of failed uploads by 1. Distinguishes between differ ent types of crashes
271 * by looking into the file contents. Because this code can execute in a con text when the main
272 * Chrome activity is no longer running, the counts are stored in shared pre ferences; they are
273 * later read and recorded as metrics by the main Chrome activity.
274 * NOTE: This method should be called *prior* to renaming the file.
275 * @param originalFilename The name of the successfully uploaded minidump, * prior* to uploading.
276 */
277 public static void incrementCrashFailureUploadCount(String originalFilename) {
278 ChromePreferenceManager.getInstance().incrementCrashFailureUploadCount(
279 getCrashType(originalFilename));
280 }
281
282 /**
226 * Factory method for creating minidump callables. 283 * Factory method for creating minidump callables.
227 * 284 *
228 * This may be overridden for tests. 285 * This may be overridden for tests.
229 * 286 *
230 * @param minidumpFile the File to upload. 287 * @param minidumpFile the File to upload.
231 * @param logfile the Log file to write to upon successful uploads. 288 * @param logfile the Log file to write to upon successful uploads.
232 * @return a new MinidumpUploadCallable. 289 * @return a new MinidumpUploadCallable.
233 */ 290 */
234 @VisibleForTesting 291 @VisibleForTesting
235 MinidumpUploadCallable createMinidumpUploadCallable(File minidumpFile, File logfile) { 292 MinidumpUploadCallable createMinidumpUploadCallable(File minidumpFile, File logfile) {
236 return new MinidumpUploadCallable( 293 return new MinidumpUploadCallable(
237 minidumpFile, logfile, getCrashReportingPermissionManager()); 294 minidumpFile, logfile, getCrashReportingPermissionManager());
238 } 295 }
239 296
240 /** 297 /**
241 * Attempts to upload the specified {@param minidumpFile}. 298 * Attempts to upload the specified {@param minidumpFile}.
242 * 299 *
243 * Note that this method is asynchronous. All that is guaranteed is that an upload attempt will 300 * Note that this method is asynchronous. All that is guaranteed is that an upload attempt will
244 * be enqueued. 301 * be enqueued.
245 * 302 *
246 * @param context The application context, in which to initiate the crash re port upload. 303 * @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 304 * @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. 305 * service. This can only happen on KitKat and below, due to a frame work bug.
249 */ 306 */
250 public static void tryUploadCrashDump(Context context, File minidumpFile) 307 public static void tryUploadCrashDump(Context context, File minidumpFile)
251 throws SecurityException { 308 throws SecurityException {
309 assert !shouldUseJobSchedulerForUploads();
252 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( )); 310 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( ));
253 Intent intent = new Intent(context, MinidumpUploadService.class); 311 Intent intent = new Intent(context, MinidumpUploadService.class);
254 intent.setAction(ACTION_UPLOAD); 312 intent.setAction(ACTION_UPLOAD);
255 intent.putExtra(FILE_TO_UPLOAD_KEY, minidumpFile.getAbsolutePath()); 313 intent.putExtra(FILE_TO_UPLOAD_KEY, minidumpFile.getAbsolutePath());
256 intent.putExtra(UPLOAD_LOG_KEY, fileManager.getCrashUploadLogFile().getA bsolutePath()); 314 intent.putExtra(UPLOAD_LOG_KEY, fileManager.getCrashUploadLogFile().getA bsolutePath());
257 context.startService(intent); 315 context.startService(intent);
258 } 316 }
259 317
260 /** 318 /**
261 * Attempts to upload all minidump files using the given {@link android.cont ent.Context}. 319 * Attempts to upload all minidump files using the given {@link android.cont ent.Context}.
262 * 320 *
263 * Note that this method is asynchronous. All that is guaranteed is that 321 * Note that this method is asynchronous. All that is guaranteed is that
264 * upload attempts will be enqueued. 322 * upload attempts will be enqueued.
265 * 323 *
266 * This method is safe to call from the UI thread. 324 * This method is safe to call from the UI thread.
267 * 325 *
268 * @param context Context of the application. 326 * @param context Context of the application.
269 */ 327 */
270 public static void tryUploadAllCrashDumps(Context context) { 328 public static void tryUploadAllCrashDumps(Context context) {
329 assert !shouldUseJobSchedulerForUploads();
271 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( )); 330 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( ));
272 File[] minidumps = fileManager.getAllMinidumpFiles(MAX_TRIES_ALLOWED); 331 File[] minidumps = fileManager.getAllMinidumpFiles(MAX_TRIES_ALLOWED);
273 Log.i(TAG, "Attempting to upload accumulated crash dumps."); 332 Log.i(TAG, "Attempting to upload accumulated crash dumps.");
274 for (File minidump : minidumps) { 333 for (File minidump : minidumps) {
275 MinidumpUploadService.tryUploadCrashDump(context, minidump); 334 tryUploadCrashDump(context, minidump);
276 } 335 }
277 } 336 }
278 337
279 /** 338 /**
280 * Attempts to upload the crash report with the given local ID. 339 * Attempts to upload the crash report with the given local ID.
281 * 340 *
282 * Note that this method is asynchronous. All that is guaranteed is that 341 * Note that this method is asynchronous. All that is guaranteed is that
283 * upload attempts will be enqueued. 342 * upload attempts will be enqueued.
284 * 343 *
285 * This method is safe to call from the UI thread. 344 * This method is safe to call from the UI thread.
(...skipping 12 matching lines...) Expand all
298 File minidumpFile = fileManager.getCrashFileWithLocalId(localId); 357 File minidumpFile = fileManager.getCrashFileWithLocalId(localId);
299 if (minidumpFile == null) { 358 if (minidumpFile == null) {
300 Log.w(TAG, "Could not find a crash dump with local ID " + localId); 359 Log.w(TAG, "Could not find a crash dump with local ID " + localId);
301 return; 360 return;
302 } 361 }
303 File renamedMinidumpFile = CrashFileManager.trySetForcedUpload(minidumpF ile); 362 File renamedMinidumpFile = CrashFileManager.trySetForcedUpload(minidumpF ile);
304 if (renamedMinidumpFile == null) { 363 if (renamedMinidumpFile == null) {
305 Log.w(TAG, "Could not rename the file " + minidumpFile.getName() + " for re-upload"); 364 Log.w(TAG, "Could not rename the file " + minidumpFile.getName() + " for re-upload");
306 return; 365 return;
307 } 366 }
308 MinidumpUploadService.tryUploadCrashDump(context, renamedMinidumpFile); 367
368 if (shouldUseJobSchedulerForUploads()) {
369 scheduleUploadJob(context);
370 } else {
371 tryUploadCrashDump(context, renamedMinidumpFile);
372 }
309 } 373 }
310 } 374 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698