In an Android application (API level 10),I do not use below permission in manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
So application would not write on root of external storage.But when I try to check read/write permission via AccessController by this code:
File f = Environment.getExternalStorageDirectory();
String path = f.getAbsolutePath();
String actions = "read,write";
try {
AccessController.checkPermission(new FilePermission(path, actions));
Log.d("Tag", "You have read/write permition to use : " + path);
} catch (AccessControlException e) {
Log.d("Tag", "You have not read/write permition to use : " + path);
} catch (SecurityException e) {
Log.d("Tag", "You have not read/write permition to use : " + path);
}
Result will be:
You have read/write permition to use : /storage/sdcard0
So why AccessController.checkPermission does not throw exception on root of External Storage when application has no permission to write on it?
Related
I have to develop a little function that, besides other things, writes a file in the SD card. I have to use it for two specific Android tablets provided by a supplier. One tablet uses Android 5 and the other uses Android 7. The application that I am modifying is a system app and it doesn't have UI. I'm calling the code from a Service, and I want to call it from a FirebaseMessagingService. I have problems to write a file only in Android 7 tablet.
I have no problems with the Android 5 tablet, I identified the external storage folder and I can create files in it. But I do have problems in Android 7, I identified the external storage folder and I have a problem: Permission denied.
I have this permissions in the manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
This is the piece of code that is giving me problems:
public void myFunction()
{
String sdPath;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
sdPath = "/storage/extsd";
else
sdPath = "/storage/0665-3426";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (my_context.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
Log.d(TAG, "Permission is granted");
else
Log.d(TAG, "Permission is denied");
}
File folder = new File(sdPath);
if (folder.exists()) {
Log.d(TAG, sdPath + " exists, can write: " + folder.canWrite());
File file = new File(sdPath + "/new_file");
boolean fileExists = file.exists();
Log.d(TAG, file.getAbsolutePath() + " file exists: " + fileExists + ", can write: " + file.canWrite());
if (!fileExists) {
try {
file.createNewFile();
Log.d(TAG, "Can write in " + sdPath);
}
catch (Exception exception) {
Log.e(TAG, "Cannot write in " + sdPath + ": " + exception.getMessage());
}
}
}
else
Log.e(TAG, sdPath + " does not exist.");
...
}
Here the logs in Android 5 tablet:
10-22 14:44:51.271 10450-10450/com.my.app D/MY_TAG: /storage/extsd exists, can write: true
10-22 14:44:51.368 10450-10450/com.my.app D/MY_TAG: /storage/extsd/new_file file exists: false, can write: false
10-22 14:44:51.479 10450-10450/com.my.app D/MY_TAG: Can write in /storage/extsd
And here the logs in Android 7 tablet:
2020-10-22 15:11:56.383 19689-19689/com.my.app D/MY_TAG: Permission is granted
2020-10-22 15:11:59.037 19689-19689/com.my.app D/MY_TAG: /storage/0665-3426 exists, can write: false
2020-10-22 15:12:07.956 19689-19689/com.my.app D/MY_TAG: /storage/0665-3426/new_file file exists: false, can write: false
2020-10-22 15:12:07.957 19689-19689/com.my.app E/MY_TAG: Cannot write in /storage/0665-3426: Permission denied
As you can see, even if permission is granted, canWrite() method returns false in Android 7. Do you know the reason? How can I solve this problem?
I have read some other questions from stack overflow but I didn't find the solution.
I'm referring to one of the answers in this Stack Overflow thread.
I am not aware of the target SDK version in your case, but if you're building for version 29, try adding this to your AndroidManifest.xml file:
<manifest>
<application
<!-- other stuff -->
android:requestLegacyExternalStorage="true">
<!-- other stuff -->
</application>
</manifest>
Also, are you requesting permissions at runtime correctly?
Removable micro sd cards are read only since Android Kitkat/Lollipop.
Hence you cannot write to paths like "/storage/0665-3426".
Only one app specific directory is writable on a removable micro sd card.
To determine the path of that folder have a look at the second item returned by
getExternalFilesDirs()
I have decided not to use the Android API. Since the application has elevated privileges, I have created the file by executing a shell command. This is the code to create a file (works with removable SD card folder):
public static String createFile(String filePath)
{
String returnValue = "";
try
{
Runtime runtime = Runtime.getRuntime();
String[] command = new String[]{ "su", "0", "touch", filePath};
Process p = runtime.exec(command);
p.waitFor();
java.io.BufferedReader errorIn = new java.io.BufferedReader(
new java.io.InputStreamReader(p.getErrorStream()));
String line = "";
while ((line = errorIn.readLine()) != null)
returnValue += line + "\n";
}
catch (IOException | InterruptedException e)
{
e.printStackTrace();
}
return returnValue;
}
I am creating an app that requier an app-specific file that I called "conf.cfg" for example. this file need to be read by my application to create some object etc... the body of the file look like that :
#activation, level, type, regex or array
0, "critic", 0,"\\d{4}\\w\\d{3}"
1, "critic", 1, [word1,word2]
1,"minor", 0,"\\d{2}-\\w{3}-\\d{4}\\s?\\/?\\s?\\d{2}:\\d{2}"
Doing my research I found that there is two type of Storage in android :
Internal storage :
Internal storage is best when you want to be sure that neither the user nor other apps can access your files.
External storae :
External storage is the best place for files that don't require access restrictions and for files that you want to share with other apps or allow the user to access with a computer.
As I want the user to be able to edit/download/upload/USE this file, External Storage seems to be a good choice. However on Developper Android they said :
Caution: The external storage might become unavailable if the user removes the SD card or connects the device to a computer. And the files are still visible to the user and other apps that have the READ_EXTERNAL_STORAGE permission. So if your app's functionality depends on these files or you need to completely restrict access, you should instead write your files to the internal storage.
Caution: Files on external storage are not always accessible, because users can mount the external storage to a computer for use as a storage device. So if you need to store files that are critical to your app's functionality, you should instead store them on internal storage.
As this file need to be always available and is critical to my app's functionality
So... Internal Storage seems to be better. But I need the user to see and be able to use the file. And here I'm stuck.
Anyone has an idea of where and how to put/create this file ?
EDIT : following #greenapps answer
heer is a piece of code I've wrote. I use the getExternalFilesDir(null) command to write and store my file
String folderName = "Innovation";
String confFileName = "conf.txt";
String commentSymbol = "#";
String commentLine = commentSymbol + " activation, level, type , regex or array";
File storage = getExternalFilesDir(null);
File folder = new File(storage, folderName);
File conf = new File(folder, confFileName);
Log.d(TAG, "Folder action!");
if (!folder.exists()) {
if (folder.mkdirs()) {
Log.d(TAG, "Created : " + folder.getAbsolutePath());
} else {
Log.e(TAG, "folder not created!");
}
}
Log.d(TAG, "File action!");
if (!conf.exists()) {
try {
Log.d(TAG, "opening...");
FileOutputStream fos = new FileOutputStream(conf);
fos.write(commentLine.getBytes());
fos.close();
Log.d(TAG, "Created : " + conf.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
if (conf.exists()) {
Log.d(TAG, "File exist at : " + conf.getAbsolutePath());
}
the file is created, as shown by the last log
Created : /storage/emulated/0/Android/data/com.aralex.innovation/files/Innovation/conf.txt
But when I search the file with the native file explorer application of the phone, I can't find it. I can go to the file folder but the folder "Innovation/" is hidden.
This is a problem because I want the file to be visible.
Phone : Samsung s7, s7edge, s9+
Default File Explorer Icon
Default File Explorer Oppened
Well I finally found an answer myself.
On this post Android create folders in Internal Memory #prodev specify that Environment.getExternalStorageDirectory() is a good place, because the file will be accessible and :
note that ExternalStorage in Environment.getExternalStorageDirectory() does not necessarily refers to sdcard, it returns phone primary storage memory
it requires permissions (only for build version >= M) :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
So here is a code to answer my problem (it ask permission on runtime) :
private ArrayList<Rule> ruleList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[...]
// Check for the storage permission before accessing the camera. If the
// permission is not granted yet, request permission.
if (hasPermissions(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
ruleList = createRules();
} else {
requestStoragePermission();
}
}
private boolean hasPermissions(Context context, String... permissions) {
if (context != null && permissions != null) {
for (String permission : permissions) {
Log.d(TAG, "Checking permission : " + permission);
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "not granted : " + permission);
return false;
} else {
Log.d(TAG, "granted : " + permission);
}
}
}
return true;
}
/**
* Handles the requesting of the storage permission. This includes
* showing a "Snackbar" errorMessage of why the permission is needed then
* sending the request.
*/
private void requestStoragePermission() {
Log.w(TAG, "Storage permission is not granted. Requesting permission");
final String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_EXTERNAL_PERM);
return;
}
final Activity thisActivity = this;
View.OnClickListener listener = view -> ActivityCompat.requestPermissions(thisActivity, permissions,
RC_HANDLE_EXTERNAL_PERM);
Snackbar.make(findViewById(android.R.id.content), R.string.permission_storage_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, listener)
.show();
}
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode != RC_HANDLE_EXTERNAL_PERM) {
Log.d(TAG, "Got unexpected permission result: " + requestCode);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Storage permission granted");
// We have permission
ruleList = createRules();
return;
}
Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
" Result code = " + (grantResults.length > 1 ? grantResults[0] + " " + grantResults[1] : grantResults.length > 0 ? grantResults[0] : "(empty)"));
DialogInterface.OnClickListener listener = (dialog, id) -> finish();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Assisting Tool")
.setMessage(R.string.no_storage_permission)
.setPositiveButton(R.string.ok, listener)
.show();
}
private ArrayList<Rule> createRules() {
Log.d(TAG, "=========================READING FILE======================");
ArrayList<Rule> ruleList = new ArrayList<>();
String folderName = "Innovation";
String confFileName = "conf.txt";
String commentSymbol = "#";
String commentLine = commentSymbol + " activation, level, type , regex or array";
File storage = Environment.getExternalStorageDirectory();
File folder = new File(storage, folderName);
File conf = new File(folder, confFileName);
Log.d(TAG, "Folder action!");
if (!folder.exists()) {
if (folder.mkdirs()) {
Log.d(TAG, "Created : " + folder.getAbsolutePath());
} else {
Log.e(TAG, "folder not created!");
}
}
Log.d(TAG, "File action!");
if (!conf.exists()) {
try {
Log.d(TAG, "opening...");
FileOutputStream fos = new FileOutputStream(conf);
fos.write(commentLine.getBytes());
fos.close();
Log.d(TAG, "Created : " + conf.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
if (conf.exists()) {
Log.d(TAG, "File exist at : " + conf.getAbsolutePath());
} else {
Log.e(TAG, "The file doesn't exist...");
}
}
Now it create a app-specific file
/storage/emulated/0/Innovation/conf.txt
that is accessible by user !
So external storage.
No internal as file explorers have no access to your apps private internal memory.
You could use getExternalFilesDir(null) as then you dont need read and write permissions
The issue is to save image to the storage
This code worked fine, but not with Android 6 and N
What do I need to fix here?
Or as an option - using another example for saving to internal\external files
public void saveImage(Bitmap icon) {
File ff;
File file = new File(android.os.Environment.getExternalStorageDirectory(), "Folder Name");
ff = new File(file.getAbsolutePath() + file.separator + imageName + ".jpg");
if(ff.exists()){
Log.i("sharing", "File exist SD");
} else{
try {
File f = null;
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
if (!file.exists()) {
file.mkdirs();
}
Log.i("sharing", "File exist Internal");
f = new File(file.getAbsolutePath() + file.separator + imageName + ".jpg");
}
FileOutputStream ostream = new FileOutputStream(f);
bitmap.compress(Bitmap.CompressFormat.PNG, 10, ostream);
ostream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
And permissions from Manifest:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Thanx
You should put the uses-permission-sdk-23 tag and set the permission you want.
Then
You should check whether the permission is granted or not by calling the checkSelfPermission and pass the permission name as argument
If the permission is not granted you should gain the permissions by calling the requestPermissions method and pass the permissions names as string array and the request code
After that
A dialog will be shown to user and ask them if the permission is granted or not
Then a interface onRequestPermissionResult will be called and you should implement this into your activity class
After that
You can gain access to the requested permissions if the user granted it
I added permissions for android sdk > 22
This code works perfectly
if (ContextCompat.checkSelfPermission(HomeActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(HomeActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},23
);
}
I need to open gallery to show images in a specific directory,after a search I follow this
How to open gallery to show images in a specific directory
but the file.list returns me a null String[] on android 4.4
Than I write some test code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listDirectory("ExternalStorageRoot",
Environment.getExternalStorageDirectory());
listDirectory(
"DCIM",
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
writeFileTest();
}
private void writeFileTest() {
String tag = "WriteTest";
String path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath();
path = path + "/testFile.txt";
Log.d(tag, "path : " + path);
File file=new File(path);
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(file);
fileOutputStream.write("THIS IS A TEST LINE".getBytes());
fileOutputStream.flush();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void listDirectory(String tag, File f) {
Log.d(tag, "absolute path " + f.getAbsolutePath());
Log.d(tag, "is dir " + f.isDirectory());
Log.d(tag, "can read " + f.canRead());
Log.d(tag, "can write " + f.canWrite());
String[] fileNames = f.list();
Log.d(tag, fileNames == null ? "fileNames is null"
: "fileNames is not null");
if (fileNames != null) {
for (String string : fileNames) {
Log.d(tag, "file -- " + string);
}
}
}
I also add these lines in AndroidManifest.xml
<permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
TO:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
EDIT:
I mess up the permission.It's Now every thing works fine!
4.4 changed the file permissions for the sd card. You can't write outside of your personal directory anymore. See also: http://www.androidcentral.com/kitkat-sdcard-changes
I want to create a folder in SD card ,and i already add the permission
<user-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
in manifest file.below is my code,but mkdirs return false! Can you help me!
File exportDir = new File(
Environment.getExternalStorageDirectory().toString(), "happydiarybackup");
if (!exportDir.exists()) {
boolean a = exportDir.mkdirs();
Log.d("mkdir ",exportDir.getAbsolutePath() + " make "+ a);
}
Try this. It might help you.
String fullPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/happydiarybackup/";
try
{
File dir = new File(fullPath);
if (!dir.exists()) {
dir.mkdirs();
}
}
catch (Exception e) {
Log.e("App", "Exception" + e.getMessage());
}
1.Check your compileSdkVersion
2.Android: mkdirs()/mkdir() on external storage returns false.
Make sure your put the permission tag in.