ngageoint / geopackage-android

GeoPackage Android Library
http://ngageoint.github.io/geopackage-android
MIT License
94 stars 32 forks source link

When a raster Geopackage file is copied locally from a file import, it seems corrupt when it is not #76

Closed eclectice closed 2 years ago

eclectice commented 2 years ago

Please fill out as much known and relevant information as possible.

Version Information:

Android Studio Arctic Fox | 2020.3.1 Patch 3
Build #AI-203.7717.56.2031.7784292, built on October 1, 2021
Runtime version: 11.0.10+0-b96-7249189 amd64
VM: OpenJDK 64-Bit Server VM by Oracle Corporation
Windows 10 10.0
GC: G1 Young Generation, G1 Old Generation
Memory: 2560M
Cores: 4
Registry: analyze.exceptions.on.the.fly=true, external.system.auto.import.disabled=true
Non-Bundled Plugins: com.alayouni.ansiHighlight, com.intellij.ideolog, JFormDesigner, idea.plugin.protoeditor, io.protostuff.protostuff-jetbrains-plugin, Dart, org.jetbrains.kotlin, io.flutter, org.intellij.plugins.markdown

Expected Results:

However, following import, it appears "corrupt." However, after restarting the app, the "failed" map renders correctly.

Import feature ![import from file 1](https://user-images.githubusercontent.com/1452656/162661139-c56c128b-2f77-480a-b9be-3fac8f2318b7.png)
Importing with copy locally option selected ![import from file 2](https://user-images.githubusercontent.com/1452656/162661326-1914a433-6379-4d9e-a7ff-916567c43a82.png)

Observed Results:

Failed to import popup message ![import from file 3](https://user-images.githubusercontent.com/1452656/162661426-d6906bee-f733-4ad2-abff-ae76db49aae4.png)
But it loads fine after app restart ![import from file 4](https://user-images.githubusercontent.com/1452656/162663437-109cab04-f574-48a2-b261-b1b79231021c.png)
2022-04-11 11:34:33.529 1920-1920/? I/OsmDroid: Geopackage database list: [5312.corrupt]
2022-04-11 11:34:33.533 1920-1920/? I/OsmDroid: Geopackage database size: 1
2022-04-11 11:34:33.564 1920-1920/? I/OsmDroid: Geopackage database open: 5312.corrupt (true)

Output:

Error 1 ``` 2022-04-11 10:46:28.214 16539-16539/? I/OsmDroid: Geopackage database list: [5312] 2022-04-11 10:46:28.217 16539-16539/? I/OsmDroid: Geopackage database size: 1 2022-04-11 10:46:28.704 16539-16539/? E/com.my.app.android.component.OsmMapFragment: GPKG file setupMap() load error: mil.nga.geopackage.validate.GeoPackageValidate.validateMinimumTables(GeoPackageValidate.java:155) mil.nga.geopackage.GeoPackageException: Invalid GeoPackage. Does not contain required tables: gpkg_spatial_ref_sys & gpkg_contents, GeoPackage Name: 5312 at mil.nga.geopackage.validate.GeoPackageValidate.validateMinimumTables(GeoPackageValidate.java:155) at mil.nga.geopackage.GeoPackageCreator.createGeoPackage(GeoPackageCreator.java:228) at mil.nga.geopackage.GeoPackageManagerImpl.open(GeoPackageManagerImpl.java:1101) at mil.nga.geopackage.GeoPackageManagerImpl.open(GeoPackageManagerImpl.java:1070) at com.my.app.android.gpkg.tiles.raster.SafGeoPackageMapTileModuleProvider.(SafGeoPackageMapTileModuleProvider.java:90) ```
Error 2 ``` 2022-04-11 11:16:15.032 1920-1920/? I/OsmDroid: Geopackage database list: [5312] 2022-04-11 11:16:15.037 1920-1920/? I/OsmDroid: Geopackage database size: 1 2022-04-11 11:16:15.118 1920-1920/? E/com.my.app.android.component.OsmMapFragment: GPKG file setupMap() load error: android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method) android.database.sqlite.SQLiteDatabaseCorruptException: database disk image is malformed (code 11) ################################################################# Error Code : 11 (SQLITE_CORRUPT) Caused By : The database disk image is malformed. (database disk image is malformed (code 11)) ################################################################# at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method) at android.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:930) at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:836) at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62) at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143) at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:132) at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:219) at android.database.AbstractCursor.moveToNext(AbstractCursor.java:268) at android.database.CursorWrapper.moveToNext(CursorWrapper.java:202) at mil.nga.geopackage.db.ResultUtils.buildResults(ResultUtils.java:395) at mil.nga.geopackage.db.GeoPackageConnection.queryResults(GeoPackageConnection.java:192) at mil.nga.geopackage.db.GeoPackageCoreConnection.queryResults(GeoPackageCoreConnection.java:998) at mil.nga.geopackage.db.CoreSQLUtils.foreignKeyCheck(CoreSQLUtils.java:363) at mil.nga.geopackage.db.GeoPackageCoreConnection.foreignKeyCheck(GeoPackageCoreConnection.java:194) at mil.nga.geopackage.db.GeoPackageCoreConnection.enableForeignKeys(GeoPackageCoreConnection.java:147) at mil.nga.geopackage.GeoPackageCreator.createGeoPackage(GeoPackageCreator.java:222) at mil.nga.geopackage.GeoPackageManagerImpl.open(GeoPackageManagerImpl.java:1101) at mil.nga.geopackage.GeoPackageManagerImpl.open(GeoPackageManagerImpl.java:1070) at com.my.app.android.gpkg.tiles.raster.SafGeoPackageMapTileModuleProvider.(SafGeoPackageMapTileModuleProvider.java:90) at com.my.app.android.gpkg.tiles.raster.SafGeoPackageProvider.(SafGeoPackageProvider.java:67) at com.my.app.android.gpkg.tiles.raster.SafGeoPackageProvider.(SafGeoPackageProvider.java:44) at com.my.app.android.component.OsmMapFragment$LoadGeoPackageTask.onPreExecute(OsmMapFragment.java:1860) at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:613) at com.my.app.android.component.OsmMapFragment.loadMap(OsmMapFragment.java:1634) at com.my.app.android.component.gpkg.load.ImportTask$2.onClick(ImportTask.java:232) at androidx.appcompat.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6682) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410) ```

Steps to Reproduce:

It is not relevant since it is my custom codes

Relevant Code:

The code snippet where the exception occurs at (SafGeoPackageMapTileModuleProvider.java:90): GeoPackage open = manager.open(databases.get(i));

I'm not sure if removing the extension ".gpkg" (GEO_PACKAGE_EXTENSION) from the file name before adding the file into the GeoPackageManager.importGeoPackage() call is a problem or not.

    public SafGeoPackageMapTileModuleProvider(DocumentFile[] pFile,
                                              final Context context, IFilesystemCache cache) {
        //int pThreadPoolSize, final int pPendingQueueSize
        super(Configuration.getInstance().getTileFileSystemThreads(), Configuration.getInstance().getTileFileSystemMaxQueueSize());
        Log.i(IMapView.LOGTAG, "Geopackage support is BETA. Please report any issues");
        tileWriter = cache;
        // Get a manager
        manager = GeoPackageFactory.getManager(context);
        // Available databases

        String[] list = manager.databases().stream().toArray(String[]::new);
        Log.i(IMapView.LOGTAG, "Geopackage database list: " + Arrays.toString(list));

        // Import database but strip the .gpkg extension from the file name
        for (int i = 0; i < pFile.length; i++) {
            String name = pFile[i].getName();
            name = name.replace(GEO_PACKAGE_EXTENSION, "");
            //Log.i(IMapView.LOGTAG, "Geopackage database new entry: " + name);
            if (StringUtils.indexOfAny(name, list) == -1) {
                Log.i(IMapView.LOGTAG, "Geopackage database new entry: " + pFile[i].getName());
                manager.importGeoPackage((pFile[i]));
            }
        }

        // Available databases
        List<String> databases = manager.databases();
        Log.i(IMapView.LOGTAG, String.format(Locale.getDefault(), "Geopackage database size: %d", databases.size()));
        // Open database
        for (int i = 0; i < databases.size(); i++) {
            GeoPackage open = manager.open(databases.get(i));
            boolean newTile = tileSources.add(open);
            Log.i(IMapView.LOGTAG, String.format(Locale.getDefault(), "Geopackage database open: %s (%s)", databases.get(i), newTile ? "true":"false"));
            open.close();
        }

    }

Test Files:

Additional Information:

bosborn commented 2 years ago

Are you doing to this on a background thread? Are you using a PowerManager WakeLock? Wondering if the file is partially copied over causing different errors. But not sure why it fully works the second time unless it is a temporary partial thing.

The first error makes it a little farther and fails to find the required tables. The second error fails a few steps earlier trying to enable foreign keys (pragma) due to a corrupted database.

Does this happen to all GeoPackages or only some? If not all, can you provide an example file?

Turned your code into a test. Ran with version 6.1.2 on a Nexus 5X API 31 emulator (closest to a Samsung Note 5 I could think of). Works fine and could not duplicate the errors.

    public void safGeoPackageMapTileModuleProvider(DocumentFile[] pFile,
                                                   final Context context /*, IFilesystemCache cache*/) {
        String GEO_PACKAGE_EXTENSION = ".gpkg";
        //int pThreadPoolSize, final int pPendingQueueSize
        //super(Configuration.getInstance().getTileFileSystemThreads(), Configuration.getInstance().getTileFileSystemMaxQueueSize());
        //Log.i(IMapView.LOGTAG, "Geopackage support is BETA. Please report any issues");
        //tileWriter = cache;
        // Get a manager
        GeoPackageManager manager = GeoPackageFactory.getManager(context);
        // Available databases

        //String[] list = manager.databases().stream().toArray(String[]::new);
        //Log.i(IMapView.LOGTAG, "Geopackage database list: " + Arrays.toString(list));

        List<String> names = new ArrayList<>();

        // Import database but strip the .gpkg extension from the file name
        for (int i = 0; i < pFile.length; i++) {
            String name = pFile[i].getName();
            name = name.replace(GEO_PACKAGE_EXTENSION, "");
            //Log.i(IMapView.LOGTAG, "Geopackage database new entry: " + name);
            //if (StringUtils.indexOfAny(name, list) == -1) {
            //Log.i(IMapView.LOGTAG, "Geopackage database new entry: " + pFile[i].getName());
            manager.importGeoPackage((pFile[i]));
            names.add(name);
            //}
        }

        // Available databases
        List<String> databases = manager.databases();
        //Log.i(IMapView.LOGTAG, String.format(Locale.getDefault(), "Geopackage database size: %d", databases.size()));
        // Open database
        for(String name : names){
            //for (int i = 0; i < databases.size(); i++) {
            //GeoPackage open = manager.open(databases.get(i));
            GeoPackage open = manager.open(name);
            //boolean newTile = tileSources.add(open);
            //Log.i(IMapView.LOGTAG, String.format(Locale.getDefault(), "Geopackage database open: %s (%s)", databases.get(i), newTile ? "true":"false"));
            assertNull(open.foreignKeyCheck());
            assertTrue(open.enableForeignKeys());            
            GeoPackageValidate.validateMinimumTables(open);
            assertNull(open.quickCheck());
            assertNull(open.integrityCheck());
            open.close();
        }

    }
eclectice commented 2 years ago

@bosborn Thanks for your input.

Yes, my app already has the android.permission.WAKE_LOCK permission in the app manifest because it uses the permission, and I discovered that the ImportGeoTask class within the ImportTask class also uses the PowerManager's WakeLock.

As an Intent result of the activity in the main thread, the main activity calls the ImportTask object to import the selected Geopackage file.

However, the SafGeoPackageMapTileModuleProvider object is called within a AsyncTask.OnPreExecute() call to set the tile sources from the entire or individual raster Geopackage file returned by either SafGeoPackageMapTileModuleProvider.getTileSources() or SafGeoPackageMapTileModuleProvider.getTileSource(database) before rendering them with the MapView.setTileSource(tilesource) call in the AsyncTask.doInBackground().

In the end, I have resolved the errors listed above by combining your solution for SafGeoPackageMapTileModuleProvider and my solution for a few issues listed below:

Problem 1:

I found out that looping allGeos.getDatabases() via iterator causes a ConcurrentModificationException from this code snippet that I call it inside the AsyncTask.OnPreExecute() call:

geoPackageViewModel.getGeos().observe(getViewLifecycleOwner(), newGeos -> {
    allGeos = newGeos;
    for (GeoPackageDatabase : allGeos.getDatabases()) {}
});

As a result, I revert the code snippet to a canonical for-loop with indexing instead.

Problem 2:

If any of the imported Geopackage files stored in data/user/0/{app package}/databases is corrupted, the GeoPackageRepository.regenerateTableList() call from the GeoPackageViewModel.init() call can cause the app to crash with the following stacktrace:

stacktrace ``` 04-14 10:45:27.471 D/AndroidRuntime(30951): Shutting down VM 04-14 10:45:27.486 E/ReportHandler(30951): FATAL EXCEPTION: main 04-14 10:45:27.486 E/ReportHandler(30951): android.database.sqlite.SQLiteDatabaseCorruptException: database disk image is malformed (code 11): , while compiling: PRAGMA journal_mode 04-14 10:45:27.486 E/ReportHandler(30951): ################################################################# 04-14 10:45:27.486 E/ReportHandler(30951): Error Code : 11 (SQLITE_CORRUPT) 04-14 10:45:27.486 E/ReportHandler(30951): Caused By : The database disk image is malformed. 04-14 10:45:27.486 E/ReportHandler(30951): (database disk image is malformed (code 11): , while compiling: PRAGMA journal_mode) 04-14 10:45:27.486 E/ReportHandler(30951): ################################################################# 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1008) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnection.executeForString(SQLiteConnection.java:712) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnection.setJournalMode(SQLiteConnection.java:389) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnection.setWalModeFromConfiguration(SQLiteConnection.java:363) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:252) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:203) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:518) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:209) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:181) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:1152) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:1100) 04-14 10:45:27.486 E/ReportHandler(30951): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:828) 04-14 10:45:27.486 E/ReportHandler(30951): at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:681) 04-14 10:45:27.486 E/ReportHandler(30951): at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:666) 04-14 10:45:27.486 E/ReportHandler(30951): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:313) 04-14 10:45:27.486 E/ReportHandler(30951): at mil.nga.geopackage.GeoPackageManagerImpl.isValid(GeoPackageManagerImpl.java:1299) 04-14 10:45:27.486 E/ReportHandler(30951): at mil.nga.geopackage.GeoPackageManagerImpl.validateHeader(GeoPackageManagerImpl.java:1261) 04-14 10:45:27.486 E/ReportHandler(30951): at com.my.app.android.component.gpkg.repository.GeoPackageRepository.regenerateTableList(GeoPackageRepository.java:395) 04-14 10:45:27.486 E/ReportHandler(30951): at com.my.app.android.component.gpkg.viewmodel.GeoPackageViewModel.regenerateGeoPackageTableList(GeoPackageViewModel.java:457) 04-14 10:45:27.486 E/ReportHandler(30951): at com.my.app.android.component.gpkg.viewmodel.GeoPackageViewModel.init(GeoPackageViewModel.java:89) 04-14 10:45:27.486 E/ReportHandler(30951): at com.my.app.android.component.OsmMapFragment.onCreateView(OsmMapFragment.java:586) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1971) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:311) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.fragment.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:274) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1244) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1092) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.viewpager.widget.ViewPager.onMeasure(ViewPager.java:1622) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.View.measure(View.java:21051) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6459) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:760) 04-14 10:45:27.486 E/ReportHandler(30951): at com.google.android.material.appbar.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:99) 04-14 10:45:27.486 E/ReportHandler(30951): at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:2003) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:831) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.View.measure(View.java:21051) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6459) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.FrameLayout.onMeasure(FrameLayout.java:185) 04-14 10:45:27.486 E/ReportHandler(30951): at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.View.measure(View.java:21051) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6459) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.LinearLayout.measureVertical(LinearLayout.java:758) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.LinearLayout.onMeasure(LinearLayout.java:640) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.View.measure(View.java:21051) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6459) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.FrameLayout.onMeasure(FrameLayout.java:185) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.View.measure(View.java:21051) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6459) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.LinearLayout.measureVertical(LinearLayout.java:758) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.LinearLayout.onMeasure(LinearLayout.java:640) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.View.measure(View.java:21051) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6459) 04-14 10:45:27.486 E/ReportHandler(30951): at android.widget.FrameLayout.onMeasure(FrameLayout.java:185) 04-14 10:45:27.486 E/ReportHandler(30951): at com.android.internal.policy.DecorView.onMeasure(DecorView.java:853) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.View.measure(View.java:21051) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2583) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1642) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1893) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1522) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7098) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:927) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.Choreographer.doCallbacks(Choreographer.java:702) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.Choreographer.doFrame(Choreographer.java:638) 04-14 10:45:27.486 E/ReportHandler(30951): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:913) 04-14 10:45:27.486 E/ReportHandler(30951): at android.os.Handler.handleCallback(Handler.java:751) 04-14 10:45:27.486 E/ReportHandler(30951): at android.os.Handler.dispatchMessage(Handler.java:95) 04-14 10:45:27.486 E/ReportHandler(30951): at android.os.Looper.loop(Looper.java:154) 04-14 10:45:27.486 E/ReportHandler(30951): at android.app.ActivityThread.main(ActivityThread.java:6682) 04-14 10:45:27.486 E/ReportHandler(30951): at java.lang.reflect.Method.invoke(Native Method) 04-14 10:45:27.486 E/ReportHandler(30951): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520) 04-14 10:45:27.486 E/ReportHandler(30951): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410) ```

In the GeoPackageRepository.regenerateTableList() method, I made the following change to properly catch the exception when GeoPackageManager.validateHeader() is called:

    public List<List<GeoPackageTable>> regenerateTableList() {
        geoPackages.clear();
        geos.getValue().getDatabases().clear();
        List<List<GeoPackageTable>> databaseTables = new ArrayList<List<GeoPackageTable>>();
        StringBuilder errorMessage = new StringBuilder();
        Iterator<String> databasesIterator = manager.databases().iterator();

        // Read the feature and tile tables from the GeoPackage
        while (databasesIterator.hasNext()) {
            boolean invalidGP = false;
            boolean validHeader = false;
            String database = databasesIterator.next();

            List<Exception> headerExceptions = new ArrayList<>();

            try {
                validHeader = manager.validateHeader(database);
            } catch (Exception e) {
                headerExceptions.add(e);
            }

            if (!headerExceptions.isEmpty())
            {
                if (errorMessage.length() > 0) {
                    errorMessage.append("\n\n\n");
                }
                errorMessage.append(database).append(" Errors:");
                for (Exception exception : headerExceptions) {
                    errorMessage.append("\n\n");
                    errorMessage.append(exception.getMessage());
                }
            }

            // Delete any databases with invalid headers
            if (!validHeader) {
                if (manager.delete(database)) {
                    databasesIterator.remove();
                }
            }
            else {
                //...truncate whatever in here since it is very long
            }
        }
        // Make sure to still post the value of Geos if it's empty
        if(geos.getValue().isEmpty()){
            geos.setValue(geos.getValue());
        }
        return databaseTables;
    }