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

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java

Issue 2822633002: 🔍 General widget fixes (Closed)
Patch Set: 🔍 Further widget cleanup Created 3 years, 8 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 side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
index 8116977bad3b43b407140606606207405cd8e4b1..be13cdd29a2557eec51aab66d90220208b2845c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.searchwidget;
+import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
@@ -13,6 +14,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
+import android.os.StrictMode;
import android.support.v4.app.ActivityOptionsCompat;
import android.text.TextUtils;
import android.view.View;
@@ -38,6 +40,11 @@ import org.chromium.chrome.browser.util.IntentUtils;
*
* This class avoids loading the native library because it can be triggered at regular intervals by
* Android when it tells widgets that they need updates.
+ *
+ * Methods on instances of this class called directly by Android (when a broadcast is received e.g.)
+ * catch all Exceptions up to some number of times before letting them go through to allow us to get
+ * a crash stack. This is done to prevent Android from labeling the whole process as "bad" and
+ * blocking taps on the widget. See http://crbug.com/712061.
*/
public class SearchWidgetProvider extends AppWidgetProvider {
/** Wraps up all things that a {@link SearchWidgetProvider} can request things from. */
@@ -110,9 +117,14 @@ public class SearchWidgetProvider extends AppWidgetProvider {
private static final String PREF_IS_VOICE_SEARCH_AVAILABLE =
"org.chromium.chrome.browser.searchwidget.IS_VOICE_SEARCH_AVAILABLE";
+ private static final String PREF_NUM_CONSECUTIVE_CRASHES =
+ "org.chromium.chrome.browser.searchwidget.NUM_CONSECUTIVE_CRASHES";
private static final String PREF_SEARCH_ENGINE_SHORTNAME =
"org.chromium.chrome.browser.searchwidget.SEARCH_ENGINE_SHORTNAME";
+ /** Number of consecutive crashes this widget will absorb before giving up. */
+ private static final int CRASH_LIMIT = 3;
+
private static final String TAG = "searchwidget";
private static final Object DELEGATE_LOCK = new Object();
private static final Object OBSERVER_LOCK = new Object();
@@ -151,17 +163,27 @@ public class SearchWidgetProvider extends AppWidgetProvider {
}
@Override
- public void onReceive(Context context, Intent intent) {
- if (IntentHandler.isIntentChromeOrFirstParty(intent)) {
- handleAction(intent.getAction());
- } else {
- super.onReceive(context, intent);
- }
+ public void onReceive(final Context context, final Intent intent) {
+ run(new Runnable() {
+ @Override
+ public void run() {
+ if (IntentHandler.isIntentChromeOrFirstParty(intent)) {
+ handleAction(intent.getAction());
+ } else {
+ SearchWidgetProvider.super.onReceive(context, intent);
+ }
+ }
+ });
}
@Override
- public void onUpdate(Context context, AppWidgetManager manager, int[] ids) {
- performUpdate(ids);
+ public void onUpdate(final Context context, final AppWidgetManager manager, final int[] ids) {
+ run(new Runnable() {
+ @Override
+ public void run() {
+ performUpdate(ids);
+ }
+ });
}
/** Handles the intent actions to the widget. */
@@ -244,6 +266,14 @@ public class SearchWidgetProvider extends AppWidgetProvider {
return views;
}
+ /** Creates a trusted Intent that lets the user begin performing queries. */
+ private static Intent createStartQueryIntent(Context context, String action, int widgetId) {
+ Intent intent = new Intent(action, Uri.parse(String.valueOf(widgetId)));
+ intent.setClass(context, SearchWidgetProvider.class);
+ IntentHandler.addTrustedIntentExtras(intent);
+ return intent;
+ }
+
/** Caches whether or not a voice search is possible. */
static void updateCachedVoiceSearchAvailability(boolean isVoiceSearchAvailable) {
SharedPreferences prefs = getDelegate().getSharedPreferences();
@@ -266,19 +296,26 @@ public class SearchWidgetProvider extends AppWidgetProvider {
}
}
- /** Creates a trusted Intent that lets the user begin performing queries. */
- private static Intent createStartQueryIntent(Context context, String action, int widgetId) {
- Intent intent = new Intent(action, Uri.parse(String.valueOf(widgetId)));
- intent.setClass(context, SearchWidgetProvider.class);
- IntentHandler.addTrustedIntentExtras(intent);
- return intent;
- }
+ /** Updates the number of consecutive crashes this widget has absorbed. */
+ @SuppressLint("CommitPrefEdits")
+ static void updateNumConsecutiveCrashes(int newValue) {
+ SharedPreferences prefs = getDelegate().getSharedPreferences();
+ if (getNumConsecutiveCrashes(prefs) == newValue) return;
- /** Sets an {@link SearchWidgetProviderDelegate} to interact with. */
- @VisibleForTesting
- static void setDelegateForTest(SearchWidgetProviderDelegate delegate) {
- assert sDelegate == null;
- sDelegate = delegate;
+ SharedPreferences.Editor editor = prefs.edit();
+ if (newValue == 0) {
+ editor.remove(PREF_NUM_CONSECUTIVE_CRASHES);
+ } else {
+ editor.putInt(PREF_NUM_CONSECUTIVE_CRASHES, newValue);
+ }
+
+ // This metric is committed synchronously because it relates to crashes.
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ editor.commit();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
private static boolean getCachedVoiceSearchAvailability(SharedPreferences prefs) {
@@ -289,10 +326,41 @@ public class SearchWidgetProvider extends AppWidgetProvider {
return prefs.getString(PREF_SEARCH_ENGINE_SHORTNAME, null);
}
+ @VisibleForTesting
+ static int getNumConsecutiveCrashes(SharedPreferences prefs) {
+ return prefs.getInt(PREF_NUM_CONSECUTIVE_CRASHES, 0);
+ }
+
private static SearchWidgetProviderDelegate getDelegate() {
synchronized (DELEGATE_LOCK) {
if (sDelegate == null) sDelegate = new SearchWidgetProviderDelegate(null);
}
return sDelegate;
}
+
+ @VisibleForTesting
+ static void run(Runnable runnable) {
+ try {
+ runnable.run();
+ updateNumConsecutiveCrashes(0);
+ } catch (Exception e) {
+ int numCrashes = getNumConsecutiveCrashes(getDelegate().getSharedPreferences()) + 1;
+ updateNumConsecutiveCrashes(numCrashes);
+
+ if (numCrashes < CRASH_LIMIT) {
+ // Absorb the crash.
+ Log.e(TAG, "Absorbing exception caught when attempting to launch widget.", e);
+ } else {
+ // Too many crashes have happened consecutively. Let Android handle it.
+ throw e;
+ }
+ }
+ }
+
+ /** Sets an {@link SearchWidgetProviderDelegate} to interact with. */
+ @VisibleForTesting
+ static void setDelegateForTest(SearchWidgetProviderDelegate delegate) {
+ assert sDelegate == null;
+ sDelegate = delegate;
+ }
}

Powered by Google App Engine
This is Rietveld 408576698