Merge "Fix crash in non-indexable keys collection" into pi-dev
diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
index e0efaac..5de49bb 100644
--- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
@@ -47,6 +47,9 @@
 
     @Override
     public int getAvailabilityStatus() {
+        if (mConfig == null) {
+            mConfig = new AmbientDisplayConfiguration(mContext);
+        }
         return isAvailable(mConfig) ? AVAILABLE : DISABLED_UNSUPPORTED;
     }
 
diff --git a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
index ab769e1..9cff088 100644
--- a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
@@ -81,6 +81,9 @@
 
     @Override
     public int getAvailabilityStatus() {
+        if (mConfig == null) {
+            mConfig = new AmbientDisplayConfiguration(mContext);
+        }
         return mConfig.pulseOnNotificationAvailable() ? AVAILABLE : DISABLED_UNSUPPORTED;
     }
 
diff --git a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
index 3ef1b85..b9134e5 100644
--- a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
+++ b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
@@ -41,6 +41,7 @@
 import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
 import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
 import static android.provider.SearchIndexablesContract.SITE_MAP_COLUMNS;
+
 import static com.android.settings.dashboard.DashboardFragmentRegistry.CATEGORY_KEY_TO_PARENT_MAP;
 
 import android.content.Context;
@@ -63,7 +64,16 @@
 import java.util.List;
 
 public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
+
     public static final boolean DEBUG = false;
+
+    /**
+     * Flag for a system property which checks if we should crash if there are issues in the
+     * indexing pipeline.
+     */
+    public static final String SYSPROP_CRASH_ON_ERROR =
+            "debug.com.android.settings.search.crash_on_error";
+
     private static final String TAG = "SettingsSearchProvider";
 
     private static final Collection<String> INVALID_KEYS;
@@ -183,7 +193,23 @@
             final long startTime = System.currentTimeMillis();
             Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
                     clazz);
-            List<String> providerNonIndexableKeys = provider.getNonIndexableKeys(context);
+
+            List<String> providerNonIndexableKeys;
+            try {
+                providerNonIndexableKeys = provider.getNonIndexableKeys(context);
+            } catch (Exception e) {
+                // Catch a generic crash. In the absence of the catch, the background thread will
+                // silently fail anyway, so we aren't losing information by catching the exception.
+                // We crash when the system property exists so that we can test if crashes need to
+                // be fixed.
+                // The gain is that if there is a crash in a specific controller, we don't lose all
+                // non-indexable keys, but we can still find specific crashes in development.
+                if (System.getProperty(SYSPROP_CRASH_ON_ERROR) != null) {
+                    throw new RuntimeException(e);
+                }
+                Log.e(TAG, "Error trying to get non-indexable keys from: " + clazz.getName() , e);
+                continue;
+            }
 
             if (providerNonIndexableKeys == null || providerNonIndexableKeys.isEmpty()) {
                 if (DEBUG) {
diff --git a/tests/unit/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java b/tests/unit/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java
index aed94a0..3659fdb 100644
--- a/tests/unit/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java
+++ b/tests/unit/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java
@@ -21,11 +21,13 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.platform.test.annotations.Presubmit;
 import android.provider.SearchIndexablesContract;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -36,12 +38,16 @@
 
     private Context mContext;
 
-
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getTargetContext();
     }
 
+    @After
+    public void cleanUp() {
+        System.clearProperty(SettingsSearchIndexablesProvider.SYSPROP_CRASH_ON_ERROR);
+    }
+
     @Test
     public void testSiteMapPairsFetched() {
         final Uri uri = Uri.parse("content://" + mContext.getPackageName() + "/" +
@@ -59,4 +65,21 @@
                     .isNotEmpty();
         }
     }
+
+    /**
+     * All {@link Indexable.SearchIndexProvider} should collect a list of non-indexable keys
+     * without crashing. This test enables crashing of individual providers in the indexing pipeline
+     * and checks that there are no crashes.
+     */
+    @Test
+    @Presubmit
+    public void nonIndexableKeys_shouldNotCrash() {
+        // Allow crashes in the indexing pipeline.
+        System.setProperty(SettingsSearchIndexablesProvider.SYSPROP_CRASH_ON_ERROR,
+                "enabled");
+
+        final Uri uri = Uri.parse("content://" + mContext.getPackageName() + "/" +
+                SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
+        mContext.getContentResolver().query(uri, null, null, null, null);
+    }
 }