Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-26878] Android: Ti.Database.install() fails if destination directory does not exist

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionFixed
Resolution Date2019-05-16T22:54:02.000+0000
Affected Version/sRelease 7.5.0
Fix Version/sRelease 8.1.0
ComponentsAndroid
Labelsandroid, database, engSchedule, install
ReporterAndreas Pingas
AssigneeJoshua Quick
Created2019-02-26T15:03:32.000+0000
Updated2019-05-16T22:54:02.000+0000

Description

*Summary:* As of Titanium 7.5.0, the Ti.Database.install() function will throw a FileNotFoundException on an Android 6.0 device if analytics is disabled and the database was never installed on that device before. This issue only happens if the destination directory does not exist, which seems to happen on Android 6.0 but not newer OS versions. Note that you need to disable "analytics" in the "tiapp.xml" since it will create the "databases" directory before the "app.js" has a chance to load, which skirts the issue. *Steps to reproduce:*

Create a Classic Titanium app.

Set the "tiapp.xml" property "analytics" to false as shown below.

Copy the below code to the project's "app.js" file.

Copy the [^Database1.sqlite] file to the project's "Resources" directory.

Build and run on Android 6.0.

Notice the app crashes on startup.

tiapp.xml
<?xml version="1.0" encoding="UTF-8"?>
<ti:app xmlns:ti="http://ti.appcelerator.org">
	<analytics>false</analytics>
