Android Question Random access with Android 13

Jeanc161

Member
Licensed User
Longtime User
Hi guys!
It's been a while but i'm king of stuck in a snag with file access on android 13, don't have nay problem with earlier version of android but when i read the specs for android 13 accessing File.DirDefaultExternal or File.DirInternal (In some cases) it always return an error as the file can't be located either by the path for DirInternal or DidDefaultExternal and does not want to read or write to that specific folder.
So My question is Has anyone been able to circonvent this problem with android 13 and able to save and read file on those directories
wich would be Storage/Emulated/0/app.domain.com/android/data/files for external default or the internal dir for the same thing

The funny thing is i have others files that are read and save on DirInternal on the same device that works correctly that where created under android 7 but with the new files created with B4A version 11 it does not seems to work using the same method using the random access library to store list of array object.

I included some sample code and the manifest stuff for permissions if interested

I modify the code so i use user type instead of class in the array. The array is now only used to store temporary data from the form that can be modified in the medList of objects that now is of user type like i use for all other type of random.writeB4XObject. but so far still no luck for writing or reading those files if android/data/files
on android device with version 13 on my old cell Motorola Stylus using android 7 where there is no problem

The manifest file
Manifest Editor:
AddManifestText(
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)

addPermission(android.permission.READ_MEDIA_AUDIO)  
AddPermission(android.permission.SYSTEM_ALERT_WINDOW)
AddPermission(android.permission.WRITE_EXTERNAL_STORAGE)
AddPermission(android.permission.READ_EXTERNAL_STORAGE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.ACCESS_WIFI_STATE)

This is some extract of code that i use inside the activity and i don't use the B4XPage
Activity Code:
Global Vars
    Public random As RandomAccessFile
    Public rp As PermissionsManager
    Public medList As List
    Public medData(9) As medCounter
    Type medsParm( medId As String,medDescription As String,medMaxQty As Int,medRemoveQty As Int,medCount As Int,medHours As Int,affectClock As Int)


Sub Activity_Resume
medList.initialize
      ' permission not granted request it to write to external storage
      rp.CheckAndRequestPermission(PermissionsManager.DANGEROUS_WRITE_EXTERNAL_STORAGE)
      wait For Activity_PermissionResult (Permission As String,resultat As Boolean)
          If resultat Then
            isExternalStorage = True
            ToastMessageShow("Permission WRITEExternal storage autorised",True)
        Else
            isExternalStorage = False
            ToastMessageShow("External WRITE storage access denied",True)
        End If  
End Sub

'Activity_Pause
Sub Activity_Pause (UserClosed As Boolean)
    'this works
    Log("activity paused no action taken")
    File.WriteString(File.DirInternal,countDownFile,countDownTime)
    File.WriteString(File.DirInternal,"PAUSED.PP","1")
' + Other stuff for socket closing
End sub

