{ "id": "174863", "key": "MOD-2589", "fields": { "issuetype": { "id": "4", "description": "An improvement or enhancement to an existing feature or task.", "name": "Improvement", "subtask": false }, "project": { "id": "10034", "key": "MOD", "name": "Appcelerator Modules", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "21025", "name": "appcelerator.encrypteddatabase 3.3.0", "archived": false, "released": false } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2020-04-22T17:37:30.000+0000", "created": "2020-03-27T00:25:55.000+0000", "priority": { "name": "Medium", "id": "3" }, "labels": [ "android", "database", "encrypted_database", "open", "performance", "sqlite" ], "versions": [], "issuelinks": [ { "id": "58289", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "outwardIssue": { "id": "174808", "key": "TIMOB-27803", "fields": { "summary": " Performance delays with encrypted db module 2.0.7", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "High", "id": "2" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } } ], "assignee": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2020-04-22T17:37:30.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "13609", "name": "Encrypted SQLite DB" } ], "description": "*Summary:*\r\nAn Android encrypted database file created via \"appcelerator.encrypteddatabase\" module version {{3.0.2}} or older needs to be \"migrated\" when opened with module version {{3.0.3}} and higher or else it will fail to open. As of module version {{3.1.0}}, the module will automatically migrate the database when you call the {{open()}} method. This migration operation adds about {{500ms}} to the open time every time you open the database, even if it has already been migrated.\r\n\r\nWe should only migrate an encrypted database once and subsequent opens should not execute the migration operation.\r\n\r\n*Test:*\r\n# Create a Classic Titanium project using the below code for your \"app.js\".\r\n# Set up \"tiapp.xml\" to use module \"appcelerator.encrypteddatabase\" version {{3.0.2}}.\r\n# Build and run on Android.\r\n# Set up \"tiapp.xml\" to use newest version of module \"appcelerator.encrypteddatabase\".\r\n# Build and run on Android.\r\n# In the log, look up the \"DB open duration\" time. It should be around {{900ms}}.\r\n# Press the Android back button.\r\n# Relaunch the app.\r\n# In the log, notice the \"DB open duration\" time is just as long. (This is what we want to optimize.)\r\n\r\n{code:javascript}\r\nvar database = require(\"appcelerator.encrypteddatabase\");\r\ndatabase.setPassword(\"password\");\r\n\r\nvar openStartTime = new Date();\r\nvar dbConnection = database.open(\"test_encrypted.db\");\r\nTi.API.info(\"@@@ DB open duration: \" + (new Date() - openStartTime) + \" ms\");\r\n\r\ndbConnection.execute(\"DROP TABLE IF EXISTS test;\");\r\ndbConnection.execute(\"CREATE TABLE test(id integer PRIMARY KEY, name TEXT);\");\r\ndbConnection.close();\r\n{code}\r\n\r\n*Recommended Solution:*\r\nWhen we successfully open/create a database, we should store the sqlcipher version to a {{SharedPreference}}. This way the next time we open the database, we will know what version it was last opened with.\r\n\r\nWe can obtain the sqlcipher version via the follow Java constant.\r\n[SQLiteDatabase.SQLCIPHER_ANDROID_VERSION|https://github.com/sqlcipher/android-database-sqlcipher/blob/23325436094c08f39dc704a851d2c989bfb6783e/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabase.java#L95]\r\n\r\nOur code should do something similar to as follows...\r\n{code:java}\r\n// The preferences file should use the module's name to avoid file collision.\r\nfinal String MODULE_PREFERENCES_NAME = \"appcelerator.encrypteddatabase\";\r\nSharedPreferences preferencesReader = TiApplication.getInstance().getSharedPreferences(\r\n\tMODULE_PREFERENCES_NAME, Context.MODE_PRIVATE);\r\n\r\n// After opening the database, use the db file path as the key and store the db library version to it.\r\nSharedPreferences.Editor preferencesWriter = preferencesReader.edit();\r\npreferencesWriter.putString(dbFilePath, SQLiteDatabase.SQLCIPHER_ANDROID_VERSION);\r\n{code}\r\n\r\nIn our module, we have a {{migrationHook}} that gets invoked after the database has been open.\r\n[EncrypteddatabaseModule.java - migrationHook |https://github.com/appcelerator-modules/appcelerator.encrypteddatabase/blob/ff743b74058d662014f53164355e4c8de3c308f2/android/src/appcelerator/encrypteddatabase/EncrypteddatabaseModule.java#L80]\r\n\r\nWe should only execute {{PRAGMA cipher_migrate;}} if the major version component of {{SQLiteDatabase.SQLCIPHER_ANDROID_VERSION}} is higher than what is stored to the above shared preference. If the shared preference cannot be found for the database file, then we should assume the worse and do the migration anyways. This is in case the database file is old and has been copied to a different directory.", "attachment": [], "flagged": false, "summary": "Android: Optimize encrypted db open() when migration from older version not needed", "creator": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "environment": null, "closedSprints": [ { "id": 1192, "state": "closed", "name": "2020 Sprint 8", "startDate": "2020-04-10T18:08:36.092Z", "endDate": "2020-04-24T18:08:00.000Z", "completeDate": "2020-04-24T16:23:30.466Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "454898", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "body": "-PR:- https://github.com/appcelerator-modules/appcelerator.encrypteddatabase/pull/46\r\n_(Closed in favor of below PR.)_\r\n", "updateAuthor": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-03-27T20:09:08.000+0000", "updated": "2020-04-04T04:09:10.000+0000" }, { "id": "454971", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "body": "PR: https://github.com/appcelerator-modules/appcelerator.encrypteddatabase/pull/48", "updateAuthor": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-04-04T04:08:46.000+0000", "updated": "2020-04-04T04:08:46.000+0000" }, { "id": "455192", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Here is a back-port which will work with Titanium 7.0.0+. (Not built with gradle.)\r\nPR (android)(3_3_X): https://github.com/appcelerator-modules/appcelerator.encrypteddatabase/pull/50\r\n", "updateAuthor": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-04-20T20:35:52.000+0000", "updated": "2020-04-20T20:35:52.000+0000" }, { "id": "455204", "author": { "name": "lchoudhary", "key": "lchoudhary", "displayName": "Lokesh Choudhary", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Backport PR Merged.", "updateAuthor": { "name": "lchoudhary", "key": "lchoudhary", "displayName": "Lokesh Choudhary", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-04-21T22:01:07.000+0000", "updated": "2020-04-21T22:01:07.000+0000" }, { "id": "455215", "author": { "name": "lchoudhary", "key": "lchoudhary", "displayName": "Lokesh Choudhary", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Fix works as expected.\r\nClosing.", "updateAuthor": { "name": "lchoudhary", "key": "lchoudhary", "displayName": "Lokesh Choudhary", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-04-22T17:37:24.000+0000", "updated": "2020-04-22T17:37:24.000+0000" } ], "maxResults": 5, "total": 5, "startAt": 0 } } }