Search code examples
androidsqliteandroid-sqliteroot

SQLiteCantOpenDatabaseException => unknown error (code 14): Could not open database (Android 6.0)


Following is working on many thousands of devices but recently I got following errors, twice, both from android 6.0, so it may be related to the new android version:

RootUtils.copyDatabase(path, pathApp);
if (new File(pathApp).exists())
{
    L.d(this, "Database copied!"); // <= this is called, so copying file succeeds!!!

    SQLiteDatabase db = SQLiteDatabase.openDatabase(pathApp, null, SQLiteDatabase.OPEN_READONLY);
    final Cursor cursor = db.rawQuery("select * from contacts", new String[0]); // <= this line throws the exception
    cursor.moveToFirst();
    ....
}

Log/Exception

c.m.s.utils.RootUtils   [RootUtils-91]  copyDatabase: true
c.m.s.networks.utils.Util   [FUtil-105] Database copied!
c.m.s.networks.utils.Util   [FUtil-185] unknown error (code 14): Could not open database
android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
    at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
    at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:207)
    at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:191)
    at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
    at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
    at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
    at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:806)
    at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:791)
    at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
    at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
    ...

My copy function is following:

public final static boolean copyDatabase(String pathSource, String pathTarget) throws IOException, InterruptedException, TimeoutException, RootDeniedException
{
    Shell shell = RootTools.getShell(true);
    Command command = new Command(0,
            "su\n",
            "rm " + pathTarget + "\n",
            "cat " + pathSource + " > " + pathTarget + "\n",
            "chown root.root " + pathTarget + "\n",
            "chmod 777 " + pathTarget + "\n");
    command = shell.add(command);
    int exitCode = command.getExitCode();
    while (!command.isFinished()) {
        Thread.sleep(50);
    }
    shell.close();
    boolean success = exitCode == -1 && command.isFinished();
    L.d(RootUtils.class, "copyDatabase: " + success);
    return success;
}

Edit: My new copy function that even sets the Owner/Group to my apps process - only got one feedback yet, but seems to not solve the problem

public final static boolean copyDatabase(String ownAppDatabase, String pathSource, String pathTarget) throws IOException, InterruptedException, TimeoutException, RootDeniedException
{
    Shell shell = RootTools.getShell(true);

    boolean getRealUser = true;
    String owner = null;
    String group = null;
    if (getRealUser)
    {
        final  StringHolder lsResult = new StringHolder("");
        Command lsCommand = new Command(0,
                "su\n",
                "ls -ld " + ownAppDatabase + "\n")
        {
            public void commandOutput(int id, String line) {
                super.commandOutput(id, line);
                lsResult.set(line);
            }

        };
        lsCommand = shell.add(lsCommand);
        int lsExitCode = lsCommand.getExitCode();
        while (!lsCommand.isFinished()) {
            Thread.sleep(50);
        }

        L.d(RootUtils.class, "ls exit code: " + lsExitCode);
        L.d(RootUtils.class, "ls result: " + lsResult.get());

        String[] parts = lsResult.get().split("\\s+");
        if (parts.length > 3)
        {
            owner = parts[1];
            group = parts[2];
        }
    }

    if (owner == null || group == null)
    {
        L.d(RootUtils.class, "owner or group is NULL!");
        getRealUser = false;
    }
    else
        L.d(RootUtils.class, "owner=" + owner + " | group=" + group);

    Command command = new Command(0,
            "su\n",
            "rm " + pathTarget + "\n",
            "cat " + pathSource + " > " + pathTarget + "\n",
            "chown " + (getRealUser ? (owner + "." + group + " ") : "chown root.root ") + pathTarget + "\n",
            "chmod 777 " + pathTarget + "\n");
    command = shell.add(command);
    int exitCode = command.getExitCode();
    while (!command.isFinished()) {
        Thread.sleep(50);
    }
    shell.close();
    boolean success = exitCode == -1 && command.isFinished();
    L.d(RootUtils.class, "copyDatabase: " + success);
    return success;
}

Solution

  • Following is the only solution I found out (it should work on all older devices as well):

    Dump the database!

    Copying the database does not work, reading the database either. Probably because the database is opened by another process. But dumping the database works. You can dump a single table as well, here's an example that works on all devices:

    String dump = pathSQLite3 + " " + pathDB + " \".dump '" + tablename + "'\"\n";
    

    Only drawback, you have to add the sqlite executables to your app and you have to find out, which architecture the target device has so that you know which sqlite executable you can use...

    But at least this works fine.