Realm Android Decryption issue - android

When I try to access the realm db after installing the app, I'm getting the following error:
Caused by: java.lang.IllegalArgumentException: Wrong key used to decrypt Realm.
This is the method which return realm instance:
RealmConfiguration config = new RealmConfiguration.Builder(mContext)
.name(dbname)
.schemaVersion(0)
.migration(new DataBaseMigration())
.encryptionKey(key)
.build();
Realm realm = Realm.getInstance(config);
return realm;
I am getting the error in the following line.
Realm realm = Realm.getInstance(config);
Help me to solve this issue. I am using realm 0.85 version.

Related

Storing unicode characters with Realm

I want to read a JSON string from a text file and store it's objects to a Realm file.
Text file created with Delphi and encoded in UTF-8. I'm reading String from text-file with a Scanner class and then extract from it some JSONObjects and JSONArrays. there is no problem in JSON Objects and Arrays with unicode characters. I put them in the realm file with createAllFromJson method of Realm object :
RealmConfiguration RealmConfig = new RealmConfiguration.Builder()
.name("info.realm")
.deleteRealmIfMigrationNeeded()
.build();
Realm realm = Realm.getInstance(RealmConfig);
try{
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.createAllFromJson(AccountRecordObject.class, accounts);
realm.createAllFromJson(SanadRecordObject.class, sanads);
realm.createObjectFromJson(ConfigRecordObject.class, config);
}
});
}finally {
realm.close();
}
and reading from Realm object :
RealmConfiguration RealmConfig = new RealmConfiguration.Builder()
.name("info.realm")
.deleteRealmIfMigrationNeeded()
.build();
Realm realm = Realm.getInstance(RealmConfig);
try{
TextView txt = parentView.findViewById(R.id.fileSampleTxt);
String S = realm.where(AccountRecordObject.class).equalTo("AccNo", 300015).findFirst().getAccName();
txt.setText(S);
}finally {
realm.close();
}
The problem is when I want to get a result from Realm file, unicode characters are shown as '?' :
Edit :
There is no problem in unicode characters when I use createObject method instead of createAllFromJson and passing a value from JSONArray :
AccountRecordObject obj = realm.createObject(AccountRecordObject.class);
obj.setAccNo(10000001);
try {
obj.setAccName(accounts.getJSONObject(4).getString("AccName"));
}catch (JSONException E){
E.printStackTrace();
}
It seems there is a problem in createAllFromJson and createObjectFromJson methods with unicode characters
Realm stores unicode just fine, so most likely it is the font used in Android Studio that does not support the glyphs actually in the file.

Get Data From Realm File in SdCard or Another Path

I have a db.realm file in AssetFolder and In First Start My App I Copy This file To This Address :
/data/data/" + getPackageName()+"/files/
I want to Get Data From This File In android How Can I do It ?
Exactly How Can I Config realmfile from Path?
I use This Code :
RealmConfiguration configC = new RealmConfiguration.Builder("/data/data/" + getPackageName()+"/files/")
.deleteRealmIfMigrationNeeded()
.modules(new WordRealm())
.build();
Then I got This Error:
note 1 : I donot want to read realmfile from asset like this code :
RealmConfiguration config = new RealmConfiguration.Builder()
.assetFile("path/to/file/in/assets") // e.g "default.realm" or "lib/data.realm"
.deleteRealmIfMigrationNeeded()
.build()
I want to get it from internal or external path.
note 2 : i use Realm Java 3.4.0
Thanks.
The default location for Realm files are in /data/data/<packageName>/files so doing this should work:
RealmConfiguration config = new RealmConfiguration.Builder()
.name("db.realm")
.deleteRealmIfMigrationNeeded()
.modules(new WordRealm())
.build();

How to use Room Persistence Library with pre-populated database?

