Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-20470] Android 6.0 (Post 5.2.0 SDK): java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)

GitHub Issuen/a
TypeBug
PriorityCritical
StatusClosed
ResolutionFixed
Resolution Date2016-04-05T02:05:02.000+0000
Affected Version/sRelease 5.2.0
Fix Version/sRelease 5.4.0
ComponentsAndroid
Labelspermissions
ReporterEric Cheung
AssigneeHieu Pham
Created2016-02-24T22:13:19.000+0000
Updated2016-07-15T06:25:36.000+0000

Description

Steps to Reproduce

I played around with the latest 5.2.0 SDK and Nexus 6P... for the most part everything seems to work except for one case with runtime permission. if I use Ti.Filesystem.tempDirectory as my storage and created the permission checks, this still fails on first try even if you accept the Permission. Log: [INFO] : Permission granted? true [ERROR] : TiFileProxy: (KrollRuntimeThread) [66154,66154] IOException encountered [ERROR] : TiFileProxy: java.io.FileNotFoundException: /storage/emulated/0/Android/data/com.apptest.tempdirperm/cache/_tmp/GettingStartedTitanium_Linux.pdf: open failed: ENOENT (No such file or directory) [ERROR] : TiFileProxy: at libcore.io.IoBridge.open(IoBridge.java:452) [ERROR] : TiFileProxy: at java.io.FileOutputStream.(FileOutputStream.java:87) [ERROR] : TiFileProxy: at org.appcelerator.titanium.io.TiFile.getOutputStream(TiFile.java:273) [ERROR] : TiFileProxy: at org.appcelerator.titanium.io.TiFile.open(TiFile.java:336) [ERROR] : TiFileProxy: at org.appcelerator.titanium.io.TiFile.write(TiFile.java:382) [ERROR] : TiFileProxy: at org.appcelerator.titanium.TiFileProxy.write(TiFileProxy.java:290) [ERROR] : TiFileProxy: at org.appcelerator.kroll.runtime.v8.V8Object.nativeCallProperty(Native Method) [ERROR] : TiFileProxy: at org.appcelerator.kroll.runtime.v8.V8Object.callProperty(V8Object.java:73) [ERROR] : TiFileProxy: at org.appcelerator.kroll.KrollProxy.handleMessage(KrollProxy.java:1155) [ERROR] : TiFileProxy: at android.os.Handler.dispatchMessage(Handler.java:98) [ERROR] : TiFileProxy: at android.os.Looper.loop(Looper.java:148) [ERROR] : TiFileProxy: at org.appcelerator.kroll.KrollRuntime$KrollRuntimeThread.run(KrollRuntime.java:118) [ERROR] : TiFileProxy: Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory) [ERROR] : TiFileProxy: at libcore.io.Posix.open(Native Method) [ERROR] : TiFileProxy: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186) [ERROR] : TiFileProxy: at libcore.io.IoBridge.open(IoBridge.java:438) [ERROR] : TiFileProxy: ... 11 more [INFO] : ALERT: (KrollRuntimeThread) [41,66195] File exist? false}} Steps to recreate: Create new Mobile App Project and paste in code to index.js Compile and run, make sure it is a clean install on device Click the 'Hello, World' to trigger the download and permission. Accept the permission on popup. Accessing the file in temporary directory fails. (alert will say file does not exist) Close the app Check permission in settings > app permission (storage should be enabled) Restart the app Click 'Hello, World' again to trigger download. Alert will now say file exists. Workaround: If I switch the Ti.Filesystem.tempDirectory to Ti.Filesystem.applicationDataDirectory, I do not run into the issue of having to restart the app to open the file. But I have to make sure those files are deleted after use. It would be best to use temporary directory so that the files are automatically removed after app is closed.

Actual Result

File does not exist in tempDirectory when permission is enabled on RunTime.

Expected Result

File should exist in tempDirectory.

Attachments

FileDateSize
index.js2016-02-24T22:13:31.000+0000981
Screenshot_20160224-165334.png2016-02-24T22:13:30.000+000086518
Screenshot_20160224-165342.png2016-02-24T22:13:29.000+000064817

Comments

  1. Eric Cheung 2016-02-26

    Ti.Filesystem.tempDirectory cannot access filesystem even if Storage Permissions is allowed after the *very first* run-time prompt. After the app restart, permission works as normal for temp directory.
  2. Fokke Zandbergen 2016-02-26

    Looks like something we didn't fix with TIMOB-20251
  3. Shawn Lan 2016-03-12

    What if you do a setTimeout in the permission request callback? Maybe the permission is not yet available immediately right after the callback is called.
  4. Peter Ladis 2016-03-20

    Im having the same issue in production for my android marshmallow users. Any update on this fix?
  5. Ashraf Abu 2016-03-22

    [~echeung@leviton.com] Could you try this. Writing it this way will allow it to work:-
        function getFile() {
         var url = "http://www.appcelerator.com/wp-content/uploads/GettingStartedTitanium_Linux.pdf";
         var fileName = "GettingStartedTitanium_Linux.pdf";
         
         var httpClient = Ti.Network.createHTTPClient();
         httpClient.onerror = function(e) {
           alert('Download Error: ' + e.error);
         };
       // Adding this to see progress
         httpClient.ondatastream = function(e) {
           Ti.API.info('ondatastream called, progress = '+e.progress);
         };
         httpClient.onload = function(e) {
             
           var tempDir = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory);
           if(!tempDir.exists()){
               tempDir.createDirectory();
           }
           var f = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory, fileName);
           var file = f.write(this.responseData);
           alert('File exist? ' + f.exists());
         };
         httpClient.open('GET', url);
         httpClient.send();
       }
       
    If it's okay, I'll resolve this ticket.
  6. Fokke Zandbergen 2016-03-22

    [~msamah] shouldn't we handle creation of the temp directory?
  7. Andrey Tkachenko 2016-03-22

    Ashraf Abu this is my test:
       var f = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory);
       	Ti.API.info('directory exists', f.exists());
       	f.createDirectory();
       	Ti.API.info('directory exists', f.exists());
           f = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory, 'fileter.txt');	
           f.write('writing to the file would be enough to create it');
       	Ti.API.info('file exists', f.exists());
           alert(f.read());
       
    Result:
       [INFO] :   directory exists false
       [INFO] :   directory exists false
       [ERROR] :  TiFileProxy: (KrollRuntimeThread) [67422,67422] IOException encountered
       [ERROR] :  TiFileProxy: java.io.FileNotFoundException: /storage/emulated/0/Android/data/falkolab.test556/cache/_tmp/fileter.txt: open failed: ENOENT (No such file or directory)
       [ERROR] :  TiFileProxy: 	at libcore.io.IoBridge.open(IoBridge.java:452)
       [ERROR] :  TiFileProxy: 	at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
       [ERROR] :  TiFileProxy: 	at org.appcelerator.titanium.io.TiFile.getOutputStream(TiFile.java:273)
       ...
       
  8. Ashraf Abu 2016-03-23

    I'll check this out more.
  9. Andrey Tkachenko 2016-03-23

    It must be fixed in 5.2.1.GA. Which meaning to release sdk if it not usable in last Android ?
  10. Ashraf Abu 2016-03-23

    With ref to the code I wrote earlier, fully written it should be:-
        function doClick(e) {
          if (!Ti.Filesystem.hasStoragePermissions()) {
            Ti.Filesystem.requestStoragePermissions(function(result) {
              console.log('Permission granted? ' + result.success);
              if (result.success) {
                getFile();
              }
              else {
                alert('Permission denied.');
              }
            });
          }
          else {
            getFile();
          }
        }
        
        function getFile() {
          var url = "http://www.appcelerator.com/wp-content/uploads/GettingStartedTitanium_Linux.pdf";
          var fileName = "GettingStartedTitanium_Linux.pdf";
          
          var httpClient = Ti.Network.createHTTPClient();
          httpClient.onerror = function(e) {
            alert('Download Error: ' + e.error);
          };
        // Adding this to see progress
          httpClient.ondatastream = function(e) {
            Ti.API.info('ondatastream called, progress = '+e.progress);
          };
          httpClient.onload = function(e) {
              
            var tempDir = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory);
            if(!tempDir.exists()){
                tempDir.createDirectory();
            }
            var f = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory, fileName);
            var file = f.write(this.responseData);
            alert('File exist? ' + f.exists());
          };
          httpClient.open('GET', url);
          httpClient.send();
        }
        
        $.index.open();
        
    Permission is still required. Directory needing to be created seems to be an Android 6 thing/issue. Meanwhile, the issue does have a workaround which is to create the dir (with permissions). Even though this workaround exists, this should continue to be looked into.
  11. Carlos Henrique Zinato 2016-04-01

    some news on that?
  12. Sergey Volkov 2016-04-04

    https://github.com/appcelerator/titanium_mobile/pull/7913
  13. Ashraf Abu 2016-04-05

    PR https://github.com/appcelerator/titanium_mobile/pull/7913 reviewed and merged.
  14. Andrey Tkachenko 2016-04-05

    We must notify the developer that the files can in some cases be available to other applications. Files will be in private storage if getCacheDir but getExternalCacheDir not: {quote} There is no security enforced with these files. For example, any application holding WRITE_EXTERNAL_STORAGE can write to these files. {quote} Also: {quote} Starting in KITKAT, no permissions are required to read or write to the returned path; it's always accessible to the calling app. This only applies to paths generated for package name of the calling application. To access paths belonging to other packages, WRITE_EXTERNAL_STORAGE and/or READ_EXTERNAL_STORAGE are required. {quote} In both cases this note for Android, may be for iOS: {quote} The returned path may change over time if different shared storage media is inserted, so only relative paths should be persisted. {quote} [getCacheDir()](http://developer.android.com/intl/ru/reference/android/content/Context.html#getCacheDir%28%29) [getExternalCacheDir()](http://developer.android.com/intl/ru/reference/android/content/Context.html#getExternalCacheDir%28%29)
  15. Fokke Zandbergen 2016-04-05

    [~bimmel] ^^
  16. Carlos Henrique Zinato 2016-04-11

    Guys, I'm having this problem on Android 6 using the latest SDK 5.2.2.GA. Just downloaded the 5.4.0 and it's gone. What should I do? What do you think is better? Is there a way to merge these fixes into the current SDK (5.2.2)? Because I can not wait till the GA release of 5.4. Thanks in advance
  17. Ashraf Abu 2016-04-12

    [~chmiiller] You could cherry-pick the changes https://github.com/appcelerator/titanium_mobile/pull/7913 into the 5.2.X branch and create a custom SDK for yourself. Related info: http://docs.appcelerator.com/platform/latest/#!/guide/Building_the_Titanium_SDK_From_Source 5.4.0 is not GA yet.
  18. Carlos Henrique Zinato 2016-04-12

    Ok good, thanks
  19. Lokesh Choudhary 2016-06-10

    Verified the fix. No java.io.FileNotFoundException seen & the file could be accessed. Closing. Environment: Appc Studio : 4.6.0.201605201934 Ti SDK : 5.4.0.v20160608165242 Ti CLI : 5.0.8 Alloy : 1.8.7 MAC El Capitan : 10.11.4 Appc NPM : 4.2.7-2 Appc CLI : 5.4.0-11 Node: 4.4.4 Nexus 6 - Android 6.0.0
  20. Ashraf Abu 2016-07-15

    [~lchoudhary] There seems to be some issues in TIMOB-23562 Could you also check that TIMOB-23562 is a valid bug which affects this ticket? And if so, either reopen this ticket or do whatever actions you deem necessary.
  21. Ashraf Abu 2016-07-15

    [~lokeshchdhry] Ignore my last comment. It's another thing which I'll address in TIMOB-23562.

JSON Source