FileInputStream error when reading from uri [duplicate] - java

I am getting
open failed: EACCES (Permission denied)
on the line OutputStream myOutput = new FileOutputStream(outFileName);
I checked the root, and I tried android.permission.WRITE_EXTERNAL_STORAGE.
How can I fix this problem?
try {
InputStream myInput;
myInput = getAssets().open("XXX.db");
// Path to the just created empty db
String outFileName = "/data/data/XX/databases/"
+ "XXX.db";
// Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
// Transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
// Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
buffer = null;
outFileName = null;
}
catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

Google has a new feature on Android Q: filtered view for external storage. A quick fix for that is to add this code in the AndroidManifest.xml file:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android Q. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
You can read more about it here: https://developer.android.com/training/data-storage/use-cases
Edit: I am starting to get downvotes because this answer is out of date for Android 11. So whoever sees this answer please go to the link above and read the instructions.

For API 23+ you need to request the read/write permissions even if they are already in your manifest.
// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
/**
* Checks if the app has permission to write to device storage
*
* If the app does not has permission then the user will be prompted to grant permissions
*
* #param activity
*/
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
For official documentation about requesting permissions for API 23+, check https://developer.android.com/training/permissions/requesting.html

I had the same problem... The <uses-permission was in the wrong place. This is right:
<manifest>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
<application>
...
<activity>
...
</activity>
</application>
</manifest>
The uses-permission tag needs to be outside the application tag.

Add android:requestLegacyExternalStorage="true" to the Android Manifest
It's worked with Android 10 (Q) at SDK 29+
or After migrating Android X.
<application
android:name=".MyApplication"
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon=""
android:label=""
android:largeHeap="true"
android:supportsRtl=""
android:theme=""
android:requestLegacyExternalStorage="true">

I have observed this once when running the application inside the emulator. In the emulator settings, you need to specify the size of external storage ("SD Card") properly. By default, the "external storage" field is empty, and that probably means there is no such device and EACCES is thrown even if permissions are granted in the manifest.

In addition to all the answers, make sure you're not using your phone as a USB storage.
I was having the same problem on HTC Sensation on USB storage mode enabled. I can still debug/run the app, but I can't save to external storage.

My issue was with "TargetApi(23)" which is needed if your minSdkVersion is bellow 23.
So, I have request permission with the following snippet
protected boolean shouldAskPermissions() {
return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
}
#TargetApi(23)
protected void askPermissions() {
String[] permissions = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"
};
int requestCode = 200;
requestPermissions(permissions, requestCode);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
if (shouldAskPermissions()) {
askPermissions();
}
}

Be aware that the solution:
<application ...
android:requestLegacyExternalStorage="true" ... >
Is temporary, sooner or later your app should be migrated to use Scoped Storage.
In Android 10, you can use the suggested solution to bypass the system restrictions, but in Android 11 (R) it is mandatory to use scoped storage, and your app might break if you kept using the old logic!
This video might be a good help.

Android 10 (API 29) introduces Scoped Storage. Changing your manifest to request legacy storage is not a long-term solution.
I fixed the issue when I replaced my previous instances of Environment.getExternalStorageDirectory() (which is deprecated with API 29) with context.getExternalFilesDir(null).
Note that context.getExternalFilesDir(type) can return null if the storage location isn't available, so be sure to check that whenever you're checking if you have external permissions.
Read more here.

I'm experiencing the same. What I found is that if you go to Settings -> Application Manager -> Your App -> Permissions -> Enable Storage, it solves the issue.

It turned out, it was a stupid mistake since I had my phone still connected to the desktop PC and didn't realize this.
So I had to turn off the USB connection and everything worked fine.

I had the same problem on Samsung Galaxy Note 3, running CM 12.1. The issue for me was that i had
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
and had to use it to take and store user photos. When I tried to load those same photos in ImageLoader i got the (Permission denied) error. The solution was to explicitly add
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
since the above permission only limits the write permission up to API version 18, and with it the read permission.

In addition to all answers, if the clients are using Android 6.0, Android added new permission model for (Marshmallow).
Trick: If you are targeting version 22 or below, your application will request all permissions at install time just as it would on any device running an OS below Marshmallow. If you are trying on the emulator then from android 6.0 onwards you need to explicitly go the settings->apps-> YOURAPP -> permissions and change the permission if you have given any.

Strangely after putting a slash "/" before my newFile my problem was solved. I changed this:
File myFile= new File(Environment.getExternalStorageDirectory() + "newFile");
to this:
File myFile= new File(Environment.getExternalStorageDirectory() + "/newFile");
UPDATE:
as mentioned in the comments, the right way to do this is:
File myFile= new File(Environment.getExternalStorageDirectory(), "newFile");

I had the same problem and none of suggestions helped. But I found an interesting reason for that, on a physical device, Galaxy Tab.
When USB storage is on, external storage read and write permissions don't have any effect.
Just turn off USB storage, and with the correct permissions, you'll have the problem solved.

I would expect everything below /data to belong to "internal storage". You should, however, be able to write to /sdcard.

Change a permission property in your /system/etc/permission/platform.xml
and group need to mentioned as like below.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
<group android:gid="sdcard_rw" />
<group android:gid="media_rw" />
</uses-permission>

I had the same error when was trying to write an image in DCIM/camera folder on Galaxy S5 (android 6.0.1) and I figured out that only this folder is restricted. I simply could write into DCIM/any folder but not in camera.
This should be brand based restriction/customization.

Maybe the answer is this:
on the API >= 23 devices, if you install app (the app is not system app), you should check the storage permission in "Setting - applications", there is permission list for every app, you should check it on! try

To store a file in a directory which is foreign to the app's directory is restricted above API 29+. So to generate a new file or to create a new file use your application directory like this :-
So the correct approach is :-
val file = File(appContext.applicationInfo.dataDir + File.separator + "anyRandomFileName/")
You can write any data into this generated file !
The above file is accessible and would not throw any exception because it resides in your own developed app's directory.
The other option is android:requestLegacyExternalStorage="true" in manifest application tag as suggested by Uriel but its not a permanent solution !

When your application belongs to the system application, it can't access the SD card.