' this is how i set my values into the class object
' using the form on screen with EditBox, labels and CheckBox
Sub getMedDataValues
    ' set the values fromarray object to edit views
    If labl0.IsInitialized = True Then
    For i = 0 To medList.Size-1
        Dim medp As medsParm
            medp.Initialize
            medp = medList.Get(i)
            medData(i).medId = medp.medId
            medData(i).medMaxQty = medp.medMaxQty
            medData(i).medRemoveQty = medp.medRemoveQty
            medData(i).medCount = medp.medCount
            medData(i).medDescription = medp.medDescription
            medData(i).medHours = medp.medHours
            medData(i).affectClock = medp.affectClock

    Next

        Edit00.Text = medData(0).medId : Edit10.text = medData(0).medmaxQty: Edit20.TEXT = medData(0).medRemoveQty: Edit30.text = medData(0).medCount : Edit40.text = medData(0).medHours
        Edit01.Text = medData(1).medId : Edit11.text = medData(1).medmaxQty: Edit21.TEXT = medData(1).medRemoveQty: Edit31.text = medData(1).medCount : Edit41.text = medData(1).medHours
        Edit02.Text = medData(2).medId : Edit12.text = medData(2).medmaxQty: Edit22.TEXT = medData(2).medRemoveQty: Edit32.text = medData(2).medCount : Edit42.text = medData(2).medHours
        Edit03.Text = medData(3).medId : Edit13.text = medData(3).medmaxQty: Edit23.TEXT = medData(3).medRemoveQty: Edit33.text = medData(3).medCount : Edit43.text = medData(3).medHours
        Edit04.Text = medData(4).medId : Edit14.text = medData(4).medmaxQty: Edit24.TEXT = medData(4).medRemoveQty: Edit34.text = medData(4).medCount : Edit44.text = medData(4).medHours
        Edit05.Text = medData(5).medId : Edit15.text = medData(5).medmaxQty: Edit25.TEXT = medData(5).medRemoveQty: Edit35.text = medData(5).medCount : Edit45.text = medData(5).medHours
        Edit06.Text = medData(6).medId : Edit16.text = medData(6).medmaxQty: Edit26.TEXT = medData(6).medRemoveQty: Edit36.text = medData(6).medCount : Edit46.text = medData(6).medHours
        Edit07.Text = medData(7).medId : Edit17.text = medData(7).medmaxQty: Edit27.TEXT = medData(7).medRemoveQty: Edit37.text = medData(7).medCount : Edit47.text = medData(7).medHours
        CheckBox00.checked = valBoolean(medData(0).affectClock)    : CheckBox01.checked = valBoolean(medData(1).affectClock) : CheckBox02.checked = valBoolean(medData(2).affectClock) : CheckBox03.checked = valBoolean(medData(3).affectClock)
        CheckBox04.checked = valBoolean(medData(4).affectClock) : CheckBox05.checked = valBoolean(medData(5).affectClock) : CheckBox06.checked = valBoolean(medData(6).affectClock) : CheckBox07.checked = valBoolean(medData(7).affectClock)
        labl0.Text = medData(0).medDescription : Labl1.text = medData(1).medDescription
        Labl2.Text = medData(2).medDescription : Labl3.text = medData(3).medDescription
        Labl4.Text = medData(4).medDescription : Labl5.text = medData(5).medDescription
        Labl6.Text = medData(6).medDescription : Labl7.text = medData(7).medDescription
        ' update the medList with actual data
        ' put back the values in medList
        For i = 0 To medList.Size-1
            Dim medp As medsParm
                medp.Initialize
                medp.medId = medData(i).medId
                medp.medMaxQty = medData(i).MaxQty
                medp.medRemoveQty = medData(i).medRemoveQty
                medp.medCount = medData(i).medCount
                medp.medDescription = medData(i).medDescription
                medp.medHours = medData(i).medHours
                medp.affectClock = medData(i).affectClock
                medList.Set(i,medp)        
        Next

    End If
End Sub

