Android Question Starter service EACCESS (Permission denied) Error

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
I am creating an upgrade to an old app which used File.DirDefaultExternal to store its databases. Under the newer APIs, when compiled under release mode I'm getting an EACCES permission error error trying to open the file:
B4X:
--------- beginning of main
--------- beginning of system
~i:** Service (starter) Create **
~e:starter_service_create (java line: 940)
~e:java.io.FileNotFoundException: /storage/emulated/0/MyAndroidApp/db_food_data_v02.db3: open failed: EACCES (Permission denied)
~e: at libcore.io.IoBridge.open(IoBridge.java:452)
~e: at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
~e: at anywheresoftware.b4a.objects.streams.File.OpenOutput(File.java:370)
~e: at anywheresoftware.b4a.objects.streams.File.Copy(File.java:336)
~e: at phx.rcc.starter._service_create(starter.java:940)
~e: at java.lang.reflect.Method.invoke(Native Method)
~e: at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
~e: at anywheresoftware.b4a.BA.raiseEvent(BA.java:153)
~e: at phx.rcc.starter.onCreate(starter.java:55)
~e: at android.app.ActivityThread.handleCreateService(ActivityThread.java:3808)
~e: at android.app.ActivityThread.access$2100(ActivityThread.java:222)
~e: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1883)
~e: at android.os.Handler.dispatchMessage(Handler.java:102)
~e: at android.os.Looper.loop(Looper.java:158)
~e: at android.app.ActivityThread.main(ActivityThread.java:7229)
~e: at java.lang.reflect.Method.invoke(Native Method)
~e: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
~e: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
~e:Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
~e: at libcore.io.Posix.open(Native Method)
~e: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
~e: at libcore.io.IoBridge.open(IoBridge.java:438)
~e: ... 17 more
java.io.FileNotFoundException: /storage/emulated/0/MyAndroidApp/db_food_data_v02.db3: 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:370)
at anywheresoftware.b4a.objects.streams.File.Copy(File.java:336)
at phx.rcc.starter._service_create(starter.java:940)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:153)
at phx.rcc.starter.onCreate(starter.java:55)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3808)
at android.app.ActivityThread.access$2100(ActivityThread.java:222)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1883)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
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)
... 17 more
I found the (https://www.b4x.com/android/forum/threads/runtime-permissions-android-6-0-permissions.67689/) following information on the new access requests:
READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE
This is the most common dangerous permission. It is added automatically when you use File.DirDefaultExternal or File.DirRootExternal.
However there is a simple workaround for this.
1. Use RuntimePermissions.GetSafeDirDefaultExternal("") instead of File.DirDefaultExternal. The parameter passed is an optional subfolder that will be created under the default folder.
2. Add this code to the manifest editor:
AddManifestText(
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
)

The explanation for this is that GetSafeDirDefaultExternal doesn't require any permission on Android 4.4+ (API 18) and requires the WRITE_EXTERNAL_STORAGE on older versions. The code above adds the permission to older devices

I added the manifest text, created a RuntimePermissions object and used its .GetSafeDirDefaultExternal method instead of File.DirDefaultExternal but the error persists.

What did I miss?
 

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
The original program (pre-starter service days) used File.DirRootExternal to store the database if it was available. I have no problem using File.DirInternal going forward given the size of most new phone's internal storage sizes.

The old code originally set the program's working folder in a "main" module at startup:
B4X:
    If File.ExternalWritable Then
        WorkingFolder = File.DirRootExternal 
    Else
        WorkingFolder = File.DirInternal
    End If
And I just left that for the new version when I migrated the old code to the new starter service method. And that worked great the whole time I was in debug mode. Once I hit release mode testing, that's when the access error started.

Again, going forward, the new version can create a new folder in File.DirInternal and copy the new database from assets to there (this works fine). Where I'm getting the error is when I try to access the old version's database in File.DirRootExternal ("/storage/emualted/0/ProgramName/database_v01.db") that is when the EACCESS error is thrown.

I need to copy the old database to the new folder location and migrate the data to the new database then delete the copied version of the database. This means that I somehow need to know if the old version was using .DirRootExternal or .DirInternal and if external need privileges to read/write/delete.

And, ideally, I should delete the original database in the root external/internal folder (the whole folder preferably) as well if the migration was successful. This method works perfectly in debug mode.

What methodology do I need to use to accomplish this process in release mode and figure out where the old database resides?
 
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
Target SDK is currently set to 25 as I was experimenting with the AppCompat library, I never set it back it seems.

I gave up trying to figure out why I was getting the error, and I just moved the upgrade checking code out of the starter service and into the Main activity. I then added a check-and-request for PERMISSION_WRITE_EXTERNAL_STORAGE and moved the copying/converting code to the permission result and it's working fine once again in release mode.

/shrug
 
Upvote 0
Top