0

I have an application that I want to export the current SQLite database to the Internal storage Downloads folder of the device. I am getting the below error.

java.io.FileNotFoundException: /storage/emulated/0/Download/Pipe_Tally: open failed: EACCES (Permission denied)

I have what I think are the correct permissions included in the AndroidManifest.xml file for reading and writing to storage. Here is the AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          package="com.example.bigdaddy.pipelinepipetally">

    <uses-feature android:name="android.hardware.camera2"
                  android:required="true"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="18"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>

    <application
        android:allowBackup="true"
        android:debuggable="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="HardcodedDebugMode">
        <activity
            android:name=".MainActivity"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".TallyActivity2"
            android:windowSoftInputMode="adjustResize">
        </activity>
        <activity
            android:name=".JobAndDbActivity"
            android:windowSoftInputMode="adjustResize">
        </activity>
        <activity
            android:name=".ExistingTallyActivity"
            android:windowSoftInputMode="adjustResize">
        </activity>
        <activity android:name=".ImageToFullscreen"
            android:windowSoftInputMode="adjustResize">
        </activity>

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.bigdaddy.pipelinepipetally.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths">
            </meta-data>
        </provider>

    </application>
</manifest>

Here is the method I am trying to use to create the file for the /Downloads/ folder within the internal device storage:

public void backUpDatabase() {
    /* Open your local db as the input stream */
    DBHelper anotherDbHelper = null;
    try {
        try {
            anotherDbHelper = new DBHelper(ExistingTallyActivity.this);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    String path = null;
    if (anotherDbHelper != null) {
        path = String.valueOf(getApplicationContext().getDatabasePath(anotherDbHelper.getDatabaseName()));
        Log.i(TAG, "backUpDatabase: 1" +path);
    }
    File dbFile = null;
    if (path != null) {
        dbFile = new File(path);
        Log.i(TAG, "backUpDatabase: 2" + String.valueOf(dbFile));
    }
    FileInputStream fis = null;
    try {
        if (dbFile != null) {
            fis = new FileInputStream(dbFile);
            Log.i(TAG, "backUpDatabase: 3" + String.valueOf(dbFile));
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    String outFileName = (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()+ "/Pipe_Tally/");
    Log.i(TAG, "backUpDatabase: 4"+ outFileName);

    // Open the empty db as the output stream
    OutputStream outputStream = null;
    //FileOutputStream outputStream = null;
    try {
         outputStream = new FileOutputStream(outFileName);
        Log.i(TAG, "backUpDatabase: 5"+ outFileName);
        Log.i(TAG, "backUpDatabase: 6"+ String.valueOf(outputStream));
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    // Transfer bytes from the input-file to the output-file
    byte[] buffer = new byte[1024];
    int length;
    try {
        if (fis != null) {
            while ((length = fis.read(buffer)) > 0) {
                try {
                    if (outputStream != null) {
                        outputStream.write(buffer, 0, length);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    //Close the streams
    try {
        if (outputStream != null) {
            outputStream.flush();
            outputStream.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        if (fis != null) {
            fis.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Here is the method I am using to check for the correct permissions at Runtime:

private boolean checkSdCardPermissions() {
    /* Checking here if we already have permissions*/
    if (ActivityCompat.checkSelfPermission(ExistingTallyActivity.this,
            Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(ExistingTallyActivity.this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { /* Added this for Write External Storage permissions */
        /* Checking to see if we are equal to or at a higher version than Marshmallow */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            /* System default popup box here to ask for permissions. */
            ActivityCompat.requestPermissions(ExistingTallyActivity.this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    MY_PERMISSION_READ_EXTERNAL_STORAGE);
            /* Added this today for trying to prompt for writing external storage*/
            ActivityCompat.requestPermissions(ExistingTallyActivity.this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    MY_PERMISSION_WRITE_EXTERNAL_STORAGE);
        }
        return true;
    } else {
        return false;
    }
}

This is the onOptionsItemSelected() method that has the switch in it to handle the menu and what is selected. The case:R.id.menu_save_and_export: is where I am checking if checkSdCardPermissions() method is true, then calling the backUpDatabase() method.

 @Override
    public boolean onOptionsItemSelected(MenuItem menuItem) {
        switch (menuItem.getItemId()) {
            /*
            When Tally New Pipe is selected from the Menu, it sends the user to the TallyActivity
            page, all encapsulated in a runnable and passed to thread below.
            */
            case R.id.menu_new_pipe:
                class ToTallyActivityManager implements Runnable {
                    @Override
                    public void run() {
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        Intent intentToTallyActivity =
                                new Intent(ExistingTallyActivity.this, TallyActivity2.class);
                        startActivity(intentToTallyActivity); /* Heading to TallyActivity.*/
                    } /* run method ends here. */
                } /* Runnable ends here. */
                /* This thread takes care of the call to go to TallyActivity page from here.*/
                mThreadToTallyActivity = new Thread(new ToTallyActivityManager());
                mThreadToTallyActivity.start();
                break;
            /* When selected, sends the user to the MainActivity page. */
            case R.id.menu_to_main:
                Intent intentToMainActivity =
                        new Intent(ExistingTallyActivity.this, MainActivity.class);
                startActivity(intentToMainActivity); // Heading to MainActivity
                break;
            /* When chosen allows the option to save and export the current DB.*/
            case R.id.menu_save_and_export:

                if (checkSdCardPermissions()) {
                    try {
                        backUpDatabase();
                    } catch (/*IOException*/ SQLException e) {
                        e.printStackTrace();
                    }
                }

                break;
            /* When chosen from the menu, this kills the app completely. Has popup embedded.*/
            default:
                AlertHelperDialog.displayExitAlertDialog(ExistingTallyActivity.this);
        } /* switch/case ends here */

        return super.onOptionsItemSelected(menuItem);
    } /* onOptionsItemSelected method ends here. */

I really appreciate any help or advice with fixing this issue. Thank you.

J2112O
  • 636
  • 3
  • 11
  • 24
  • Related: http://stackoverflow.com/q/40104793/6613600 (particularly the answer) –  Dec 17 '16 at 14:33

1 Answers1

0

You have a android:maxSdkVersion="18" set on the WRITE_EXTERNAL_STORAGE permission. If you are running on a phone with SDK > 18 the app will not have permission to WRITE_EXTERNAL_STORAGE. So I suggest you remove the android:maxSdkVersion attribute. Read the documentation.

android:maxSdkVersion : The highest API level at which this permission should be granted to your app. Setting this attribute is useful if the permission your app requires is no longer needed beginning at a certain API level.

Change it as follows,

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
K Neeraj Lal
  • 6,768
  • 3
  • 24
  • 33