Sub setMedDataValues
    ' get the values from the medata and the editView on layout medData

    If labl0.IsInitialized = True Then
        medData(0).medId = Edit00.Text : medData(0).medMaxQty = Edit10.text : medData(0).medRemoveQty = Edit20.text : medData(0).medCount = Edit30.text : medData(0).medHours = Edit40.text
        medData(1).medId = Edit01.Text : medData(1).medMaxQty = Edit11.text : medData(1).medRemoveQty = Edit21.text : medData(1).medCount = Edit31.text : medData(1).medHours = Edit41.text
        medData(2).medId = Edit02.Text : medData(2).medMaxQty = Edit12.text : medData(2).medRemoveQty = Edit22.text : medData(2).medCount = Edit32.text : medData(2).medHours = Edit42.text
        medData(3).medId = Edit03.Text : medData(3).medMaxQty = Edit13.text : medData(3).medRemoveQty = Edit23.text : medData(3).medCount = Edit33.text : medData(3).medHours = Edit43.text
        medData(4).medId = Edit04.Text : medData(4).medMaxQty = Edit14.text : medData(4).medRemoveQty = Edit24.text : medData(4).medCount = Edit34.text : medData(4).medHours = Edit44.text
        medData(5).medId = Edit05.Text : medData(5).medMaxQty = Edit15.text : medData(5).medRemoveQty = Edit25.text : medData(5).medCount = Edit35.text : medData(5).medHours = Edit45.text
        medData(6).medId = Edit06.Text : medData(6).medMaxQty = Edit16.text : medData(6).medRemoveQty = Edit26.text : medData(6).medCount = Edit36.text : medData(6).medHours = Edit46.text
        medData(7).medId = Edit07.Text : medData(7).medMaxQty = Edit17.text : medData(7).medRemoveQty = Edit27.text : medData(7).medCount = Edit37.text : medData(7).medHours = Edit47.text
        medData(0).affectClock = booleanVal(CheckBox00.checked) : medData(1).affectClock = booleanVal(CheckBox01.Checked)
        medData(2).affectClock = booleanVal(CheckBox02.checked) : medData(2).affectClock = booleanVal(CheckBox03.Checked)
        medData(4).affectClock = booleanVal(CheckBox04.checked) : medData(5).affectClock = booleanVal(CheckBox05.Checked)
        medData(6).affectClock = booleanVal(CheckBox06.checked) : medData(7).affectClock = booleanVal(CheckBox07.Checked)
        medData(0).medDescription = labl0.Text : medData(1).medDescription = Labl1.text
        medData(2).medDescription = Labl2.Text : medData(3).medDescription = Labl3.text
        medData(4).medDescription = Labl4.Text : medData(5).medDescription = Labl5.text
        medData(6).medDescription = Labl6.Text : medData(7).medDescription = Labl7.text

        ' update the medList with actual data
        For i = 0 To medList.Size-1
            Dim medp As medsParm
                medp = medList.Get(i)
                medp.Initialize
                medp.medId = medData(i).medId
                medp.medMaxQty = medData(i).MaxQty
                medp.medRemoveQty = medData(i).medRemoveQty
                medp.medCount = medData(i).medCount
                medp.medDescription = medData(i).medDescription
                medp.medHours = medData(i).medHours
                medp.affectClock = medData(i).affectClock
                medList.Set(i,medp)
        Next
End if
  
End Sub

Sub booleanVal(value As Boolean) As Int
    If value = True Then
        Return 1
    Else
        Return 0  
    End If
End Sub
Sub valBoolean(Value As Int) As Boolean
    If Value = 1 Then
        Return True
    Else
        Return False  
    End If
End Sub

Sub setMedDataArray
    ' set the medData with initialvalues of 0
    ' trying to get a folder values that works with
    ' permission manager but i already have the permissions
    'Dim folder As String = rp.GetSafeDirDefaultExternal("")
    folder = "/storage/emulated/0/Documents/"
    'Dim folders As List
    '    folders.Initialize
'        folders = rp.GetAllSafeDirsExternal("medData")
'        folder = folders.Get(0)
    ' initialise the values by default on the class object
    If medData(0).IsInitialized = False Then
      For i = 0 To 8
        medData(i).initialize
        medData(i).setid(i+1)
        medData(i).setmaxqty(0)
        medData(i).setRemoveQty(1)
        medData(i).setMedCount(100,True)
        medData(i).medDescription = "Medocs x"
        medData(i).affectClock = 1
        medList.Add(medData(i))
      Next
    End If
    ' this part work for some reason
    Dim lbls As checkboxLabel
        random.Initialize(File.DirInternal,labelsFileName,False)
        lbls = random.ReadB4XObject(random.CurrentPosition)
        random.Close

    setMedDAtaLabels(lbls)

    If File.Exists(file.DirInternal,"medData.txt") = True Then
       random.Initialize(File.DirInternal,"medData.txt",True)        ' this block
       random.WriteB4XObject(medList,0)    ' write from beginning of file
       random.close
    End If