keep in mind that even if you set all the correct permissions in the manifest:
The only place 3rd party apps are allowed to write on your external card are "their own directories"
(i.e. /sdcard/Android/data/)
trying to write to anywhere else: you will get exception:
EACCES (Permission denied)

Environment.getExternalStoragePublicDirectory();
When using this deprecated method from Android 29 onwards you will receive the same error:
java.io.FileNotFoundException: open failed: EACCES (Permission denied)
Resolution here:
getExternalStoragePublicDirectory deprecated in Android Q

In my case I was using a file picker library which returned the path to external storage but it started from /root/. And even with the WRITE_EXTERNAL_STORAGE permission granted at runtime I still got error EACCES (Permission denied).
So use Environment.getExternalStorageDirectory() to get the correct path to external storage.
Example:
Cannot write: /root/storage/emulated/0/newfile.txt
Can write: /storage/emulated/0/newfile.txt
boolean externalStorageWritable = isExternalStorageWritable();
File file = new File(filePath);
boolean canWrite = file.canWrite();
boolean isFile = file.isFile();
long usableSpace = file.getUsableSpace();
Log.d(TAG, "externalStorageWritable: " + externalStorageWritable);
Log.d(TAG, "filePath: " + filePath);
Log.d(TAG, "canWrite: " + canWrite);
Log.d(TAG, "isFile: " + isFile);
Log.d(TAG, "usableSpace: " + usableSpace);
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
Output 1:
externalStorageWritable: true
filePath: /root/storage/emulated/0/newfile.txt
isFile: false
usableSpace: 0
Output 2:
externalStorageWritable: true
filePath: /storage/emulated/0/newfile.txt
isFile: true
usableSpace: 1331007488

I am creating a folder under /data/ in my init.rc (mucking around with the aosp on Nexus 7) and had exactly this problem.
It turned out that giving the folder rw (666) permission was not sufficient and it had to be rwx (777) then it all worked!

The post 6.0 enforcement of storage permissions can be bypassed if you have a rooted device via these adb commands:
root#msm8996:/ # getenforce
getenforce
Enforcing
root#msm8996:/ # setenforce 0
setenforce 0
root#msm8996:/ # getenforce
getenforce
Permissive

i faced the same error on xiaomi devices (android 10 ). The following code fixed my problem.
Libraries: Dexter(https://github.com/Karumi/Dexter) and Image picker(https://github.com/Dhaval2404/ImagePicker)
Add manifest ( android:requestLegacyExternalStorage="true")
public void showPickImageSheet(AddImageModel model) {
BottomSheetHelper.showPickImageSheet(this, new BottomSheetHelper.PickImageDialogListener() {
#Override
public void onChooseFromGalleryClicked(Dialog dialog) {
selectedImagePickerPosition = model.getPosition();
Dexter.withContext(OrderReviewActivity.this) .withPermissions(Manifest.permission.READ_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
#Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
ImagePicker.with(OrderReviewActivity.this)
.galleryOnly()
.compress(512)
.maxResultSize(852,480)
.start();
}
}
#Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> list, PermissionToken permissionToken) {
permissionToken.continuePermissionRequest();
}
}).check();
dialog.dismiss();
}
#Override
public void onTakePhotoClicked(Dialog dialog) {
selectedImagePickerPosition = model.getPosition();
ImagePicker.with(OrderReviewActivity.this)
.cameraOnly()
.compress(512)
.maxResultSize(852,480)
.start();
dialog.dismiss();
}
#Override
public void onCancelButtonClicked(Dialog dialog) {
dialog.dismiss();
}
});
}

In my case the error was appearing on the line
target.createNewFile();
since I could not create a new file on the sd card,so I had to use the DocumentFile approach.
documentFile.createFile(mime, target.getName());
For the above question the problem may be solved with this approach,
fos=context.getContentResolver().openOutputStream(documentFile.getUri());
See this thread too,
How to use the new SD card access API presented for Android 5.0 (Lollipop)?

I Use the below process to handle the case with android 11 and targetapi30
As pre-created file dir as per scoped storage in my case in root dir files//<Image/Video... as per requirement>
Copy picked file and copy the file in cache directory at the time of picking from my external storage
Then at a time to upload ( on my send/upload button click) copy the file from cache dir to my scoped storage dir and then do my upload process
use this solution due to at time upload app in play store it generates warning for MANAGE_EXTERNAL_STORAGE permission and sometimes rejected from play store in my case.
Also as we used target API 30 so we can't share or forward file from our internal storage to app

2022 Kotlin way to ask permission:
private val writeStoragePermissionResult =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->}
private fun askForStoragePermission(): Boolean =
if (hasPermissions(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
) {
true
} else {
writeStoragePermissionResult.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
)
false
}
fun hasPermissions(context: Context, vararg permissions: String): Boolean = permissions.all {
ActivityCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}

Related

How does the Google camera app save photos without asking for a storage permission?

The official Google camera app (https://github.com/android/camera-samples/tree/main/CameraXBasic) does not ask for any storage permissions. So, how does it save the photos to the device? just look at the AndroidManifest.xml file: https://github.com/android/camera-samples/blob/main/CameraXBasic/app/src/main/AndroidManifest.xml As you can see, it does not ask for that storage permission.
If you look at their getOutputDirectory method:
fun getOutputDirectory(context: Context): File {
val appContext = context.applicationContext
val mediaDir = context.externalMediaDirs.firstOrNull()?.let {
File(it, appContext.resources.getString(R.string.app_name)).apply { mkdirs() } }
return if (mediaDir != null && mediaDir.exists())
mediaDir else appContext.filesDir
}
They are using getExternalMediaDirs() and, if none are available (which isn't ever the case in reality), getFilesDir() and both of those methods specifically mention in their documentation:
No additional permissions are required for the calling app to read or write files under the returned path.

Android Q - Delete Media (Audio) File

I've been trying to get my app to be able to delete an audio file. However, after trying many possible solutions, I couldn't really find one that works.
Here is my solution so far:
public static void deleteFiles(List<Track> tracks, Context context,
final MutableLiveData<IntentSender> deletionIntentSenderLD){
final Uri AUDIO_URI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
for(Track t : tracks){
try {
context.getContentResolver().delete(ContentUris
.withAppendedId(AUDIO_URI, t.getUriId()), null, null);
}catch (SecurityException securityException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (securityException instanceof RecoverableSecurityException) {
deletionIntentSenderLD
.postValue(((RecoverableSecurityException) securityException)
.getUserAction().getActionIntent().getIntentSender());
} else
throw securityException;
} else
throw securityException;
}
}
}
When the try block fails a SecurityException is catch then the IntentSender is passed to the live data that is observed in a fragment:
audioViewModel.getDeletionIntentSenderLD().observe(getViewLifecycleOwner(),
intentSender -> {
try {
startIntentSenderForResult(intentSender, DELETE_PERMISSION_REQUEST,
null, 0 ,0, 0,
null);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
});
I've tried implementing the onRequestPermissionResult() method but that doesn't do anything. I've also tried deleting the files using File file = new File(), however, due to the changes made to Android 10, I didn't expect it to work.
So after many Google searches, I've come to the conclusion that the best approach (to my knowledge) is to simply turn off scoped storage for Android Q (10).
Here, I'll provide two solutions. The first is the one where I turn it off and the second is the one where scope storage is still enable. However, a thing you should note is that the second solution is a little buggy, at times it actually does delete both the actual media file and updates the Media Store, but most times it simply deletes from the Media Store only. Obviously, this isn't a very good solution as on reboot your application would then load those files back in because the Media Store would scan for them.
Solution 1 - Turn off Scoped Storage
For this solution you can still target Android 11. All you have to do is go to the build.gradle file at the Module Level and set the compileSdkVersion and targetSdkVersion to 30.
After that, you go into the AndroidManifest.xml and have the uses-permission and application tag set up like this:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:ignore="ScopedStorage"/>
<application
android:requestLegacyExternalStorage="true"
...
After having done that, you could use the Content Resolver to delete the media file (and update the Media Store) and you do not have to worry about catching a security exception like its said in the Android docs. Your implementation for Android 11s delete operation should not be affected.
Solution-ish 2 - Turn on Scoped Storage
Firstly, in your manifest ensure that the WRITE_EXTERNAL_STORAGE permissions maxSdkVersion is set to 28. Also ensure that requestLegacyExternalStorage is set to false (don't think this is required). Then simply copy the code in my original post. You do not require a Live Data if you are doing the delete operation from your activity/fragment. But you should note that startIntentSenderForResult() requires an activity.
But as I mentioned before, I did experience some bugs with this. The most frustrating thing about this solution though is that it does not delete the actual file but instead deletes the entry from the Media Store. Maybe this has something to do with the fact that #blackapps mentioned, which is that you cannot bulk delete and I might have implemented it slightly wrong. Nevertheless, this is horrible for user experience if bulk deletion is impossible in Android 10.
The tutorials I followed for this are:
https://developer.android.com/training/data-storage/shared/media#remove-item
https://www.raywenderlich.com/9577211-scoped-storage-in-android-10-getting-started#toc-anchor-007
https://www.solutionanalysts.com/blog/scoped-storage-in-android-10/
Side Note - Delete on Android 11
To delete on Android 11 you just need to call createDeleteRequest() which should return a PendingIntent. From this PendingIntent you could get the IntentSender by using getIntentSender. Pass this intent sender to the activity/fragment then call startIntentSenderForResult() in your activity/fragment. This pops up a dialog to the user asking them if the application can delete a file. If the user gives permission the system goes ahead and deletes the file and updates the Media Store.
Side Side Note - Scoped Storage, Android 10 and Future
From everything I've seen, it seems to suggest that scoped storage is only enforced in Android 11 but I'm not entirely sure if the legacy option would still be available in Android 10 indefinitely. But I would have to do more research on this...

Universal way to write to external SD card on Android

In my application, I need to store lots of images in the device storage. Such files tend to fulfill the device storage, and I want to allow users to be able to choose external SD card as the destination folder.
I read everywhere that Android doesn't allow users to write to external SD card, by SD card I mean the external and mountable SD card and not the external storage, but file manager applications manage to write to External SD on all Android versions.
What is the better way to grant read/write access to external SD card on different API levels (Pre-KitKat, KitKat, Lollipop+)?
Update 1
I tried Method 1 from Doomknight's answer, with no avail:
As you can see I'm checking for permissions at runtime before attempting to write on SD:
HashSet<String> extDirs = getStorageDirectories();
for(String dir: extDirs) {
Log.e("SD",dir);
File f = new File(new File(dir),"TEST.TXT");
try {
if(ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED) {
f.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
}
But I get an access error, tried on two different devices: HTC10 and Shield K1.
10-22 14:52:57.329 30280-30280/? E/SD: /mnt/media_rw/F38E-14F8
10-22 14:52:57.329 30280-30280/? W/System.err: java.io.IOException: open failed: EACCES (Permission denied)
10-22 14:52:57.329 30280-30280/? W/System.err: at java.io.File.createNewFile(File.java:939)
10-22 14:52:57.329 30280-30280/? W/System.err: at com.myapp.activities.TestActivity.onResume(TestActivity.java:167)
10-22 14:52:57.329 30280-30280/? W/System.err: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1326)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.Activity.performResume(Activity.java:6338)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3336)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2574)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.access$900(ActivityThread.java:150)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1399)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.os.Looper.loop(Looper.java:168)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5885)
10-22 14:52:57.330 30280-30280/? W/System.err: at java.lang.reflect.Method.invoke(Native Method)
10-22 14:52:57.330 30280-30280/? W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:819)
10-22 14:52:57.330 30280-30280/? W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:709)
10-22 14:52:57.330 30280-30280/? W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
10-22 14:52:57.330 30280-30280/? W/System.err: at libcore.io.Posix.open(Native Method)
10-22 14:52:57.330 30280-30280/? W/System.err: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
10-22 14:52:57.330 30280-30280/? W/System.err: at java.io.File.createNewFile(File.java:932)
10-22 14:52:57.330 30280-30280/? W/System.err: ... 14 more
Summary
You can grant read/write access to external SD card on the different api levels (API23+ at run time).
Since KitKat, permissions are not necessary if you use app-specific directories, required otherwise.
Universal way:
The history says that there is no universal way to write to external SD card but continues...
This fact is demonstrated by these examples of external storage configurations for devices.
API-based way:
Prior to KitKat try to use Doomsknight method 1, method 2 otherwise.
Request permissions in manifest (Api < 23) and at run time (Api >= 23).
Recommended way:
ContextCompat.getExternalFilesDirs solves the access error when you don't need to share files.
The secure way of sharing it is to use a content provider or the new Storage Access Framework.
Privacy-aware way:
As of Android Q Beta 4, apps that target Android 9 (API level 28) or lower see no change, by default.
Apps targeting Android Q by default (or opting into it) are given a filtered view into external storage.
Initial answer.
Universal way to write to external SD card on Android
There is no universal way to write to external SD card on Android due to continuous changes:
Pre-KitKat: official Android platform has not supported SD cards at all except for exceptions.
KitKat: introduced APIs that let apps access files in app-specific directories on SD cards.
Lollipop: added APIs to allow apps to request access to folders owned by other providers.
Nougat: provided a simplified API to access common external storage directories.
... Android Q privacy change: App-scoped and media-scoped storage
What is the better way to grant read/write access to external SD card
on different API levels
Based on Doomsknight's answer and mine, and Dave Smith and Mark Murphy blog posts: 1, 2, 3:
Ideally, use the Storage Access
Framework
and
DocumentFile
as Jared Rummler pointed. Or:
Use your app specific path/storage/extSdCard/Android/data/com.myapp.example/files.
Add read/write permission to manifest for pre-KitKat, no permission required later for this path.
Try to use your App path and Doomsknight's methods considering KitKat and Samsung case.
Filter and use getStorageDirectories, your App path and read/write permissions prior to KitKat.
ContextCompat.getExternalFilesDirs since KitKat. Considering devices that return internal first.
Updated answer.
Update 1. I tried Method 1 from Doomknight's answer, with no avail:
As you can see I'm checking for permissions at runtime before
attempting to write on SD...
I would use application-specific directories to avoid the issue of your updated question and ContextCompat.getExternalFilesDirs() using getExternalFilesDir documentation as reference.
Improve the heuristics to determine what represents removable media based on the different api levels like android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT
... But I get an access error, tried on two different devices: HTC10
and Shield K1.
Remember that Android 6.0 supports portable storage devices and third-party apps must go through the Storage Access Framework. Your devices HTC10 and Shield K1 are probably API 23.
Your log shows a permission denied exception accessing /mnt/media_rw, like this fix for API 19+:
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_r" />
<group gid="sdcard_rw" />
<group gid="media_rw" /> // this line is added via root in the link to fix it.
</permission>
I never tried it so I can not share code but I would avoid the for trying to write on all the returned directories and look for the best available storage directory to write into based on remaining space.
Perhaps Gizm0's alternative to your getStorageDirectories() method it's a good starting point.
ContextCompat.getExternalFilesDirs solves the issue if you don't need access to other folders.
Android 1.0 .. Pre-KitKat.
Prior to KitKat try to use Doomsknight method 1 or read this response by Gnathonic.
public static HashSet<String> getExternalMounts() {
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try {
final Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
s = s + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
// parse output
final String[] lines = s.split("\n");
for (String line : lines) {
if (!line.toLowerCase(Locale.US).contains("asec")) {
if (line.matches(reg)) {
String[] parts = line.split(" ");
for (String part : parts) {
if (part.startsWith("/"))
if (!part.toLowerCase(Locale.US).contains("vold"))
out.add(part);
}
}
}
}
return out;
}
Add the next code to your AndroidManifest.xml and read Getting access to external storage
Access to external storage is protected by various Android
permissions.
Starting in Android 1.0, write access is protected with
the WRITE_EXTERNAL_STORAGE permission.
Starting in Android 4.1, read
access is protected with the READ_EXTERNAL_STORAGE permission.
In order to ... write files on the external storage, your app must
acquire ... system
permissions:
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
If you need to both..., you need to request
only the WRITE_EXTERNAL_STORAGE permission.
Read Mark Murphy's explanation and recommended Dianne Hackborn and Dave Smith posts
Until Android 4.4, there was no official support for removable media in Android, Starting in KitKat, the concept of “primary” and “secondary” external storage emerges in the FMW API.
Prior apps are just relying on MediaStore indexing, ship with the hardware or examine mount points and apply some heuristics to determine what represents removable media.
Android 4.4 KitKat introduces the Storage Access Framework (SAF).
Ignore the next note due to bugs, but try to use ContextCompat.getExternalFilesDirs():
Since Android 4.2, there has been a request from Google for device manufacturers to lock down removable media for security (multi-user support) and new tests were added in 4.4.
Since KitKat getExternalFilesDirs() and other methods were added to return a usable path on all available storage volumes (The first
item returned is the primary volume).
The table below indicates what a developer might try to do and how KitKat will respond:
Note: Beginning with Android 4.4, these permissions are not required
if you're reading or writing only files that are private to your app.
For more info..., see saving files that
are app-private.
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
</manifest>
Also read Paolo Rovelli's explanation and try to use Jeff Sharkey's solution since KitKat:
In KitKat there's now a public API for interacting with
these secondary shared storage devices.
The new Context.getExternalFilesDirs() and
Context.getExternalCacheDirs() methods can return multiple paths,
including both primary and secondary devices.
You can then iterate
over them and check Environment.getStorageState() and
File.getFreeSpace() to determine the best place to store your files.
These methods are also available on ContextCompat in the support-v4 library.
Starting in Android 4.4, the owner, group and modes of files on
external storage devices are now synthesized based on directory
structure. This enables apps to manage their package-specific
directories on external storage without requiring they hold the broad
WRITE_EXTERNAL_STORAGE permission. For example, the app with package
name com.example.foo can now freely access
Android/data/com.example.foo/ on external storage devices with no
permissions. These synthesized permissions are accomplished by
wrapping raw storage devices in a FUSE daemon.
With KitKat your chances for a "complete solution" without rooting are
pretty much zero:
The Android project has definitely screwed up here.
No apps get full access to external SD cards:
file managers: you cannot use them to manage your external SD card. In
most areas, they can only read but not write.
media apps: you cannot
retag/re-organize your media collection any longer, as those apps
cannot write to it.
office apps: pretty much the same
The only place 3rd party apps are allowed to write on your
external card are "their own directories" (i.e.
/sdcard/Android/data/<package_name_of_the_app>).
The only ways to
really fix that require either the manufacturer (some of them fixed
it, e.g. Huawei with their Kitkat update for the P6) – or root... (Izzy's explanation continues here)
Android 5.0 introduced changes and the DocumentFile helper class.
getStorageState Added in API 19, deprecated in API 21,
use getExternalStorageState(File)
Here's a great tutorial for interacting with the Storage Access
Framework in KitKat.
Interacting with the new APIs in Lollipop is very similar (Jeff Sharkey's explanation).
Android 6.0 Marshmallow introduces a new runtime permissions model.
Request permissions at runtime if API level 23+ and read Requesting Permissions at Run Time
Beginning in Android 6.0 (API level 23), users grant permissions to
apps while the app is running, not when they install the app ... or update the app ... user can revoke the permissions.
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
Android 6.0 introduces a new runtime permissions model where apps
request capabilities when needed at runtime. Because the new model
includes the READ/WRITE_EXTERNAL_STORAGE permissions, the platform
needs to dynamically grant storage access without killing or
restarting already-running apps. It does this by maintaining three
distinct views of all mounted storage devices:
/mnt/runtime/default is shown to apps with no special storage
permissions...
/mnt/runtime/read is shown to apps with
READ_EXTERNAL_STORAGE
/mnt/runtime/write is shown to apps with
WRITE_EXTERNAL_STORAGE
Android 7.0 provides a simplified API to access external storage dirs.
Scoped Directory Access
In Android 7.0, apps can use new APIs to request access to specific
external storage directories, including directories on removable media
such as SD cards...
For more information, see the Scoped Directory Access training.
Read Mark Murphy posts: Be Careful with Scoped Directory Access. It was deprecated in Android Q:
Note that the scoped directory access added in 7.0 is deprecated in
Android Q.
Specifically, the createAccessIntent() method on StorageVolume is
deprecated.
They added a createOpenDocumentTreeIntent() that can be used as an
alternative.
Android 8.0 Oreo .. Android Q Beta changes.
Starting in Android
O, the
Storage Access Framework allows custom documents
providers
to create seekable file descriptors for files residing in a remote
data source...
Permissions,
prior to Android O, if an app requested a permission at runtime and the permission was granted, the system also incorrectly granted
the app the rest of the permissions that belonged to the same
permission group, and that were registered in the manifest.
For apps targeting Android O, this behavior has been corrected. The app is granted only the permissions it has explicitly requested.
However, once the user grants a permission to the app, all subsequent
requests for permissions in that permission group are automatically
granted.
For example, READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE...
Update: An Android Q earlier beta release temporarily replaced the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions with more fine-grained, media-specific permissions.
Note: Google introduced roles on Beta 1 and removed them from the documentation before Beta 2...
Note: The permissions specific to media collections that were introduced in earlier beta releases—READ_MEDIA_IMAGES, READ_MEDIA_AUDIO, and READ_MEDIA_VIDEO—are now obsolete. More info:
Q Beta 4 (final APIs) review by Mark Murphy: The Death of External Storage: The End of the Saga(?)
"Death is more universal than life. Everyone dies, but not everyone
lives." ― Andrew Sachs
Related questions and recommended answers.
How can I get external SD card path for Android 4.0+?
mkdir() works while inside internal flash storage, but not SD card?
Diff between getExternalFilesDir and getExternalStorageDirectory()
Why getExternalFilesDirs() doesn't work on some devices?
How to use the new SD card access API presented for Android 5.0 (Lollipop)
Writing to external SD card in Android 5.0 and above
Android SD Card Write Permission using SAF (Storage Access Framework)
SAFFAQ: The Storage Access Framework FAQ
Related bugs and issues.
Bug: On Android 6, when using getExternalFilesDirs, it won't let you create new files in its results
Writing to directory returned by getExternalCacheDir() on Lollipop fails without write permission
I believe there are two methods to achieve this:
METHOD 1: (does NOT work on 6.0 and above, due to permission changes)
I have been using this method for years on many device version with no issue. Credit is due to the original source, as it was not me who wrote it.
It will return all mounted media (including Real SD Cards) in a list of strings directory locations. With the list you can then ask the user where to save, etc.
You can call it with the following:
HashSet<String> extDirs = getStorageDirectories();
Method:
/**
* Returns all the possible SDCard directories
*/
public static HashSet<String> getStorageDirectories() {
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try {
final Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
s = s + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
// parse output
final String[] lines = s.split("\n");
for (String line : lines) {
if (!line.toLowerCase().contains("asec")) {
if (line.matches(reg)) {
String[] parts = line.split(" ");
for (String part : parts) {
if (part.startsWith("/"))
if (!part.toLowerCase().contains("vold"))
out.add(part);
}
}
}
}
return out;
}
METHOD 2:
Use the v4 support library
import android.support.v4.content.ContextCompat;
Just call the following to get a list of File locations of storage.
File[] list = ContextCompat.getExternalFilesDirs(myContext, null);
The locations differ in usage however though.
Returns absolute paths to application-specific directories on all
external storage devices where the application can place persistent files
it owns. These files are internal to the application, and not typically
visible to the user as media.
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.
An application may store data on any or all of the returned devices. For
example, an app may choose to store large files on the device with the
most available space
More Info on ContextCompat
They are like app specific files. Hidden from other apps.
Just another answer. This answer only shows 5.0+ because I believe Doomknight's answer posted here is the best way to do for Android 4.4 and below.
This is originally posted here (Is there a way to get SD Card size in Android?) by me to get the external SD Card's size on Android 5.0+
To get the External SD card as a File:
public File getExternalSdCard() {
File externalStorage = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
File storage = new File("/storage");
if(storage.exists()) {
File[] files = storage.listFiles();
for (File file : files) {
if (file.exists()) {
try {
if (Environment.isExternalStorageRemovable(file)) {
externalStorage = file;
break;
}
} catch (Exception e) {
Log.e("TAG", e.toString());
}
}
}
}
} else {
// do one of many old methods
// I believe Doomsknight's method is the best option here
}
return externalStorage;
}
Note: I only get the "first" external sd card however you can modify it and return ArrayList<File> instead of File and let the loop continue instead of calling break after the first one is found.
In addition to all other nice answers, I could add a bit more to this question so it can give wider coverage for readers. In my answer here, I would use 2 countable resources to present External Storage.
The first resource is from Android Programming, The Big Nerd Ranch Guide 2nd edition, chapter 16, page 294.
The book describes the basic and external file and directory methods. I will try to make a resume of what could be relevant to your question.
The following part from the book:
External Storage
Your photo needs more than a place on the screen. Full-size pictures are too large to stick inside a
SQLite database, much less an Intent. They will need a place to live on your device’s filesystem.
Normally, you would put them in your private storage. Recall that you used your private storage
to save your SQLite database. With methods like Context.getFileStreamPath(String) and
Context.getFilesDir(), you can do the same thing with regular files, too (which will live in a
subfolder adjacent to the databases subfolder your SQLite database lives in)
Basic file and directory methods in Context
| Method |
|---------------------------------------------------------------------------------------|
|File getFilesDir() |
| - Returns a handle to the directory for private application files. |
| |
|FileInputStream openFileInput(String name) |
| - Opens an existing file for input (relative to the files directory). |
| |
|FileOutputStream openFileOutput(String name, int mode) |
| - Opens a file for output, possibly creating it (relative to the files directory). |
| |
|File getDir(String name, int mode) |
| - Gets (and possibly creates) a subdirectory within the files directory. |
| |
|String[] fileList() |
| - Gets a list of file names in the main files directory, such as for use with |
| openFileInput(String). |
| |
|File getCacheDir() |
| - Returns a handle to a directory you can use specifically for storing cache files. |
| You should take care to keep this directory tidy and use as little space as possible|
If you are storing files that only your current application needs to use, these methods are exactly what
you need.
On the other hand, if you need another application to write to those files, you are out of luck: while
there is a Context.MODE_WORLD_READABLE flag you can pass in to openFileOutput(String, int), it is
deprecated, and not completely reliable in its effects on newer devices. If you are storing files to share
with other apps or receiving files from other apps (files like stored pictures), you need to store them on
external storage instead.
There are two kinds of external storage: primary, and everything else. All Android devices have at
least one location for external storage: the primary location, which is located in the folder returned by
Environment.getExternalStorageDirectory(). This may be an SD card, but nowadays it is more
commonly integrated into the device itself. Some devices may have additional external storage. That
would fall under “everything else.”
Context provides quite a few methods for getting at external storage, too. These methods provide easy
ways to get at your primary external storage, and kinda-sorta-easy ways to get at everything else. All of
these methods store files in publicly available places, too, so be careful with them.
External file and directory methods in Context
| Method |
| --------------------------------------------------------------------------------------|
|File getExternalCacheDir() |
| - Returns a handle to a cache folder in primary external storage. Treat it like you do|
| getCacheDir(), except a little more carefully. Android is even less likely to clean |
| up this folder than the private storage one. |
| |
|File[] getExternalCacheDirs() |
| - Returns cache folders for multiple external storage locations. |
| |
|File getExternalFilesDir(String) |
| - Returns a handle to a folder on primary external storage in which to store regular |
| files. If you pass in a type String, you can access a specific subfolder dedicated |
| to a particular type of content. Type constants are defined in Environment, where |
| they are prefixed with DIRECTORY_. |
| For example, pictures go in Environment.DIRECTORY_PICTURES. |
| |
|File[] getExternalFilesDirs(String) |
| - Same as getExternalFilesDir(String), but returns all possible file folders for the |
| given type. |
| |
|File[] getExternalMediaDirs() |
| - Returns handles to all the external folders Android makes available for storing |
| media – pictures, movies, and music. What makes this different from calling |
| getExternalFilesDir(Environment.DIRECTORY_PICTURES) is that the media scanner |
| automatically scans this folder. The media scanner makes files available to |
| applications that play music, or browse movies and photos, so anything that you |
| put in a folder returned by getExternalMediaDirs() will automatically appear in |
| those apps. |
Technically, the external folders provided above may not be available, since some devices use a removable SD card for external storage. In practice this is rarely an issue, because almost all modern devices have nonremovable internal storage for their “external” storage. So it is not worth going to extreme lengths to account for it. But we do recommended including simple code to guard against the possibility, which you will do in a moment.
External storage permission
In general, you need a permission to write or read from external storage. Permissions are well-known string values you put in your manifest using the <uses-permission> tag. They tell Android that you want to do something that Android wants you to ask permission for.
Here, Android expects you to ask permission because it wants to enforce some accountability. You tell Android that you need to access external storage, and Android will then tell the user that this is one of the things your application does when they try to install it. That way, nobody is surprised when you start saving things to their SD card.
In Android 4.4, KitKat, they loosened this restriction. Since Context.getExternalFilesDir(String) returns a folder that is specific to your app, it makes sense that you would want to be able to read and write files that live there. So on Android 4.4 (API 19) and up, you do not need this permission for this folder. (But you still need it for other kinds of external storage.)
Add a line to your manifest that requests the permission to read external storage, but only up to API Listing 16.5 Requesting external storage permission (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.criminalintent" >
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
The maxSdkVersion attribute makes it so that your app only asks for this permission on versions of Android that are older than API 19, Android KitKat.
Note that you are only asking to read external storage. There is also a WRITE_EXTERNAL_STORAGE permission, but you do not need it. You will not be writing anything to external storage: The camera app will do that for you
The second resource is this link read all of it, but you can also jump to Using the External Storage section.
Reference:
Android Storage Options: https://developer.android.com/guide/topics/data/data-storage.html
Book site: https://www.bignerdranch.com/we-write/android-programming
More reading stuff:
https://developer.android.com/reference/android/os/Environment.html#getExternalStoragePublicDirectory%28java.lang.String%29
https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_PICTURES
https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_MOVIES
How can I get external SD card path for Android 4.0+?
Disclaimer: This information was taken from Android Programming: The Big Nerd Ranch Guide with permission from the authors. For more information on this book or to purchase a copy, please visit bignerdranch.com.
This topic is a bit old but I was looking for a solution and after some research I came with the code below to retrieve a list of available "external" mount points that, according to my knowledge, works on many different devices.
Basically, it reads available mount points, filters out invalid ones, tests the rest if they are accessible and adds them if all the conditions are satisfied.
Of course, required permissions must be granted before the code is invoked.
// Notice: FileSystemDevice is just my own wrapper class. Feel free to replace it with your own.
private List<FileSystemDevice> getDevices() {
List<FileSystemDevice> devices = new ArrayList<>();
// Add default external storage if available.
File sdCardFromSystem = null;
switch(Environment.getExternalStorageState()) {
case Environment.MEDIA_MOUNTED:
case Environment.MEDIA_MOUNTED_READ_ONLY:
case Environment.MEDIA_SHARED:
sdCardFromSystem = Environment.getExternalStorageDirectory();
break;
}
if (sdCardFromSystem != null) {
devices.add(new FileSystemDevice(sdCardFromSystem));
}
// Read /proc/mounts and add all mount points that are available
// and are not "special". Also, check if the default external storage
// is not contained inside the mount point.
try {
FileInputStream fs = new FileInputStream("/proc/mounts");
String mounts = IOUtils.toString(fs, "UTF-8");
for(String line : mounts.split("\n")) {
String[] parts = line.split(" ");
// parts[0] - mount type
// parts[1] - mount point
if (parts.length > 1) {
try {
// Skip "special" mount points and mount points that can be accessed
// directly by Android's functions.
if (parts[0].equals("proc")) { continue; }
if (parts[0].equals("rootfs")) { continue; }
if (parts[0].equals("devpts")) { continue; }
if (parts[0].equals("none")) { continue; }
if (parts[0].equals("sysfs")) { continue; }
if (parts[0].equals("selinuxfs")) { continue; }
if (parts[0].equals("debugfs")) { continue; }
if (parts[0].equals("tmpfs")) { continue; }
if (parts[1].equals(Environment.getRootDirectory().getAbsolutePath())) { continue; }
if (parts[1].equals(Environment.getDataDirectory().getAbsolutePath())) { continue; }
if (parts[1].equals(Environment.getExternalStorageDirectory().getAbsolutePath())) { continue; }
// Verify that the mount point is accessible by listing its content.
File file = new File(parts[1]);
if (file.listFiles() != null) {
try {
// Get canonical path for case it's just symlink to another mount point.
String devPath = file.getCanonicalPath();
for(FileSystemDevice device : devices) {
if (!devices.contains(devPath)) {
devices.add(new FileSystemDevice(new File(devPath)));
}
}
} catch (Exception e) {
// Silently skip the exception as it can only occur if the mount point is not valid.
e.printStackTrace();
}
}
} catch (Exception e) {
// Silently skip the exception as it can only occur if the mount point is not valid.
e.printStackTrace();
}
}
}
fs.close();
} catch (FileNotFoundException e) {
// Silently skip the exception as it can only occur if the /proc/mounts file is unavailable.
// Possibly, another detection method can be called here.
e.printStackTrace();
} catch (IOException e) {
// Silently skip the exception as it can only occur if the /proc/mounts file is unavailable.
// Possibly, another detection method can be called here.
e.printStackTrace();
}
return devices;
}
Here is a way of creating a new file in the External Storage (SDCard if present in the device or device External Storage if not). Just replace "foldername" with the name of your desired destination folder and "filename" with the name of the file you are saving. Of course here you can see how to save a generic File, now you can search for how to save images maybe here or whatever in a file.
try {
File dir = new File(Environment.getExternalStorageDirectory() + "/foldername/");
if (!dir.exists()){
dir.mkdirs();
}
File sdCardFile = new File(Environment.getExternalStorageDirectory() + "/foldername/" + fileName );
int num = 1;
String fileNameAux = fileName;
while (sdCardFile.exists()){
fileNameAux = fileName+"_"+num;
sdCardFile = new File(Environment.getExternalStorageDirectory() + "/foldername/" + fileNameAux);
num++;
}
This also controls that file exists and adds a number in the end of the name of the new file to save it.
Hope it helps!
EDIT: Sorry, i forgot you have to ask for <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> in your manifest
(or programatically if you prefer from Marshmallow)
For versions below Marshmallow you can directly give the permissions in the manifest.
But for devices with Marshmallow and above you need to grant the permissions on run time.
By using
Environment.getExternalStorageDirectory();
you can directly access the External SD card (Mounted one)
Hope this helps.

Reliable way of detecting whether an Android app is running in 'BlueStacks'

I would like to ascertain at run-time inside an Android app whether it is running within the BlueStacks Android emulator. This is so I can modify the way the app runs when running inside BlueStacks.
BlueStacks does not support multi-touch so I want to implement an alternative to the standard pinch-to-zoom functionality my current app has.
E.g.
If (appIsRunningInBlueStacks){
mySurfaceView.enableMultiTouchAlternatives();
} else{
mySurfaceView.enableMultiTouchFeatures();
}
What is a reliable way of ascertaining the value of appIsRunningInBlueStacks?
EDIT Summary of answers to comments on question:
Ben, Taras, thanks for the suggestions. The Build.MODEL etc. values for BlueStacks are:
Model: "GT-I9100"
Manufacturer: "samsung"
Device: "GT-I9100"
Product: "GT-I9100"
This is the same model number as the Samsung Galaxy SII so it would not be ideal to use this for fear of treating all users with SIIs the same as those on BlueStacks.
CommonsWare, the app continues to run in BlueStacks even with the < uses-feature> for multitouch in the manifest. In fact (also answering iagreen's question)...
packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
... returns true! This is to be expected I suppose as the emulator is convinced it is a Samsung Galaxy SII!
Therefore we are still without a way of reliably detecting whether an app is running on BlueStacks without also throwing all Samsung Galaxy SII users in the same bucket. Any other ideas?
All the above methods are not working on BlueStacks 5. The correct way to do is checking if the path of /mnt/windows/BstSharedFolder exists. It is working fine on both BlueStacks 4 and 5.
fun checkFilesExist(files: Array<String>): Boolean {
files.forEach {
val file = File(it)
if (file.exists()) {
return true
}
}
return false
}
fun isBlueStacks(): Boolean {
val BLUE_STACKS_FILES = arrayOf(
"/mnt/windows/BstSharedFolder"
)
return checkFilesExist(BLUE_STACKS_FILES)
}
You can check that the Bluestacks shared folder exist
/sdcard/windows/BstSharedFolder
Boolean onBlueStacks()
{
File sharedFolder = new File(Environment
.getExternalStorageDirectory().toString()
+ File.separatorChar
+ "windows"
+ File.separatorChar
+ "BstSharedFolder");
if (sharedFolder.exists())
{
return true;
}
return false;
}
After trying all the suggested solutions available online we found the Google's SafetyNet Attestation API is the only solution for detecting VMs like BlueStack(any version) and NoxPlayer.
Apps that care about content piracy (and other security issues) can filter their availability on the Google Play like Netflix filters devices on the PlayStore.
The new “device catalog” section of the console includes an option
called “SafetyNet exclusion,” which can be used to prevent “devices
that fail integrity tests or those that are uncertified by Google,”
from downloading a specific app: among these would be rooted devices
and those running custom ROMs.
But there is a catch user will still find the APK from cross-sharing or other distribution systems, so the client must implement SafetyNet Attestation API on the app level.
How does it work?
SafetyNet examines software and hardware information on the device
where your app is installed to create a profile of that device. The
service then attempts to find this same profile within a list of
device models that have passed Android compatibility testing. The API
also uses this software and hardware information to help you assess
the basic integrity of the device, as well as the APK information of
the calling app. This attestation helps you to determine whether or
not the particular device has been tampered with or otherwise
modified.
It's an (easy to implement) paid API from the Google which allows 10,000 free hits per day as of now :\
If anyone is interested in detecting VMs by them self, these are the good papers available suggesting heuristic approaches :
Evading Android Runtime Analysis via Sandbox Detection
Rage Against the Virtual Machine:
Hindering Dynamic Analysis of Android Malware
My version of BlueStacks is reporting my Build.Model as GT-N7100.
Using: android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_RENDERER) I get Bluestacks.
It maybe too late but for the sake of others who have the same problem :
public boolean isRunningOnEmulator() {
return Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|| "google_sdk".equals(Build.PRODUCT)
|| Build.PRODUCT.contains("vbox86p")
|| Build.DEVICE.contains("vbox86p")
|| Build.HARDWARE.contains("vbox86");
}
Based on Mr. Regis' answer, you can detect it when the shared folder is present. However in Bluestacks 4, using file.exists() will only return false. This is because the shared folder has no permissions (000 or ----------). But listing files in the directory will detect the folder.
String path = Environment.getExternalStorageDirectory().toString();
Log.d("FILES", "Path: " + path);
File directory = new File(path);
File[] files = directory.listFiles();
for (File file : files) {
if (file.getName().contains("windows")) {
Log.d("FILES", "windows file exists, it's a bluestacks emu");
}
}
This Will be unique.
There is no bluetooth device in Bluestack.
So try to get The Bluetooth Address string which is always 'null' on Bluestack or Any emulator.Make sure you are adding Bluetooth permission on your project manifest.
BluetoothAdapter m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String m_bluetoothAdd = m_BluetoothAdapter.getAddress();

Null Issue with NeighboringCellInfo, CID and LAC

For a while I was trying to get CellID and LAC of near base stations. Unfortunately I did not manage to do that. First option was to use:
GsmCellLocation xXx = new GsmCellLocation();
CID = xXx.getCid();
LAC = xXx.getLac();
Toast output = Toast.makeText(getApplicationContext(), "Base station LAC is "+LAC+"\n"
+"Base station CID is " +CID, Toast.LENGTH_SHORT);
output.show();
But in this case I receive -1 value (as I understand that means it is not a GSM, but when i check with isGSM it shows "true").
Another way I have found surfing the net (i updated it a bit)
public void GetID(){
List<NeighboringCellInfo> neighCell = null;
TelephonyManager telManager = ( TelephonyManager )getSystemService(Context.TELEPHONY_SERVICE);
neighCell = telManager.getNeighboringCellInfo();
for (int i = 0; i < neighCell.size(); i++) {
try {
NeighboringCellInfo thisCell = neighCell.get(i);
int thisNeighCID = thisCell.getCid();
int thisNeighRSSI = thisCell.getRssi();
log(" "+thisNeighCID+" - "+thisNeighRSSI);
} catch (NumberFormatException e) {
e.printStackTrace();
NeighboringCellInfo thisCell = neighCell.get(i);
log(neighCell.toString());
}
}
}
But in this case the application just crashes right after I press the execute button. Eclipse shows no errors. May be someone have any ideas how to fix my problems?
Logcat says: 10-05 22:53:27.923: ERROR/dalvikvm(231): Unable to open
stack trace file '/data/anr/traces.txt': Permission denied
Used permissions:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_COARSE_UPDATES" />
May be the problem is that i forgot to include:
TelephonyManager telManager = ( TelephonyManager )getSystemService(Context.TELEPHONY_SERVICE);
Update. I included row from above, crash is gone but now after I press the button nothing happens.
Updated source code.
Have you verified that you have the correct permissions set in your manifest file?
The TelephonyManager requires a number of permissions depending on the API you use. You need READ_PHONE_STATE for most of the API's, in addition the documentation for getNeighboringCellInfo mentions ACCESS_COARSE_UPDATES, however I think this may be a doc mistake and you actually need ACCESS_COARSE_LOCATION which is documented as "Allows an application to access coarse (e.g., Cell-ID, WiFi) location"
The cellid implementation varies from mobile device to mobile device since these features are considered to be optional. for example:
Samsung (all devices): getNeigbouringCells () is not supported at all and always returns an empty list.
according to this:
http://wiki.opencellid.org/wiki/Android_library
Had the same problem. Try to run the query for neighboring cells on a forked thread (non-UI).
At least where I had the problem was that hammerhead android check for that and would return null immediately leaving a log in logcat:
#PhoneInterfaceManager: getNeighboringCellInfo RuntimeException - This method will deadlock if called from the main thread.

Categories

Resources