I'd like to use Room with a pre-populated database, but I can't understand how to tell Room where to find my database.
I've now put it in src/main/assets/databases and when I create the instance for the Room database I create it this way:
Room.databaseBuilder(
getApplicationContext(),
AppDatabase.class,
"justintrain.db"
)
.allowMainThreadQueries()
.build();
This way tho, I think it's creating a new database every time, or anyways, it's not using the pre-populated one.
How can I make it to find my database?
This is how I solved it, and how you can ship your application with a pre-populated database (up to Room v. alpha5)
put your SQLite DB database_name.db into the assets/databases folder
take the files from this repo and put them in a package called i.e. sqlAsset
in your AppDatabase class, modify your Room's DB creation code accordingly:
Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class,
"database_name.db")
.openHelperFactory(new AssetSQLiteOpenHelperFactory())
.allowMainThreadQueries()
.build();
Note that you have to use "database_name.db" and not getDatabasePath() or other methods: it just needs the name of the file.
UPDATE (Nov 7th 2019)
Room now supports using a pre-packaged database out of the box, since version 2.2.0
https://developer.android.com/jetpack/androidx/releases/room#2.2.0
Solution before version 2.2.0: Simple solution without any other external libraries.
Room relies on the existing Android framework code to create or open a database. If you look into the source code of FrameworkSQLiteOpenHelper (Room's version of SQLiteOpenHelper) it internally calls SQLiteOpenHelper.getReadableDatabase() and other methods wherever needed.
So, the simplest solution is to just copy the DB file from assets directory to mContext.getDatabasePath("my-database.sqlite") before creating the DB with Room.
In your case, the code looks something like this -
private final String DB_NAME = "my-database.sqlite";
private MyDatabase buildDatabase(Context context) {
final File dbFile = context.getDatabasePath(DB_NAME);
if(!dbFile.exists()) {
copyDatabaseFile(dbFile.getAbsolutePath());
}
return Room.databaseBuilder(context.getApplicationContext(),
MyDatabase.class, DB_NAME)
.build();
}
private void copyDatabaseFile(String destinationPath) {
// code to copy the file from assets/database directory to destinationPath
}
This link has the code needed to copy the DB - link with code
I was having the same problem so I created a library which does exactly that.
the accepted answer work but I think it's easier to use a library.
AppDatabase db = RoomAsset
.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "database_name.db")
.build();
Add it to your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
Add the dependency
dependencies {
// ... other dependencies
implementation 'com.github.humazed:RoomAsset:v1.0'
}
you can find the library here: https://github.com/humazed/RoomAsset
Working 2019 solution without hacks or dependencies (Kotlin)
Place your .db file in assets/databases (or really any folder in there, as long as it's under assets).
Use Room 2.2's existing createFromAsset() function, passing in the path to the database. For example, if your database file is named my_data.db and is under the databases directory of the assets folder, then you would do createFromAsset("databases/my_data.db").
Assuming your database name (e.g., my_data) is stored in a constant variable named DATABASE_NAME, you can use this sample code:
Room.databaseBuilder(
context.applicationContext,
MyDatabase::class.java,
DATABASE_NAME
)
.createFromAsset("databases/$DATABASE_NAME.db")
.build()
Important: Make sure the schema of your data class/entity precisely matches the schema of your .db file. For example, if a column isn't explicitly marked as NOT NULL in the .db file, then that means the column can have null
values in it. In Kotlin, you would have to match that with val colName: dataType? = null in your data class. If you just do val colName: dataType, Kotlin will compile that to a NOT NULL column, and that will throw an exception when you try to run your app.
Note: If instead you want to create a Room database from a database file that you download onto the Android device itself, you can alternatively use the createFromFile() function. Check out the official documentation on how to do this.
Room now supports Prepopulated Databases. Just prepare your database by using a program like SQLite Browser or any other of your choice. Then put it in Assets Folder probably in a subfolder called database then call:
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.build()
If you did not provide your database as an Asset but you downloaded it or it is in File System then then the method is:
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromFile(File("mypath"))
.build()
For more description or database migrations about this Feature you can check the Documentation Training.
Similar solution with room without using external libraries:
1. Copy your database in assets folder
2. Copy your database from assets folder
public class MainActivity extends AppCompatActivity {
public static AppDatabase db;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
copyDatabase(getApplicationContext(), "yourdatabase.db");
db = Room.databaseBuilder(getApplicationContext(), .class, "yourdatabase.db").allowMainThreadQueries().build();
}
private void copyDatabase(Context context, String databaseName) {
final File dbPath = context.getDatabasePath(databaseName);
// If the database already exists, return
if (dbPath.exists()) {
Log.d("Activity", "db Path Exists");
return;
}
// Make sure we have a path to the file
dbPath.getParentFile().mkdirs();
// Try to copy database file
try {
final InputStream inputStream = context.getAssets().open(databaseName);
final OutputStream output = new FileOutputStream(dbPath);
byte[] buffer = new byte[8192];
int length;
while ((length = inputStream.read(buffer, 0, 8192)) > 0) {
output.write(buffer, 0, length);
}
output.flush();
output.close();
inputStream.close();
}
catch (IOException e) {
Log.d("Activity", "Failed to open file", e);
e.printStackTrace();
}
}
}
Starting with Room 2.2, you can pre-populate your database using command below:
Room.databaseBuilder(appContext, TestDatabase.class, “Sample.db”)
.createFromAsset(“database/myapp.db”)
.build()
you just copy assets/databases to app/databases
and than add addMigrations() in databaseBuilder
it will keep your data

