OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |