Android Question Can't create app folder, permissions issue [solved]

Didier9

Well-Known Member
Licensed User
Longtime User
I must be missing something. I use the same code in many apps.
This code creates a directory for my app's data:
B4X:
If File.ExternalWritable = True Then
    If File.IsDirectory( File.DirRootExternal, AppName & "/" ) = False Then
        File.MakeDir( File.DirRootExternal, AppName )
    End If
    AppFolder = File.DirRootExternal & "/" & AppName
End If      
If File.Exists( File.DirAssets,"somefile.txt" ) = True Then
    File.Copy( File.DirAssets, "somefile.txt", AppFolder, "somefile.txt" )
End If
In all cases until now, I have found the desired folder being created and the file copied in it.

For some reason, on this Android 6.0 tablet (not my normal development target, I normally use an Android 5.1 phone) the AppFolder directory does not get created (even though it does find the File.ExternallWritable...
I single stepped through the code and it runs without issue including the File.MakeDir() statement until the File.Copy() call where it crashes with this error:
B4X:
*** Service (starter) Create ***
Error occurred on line: 163 (Starter)
java.io.FileNotFoundException: /storage/emulated/0/TripRecorder/somefile.txt: open failed: ENOENT (No such file or directory)
    at libcore.io.IoBridge.open(IoBridge.java:452)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
    at anywheresoftware.b4a.objects.streams.File.OpenOutput(File.java:373)
    at anywheresoftware.b4a.objects.streams.File.Copy(File.java:339)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:755)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:345)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:249)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:139)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:166)
    at b4a.TripRecorder.starter.onCreate(starter.java:56)
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2877)
    at android.app.ActivityThread.access$1900(ActivityThread.java:150)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
    at libcore.io.Posix.open(Native Method)
    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
    at libcore.io.IoBridge.open(IoBridge.java:438)
    ... 20 more

When I look for the AppFolder directory with a file manager after the app crashes, I do not find it.
Of course, the same code on the Android 5.1 phone works fine. Not sure if it is related to the Android version (probably not since I have many other apps that work fine on this tablet).

The app name is "TripRecorder". No space, no special characters.

The Build Configuration is: b4a.TripRecorder
The TargetSdkVersion is 26
I have set #CanInstallToExternalStorage: True

I have apps with the same code running on that tablet, so why this particular app does not work is a mystery to me.

Open to any suggestion and thanks in advance!

Edit: I made another test, trying to copy the file to the existing Download folder and that fails too. The app finds the file in the DirAssets folder, finds the Download folder exists but crashes when trying to write the file with this:
B4X:
Copying updated assets files (8)
*** Service (starter) Create ***
Error occurred on line: 179 (Starter)
java.io.FileNotFoundException: /storage/emulated/0/Download/somefile.txt: open failed: EACCES (Permission denied)
    at libcore.io.IoBridge.open(IoBridge.java:452)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
    at anywheresoftware.b4a.objects.streams.File.OpenOutput(File.java:373)
    at anywheresoftware.b4a.objects.streams.File.Copy(File.java:339)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:755)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:345)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:249)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:139)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:166)
    at b4a.TripRecorder.starter.onCreate(starter.java:56)
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2877)
    at android.app.ActivityThread.access$1900(ActivityThread.java:150)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
    at libcore.io.Posix.open(Native Method)
    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
    at libcore.io.IoBridge.open(IoBridge.java:438)
    ... 20 more
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Sorry, too quick answer.

If File.ExternalWritable = True Then
If File.IsDirectory( File.DirRootExternal, AppName & "/" ) = False Then
File.MakeDir( File.DirRootExternal, AppName )
End If
AppFolder = File.DirRootExternal & "/" & AppName
End If
If File.Exists( File.DirAssets,"somefile.txt" ) = True Then
File.Copy( File.DirAssets, "somefile.txt", AppFolder, "somefile.txt" )
End If

DirRootExternal could require special permissions (search for this too); usually I prefer and use DirInternal.
 
Upvote 0

Mahares

Expert
Licensed User
Longtime User
Open to any suggestion and thanks in advance!
You definitely need RuntimePermissions:
B4X:
Public rp As RuntimePermissions 'in starter service process globals
B4X:
Sub Activity_Create(FirstTime As Boolean)   'in Main
    Starter.rp.CheckAndRequest(Starter.rp.PERMISSION_WRITE_EXTERNAL_STORAGE)  '
    Wait For Activity_PermissionResult (Permission As String, PResult As Boolean)
    If PResult = False Then
        MsgboxAsync("No permission to access external storage", "")
        Return
    Else
        Dim AppName As String="TripRecorder"
        Dim AppFolder As String
        If File.ExternalWritable = True Then
            If File.IsDirectory( File.DirRootExternal, AppName & "/" ) = False Then
                File.MakeDir( File.DirRootExternal, AppName )
            End If
            AppFolder = File.DirRootExternal & "/" & AppName
        End If
        If File.Exists( File.DirAssets,"somefile.txt" ) = True Then
            File.Copy( File.DirAssets, "somefile.txt", AppFolder, "somefile.txt" )
        End If
    End If
End Sub
 
Upvote 0

Didier9

Well-Known Member
Licensed User
Longtime User
You definitely need RuntimePermissions:
B4X:
Public rp As RuntimePermissions 'in starter service process globals
B4X:
Sub Activity_Create(FirstTime As Boolean)   'in Main
    Starter.rp.CheckAndRequest(Starter.rp.PERMISSION_WRITE_EXTERNAL_STORAGE)  '
    Wait For Activity_PermissionResult (Permission As String, PResult As Boolean)
    If PResult = False Then
        MsgboxAsync("No permission to access external storage", "")
        Return
    Else
        Dim AppName As String="TripRecorder"
        Dim AppFolder As String
        If File.ExternalWritable = True Then
            If File.IsDirectory( File.DirRootExternal, AppName & "/" ) = False Then
                File.MakeDir( File.DirRootExternal, AppName )
            End If
            AppFolder = File.DirRootExternal & "/" & AppName
        End If
        If File.Exists( File.DirAssets,"somefile.txt" ) = True Then
            File.Copy( File.DirAssets, "somefile.txt", AppFolder, "somefile.txt" )
        End If
    End If
End Sub

That fixed it, thanks a lot!

The problem started because I set targetSdkVersion="26" and that combined with Android 6 was what caused the problem.

Thank you again Mahares!
 
Upvote 0

Didier9

Well-Known Member
Licensed User
Longtime User
Is there a way to request permissions from within the Starter module? When I tried to place the code there, it was not happy.
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Upvote 0
Top