How to set RealmDefaultConfiguration with encryption key

I am using realm database in my app, and currently in the Application class I am initialising realm with default configuration and everywhere in the app I am using Realm.getDefaultConfiguration() to query/save data.
Now I wanted to encrypt the database and I did as following
RealmConfiguration config = new RealmConfiguration.Builder()
.encryptionKey(getKeyFunction())
.migration(new MyMigration())
.build();
Realm.setDefaultConfiguration(config);`
But when I try to access Realm.getDefaultConfiguration() I get Invalid format of Realm File error.
What am I doing wrong ?
Here is my working code. I have tested this in my sample project
// Generate a key
// IMPORTANT! This is a silly way to generate a key. It is also never stored.
// For proper key handling please consult:
// * https://developer.android.com/training/articles/keystore.html
// * http://nelenkov.blogspot.dk/2012/05/storing-application-secrets-in-androids.html
Realm.init(this);
byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
.encryptionKey(key)
.build();
// Start with a clean slate every time
Realm.deleteRealm(realmConfiguration);
Realm.setDefaultConfiguration(realmConfiguration);
// Open the Realm with encryption enabled
realm = Realm.getDefaultInstance();
//realm = Realm.getInstance(realmConfiguration);
// Everything continues to work as normal except for that the file is encrypted on disk
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Person person = realm.createObject(Person.class);
person.setName("Happy Person");
person.setAge(14);
}
});
Person person = realm.where(Person.class).findFirst();
Log.i(TAG, String.format("Person name: %s", person.getName()));

Realm with pre populated data into assets?

Normally I use Realm as:
RealmConfiguration config = new RealmConfiguration.Builder(applicationContext).deleteRealmIfMigrationNeeded().build();
How can I add to the assets folder of my project a database with data and read it?
Since Realm Java 0.91.0 there has been an assetFile(String) option on the RealmConfiguration that automatically will copy a file from assets and use that if needed (e.g. if the Realm is opened the first time or has been deleted for some reason):
RealmConfiguration config = new RealmConfiguration.Builder()
.assetFile("path/to/file/in/assets") // e.g "default.realm" or "lib/data.realm"
.deleteRealmIfMigrationNeeded()
.build()
The above will copy the file from assets the first time the Realm is opened or if it has been deleted due to migrations (remember to update the asset Realm in that case).
OLD ANSWER:
It is possible to bundle a Realm database in the assets folder, but then you just need to copy it from there when starting the app the first time.
We have an example of how to copy the files here: https://github.com/realm/realm-java/blob/master/examples/migrationExample/src/main/java/io/realm/examples/realmmigrationexample/MigrationExampleActivity.java#L101-Lundefined
copyBundledRealmFile(this.getResources().openRawResource(R.raw.default_realm), "default.realm");
private String copyBundledRealmFile(InputStream inputStream, String outFileName) {
try {
File file = new File(this.getFilesDir(), outFileName);
FileOutputStream outputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
outputStream.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Since Realm 0.89.0 RealmConfiguration.initialData(Realm.Transaction) can now be used to populate a Realm file before it is used for the first time.
RealmConfiguration conf = new RealmConfiguration.Builder(context)
.initialData(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.beginTransaction();
realm.createObject(....)
realm.commitTransaction();
}
}).deleteRealmIfMigrationNeeded().name("mRealm.db").build();
Realm realm = Realm.getInstance(conf);
[EDIT] See Stan's answer below. Apparently Realm now supports this directly so you can ignore this answer (unless you're using older Realm versions).
We had a similar need, and also wanted support for a read-only realm database shared with an iOS version of the app.
We created a simple library and have open-sourced it. It includes the copy code given in #christian-melchior's answer, as well as some optional extra tracking for read-only realm database(s) bundled with the APK. Comments and PRs welcomed. See:
https://github.com/eggheadgames/android-realm-asset-helper
Realm has a special parameter in its RealmConfiguration.Builder called assetFile. You could use it like:
realmConfiguration = new RealmConfiguration.Builder()
.assetFile("dataBase/default.realm") // your app's packaged DB
...
.build();
just set yer assets DB path and file name and you are good to go without any android-realm-asset-helper lib or copy-file-from-assets code. In this example my app packaged DB-file lies in "assets/dataBase/default.realm".Note, version below 2 has a bit another way to call assetFile, you should pass context additionally:
realmConfiguration = new RealmConfiguration.Builder(context)
.assetFile(context, "dataBase/default.realm")
.build();
You can use assetFile() method. Please be aware that you can't use assetFile() with deleteIfMigrationNeeded().

Categories

Resources