Rethrow non-recoverable errors when using sqlite with a better error.

* WorkManager will hopefully not be attributed to these kinds of errors.

Test: Added unit tests.
Change-Id: Idbc91153919281535a2247ea5c0e7f0103a09c7c
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
index 9e10200..db571b1 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
@@ -30,6 +30,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.database.sqlite.SQLiteCantOpenDatabaseException;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -141,4 +142,12 @@
 
         verify(mScheduler, times(1)).schedule(eq(workSpec));
     }
+
+    @Test(expected = IllegalStateException.class)
+    public void test_rethrowForNonRecoverableSqliteExceptions() {
+        ForceStopRunnable runnable = spy(mRunnable);
+        when(runnable.cleanUp())
+                .thenThrow(new SQLiteCantOpenDatabaseException("Cannot open database."));
+        runnable.run();
+    }
 }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
index e54d16a..7c7e5f0 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
@@ -28,6 +28,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.database.sqlite.SQLiteAccessPermException;
+import android.database.sqlite.SQLiteCantOpenDatabaseException;
+import android.database.sqlite.SQLiteDatabaseCorruptException;
 import android.os.Build;
 
 import androidx.annotation.NonNull;
@@ -80,24 +83,37 @@
         // Clean invalid jobs attributed to WorkManager, and Workers that might have been
         // interrupted because the application crashed (RUNNING state).
         Logger.get().debug(TAG, "Performing cleanup operations.");
-        boolean needsScheduling = cleanUp();
-
-        if (shouldRescheduleWorkers()) {
-            Logger.get().debug(TAG, "Rescheduling Workers.");
-            mWorkManager.rescheduleEligibleWork();
-            // Mark the jobs as migrated.
-            mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
-        } else if (isForceStopped()) {
-            Logger.get().debug(TAG, "Application was force-stopped, rescheduling.");
-            mWorkManager.rescheduleEligibleWork();
-        } else if (needsScheduling) {
-            Logger.get().debug(TAG, "Found unfinished work, scheduling it.");
-            Schedulers.schedule(
-                    mWorkManager.getConfiguration(),
-                    mWorkManager.getWorkDatabase(),
-                    mWorkManager.getSchedulers());
+        try {
+            boolean needsScheduling = cleanUp();
+            if (shouldRescheduleWorkers()) {
+                Logger.get().debug(TAG, "Rescheduling Workers.");
+                mWorkManager.rescheduleEligibleWork();
+                // Mark the jobs as migrated.
+                mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
+            } else if (isForceStopped()) {
+                Logger.get().debug(TAG, "Application was force-stopped, rescheduling.");
+                mWorkManager.rescheduleEligibleWork();
+            } else if (needsScheduling) {
+                Logger.get().debug(TAG, "Found unfinished work, scheduling it.");
+                Schedulers.schedule(
+                        mWorkManager.getConfiguration(),
+                        mWorkManager.getWorkDatabase(),
+                        mWorkManager.getSchedulers());
+            }
+            mWorkManager.onForceStopRunnableCompleted();
+        } catch (SQLiteCantOpenDatabaseException
+                | SQLiteDatabaseCorruptException
+                | SQLiteAccessPermException exception) {
+            // ForceStopRunnable is usually the first thing that accesses a database (or an app's
+            // internal data directory). This means that weird PackageManager bugs are attributed
+            // to ForceStopRunnable, which is unfortunate. This gives the developer a better error
+            // message.
+            String message =
+                    "The file system on the device is in a bad state. WorkManager cannot access "
+                            + "the app's internal data store.";
+            Logger.get().error(TAG, message, exception);
+            throw new IllegalStateException(message, exception);
         }
-        mWorkManager.onForceStopRunnableCompleted();
     }
 
     /**