Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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.android_webview.crash; | 5 package org.chromium.android_webview.crash; |
| 6 | 6 |
| 7 import android.content.Context; | 7 import android.content.Context; |
| 8 import android.net.ConnectivityManager; | 8 import android.net.ConnectivityManager; |
| 9 import android.net.NetworkInfo; | 9 import android.net.NetworkInfo; |
| 10 import android.webkit.ValueCallback; | 10 import android.webkit.ValueCallback; |
| 11 | 11 |
| 12 import org.chromium.android_webview.PlatformServiceBridge; | 12 import org.chromium.android_webview.PlatformServiceBridge; |
| 13 import org.chromium.android_webview.command_line.CommandLineUtil; | 13 import org.chromium.android_webview.command_line.CommandLineUtil; |
| 14 import org.chromium.base.CommandLine; | 14 import org.chromium.base.CommandLine; |
| 15 import org.chromium.base.Log; | |
| 16 import org.chromium.base.ThreadUtils; | 15 import org.chromium.base.ThreadUtils; |
| 17 import org.chromium.base.VisibleForTesting; | 16 import org.chromium.base.VisibleForTesting; |
| 18 import org.chromium.components.minidump_uploader.CrashFileManager; | 17 import org.chromium.components.minidump_uploader.MinidumpUploaderDelegate; |
| 19 import org.chromium.components.minidump_uploader.MinidumpUploadCallable; | |
| 20 import org.chromium.components.minidump_uploader.MinidumpUploader; | |
| 21 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionMa nager; | 18 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionMa nager; |
| 22 | 19 |
| 23 import java.io.File; | 20 import java.io.File; |
| 24 | 21 |
| 25 /** | 22 /** |
| 26 * Class in charge of uploading Minidumps from WebView's data directory. | 23 * Android Webview-specific implementations for minidump uploading logic. |
| 27 * This class gets invoked from a JobScheduler job and posts the operation of up loading minidumps to | |
| 28 * a privately defined worker thread. | |
| 29 * Note that this implementation is state-less in the sense that it doesn't keep track of whether it | |
| 30 * successfully uploaded any minidumps. At the end of a job it simply checks whe ther there are any | |
| 31 * minidumps left to upload, and if so, the job is rescheduled. | |
| 32 */ | 24 */ |
| 33 public class MinidumpUploaderImpl implements MinidumpUploader { | 25 public class AwMinidumpUploaderDelegate implements MinidumpUploaderDelegate { |
| 34 private static final String TAG = "MinidumpUploaderImpl"; | 26 private final Context mContext; |
| 35 private Context mContext; | |
| 36 private Thread mWorkerThread; | |
| 37 private final ConnectivityManager mConnectivityManager; | 27 private final ConnectivityManager mConnectivityManager; |
| 38 private final CrashFileManager mFileManager; | |
| 39 | 28 |
| 40 private boolean mCancelUpload = false; | |
| 41 | |
| 42 private final boolean mCleanOutMinidumps; | |
| 43 private boolean mPermittedByUser = false; | 29 private boolean mPermittedByUser = false; |
| 44 | 30 |
| 45 @VisibleForTesting | 31 @VisibleForTesting |
| 46 public static final int MAX_UPLOAD_TRIES_ALLOWED = 3; | 32 public AwMinidumpUploaderDelegate(Context context) { |
| 47 | 33 mContext = context; |
| 48 /** | 34 mConnectivityManager = |
| 49 * Notify the worker thread that the current job has been canceled - so we s houldn't upload any | 35 (ConnectivityManager) context.getSystemService(Context.CONNECTIV ITY_SERVICE); |
| 50 * more minidumps. | |
| 51 */ | |
| 52 private void setCancelUpload(boolean cancel) { | |
| 53 mCancelUpload = cancel; | |
| 54 } | 36 } |
| 55 | 37 |
| 56 /** | 38 @Override |
| 57 * Check whether the current job has been canceled. | 39 public File createCrashDir() { |
| 58 */ | 40 return CrashReceiverService.createWebViewCrashDir(mContext); |
| 59 private boolean getCancelUpload() { | |
| 60 return mCancelUpload; | |
| 61 } | 41 } |
| 62 | 42 |
| 63 @VisibleForTesting | 43 @Override |
| 64 public MinidumpUploaderImpl(Context context, boolean cleanOutMinidumps) { | 44 public CrashReportingPermissionManager createCrashReportingPermissionManager () { |
| 65 mConnectivityManager = | |
| 66 (ConnectivityManager) context.getSystemService(Context.CONNECTIV ITY_SERVICE); | |
| 67 mContext = context; | |
| 68 File webviewCrashDir = CrashReceiverService.createWebViewCrashDir(contex t); | |
| 69 mFileManager = new CrashFileManager(webviewCrashDir); | |
| 70 if (!mFileManager.ensureCrashDirExists()) { | |
| 71 Log.e(TAG, "Crash directory doesn't exist!"); | |
| 72 } | |
| 73 mCleanOutMinidumps = cleanOutMinidumps; | |
| 74 } | |
| 75 | |
| 76 /** | |
| 77 * Utility method to allow us to test the logic of this class by injecting | |
| 78 * test-MinidumpUploadCallables. | |
| 79 */ | |
| 80 @VisibleForTesting | |
| 81 public MinidumpUploadCallable createMinidumpUploadCallable(File minidumpFile , File logfile) { | |
| 82 return new MinidumpUploadCallable( | |
| 83 minidumpFile, logfile, createWebViewCrashReportingManager()); | |
| 84 } | |
| 85 | |
| 86 /** | |
| 87 * Utility method to allow us to test the logic of this class by injecting | |
| 88 * a test-PlatformServiceBridge. | |
| 89 */ | |
| 90 @VisibleForTesting | |
| 91 public PlatformServiceBridge createPlatformServiceBridge() { | |
| 92 return PlatformServiceBridge.getInstance(mContext); | |
| 93 } | |
| 94 | |
| 95 @VisibleForTesting | |
| 96 protected CrashReportingPermissionManager createWebViewCrashReportingManager () { | |
| 97 return new CrashReportingPermissionManager() { | 45 return new CrashReportingPermissionManager() { |
| 98 @Override | 46 @Override |
| 99 public boolean isClientInMetricsSample() { | 47 public boolean isClientInMetricsSample() { |
| 100 // We will check whether the client is in the metrics sample bef ore | 48 // We will check whether the client is in the metrics sample bef ore |
| 101 // generating a minidump - so if no minidump is generated this c ode will | 49 // generating a minidump - so if no minidump is generated this c ode will |
| 102 // never run and we don't need to check whether we are in the sa mple. | 50 // never run and we don't need to check whether we are in the sa mple. |
| 103 // TODO(gsennton): when we switch to using Finch for this value we should use the | 51 // TODO(gsennton): when we switch to using Finch for this value we should use the |
| 104 // Finch value here as well. | 52 // Finch value here as well. |
| 105 return true; | 53 return true; |
| 106 } | 54 } |
| 107 @Override | 55 @Override |
| 108 public boolean isNetworkAvailableForCrashUploads() { | 56 public boolean isNetworkAvailableForCrashUploads() { |
| 109 // JobScheduler will call onStopJob causing our upload to be int errupted when our | 57 // JobScheduler will call onStopJob causing our upload to be int errupted when our |
| 110 // network requirements no longer hold. | 58 // network requirements no longer hold. |
| 59 // TODO(isherman): This code should really be shared with Chrome . Especially, Chrome | |
|
gsennton
2017/02/27 18:54:55
I assume you are implying that Chrome should be ch
Ilya Sherman
2017/02/28 03:54:13
Indeed! Rephrased the comment to hopefully clarif
| |
| 60 // checks for WiFi networks, rather than checking for whether th e network is | |
| 61 // metered. | |
| 111 NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkI nfo(); | 62 NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkI nfo(); |
| 112 if (networkInfo == null || !networkInfo.isConnected()) return fa lse; | 63 if (networkInfo == null || !networkInfo.isConnected()) return fa lse; |
| 113 return !mConnectivityManager.isActiveNetworkMetered(); | 64 return !mConnectivityManager.isActiveNetworkMetered(); |
| 114 } | 65 } |
| 115 @Override | 66 @Override |
| 116 public boolean isCrashUploadDisabledByCommandLine() { | 67 public boolean isCrashUploadDisabledByCommandLine() { |
| 117 return false; | 68 return false; |
| 118 } | 69 } |
| 119 /** | 70 /** |
| 120 * This method is already represented by isClientInMetricsSample() a nd | 71 * This method is already represented by isClientInMetricsSample() a nd |
| 121 * isNetworkAvailableForCrashUploads(). | 72 * isNetworkAvailableForCrashUploads(). |
| 122 */ | 73 */ |
| 123 @Override | 74 @Override |
| 124 public boolean isMetricsUploadPermitted() { | 75 public boolean isMetricsUploadPermitted() { |
| 125 return true; | 76 return true; |
| 126 } | 77 } |
| 127 @Override | 78 @Override |
| 128 public boolean isUsageAndCrashReportingPermittedByUser() { | 79 public boolean isUsageAndCrashReportingPermittedByUser() { |
| 129 return mPermittedByUser; | 80 return mPermittedByUser; |
| 130 } | 81 } |
| 131 @Override | 82 @Override |
| 132 public boolean isUploadEnabledForTests() { | 83 public boolean isUploadEnabledForTests() { |
| 133 // Note that CommandLine/CommandLineUtil are not thread safe. Th ey are initialized | 84 // Note that CommandLine/CommandLineUtil are not thread safe. Th ey are initialized |
| 134 // on the main thread, but before the current worker thread star ted - so this thread | 85 // on the main thread, but before the current worker thread star ted - so this thread |
| 135 // will have seen the initialization of the CommandLine. | 86 // will have seen the initialization of the CommandLine. |
| 87 ThreadUtils.assertOnUiThread(); | |
| 136 return CommandLine.getInstance().hasSwitch( | 88 return CommandLine.getInstance().hasSwitch( |
| 137 CommandLineUtil.CRASH_UPLOADS_ENABLED_FOR_TESTING_SWITCH ); | 89 CommandLineUtil.CRASH_UPLOADS_ENABLED_FOR_TESTING_SWITCH ); |
| 138 } | 90 } |
| 139 }; | 91 }; |
| 140 } | 92 } |
| 141 | 93 |
| 142 /** | |
| 143 * Runnable that upload minidumps. | |
| 144 * This is where the actual uploading happens - an upload job consists of po sting this Runnable | |
| 145 * to the worker thread. | |
| 146 */ | |
| 147 private class UploadRunnable implements Runnable { | |
| 148 private final MinidumpUploader.UploadsFinishedCallback mUploadsFinishedC allback; | |
| 149 | |
| 150 public UploadRunnable(MinidumpUploader.UploadsFinishedCallback uploadsFi nishedCallback) { | |
| 151 mUploadsFinishedCallback = uploadsFinishedCallback; | |
| 152 } | |
| 153 | |
| 154 @Override | |
| 155 public void run() { | |
| 156 File[] minidumps = mFileManager.getAllMinidumpFiles(MAX_UPLOAD_TRIES _ALLOWED); | |
| 157 for (File minidump : minidumps) { | |
| 158 // Store the name of the current minidump so that we can mark it as a failure from | |
| 159 // the main thread if JobScheduler tells us to stop. | |
| 160 MinidumpUploadCallable uploadCallable = createMinidumpUploadCall able( | |
| 161 minidump, mFileManager.getCrashUploadLogFile()); | |
| 162 int uploadResult = uploadCallable.call(); | |
| 163 | |
| 164 // Job was canceled -> early out: any clean-up will be performed in cancelUploads(). | |
| 165 // Note that we check whether we are canceled AFTER trying to up load a minidump - | |
| 166 // this is to allow the uploading of at least one minidump per j ob (to deal with | |
| 167 // cases where we reschedule jobs over and over again and would never upload any | |
| 168 // minidumps because old jobs are canceled when scheduling new j obs). | |
| 169 if (getCancelUpload()) return; | |
| 170 | |
| 171 if (uploadResult == MinidumpUploadCallable.UPLOAD_FAILURE) { | |
| 172 String newName = CrashFileManager.tryIncrementAttemptNumber( minidump); | |
| 173 if (newName == null) { | |
| 174 Log.w(TAG, "Failed to increment attempt number of " + mi nidump); | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 // Clean out old/uploaded minidumps. Note that this clean-up method is more strict than | |
| 180 // our copying mechanism in the sense that it keeps less minidumps. | |
| 181 if (mCleanOutMinidumps) { | |
| 182 mFileManager.cleanOutAllNonFreshMinidumpFiles(); | |
| 183 } | |
| 184 | |
| 185 // Reschedule if there are still minidumps to upload. | |
| 186 boolean reschedule = | |
| 187 mFileManager.getAllMinidumpFiles(MAX_UPLOAD_TRIES_ALLOWED).l ength > 0; | |
| 188 mUploadsFinishedCallback.uploadsFinished(reschedule); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 @Override | 94 @Override |
| 193 public void uploadAllMinidumps( | 95 public void prepareToUploadMinidumps(final Runnable startUploads) { |
| 194 final MinidumpUploader.UploadsFinishedCallback uploadsFinishedCallba ck) { | |
| 195 // This call should happen on the main thread | |
| 196 ThreadUtils.assertOnUiThread(); | |
| 197 if (mWorkerThread != null) { | |
| 198 throw new RuntimeException("Only one upload-job should be active at a time"); | |
| 199 } | |
| 200 mWorkerThread = new Thread(new UploadRunnable(uploadsFinishedCallback), "mWorkerThread"); | |
| 201 setCancelUpload(false); | |
| 202 | |
| 203 createPlatformServiceBridge().queryMetricsSetting(new ValueCallback<Bool ean>() { | 96 createPlatformServiceBridge().queryMetricsSetting(new ValueCallback<Bool ean>() { |
| 204 public void onReceiveValue(Boolean enabled) { | 97 public void onReceiveValue(Boolean enabled) { |
| 205 // This callback should be received on the main thread (e.g. mWo rkerThread | 98 // This callback should be received on the main thread (e.g. mWo rkerThread |
| 206 // is not thread-safe). | 99 // is not thread-safe). |
| 207 ThreadUtils.assertOnUiThread(); | 100 ThreadUtils.assertOnUiThread(); |
| 208 | 101 |
| 209 mPermittedByUser = enabled; | 102 mPermittedByUser = enabled; |
| 210 // Note that our job might have been cancelled by this time - ho wever, we do start | 103 |
| 211 // our worker thread anyway to try to make some progress towards uploading | 104 startUploads.run(); |
| 212 // minidumps. | |
| 213 // This is to ensure that in the case where an app is crashing o ver and over again | |
| 214 // - and we are rescheduling jobs over and over again - we are s till uploading at | |
| 215 // least one minidump per job, as long as that job starts before it is canceled by | |
| 216 // the next job. | |
| 217 // For cases where the job is cancelled because the network conn ection is lost, or | |
| 218 // because we switch over to a metered connection, we won't try to upload any | |
| 219 // minidumps anyway since we check the network connection just b efore the upload of | |
| 220 // each minidump. | |
| 221 mWorkerThread.start(); | |
| 222 } | 105 } |
| 223 }); | 106 }); |
| 224 } | 107 } |
| 225 | 108 |
| 109 /** | |
| 110 * Utility method to allow us to test the logic of this class by injecting | |
| 111 * a test-PlatformServiceBridge. | |
| 112 */ | |
| 226 @VisibleForTesting | 113 @VisibleForTesting |
| 227 public void joinWorkerThreadForTesting() throws InterruptedException { | 114 public PlatformServiceBridge createPlatformServiceBridge() { |
| 228 mWorkerThread.join(); | 115 return PlatformServiceBridge.getInstance(mContext); |
| 229 } | |
| 230 | |
| 231 /** | |
| 232 * @return whether to reschedule the uploads. | |
| 233 */ | |
| 234 @Override | |
| 235 public boolean cancelUploads() { | |
| 236 setCancelUpload(true); | |
| 237 | |
| 238 // Reschedule if there are still minidumps to upload. | |
| 239 return mFileManager.getAllMinidumpFiles(MAX_UPLOAD_TRIES_ALLOWED).length > 0; | |
| 240 } | 116 } |
| 241 } | 117 } |
| OLD | NEW |