End Sub

Sub readMedDataArray
    ' get or set the values of medData for labels and medType
    ' defined in the module
    'Dim folder As String = rp.GetSafeDirDefaultExternal("")
    folder = File.DirInternal
    'Dim folders As List
    '    folders.Initialize
    '    folders = rp.GetAllSafeDirsExternal("medData")
     '   folder = folders.Get(0)

    Log("Default Folder=" & folder)
    If medData(0).IsInitialized = False Then
        'File.Delete(folder,"medData.txt")
        If File.Exists(folder,"medData.txt") = False Then
            ' create default values in  medArray()
            setMedDataArray
            random.Initialize(folder,"medData.txt",True)
            random.WriteB4XObject(medList,0)
            random.close
        Else
      
           random.Initialize(folder,"medData.txt",False)
           medList = random.ReadB4XObject(0)
           random.close  
         
        End If
    Else if medData(0).IsInitialized = True Then
        random.Initialize(folder,"medData.txt",False)
        medList = random.ReadB4XObject(0)
        random.close
    End If
  
End Sub

Sub saveMedDataArrayToFile
    ' save the medData() to file
    'Dim folder As String = rp.GetSafeDirDefaultExternal("")
    folder = "/storage/emulated/0/Documents/"

    'Dim folders As List
     '   folders.Initialize
'        folders = rp.GetAllSafeDirsExternal("medData")
'        folder = folders.Get(0)
        Log("Safe folder is " & folder)
    File.Delete(folder,"medData.txt")
    random.Initialize(folder,"medData.txt",True)
    random.WriteB4XObject(medList,0)
    random.close
End Sub

So anyone who have the same problem and was able to make it work on android 13 or 14 please let me know
i tried many things not listed here and still the same problem with all of them and the funny thing is when i request permissions using Permission Manager or the other one RuntimePermission i get the same result
So i wonder where can we save the application data if all the Folders are blocked by the stupid android developpers, do we have to put our app on PlayStore for it to work with a private app ???????????

Thanks guys
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
'this works Log("activity paused no action taken") File.WriteString(File.DirInternal,countDownFile,countDownTime) File.WriteString(File.DirInternal,"PAUSED.PP","1")
These lines work because there are no restrictions on File.DirInternal.

