B4J Question Examine the position of the SD card lock switch

T201016

Active Member
Licensed User
Longtime User
Hi,
There is a lock switch on the left side of the SD / SDHC / SDXC card. You will not be able to modify or delete the contents of the memory card if it is pushed down.

One of the easier ways to check if the card has an unlocked switch is to try to write to the card.
Is there any other way to check the status of the card lock switch?
 
Solution
I found my SDCard reader and tried the next level. This one works:

B4X:
Public Sub IsWritable(FilePath As String) As Boolean
    Dim F As JavaObject
    F.InitializeNewInstance("java.io.File",Array(FilePath))
    Dim Files As JavaObject
    Files.InitializeStatic("java.nio.file.Files")
    Return Files.RunMethod("isWritable",Array(F.RunMethod("toPath",Null)))
End Sub


B4X:
Log(IsWritable("M:\")) 'True or False depending on the switch state
Just had to go a level deeper to the nio.

T201016

Active Member
Licensed User
Longtime User
Writing to the SD card isn't trivial. You need to use RuntimePermissions.GetAllSafeDirsExternal to find the accessible folder. If you find one then you can try to write and catch the exception.

Hello,

For a more complete explanation of the problem.
In case the memory card (SD / SDHC / SDXC) is NOT in the slot of the computer or otherwise,
the card is in the slot, but is protected against writing by the slider on the left side
- I handle it this way:

However, I would prefer not to try to write on (foreign, i.e. not your own) cards, but to examine the position of the slider itself in the card, which seems impossible without this write attempt.


'methods used so far:
B4J:
If Not(File.IsDirectory(drv1&ppk1,"")) Then
    msg.Show("Insert an external memory card with keys.", "SanDisk")
Else
    If B4JBuildKeys.Build(drv1&ppk1, prv1, txtPrivateKey.Text, pas1) = False Then
        msg.Show("The memory card has a SET LOCK,"&CRLF&"REMOVE the LOCK on the memory card !", "SanDisk")
        Return
    End If
End If

'B4JBuildKeys code module:
Public Sub Build(KeyPath As String, KeyFile As String, Value As Object, password As String) As Boolean
    Try
        If Not(File.Exists(KeyPath,"")) Or KeyFile.Length = 0 Or Value.As(String).Length = 0 Or password.Length = 0 Then
            Return False
        End If
        Dim raf As RandomAccessFile
        raf.Initialize(KeyPath, KeyFile, False)
        raf.WriteEncryptedObject(Value, password, raf.CurrentPosition)
        raf.Flush
        Dim buffer(raf.CurrentPosition) As Byte
        raf.ReadBytes(buffer, 0, buffer.Length, 0)
        raf.Close
        Return True
    Catch
        Return False
    End Try
End Sub
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
However, I would prefer not to try to write on (foreign, i.e. not your own) cards
Why? If you need to see if you can write to it presumably you are in fact going to write to it so a test write does no more harm than what you are intending to do anyway. Just test before doing the real write and inform the user if it doesn't happen.
 
Upvote 0

T201016

Active Member
Licensed User
Longtime User
Why? If you need to see if you can write to it presumably you are in fact going to write to it so a test write does no more harm than what you are intending to do anyway. Just test before doing the real write and inform the user if it doesn't happen.
Yes, you are absolutely right. I thought there was a way around this by reading the ON / OFF state of the physical lock. It happens that the card locks are damaged, and just inserting the card into the slot automatically locks it, because the switch moves down by itself.

Thanks for the reaction.šŸ‘
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
If you want to avoid writing an arbitrary file you can try this:

B4X:
Public Sub CanWrite(FilePath As String) As Boolean
    Dim F As JavaObject
    F.InitializeNewInstance("java.io.File",Array(FilePath))
    Return F.RunMethod("canWrite",Null)
End Sub

Usage:
B4X:
Log(CanWrite("C:\"))
I don't have an SD Card handy to try it on, but it should work I would have thought.
 
Upvote 0

T201016

Active Member
Licensed User
Longtime User
Usage:
B4X:
Log(CanWrite("C:\"))
I don't have an SD Card handy to try it on, but it should work I would have thought.

Hi, @stevel05
Unfortunately, your idea only works for a file with the "ReadOnly" attribute set (not a media). When I give the full path followed by a filename, the function checks to see if the file is writable or not. However, in your example: Log (CanWrite ("C: \")), ALWAYS returns (True), regardless of whether the storage device is locked or not.

PS. And this is true, because the medium is recordable, but you can't check if the switch is set to ON / OFF in this way ...

/*
* @method CanWrite
* @parameter String Dir, String Filename
* @return boolean
*/
public static boolean CanWrite(String Dir, String FileName)
{
return new java.io.File(Dir, FileName).canWrite();
}
But we are close to solving the problem :)
 
Last edited:
Upvote 0

T201016

Active Member
Licensed User
Longtime User
Obviously I'll have to delve into the windows system itself ... there has to be a setting somewhere.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
I found my SDCard reader and tried the next level. This one works:

B4X:
Public Sub IsWritable(FilePath As String) As Boolean
    Dim F As JavaObject
    F.InitializeNewInstance("java.io.File",Array(FilePath))
    Dim Files As JavaObject
    Files.InitializeStatic("java.nio.file.Files")
    Return Files.RunMethod("isWritable",Array(F.RunMethod("toPath",Null)))
End Sub


B4X:
Log(IsWritable("M:\")) 'True or False depending on the switch state
Just had to go a level deeper to the nio.
 
Last edited:
Upvote 0
Solution

T201016

Active Member
Licensed User
Longtime User
I found my SDCard reader and tried the next level. This one works:

It looks like your solution is ok.
I gave up a further low-level attempt while working on the "SetDOSAttribute ()" function,
which I also had a problem with, but it's OK.

Thanks a lot šŸ‘


SetDOSAttribute:
Public Sub SetDOSAttribute(aFile As String, attribute As String, setTo As Boolean) As Boolean
    If File.Exists(File.GetFileParent(aFile),File.GetName(aFile)) = False Then
        Log($"File ${aFile} does not exist"$)
        Return False
    End If
    Dim retVal As Boolean = True
    Dim fileAttr As String
    Select attribute.ToLowerCase
        Case "h"
            fileAttr = "dos:hidden"
        Case "r"
            fileAttr = "dos:readonly"
        Case "s"
            fileAttr = "dos:system"
        Case "a"
            fileAttr = "dos:archive"
        Case Else
            retVal = False
    End Select
    If retVal = True Then
        Dim nativeMe As JavaObject
        nativeMe = Me
        nativeMe.RunMethod("setDOSFileAttribute", Array(aFile, fileAttr, setTo))
        'Pure JavaObject -> could not get it to work
        'Dim jo As JavaObject
        'Dim aURI As JavaObject
        'Dim aPath As JavaObject
        'aURI = jo.InitializeStatic("java.net.URI").RunMethod("create",Array(File.GetUri(File.GetFileParent(aFile),File.GetName(aFile))))
        'aPath = jo.InitializeStatic("java.nio.file.Paths").RunMethod("get", Array(aURI))
        'Dim aEnum As JavaObject
        'aEnum = jo.InitializeStatic("java.nio.file.LinkOption").RunMethod("valueOf",Array("NOFOLLOW_LINKS"))
        'Never could get the below to match
        'jo.InitializeStatic("java.nio.file.Files").RunMethod("setAttribute", Array(aPath, fileAttr, setTo, aEnum))
    End If
    Return retVal
End Sub

#If JAVA
import java.io.IOException;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

//Note: Remove the word static when used in a class module
public static void setDOSFileAttribute(String aFile, String attribute, boolean flag) throws IOException {
   Path aPath = Paths.get(aFile);
   Files.setAttribute(aPath, attribute, flag);
}
#End If
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
'Never could get the below to match
To pass parameters to a Vararg param using JavaObject we have to pass an array of the proper type. That array may be empty if it is valid.

B4X:
Public Sub SetDOSAttribute(aFile As String, attribute As String, setTo As Boolean) As Boolean
    If File.Exists(File.GetFileParent(aFile),File.GetName(aFile)) = False Then
        Log($"File ${aFile} does not exist"$)
        Return False
    End If
    Dim retVal As Boolean = True
    Dim fileAttr As String
    Select attribute.ToLowerCase
        Case "h"
            fileAttr = "dos:hidden"
        Case "r"
            fileAttr = "dos:readonly"
        Case "s"
            fileAttr = "dos:system"
        Case "a"
            fileAttr = "dos:archive"
        Case Else
            retVal = False
    End Select
    If retVal = True Then
'        Dim nativeMe As JavaObject
'        nativeMe = Me
'        nativeMe.RunMethod("setDOSFileAttribute", Array(aFile, fileAttr, setTo))
        'Pure JavaObject -> could not get it to work
        Dim jo As JavaObject
        Dim F As JavaObject
        F.InitializeNewInstance("java.io.File",Array(aFile))
        Dim aPath As JavaObject = F.RunMethod("toPath",Null)
        Dim aEnum As JavaObject
        aEnum = jo.InitializeStatic("java.nio.file.LinkOption").RunMethod("valueOf",Array("NOFOLLOW_LINKS"))

        'VarArgs parameters require an array of the specific type to be passed with JavaObject.  The array can be empty if that is an acceptable state.
        Dim Enums As JavaObject
        Enums.InitializeArray("java.nio.file.LinkOption",Array(aEnum))

        jo.InitializeStatic("java.nio.file.Files").RunMethod("setAttribute", Array(aPath, fileAttr, setTo, Enums))
    End If
    Return retVal
End Sub
 
Last edited:
Upvote 0
Top