</ti:app>
app.js
var dbConnection = Ti.Database.install("Database1.sqlite", "Database1");
var resultSet = dbConnection.execute("SELECT key, value FROM properties");
Ti.API.info("@@@ Database Table 'properties' row count: " + (resultSet ? resultSet.rowCount : "<null>"));
if (resultSet) {
	for (; resultSet.isValidRow(); resultSet.next()) {
		Ti.API.info("- Key: '" + resultSet.field(0) + "', Value: '" + resultSet.field(1) + "'");
	}
}
dbConnection.execute("INSERT OR REPLACE INTO properties(key, value) VALUES (?, ?)", "LastLaunchTime", new Date());
dbConnection.close();
*Recommended Solution:* Add a mkdirs() call to create the destination directory tree (if missing) to our DatabaseModule.java class' install() method below. [DatabaseModule.java#L85](https://github.com/appcelerator/titanium_mobile/blob/master/android/modules/database/src/java/ti/modules/titanium/database/DatabaseModule.java#L85) \\ ----
var db = Ti.Database.install('/src/countries.sqlite', 'countries');
[ERROR] :  TiDatabase: (main) [1023,1023] Error installing database: countries msg=/data/user/0/com.test.a/databases/countries: open failed: ENOENT (No such file or directory)
[ERROR] :  TiDatabase: java.io.FileNotFoundException: /data/user/0/com.test.a/databases/countries: open failed: ENOENT (No such file or directory)
[ERROR] :  TiDatabase: 	at libcore.io.IoBridge.open(IoBridge.java:497)
[ERROR] :  TiDatabase: 	at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
[ERROR] :  TiDatabase: 	at java.io.FileOutputStream.<init>(FileOutputStream.java:72)
[ERROR] :  TiDatabase: 	at ti.modules.titanium.database.DatabaseModule.install(DatabaseModule.java:124)
[ERROR] :  TiDatabase: 	at org.appcelerator.kroll.runtime.v8.V8Runtime.nativeRunModule(Native Method)
[ERROR] :  TiDatabase: 	at org.appcelerator.kroll.runtime.v8.V8Runtime.doRunModule(V8Runtime.java:162)
[ERROR] :  TiDatabase: 	at org.appcelerator.kroll.KrollRuntime.runModule(KrollRuntime.java:207)
[ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiLaunchActivity.loadScript(TiLaunchActivity.java:97)
[ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiRootActivity.loadScript(TiRootActivity.java:414)
[ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiLaunchActivity.windowCreated(TiLaunchActivity.java:174)
[ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiRootActivity.windowCreated(TiRootActivity.java:283)
[ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiBaseActivity.onCreate(TiBaseActivity.java:767)
[ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiLaunchActivity.onCreate(TiLaunchActivity.java:167)
[ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiRootActivity.onCreate(TiRootActivity.java:260)
[ERROR] :  TiDatabase: 	at android.app.Activity.performCreate(Activity.java:6309)
[ERROR] :  TiDatabase: 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1113)
[ERROR] :  TiDatabase: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2519)
[ERROR] :  TiDatabase: 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2654)
[ERROR] :  TiDatabase: 	at android.app.ActivityThread.access$900(ActivityThread.java:175)
[ERROR] :  TiDatabase: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488)
[ERROR] :  TiDatabase: 	at android.os.Handler.dispatchMessage(Handler.java:111)
[ERROR] :  TiDatabase: 	at android.os.Looper.loop(Looper.java:207)
[ERROR] :  TiDatabase: 	at android.app.ActivityThread.main(ActivityThread.java:5728)
[ERROR] :  TiDatabase: 	at java.lang.reflect.Method.invoke(Native Method)
[ERROR] :  TiDatabase: 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
[ERROR] :  TiDatabase: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
[ERROR] :  TiDatabase: Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
[ERROR] :  TiDatabase: 	at libcore.io.Posix.open(Native Method)
[ERROR] :  TiDatabase: 	at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
[ERROR] :  TiDatabase: 	at libcore.io.IoBridge.open(IoBridge.java:483)
[ERROR] :  TiDatabase: 	... 25 more
[ERROR] :  TiExceptionHandler: (main) [15,1038] ti:/invoker.js:105
[ERROR] :  TiExceptionHandler: 		return delegate.apply(invoker.__thisObj__, args);
[ERROR] :  TiExceptionHandler:                   ^
[ERROR] :  TiExceptionHandler: Error: /data/user/0/com.test.a/databases/countries: open failed: ENOENT (No such file or directory)
[ERROR] :  TiExceptionHandler:     at SandboxAPI.invoker [as install] (ti:/invoker.js:105:19)
[ERROR] :  TiExceptionHandler:     at /src/lib/phone_code.js:5:20
[ERROR] :  TiExceptionHandler:     at Module._runScript (ti:/module.js:612:9)
[ERROR] :  TiExceptionHandler:     at Module.load (ti:/module.js:108:7)
[ERROR] :  TiExceptionHandler:     at Module.loadJavascriptText (ti:/module.js:457:9)
[ERROR] :  TiExceptionHandler:     at Module.loadAsFile (ti:/module.js:512:15)
[ERROR] :  TiExceptionHandler:     at Module.loadAsFileOrDirectory (ti:/module.js:429:20)
[ERROR] :  TiExceptionHandler:     at Module.require (ti:/module.js:262:17)
[ERROR] :  TiExceptionHandler:     at Module.global.Module.require (/ti.internal/extensions/binding.js:33:76)
[ERROR] :  TiExceptionHandler:     at require (ti:/module.js:570:15)
[ERROR] :  TiExceptionHandler:
[ERROR] :  TiExceptionHandler:     libcore.io.IoBridge.open(IoBridge.java:497)
[ERROR] :  TiExceptionHandler:     java.io.FileOutputStream.<init>(FileOutputStream.java:87)
[ERROR] :  TiExceptionHandler:     java.io.FileOutputStream.<init>(FileOutputStream.java:72)
[ERROR] :  TiExceptionHandler:     ti.modules.titanium.database.DatabaseModule.install(DatabaseModule.java:124)
[ERROR] :  TiExceptionHandler:     org.appcelerator.kroll.runtime.v8.V8Runtime.nativeRunModule(Native Method)
[ERROR] :  TiExceptionHandler:     org.appcelerator.kroll.runtime.v8.V8Runtime.doRunModule(V8Runtime.java:162)
[ERROR] :  TiExceptionHandler:     org.appcelerator.kroll.KrollRuntime.runModule(KrollRuntime.java:207)
[ERROR] :  TiExceptionHandler:     org.appcelerator.titanium.TiLaunchActivity.loadScript(TiLaunchActivity.java:97)
[ERROR] :  TiExceptionHandler:     org.appcelerator.titanium.TiRootActivity.loadScript(TiRootActivity.java:414)
[ERROR] :  TiExceptionHandler:     org.appcelerator.titanium.TiLaunchActivity.windowCreated(TiLaunchActivity.java:174)
[INFO] :   I/[MALI][Gralloc]: [+]r_hnd(0xb46ff060), client(26), share_fd(25)
[ERROR] :  V8Exception: Exception occurred at ti:/invoker.js:105: Uncaught Error: /data/user/0/com.test.a/databases/countries: open failed: ENOENT (No such file or directory)

Attachments

FileDateSize
app.js2019-02-27T21:29:44.000+000066
countries.sqlite2019-02-27T21:29:54.000+000017408
Database1.sqlite2019-03-12T02:06:36.000+000012288
DatabaseInstallTest.js2019-03-12T02:08:16.000+0000557
Screenshot 2019-03-05 at 10.47.59.png2019-03-05T09:32:19.000+0000340122

Comments

  1. Sharif AbuDarda 2019-02-26

    Hello, please provide a full standalone sample project for us to test the issue on our end. Thanks.
  2. Andreas Pingas 2019-02-27

    I think i Have found the issue: 1. Create a New classic project 2. Copy app.js, countries.sqlite 3. Disable false 4. Run (Environment: SDK 8.0.0 ANDROID 6.0, 5.0, 4.4) [^app.js] [^countries.sqlite]
  3. Joshua Quick 2019-03-01

    I can't reproduce the issue. The Ti.Database.install() method is correctly installing the bundled sqlite database. I've built it with Titanium 8.0.0 and ran it on a physical Android 9.0 device. I've tested it with the below code which outputs all table names within the installed database. I can see that the database contains a "countries" table name.
       var dbConnection = Ti.Database.install("/src/countries.sqlite", "countries");
       var resultSet = dbConnection.execute("SELECT name FROM sqlite_master WHERE type='table'");
       Ti.API.info("@@@ Fetched Tables: " + (resultSet ? resultSet.rowCount : "<null>"));
       if (resultSet) {
       	for (; resultSet.isValidRow(); resultSet.next()) {
       		Ti.API.info("- Table Name: " + resultSet.field(0));
       	}
       }
       dbConnection.close();
       Ti.API.info("@@@ I did it!");
       
  4. Andreas Pingas 2019-03-01

    The issue appears at Android 6.0 5.0 and 4,4 devices. At 9.0 works properly.
  5. Sharif AbuDarda 2019-03-01

    Hello [~andreas.pingas], I have tested the issue with [~jquick]'s sample code provided above with your database. I have tested on Android 6.0.1 physical device(Galaxy j2 Prime), 4.4.4 emulator and 6.0.0 emulator with SDK 7.5.1.GA. I was able to get the expected result in the console. What are the devices you are testing on? Provide the model.
       [INFO]  newclassicpro 1.0 (Powered by Titanium 7.5.1.4b82d9d6b2)
       [INFO]  @@@ Fetched Tables: 1
       [INFO]  - Table Name: android_metadata
       [INFO]  @@@ I did it!
       
    Thank you.
  6. Andreas Pingas 2019-03-01

    Environment Titanium SDK: 7.5.0.GA OR 7.5.1.GA OR 8_0_X 8.0.0.v20190228092226 Device: Samsung Galaxy S6 (Android 5.0 OR 6.0) *false (FALSE)*
       [ERROR] :  TiDatabase: (main) [306,306] Error installing database: countries msg=/data/data/com.test/databases/countries: open failed: ENOENT (No such file or directory)
       [ERROR] :  TiDatabase: java.io.FileNotFoundException: /data/data/com.test/databases/countries: open failed: ENOENT (No such file or directory)
       [ERROR] :  TiDatabase: 	at libcore.io.IoBridge.open(IoBridge.java:456)
       [ERROR] :  TiDatabase: 	at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
       [ERROR] :  TiDatabase: 	at java.io.FileOutputStream.<init>(FileOutputStream.java:72)
       [ERROR] :  TiDatabase: 	at ti.modules.titanium.database.DatabaseModule.install(DatabaseModule.java:124)
       [ERROR] :  TiDatabase: 	at org.appcelerator.kroll.runtime.v8.V8Runtime.nativeRunModule(Native Method)
       [ERROR] :  TiDatabase: 	at org.appcelerator.kroll.runtime.v8.V8Runtime.doRunModule(V8Runtime.java:180)
       [ERROR] :  TiDatabase: 	at org.appcelerator.kroll.KrollRuntime.runModule(KrollRuntime.java:247)
       [ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiLaunchActivity.loadActivityScript(TiLaunchActivity.java:135)
       [ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiLaunchActivity.windowCreated(TiLaunchActivity.java:190)
       [ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiRootActivity.windowCreated(TiRootActivity.java:185)
       [ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiBaseActivity.onCreate(TiBaseActivity.java:783)
       [ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiLaunchActivity.onCreate(TiLaunchActivity.java:176)
       [ERROR] :  TiDatabase: 	at org.appcelerator.titanium.TiRootActivity.onCreate(TiRootActivity.java:174)
       [ERROR] :  TiDatabase: 	at android.app.Activity.performCreate(Activity.java:5933)
       [ERROR] :  TiDatabase: 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
       [ERROR] :  TiDatabase: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2251)
       [ERROR] :  TiDatabase: 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
       [ERROR] :  TiDatabase: 	at android.app.ActivityThread.access$800(ActivityThread.java:144)
       [ERROR] :  TiDatabase: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
       [ERROR] :  TiDatabase: 	at android.os.Handler.dispatchMessage(Handler.java:102)
       [ERROR] :  TiDatabase: 	at android.os.Looper.loop(Looper.java:135)
       [ERROR] :  TiDatabase: 	at android.app.ActivityThread.main(ActivityThread.java:5221)
       [ERROR] :  TiDatabase: 	at java.lang.reflect.Method.invoke(Native Method)
       [ERROR] :  TiDatabase: 	at java.lang.reflect.Method.invoke(Method.java:372)
       [ERROR] :  TiDatabase: 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
       [ERROR] :  TiDatabase: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
       [ERROR] :  TiDatabase: Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
       [ERROR] :  TiDatabase: 	at libcore.io.Posix.open(Native Method)
       [ERROR] :  TiDatabase: 	at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
       [ERROR] :  TiDatabase: 	at libcore.io.IoBridge.open(IoBridge.java:442)
       [ERROR] :  TiDatabase: 	... 25 more
       [ERROR] :  TiExceptionHandler: (main) [1,307] ti:/invoker.js:101
       [ERROR] :  TiExceptionHandler: 		return delegate.apply(invoker.__thisObj__, args);
       [ERROR] :  TiExceptionHandler:                   ^
       [ERROR] :  TiExceptionHandler: Error: /data/data/com.test/databases/countries: open failed: ENOENT (No such file or directory)
       [ERROR] :  TiExceptionHandler:     at SandboxAPI.invoker [as install] (ti:/invoker.js:101:19)
       [ERROR] :  TiExceptionHandler:     at /app.js:1:122
       [ERROR] :  TiExceptionHandler:     at Module._runScript (ti:/module.js:613:9)
       [ERROR] :  TiExceptionHandler:     at Module.load (ti:/module.js:105:7)
       [ERROR] :  TiExceptionHandler:     at Module.loadJavascriptText (ti:/module.js:457:9)
       [ERROR] :  TiExceptionHandler:     at Module.loadAsFile (ti:/module.js:512:15)
       [ERROR] :  TiExceptionHandler:     at Module.loadAsFileOrDirectory (ti:/module.js:429:20)
       [ERROR] :  TiExceptionHandler:     at Module.require (ti:/module.js:256:17)
       [ERROR] :  TiExceptionHandler:     at require (ti:/module.js:570:15)
       [ERROR] :  TiExceptionHandler:     at /ti.main.js:27:3
       [ERROR] :  TiExceptionHandler:
       [ERROR] :  TiExceptionHandler:     libcore.io.IoBridge.open(IoBridge.java:456)
       [ERROR] :  TiExceptionHandler:     java.io.FileOutputStream.<init>(FileOutputStream.java:87)
       [ERROR] :  TiExceptionHandler:     java.io.FileOutputStream.<init>(FileOutputStream.java:72)
       [ERROR] :  TiExceptionHandler:     ti.modules.titanium.database.DatabaseModule.install(DatabaseModule.java:124)
       [ERROR] :  TiExceptionHandler:     org.appcelerator.kroll.runtime.v8.V8Runtime.nativeRunModule(Native Method)
       [ERROR] :  TiExceptionHandler:     org.appcelerator.kroll.runtime.v8.V8Runtime.doRunModule(V8Runtime.java:180)
       [ERROR] :  TiExceptionHandler:     org.appcelerator.kroll.KrollRuntime.runModule(KrollRuntime.java:247)
       [ERROR] :  TiExceptionHandler:     org.appcelerator.titanium.TiLaunchActivity.loadActivityScript(TiLaunchActivity.java:135)
       [ERROR] :  TiExceptionHandler:     org.appcelerator.titanium.TiLaunchActivity.windowCreated(TiLaunchActivity.java:190)
       [ERROR] :  TiExceptionHandler:     org.appcelerator.titanium.TiRootActivity.windowCreated(TiRootActivity.java:185)
       [ERROR] :  V8Exception: Exception occurred at ti:/invoker.js:101: Uncaught Error: /data/data/com.test/databases/countries: open failed: ENOENT (No such file or directory)
       
  7. Joshua Quick 2019-03-02

    Unfortunately, we're still unable to reproduce the issue. From looking at the stack trace you've posted, it looks like it's failing to create/write the file. If I were to "guess" what's wrong, I would think the issue is that the directory /data/data/com.test/databases doesn't exist on your Galaxy S6 (note that this directory path will be different on different devices too). As a quick *+test+*, try create the "databases" directory on your Galaxy S6 before installing the database. Note that the below isn't a work-around and won't work on any other device. It's merely a test to see if the real issue is that it that the "databases" subdirectory is missing on your device. Also note the "com.test" is your test app's package name, so, make sure your app's package name and subdirectory match here.
       Ti.Filesystem.getFile("{/data/data/com.test/databases"). createDirectory(true);
       // Install your database afterwards here.
       
  8. Andreas Pingas 2019-03-02

    Yes it works when the Database installs later it runs properly. To find the bug the following has to be valid: 1) Analytics to be disabled - false. If you run the app and it was true and then you disable it, it work as the Database has been installed properly since the first time. In a situation like this you have to delete all the data of the app from CLEAR DATA setting. 2) The issue applies to all devices both physical as well as simulator that run android 6.0 or below.
  9. Joshua Quick 2019-03-04

    bq. Analytics to be disabled - false. If you run the app and it was true and then you disable it, it work as the Database has been installed properly since the first time. In a situation like this you have to delete all the data of the app from CLEAR DATA setting. I've tried this, but I still can't reproduce the issue. The database under the "Resources" directory still installs fine for me. bq. Yes it works when the Database installs later it runs properly. What do you mean? Did it work when you called createDirectory() that I showed you above? If that works, then we can attempt a "blind" fix and see about creating the directory tree before installing the database file. But note that this would be a complete guess on our end since we're still unable to reproduce the issue you are seeing.
  10. Andreas Pingas 2019-03-05

    The bug does not get fixed even after Ti.Filesystem.getFile("{/data/data/com.test/databases").createDirectory(true); The bug exists on simulators as well as physical devices. The only way to resolve the issue is analytics - true. Then eveything runs properly. At this point, i will disable analytics in order for the app to work properly, as the issue has to be somewhere there. You can see the results for a TEST app that i have created just yesterday and i have only run it a few times in an Android Device. !Screenshot 2019-03-05 at 10.47.59.png|thumbnail!
  11. Joshua Quick 2019-03-05

    We can't reproduce the issue when setting analytics false. We've already tried that. But I still think this is an issue with the app's "databases" subdirectory not existing. The analytics system will automatically create that directory tree if it doesn't exist.
  12. Joshua Quick 2019-03-06

    Okay. I'm able to reproduce the issue now. I've updated the ticket summary to provide more details. Thanks.
  13. Joshua Quick 2019-03-12

    PR (master): https://github.com/appcelerator/titanium_mobile/pull/10770
  14. Keerthi Mahalingam 2019-05-16

    FR passed. PR merged
  15. Keerthi Mahalingam 2019-05-16

    Verified the fix on sdk 8.1.0.v20190516120244.Works as expected. Test information found under the PR https://github.com/appcelerator/titanium_mobile/pull/10770

JSON Source