The problem is elsewhere in the project (I didn't have the patience to read everything, also because I "don't love" multi-statement lines).
 
Upvote 1

Jeanc161

Member
Licensed User
Longtime User
These lines work because there are no restrictions on File.DirInternal.

(I didn't have the patience to read everything, also because I "don't love" multi-statement lines).

it works everywhere except in android 13 and 14 and as for multi-line statements too bad but it is a simple way to keep the code small on screen when displayed and it keep it compact on the ide cause when you have 3000 lines of code the ide became less performant even on a fast computer so sorry if you did not like that but that the way i like it.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Upvote 0

Jeanc161

Member
Licensed User
Longtime User
1. There are no restrictions on File.DirInternal (=XUI.DefaultFolder) in all Android version.

2. Signs of broken code:
B4X:
AddPermission(android.permission.WRITE_EXTERNAL_STORAGE)
AddPermission(android.permission.READ_EXTERNAL_STORAGE)

4. Another sign of broken code:
B4X:
File.DirDefaultExternal

Two important tutorials:

[B4X] Features that Erel recommends to avoid
[B4X] "Code Smells" - common mistakes and other tips
Have you tried on a device with android 13 or 14 before.
Here are some text found with AI on accessing File.DirInternal

In Android 13, you can no longer access arbitrary locations using File.DirInternal due to increased security restrictions, but you can still save and retrieve app-specific files in your application's private internal storage without permissions. For other files, you should use the Storage Access Framework to allow users to select and grant your app access to files and directories.

App-Specific Internal Storage

  • Purpose: To store data that is only accessible by your specific application.
  • Access: You do not need special permissions to access this storage, as it is private to your app.
  • Location: This data is stored in the app's private internal directory on the device.
So my solution for trying this is i created a folder under /storage/emulated/0/Meddata
I move my old files there it seem to be able to read it properly but when writing is another problem by it's own
I have change a lot of my code to be able to access the user directory that i made sofar so good i can read andd write on my own folder even with android 13
So when you say File.dirInternal is always accessible that is not true anymore i suggest you try on and android 13 or 14 you will have the same results as me.
Still now i still can access dirInternal and even dirDefaultExternal with code. so there might be a solution out there to circomvent this restriction by android
so if anybody try it on android 13 and was able to gain full access to dirInternal please follow me up on this
Thanks..
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
PERPLEXITY


The folder designated in B4A as File.DirInternal corresponds to the app's private directory, typically /data/user/0/packagename/files/, which is accessible only from the app itself. Even on Android 13 and later, this folder can still be used normally by the proprietary B4A app to read and write data. However, accessing this folder via external file managers or other apps is not permitted for security reasons, as it is an internal directory protected by the operating system.

Android 13+ doesn't appear to have changed the app's ability to use File.DirInternal, but it certainly introduced tighter restrictions on external access or visibility of this folder by tools or apps other than the B4A app itself. This is why an external file manager can't "see" or allow browsing of the files in File.DirInternal, while the app still has full access to this directory for private internal storage purposes.

In summary, on Android 13+ File.DirInternal remains a directory that can be used by the B4A app for read/write, but it is no longer visible or accessible from the outside, as was somewhat possible in older Android versions with less restrictive permissions.
 
Upvote 0

Jeanc161

Member
Licensed User
Longtime User
PERPLEXITY


The folder designated in B4A as File.DirInternal corresponds to the app's private directory, typically /data/user/0/packagename/files/, which is accessible only from the app itself. Even on Android 13 and later, this folder can still be used normally by the proprietary B4A app to read and write data. However, accessing this folder via external file managers or other apps is not permitted for security reasons, as it is an internal directory protected by the operating system.

Android 13+ doesn't appear to have changed the app's ability to use File.DirInternal, but it certainly introduced tighter restrictions on external access or visibility of this folder by tools or apps other than the B4A app itself. This is why an external file manager can't "see" or allow browsing of the files in File.DirInternal, while the app still has full access to this directory for private internal storage purposes.

In summary, on Android 13+ File.DirInternal remains a directory that can be used by the B4A app for read/write, but it is no longer visible or accessible from the outside, as was somewhat possible in older Android versions with less restrictive permissions.
Well i try again with file.dirInternal, i setup permissions on the manifest and check at the beginning of the activity if i have the read and write permissions to write to external storage. I don't know if there are other permissions that i can set for file.dirInternal but i'm still not able to read or write my object to the dirinternal folder
it returns the error
java.io.FileNotFoundException: /data/user/0/med.Medicaments.tabl/files/Medocs.txt: open failed: ENOENT (No such file or directory)
and i know that the file is out there and existing.
medicaments is a list of object of type user defenition
Type medString(bla, bla, bla,) etc

B4X:
        random.Initialize(File.DirInternal,medFilename,True)
        random.WriteB4XObject(medicaments,random.CurrentPosition)
        random.close
This is how i use the code to read my b4xObject wich is a list of type user defenition file that damn thing work cause i use it on android 7 and android 11 without any problems, but on my tablet veidoo 20inch tablet with android 13 it stuck with accessing the files on dirInternal
So unless the restrictions do not allow to save a B4XObject using the randomAccess library on these directory i have a big problem.
I read with the permissionManager library wich i tried, you use streaming inpout and output to store data.
I have tried to save a simple string to File.DirInternal i it works for reading and writing that specific type of file or object. so maybe saving a complete list of object does not work anymore, i realy don't know at this point and i'm getting frustrated cause this old thing make me loose some precious time and make the project late.
So please, please if someone else can try that on android 13 to see if i'm not crazy because at this point i'm about to drop the whole thing and change my tablet to one with and anterior version of android .

Thanks for your time and attention, i'm shure this will affect other developpers eventually.
 
Upvote 0

Brian Dean

Well-Known Member
Licensed User
Longtime User
. . . on these directory i have a big problem.

Yes - you do have a big problem. You are by no means the first person to discover it.

File.DirInternal relates to private storage accessible only from your app. It is not visible even to File Managers and it cannot be used to exchange data with the outside world. It is lost when the app is uninstalled (Edit : but see later posts). This has always been the case and has not changed in recent Android versions. It is not relevant to your problem.

If you want to write or read data elsewhere on a device, which you clearly do, and you want to use recent levels of Android (sdk 30 onwards) then you can do that only with the intervention of the User - strange but true. Maybe even stranger is that only certain areas of device storage are accessible even in some of those those cases.

If you want to research this problem within this forum then this thread is a good place to start.
 
Last edited:
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
It is lost when the app is uninstalled. This has always been the case and has not changed in recent Android versions.
I don't know if anything has changed in the latest versions, but before it was certainly possible to preserve the File.DirInternal data despite uninstallation, by writing in the Manfest file:
B4X:
SetApplicationAttribute(android:allowBackup, "true")
 
Upvote 0

Brian Dean

Well-Known Member
Licensed User
Longtime User
I don't know if anything has changed in the latest versions, but before it was certainly possible to preserve the File.DirInternal data despite uninstallation
Thanks - I didn't know that. Was the data still "private", and only accessible if the app was reinstalled, or was it transferred to some public folder?
 
Upvote 0

Jeanc161

Member
Licensed User
Longtime User
Yes - you do have a big problem. You are by no means the first person to discover it.

File.DirInternal relates to private storage accessible only from your app. It is not visible even to File Managers and it cannot be used to exchange data with the outside world. It is lost when the app is uninstalled. This has always been the case and has not changed in recent Android versions. It is not relevant to your problem.

If you want to write or read data elsewhere on a device, which you clearly do, and you want to use recent levels of Android (sdk 30 onwards) then you can do that only with the intervention of the User - strange but true. Maybe even stranger is that only certain areas of device storage are accessible even in those cases.

If you want to research this problem within this forum then this thread is a good place to start.
[UPDATE] I was able to convert my object of user type defenition to a long string representing the data and i use File.WriteString on File.dirInternal and it Works perfectly so there can be a problem with RandomAccess unable to save complex object to file.dirInternal ANYWAY i found this work around for my application and now it Works perfectly.

SO BEWARE WHENT SAVING COMPLEX OBJECTS USING A USER TYPE LIST IT MIGHT NOT WORK PROPERLY AS I FOUND OUT

is the source for RandomAccess still exists somewhere so that i can take a look at it
PLEASE if you have the link post it here Thanks!

I start to wonder many app that we find on the play store can write to the storage area i know one that ask me permission to access the file system and i give the permission and everything went well i check on android/data and look up for the app name and there was some file there that the app has deposited. so i wonder is it because of the playprotect from android that only app coming from the play store can write to those folders because of some secret security stuff that google has implemented in the playstore and that only app coming from the playstore have access to these. I found an article on google that the new version of the playstore will not allow anymore storing on the storage device that we can use safely, even if we don't program stuff to go on the playstore using playProtect that does not give permission to app that we upload using the usb connection.
If that if it is the case i think google is putting us developpers in a STRANGE case as it might loose some developpers to create app for the playstore only and not personalise app that don't need to be put on the playstore. if it is the case then as the old say F*%(K google we go on some other stuff and program for window or IOS and screw google play store.
Anyway i'm in the process to make an app that will use an alternative method to write and read files from DirInternal or my own Folder that will not imply the randomaccess library. This is work in the progress and i will keep you all about the result of my work.

Thanks all for your time and efforts trying to resolve this BS from google!
 
Last edited:
Upvote 0
Top