Env desc: Android version: 6.0.1, DB is created by customer and stored on external storage, the data of Sqlite DB will be collected after task done.
We need read and write the db per different service logical.
Problem:
Could not open the database in read/write mode.
java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=10, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.example.xx.sqlitedemo/com.example.xx.sqlitedemo.MainActivity}:
android.database.sqlite.SQLiteException: not an error (code 0): Could not open the database in read/write mode.
Code :
SQLiteDatabase.openOrCreateDatabase
Permissions are requested:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
The db file is existed checking by below code in debug mode:
new File(dbpath).exists();
dbpath is:
"/storage/4B53-98D9/System/System.db"
Dynamic permissions have got, problem still existed.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.xx.sqlitedemo">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java
package com.example.xx.sqlitedemo;
import android.Manifest;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.DatabaseErrorHandler;
import android.database.DefaultDatabaseErrorHandler;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteCursorDriver;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQuery;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity {
private static final int EXTERNAL_STORAGE_REQ_CODE = 10;
private static final int MOUNT_UNMOUNT_FILESYSTEMS = 20;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// requestMountPermission();
requestStoragePermission();
int o = 0;
}
private void requestMountPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)) {
Toast.makeText(this, "please give me the permission", Toast.LENGTH_SHORT).show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS},
MOUNT_UNMOUNT_FILESYSTEMS);
}
} else {
requestStoragePermission();
int i = 1;
}
}
public void requestStoragePermission() {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(this, "please give me the permission", Toast.LENGTH_SHORT).show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
EXTERNAL_STORAGE_REQ_CODE);
}
} else {
openSQLiteDataBase();
int i = 1;
}
}
private void openSQLiteDataBase() {
// SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase("/storage/4B53-98D9/System/System.db", new CustomCursorFactory(), new CustomDbErrorHandler());
DatabaseHelper helper = new DatabaseHelper(this,"System.db");
SQLiteDatabase database = helper.getReadableDatabase();
int ii = 0;
Cursor cursor = database.query(null,null,null,null,null,null,null,null);
database.close();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case EXTERNAL_STORAGE_REQ_CODE:
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openSQLiteDataBase();
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
break;
case MOUNT_UNMOUNT_FILESYSTEMS:
break;
}
}
class CustomCursorFactory implements SQLiteDatabase.CursorFactory {
@Override
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) {
if (Build.VERSION.SDK_INT < 11) {
return new SQLiteCursor(db, masterQuery, editTable, query);
} else {
return new SQLiteCursor(masterQuery, editTable, query);
}
}
}
class CustomDbErrorHandler implements DatabaseErrorHandler {
DefaultDatabaseErrorHandler __d = new DefaultDatabaseErrorHandler();
boolean __once = false;
@Override
public void onCorruption(SQLiteDatabase dbObj) {
if (__once) {
throw new SQLiteDatabaseCorruptException("db corrupted and cannot be recovered");
}
__once = true;
__d.onCorruption(dbObj);
}
}
class DatabaseContext extends ContextWrapper {
private static final String DEBUG_CONTEXT = "DatabaseContext";
public DatabaseContext(Context base) {
super(base);
}
@Override
public File getDatabasePath(String name) {
File sdcard = Environment.getExternalStorageDirectory();
String path = this.getFilesDir().getAbsolutePath();
String dbfile = "/storage/4B53-98D9/System/System.db"; //sdcard.getAbsolutePath() + File.separator+ "databases" + File.separator + name;
if (!dbfile.endsWith(".db")) {
dbfile += ".db" ;
}
File result = new File(dbfile);
if (!result.getParentFile().exists()) {
result.getParentFile().mkdirs();
}
return result;
}
/* this version is called for android devices >= api-11. thank to @damccull for fixing this. */
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
return openOrCreateDatabase(name,mode, factory);
}
/* this version is called for android devices < api-11 */
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
// SQLiteDatabase result = super.openOrCreateDatabase(name, mode, factory);
Log.w(DEBUG_CONTEXT, "openOrCreateDatabase(" + name + ",,) = " + result.getPath());
return result;
}
}
public class DatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
DatabaseHelper(final Context context, String databaseName) {
super(new DatabaseContext(context), databaseName, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
updated at 2017-03-05: The way provided by @greenapps is dangerous:
Have a look at getExternalFilesDirs(). Look at the second item returned.
That would then look like "/storage/4B53-98D9/Android/data/<package>/files/System.db".
When uninstall the app, data was deleted at the same time.
/storage/4B53-98D9/Android/data/<package> was deleted.
Anyone can answer my question?
Thanks in advance!