Sqlcipher __ CREATE TABLE android_metadata failed

android, sqlcipher, sqlite

Solution

Thanks a lot Nick Parker ,actually i used a code snippet from your sample and i created a new class representing SqlCipherAssestHelper that copy the encrypted DataBase from assets to another location in the device and read/write from the new copy using the database in the sample "1x.db" here

this is the Helper Calss:

package com.example.readdbfromas;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import net.sqlcipher.SQLException;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;
import net.sqlcipher.database.SQLiteException;
import net.sqlcipher.database.SQLiteOpenHelper;
import android.content.Context;
import android.os.Environment;
import android.util.Log;

public class DataBaseHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "1x.db";// Encrypted Database
    private static final String SUB_DATABASE_FOLDER = "/DatabaseCipher/";// a sub folder for database location
    public static String DATABASE_PATH;
    public static final int DATABASE_VERSION = 1;
    private SQLiteDatabase myDataBase;
    private final Context context;
    private String password = "";
    private String FULL_DB_Path;

    public DataBaseHelper(Context context) {

        super(context, DATABASE_NAME, null, DATABASE_VERSION);

        DATABASE_PATH = Environment.getExternalStorageDirectory()
                .getAbsolutePath().toString()
                + SUB_DATABASE_FOLDER;//get the device root Directory to copy data base on it 

        this.context = context;
        SQLiteDatabase.loadLibs(context.getApplicationContext());//load SqlCipher libraries

        FULL_DB_Path = DATABASE_PATH + DATABASE_NAME;//full database path
    }

    public SQLiteDatabase open(String password) {
        this.password = password;

        if (!checkDataBase()) {// if Database Not Exist
            copyDataBase();
        }

        myDataBase = getExistDataBaseFile();

        return myDataBase;
    }

    private SQLiteDatabase getExistDataBaseFile() {// this function to open an Exist database 

        SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
            public void preKey(SQLiteDatabase database) {
            }

            public void postKey(SQLiteDatabase database) {
                database.rawExecSQL("PRAGMA cipher_migrate;");

            }
        };
        return SQLiteDatabase.openOrCreateDatabase(FULL_DB_Path, password,
                null, hook);

    }


    private boolean checkDataBase() {// Check database file is already exist or not
        boolean checkDB = false;
        try {
            File dbfile = new File(FULL_DB_Path);
            checkDB = dbfile.exists();
        } catch (SQLiteException e) {
        }
        return checkDB;
    }


    public void db_delete() {// delete database
        File file = new File(FULL_DB_Path);
        if (file.exists()) {
            file.delete();
            System.out.println("delete database file.");
        }
    }

    private void copyDataBase() {//make a sub folder for database location and copy the database
        try {
            File fofo = new File(DATABASE_PATH);
            fofo.mkdirs();
            extractAssetToDatabaseDirectory(DATABASE_NAME);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public synchronized void closeDataBase() throws SQLException {
        if (myDataBase != null)
            myDataBase.close();
        super.close();
    }

    public void extractAssetToDatabaseDirectory(String fileName)
            throws IOException {// copy the database

        int length;
        InputStream sourceDatabase = context.getAssets().open(fileName);
        File destinationPath = new File(FULL_DB_Path);
        OutputStream destination = new FileOutputStream(destinationPath);

        byte[] buffer = new byte[4096];
        while ((length = sourceDatabase.read(buffer)) > 0) {
            destination.write(buffer, 0, length);
        }
        sourceDatabase.close();
        destination.flush();
        destination.close();
    }

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }

    public void onCreate(SQLiteDatabase db) {
    }

    public boolean changePassword(String newPassword) {// DataBase must be
                                                        // opened before
                                                        // changing Password

        try {
            if (myDataBase != null && myDataBase.isOpen()) {

                myDataBase.rawExecSQL("BEGIN IMMEDIATE TRANSACTION;");
                myDataBase.rawExecSQL("PRAGMA rekey = '" + newPassword + "';");

                this.close();
                myDataBase.close();

                return true;

            } else {

                Log.e("boolean changePassword()",
                        "Change Password Error : DataBase is null or not opened  !!");
                return false;
            }
        } catch (Exception e) {

            Log.e("boolean changePassword()",
                    "Change Password Error :ExecSQL Error !!");
            return false;

        }

    }

}

and this code inside activity :

     SQLiteDatabase db;
DataBaseHelper myDbHelper = new DataBaseHelper(MainActivity.this);
       db=myDbHelper.open("test");


  Cursor cursor=db.rawQuery("select * from t1", null);

Problem

I'm trying to attach a existing sqlcipher database(encrypted) in my android application but after copying it in my directory it cannot be opened using "SQLiteDatabase.openDatabase(...)" I tried the code in normal sqlite and it works correctly but when I used sqlcipher API i got this error message ``` //CREATE TABLE android_metadata failed //Failed to setLocale() when constructing, closing the database // net.sqlcipher.database.SQLiteException: file is encrypted or is not a database ``` I used the following code inside SQLiteOpenHelper Class : ``` if(!dbExist1) { this.getWritableDatabase(password); this.openDatabase(); try { this.close(); copyDataBase(); } catch (IOException e) { throw new Error("Error copying database"); } } public SQLiteDatabase openDatabase() throws SQLException { String DBPath = DATABASE_PATH + DATABASE_NAME; myDataBase = SQLiteDatabase.openDatabase(DBPath, password, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS); return myDataBase; } ``` And I used the following code inside Activity Class : ``` SQLiteDatabase.loadLibs(this); DataBaseHelper myDbHelper ; myDbHelper = new DataBaseHelper(this); SQLiteDatabase db=myDbHelper.openDatabase(); ``` i tried to use this solution but still same error Blockquote

Original source