So the app I'm working on is making use of the external files dir to store some downloaded information. The issue I'm running into is, very ocassionally the getExternalFilesDir() method will return null. My understanding of this method might be flawed, but I was under the impression that it would return null under TWO distinct scenarios:
1) The user has an SD card slot but does NOT have an sd card mounted (or the SD card is mounted but plugged into a computer as mass storage option)
2) The app does not actually have read/write permissions
The app has the read/write permissions (it must since it works in most scenarios), and I I'm checking for the first case with the following code:
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
{
editor.putBoolean(AUDIO_LOCATION_KEY, true);
subPath = context.getExternalFilesDir(null).getPath();
}
The app is crashing with a null pointer exception from the getExternalFilesDir line. I can't reproduce the crash, Is there something I'm missing? I would've that that if that check passed that we'd be guaranteed to get something back. Any insight would be much appreciated!
Thanks.
Related
I have a requirement for my Android app to be able to receive files from a server and save them in an arbitrary location on my SD card. When I say "SD card", I don't mean the /sdcard/ directory - AKA /storage/emulated/0/, I mean the physical removable SD card.
My initial attempt:
FileOutputStream(File("storage/ABCD-1234", "test_file.txt")).use {
it.write("Hello world!".toByteArray())
}
where "ABCD-1234" is replaced with the actual storage ID string. This gives me an IOException saying permission denied, even though I have the WRITE_EXTERNAL_STORAGE permission granted. Fine, there must just be some restrictions on SD card writing access. So I spent most of today researching around the internet for apps which can already do this. And I come across this one called SSH/SFTP Server, using which I was able to push a file to the device's SD card via WinSCP from a laptop over WiFi. The thing is, I can't figure out how to go about requesting this ability in my app. Can anyone point me in the right direction?
This is targeting Android Q (10), in case it matters at all.
N.B.: This question about the serial number of the physical SD card, not the UUID of the mounted volume. These are two independent pieces of data.
In some versions of Android, and other variants of Linux, it's possible to get the serial number of a mounted SD card, e.g. by reading the contents of /sys/class/mmc_host/mmc0/mmc0:0001/serial or /sys/block/mmcblk0/device/serial (specific numbers may vary). In my testing this has worked pretty reliably, as long as the SD card is inserted in a built-in SD card slot (not mounted via USB adapter).
But as of Android 7.0 Nougat, the OS is said to be blocking access to this information, at least on some devices. I tested this by running a test app on a new Alcatel A30 GSM (Android 7.0), and in fact the above approach fails with a permission error:
java.io.FileNotFoundException: /sys/block/mmcblk0/device/serial (Permission denied)
at java.io.FileInputStream.open(Native Method)
For future reference, we (testing from an adb shell) have permissions to ls -ld the following:
/sys/class/mmc_host but not /sys/class/mmc_host/mmc0
/sys/block but not /sys/block/mmcblk0
Since the above approach no longer works,
Is there another way to obtain the serial number of a mounted SD card in Android 7.0 or later?
Failing that, is there any documentation or other statement from Google on plans for providing or not providing this function? I haven't found anything in the Android issue tracker, but maybe I'm not searching right.
To make sure the question is clear, I'm talking about what an ordinary (non-system) app running on a non-rooted device can do, with any permissions that an app can normally request and receive.
FYI, the /sbin directory doesn't seem to be readable, so commands like /sbin/udevadm aren't an option.
In Android N access to /sys nad /proc was significantly restricted, this was done to provide stricter sandboxes where applications run. This is explained in https://issuetracker.google.com/issues/37091475 as intentional. Actually its not said that all the data in /sys is not accessible, and Google is open to allow access to other files from this location:
If there are specific files in /sys you believe should be available to applications, but are not, please file a new bug where the request can be evaluated. For instance, /sys/devices/system/cpu is available to all processes, so it's inaccurate to say all of /sys is restricted.
I have a bad feeling that google is making changes similar to Apple where it is not allowed to gain hardware id-s. If that is not resolved then the solution is to use google account IDs instead. But I am aware it is not always possible, and will require major changes in business logic (licensing etc.).
Hopefully your bug report will be considered positively.
another related SO I found : File system changes in Android Nougat
Use StorageVolume.getUuid() on StorageVolume which you get from StorageManager.
The value is volume ID assigned during formatting of the card, and its length/format differs depending on file system type. For FAT32 it is XXXX-XXXX, for NTFS it's longer hex string, for Internal mass storage it returns null.
public String getSDCARDiD()
{
String sd_cid = null;
try {
File file = new File("/sys/block/mmcblk1");
String memBlk;
if (file.exists() && file.isDirectory()) {
memBlk = "mmcblk1";
} else {
//System.out.println("not a directory");
memBlk = "mmcblk0";
}
Process cmd = Runtime.getRuntime().exec("cat /sys/block/"+memBlk+"/device/cid");
BufferedReader br = new BufferedReader(new InputStreamReader(cmd.getInputStream()));
sd_cid = br.readLine();
//System.out.println(sd_cid);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return sd_cid;
}
try this: reference original link :Android get id of SD Card programmatically
adb shell cat /sys/class/mmc_host/mmc1/mmc1:*/cid
You can also try
sudo hwinfo --disk
to get information on your disks, including SD Cards.
Also try
sudo hdparm -I /dev/sdb | more
As an FYI to those looking into UUID or volume serial numbers of FAT type volumes under Android: Some Fujifilm cameras, including the X-T30 (Firmware 1.10) do not write a volume serial number into the FAT volume when formatting.
Under Windows, CHKDSK displays no volume serial number at all.
On Android, calling StorageVolume.getUuid() returns "0000-0000".
This is all fine and dandy, until you on Android mount two Fujifilm-formatted cards via a hub. Then there seems to be identity collision, where the Android OS prompts the user to format one of the cards. Separately they are accessible.
I'm guessing there are two combined problems here:
1) Fujifilm is not writing a volume serial number when formatting, and
2) Android uses the volume serial number as part of the mount point path, leading to collision.
Fujifilm and Google might both want to pay attention to this issue.
EDIT: Card formatted in a Nikon D810 also has the same problem, no Volume Serial Number.
Using Android 6.0.1 (API23) on a Nexus 5X,
The different Android versions and how to access an external storage (i.e. USB-Stick in my case) can be very confusing. From what I understood, you need to give permissions in your manifest, as follows:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
After that you should be able to access external storage..
Here is my approach on how to get the external-storage path, using "getExternalFilesDirs()" method. But it turns out, only the internal SD card is recognized (i.e. first array-element returned by the method).
From what I read, there should be more array-elements returned with the "getExternalFilesDirs()"-method if more external memory devices are connected to the phone. But in my case, none of them is available.
Here is my code :
String strInfo = "";
File folders[] = getExternalFilesDirs(null);
// File folders[] = ContextCompat.getExternalFilesDirs(this,null);
strInfo += "\ngetExternalFilesDirs(null):\n";
for(File f : folders){
strInfo += f.getAbsolutePath() + "\n";
}
if (folders.length > 1) {
Toast.makeText(this, "nr of folders = " + folders.length + "/ info = " + strInfo, Toast.LENGTH_SHORT).show();
File myFile = new File(folders[1], "testfile.txt");
} else {
// !!!!!!!!!!!!! Keep ending up in this case, even tough a USB-stick is
// connected to the phone (...also tried with a second SD-card
// connected...but same thing, keeeping up ending here...
// any idea WHY ???????????????
Toast.makeText(this, "No external storage device found / info = " + strInfo, Toast.LENGTH_SHORT).show();
}
Eventually, I would like to create files and folders on an externally connected USB-stick. Can anybody tell me a reliable method on how to create new files and folders on an USB-stick connected to an Android-6.0.1 phone ??
I appreciate it.
To close this off: USB-sticks do not seem to work as SECONDARY STORAGE (= external storage) for Android-mobile phones, unfortunately.
I just found this link, saying:
External storage devices returned here are considered a permanent part
of the device, including both emulated external storage and physical
media slots, such as SD cards in a battery compartment. The returned
paths do not include transient devices, such as USB flash drives.
And similar, the official Android documentation about getExternalFilesDirs() sais:
The returned paths do not include transient devices, such as USB flash drives
connected to handheld devices.
If anybody knows a C-Type USB-stick that does fulfill the requirements of a SECONDARY STORAGE, please let me know ! The same, if anybody knows USB-C-type SDcard-Adapters that work as SECONDARY STORAGE, please let me know...
Too bad, I am not able to write files & folders from my java android app to an external USB-stick :(
Is it possible or is there a way in Android v2.2+ to detect if there is a mass storage, external drive or micro SD inserted in the device?
My objective is, I want to create an app that will be invoked once an external storage like micro SD is inserted in the device?
I want to know if:
1) Is there a standard intent broadcasted once an SD is mounted in the device?
2) Do I have to deal with it in the native driver layer to achieve this?
Any thoughts are highly appreciated.
Thanks
Yes there is a standard intent broadcast, you are probably looking for the ACTION_MEDIA_MOUNTED broadcast.
ACTION_MEDIA_MOUNTED
Since: API Level 1
Broadcast Action: External media is present and
mounted at its mount point. The path to the mount point for the
removed media is contained in the Intent.mData field. The Intent
contains an extra with name "read-only" and Boolean value to indicate
if the media was mounted read only.
Source
In my app, I store the user's app data using MySQLite databases, and I allow the user to backup the app data to the SD card within a folder that I create on the SD card (let's call it MyAppFolder). On Android devices that have only a single SD card slot, everything works fine (e.g. my Droid).
However, on devices such as the Galaxy S that have more than 1 SD card, things don't work. Unfortunately, I don't actually have one of these devices, so I can't debug anything, I can only go by user reports. I also did some searching and found this is a known issue. However, I did not find any solutions that didn't involve just hardcoding the other paths that get used, so I'm looking for some help with that.
In my app, I check and see if MyAppFolder already exists. If not, I create that folder. The folder is always created successfully, although it is created on the "internal" memory slot returned by getExternalStorageDirectory() when there are 2 slots present. However, the files do not get created and copied there. I don't understand why the folder is created, but the files are not created.
Can someone tell me how I can modify this code to work for devices with 2 card slots as well as 1 card slot? I'd prefer not to hard-code locations to check, but if that's the only way, I'll do it just to get things working.
Here is the code I use(slightly modified to make more readable here):
File root = Environment.getExternalStorageDirectory();
String state = Environment.getExternalStorageState();
if( Environment.MEDIA_MOUNTED.equals(state))
{
File dataDirectory = Environment.getDataDirectory();
if (root.canWrite())
{
String savePath = root + "/MyAppFolder/";
File directory = new File(savePath);
if( !directory.exists() )
{
directory.mkdirs(); //folder created successfully
}
String currentDBPath = "\\data\\my_app\\databases\\database.db";
File currentDB = new File(datDirectory, currentDBPath);
File backupDB = new File(savePath, "database.db");
if (currentDB.exists())
{
FileChannel src = new FileInputStream(currentDB[i]).getChannel();
FileChannel dst = new FileOutputStream(backupDB[i]).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
}
Can someone tell me how I can modify this code to work for devices with 2 card slots as well as 1 card slot?
"External storage" does not mean "removable storage". "External storage" means "mountable storage" -- IOW, the user has access to that storage when they plug their device into a host machine via a USB cable.
Android, at present, is designed to allow developers to write things to one external storage point, and it is up to the device manufacturer whether that is fixed flash or something removable. Hence, you should be backing things up to external storage, not thinking that you are backing things up to an SD card.
Can someone tell me how I can modify this code to work for devices with 2 card slots as well as 1 card slot?
Use getDatabasePath() to get a database path, rather than the gyrations you are presently going through. Never use concatenation to create paths, the way you are with root + "/MyAppFolder/" -- use a File constructor, as you are elsewhere. Make sure you hold the WRITE_EXTERNAL_STORAGE permission. Beyond that, this should work fine for any device with sufficient external storage to hold your database, regardless of how many "card slots" the device may have.