SQLiteCantOpenDatabaseException => 未知错误(代码 14):无法打开数据库(Android 6.0)
SQLiteCantOpenDatabaseException => unknown error (code 14): Could not open database (Android 6.0)
以下内容适用于数千台设备,但最近我收到两次以下错误,均来自 android 6.0,因此它可能与新的 android 版本有关:
RootUtils.copyDatabase(path, pathApp);
if (new File(pathApp).exists())
{
L.d(this, "Database copied!"); // <= this is called, so copying file succeeds!!!
SQLiteDatabase db = SQLiteDatabase.openDatabase(pathApp, null, SQLiteDatabase.OPEN_READONLY);
final Cursor cursor = db.rawQuery("select * from contacts", new String[0]); // <= this line throws the exception
cursor.moveToFirst();
....
}
Log/Exception
c.m.s.utils.RootUtils [RootUtils-91] copyDatabase: true
c.m.s.networks.utils.Util [FUtil-105] Database copied!
c.m.s.networks.utils.Util [FUtil-185] unknown error (code 14): Could not open database
android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:207)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:191)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:806)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:791)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
...
我的复制功能如下:
public final static boolean copyDatabase(String pathSource, String pathTarget) throws IOException, InterruptedException, TimeoutException, RootDeniedException
{
Shell shell = RootTools.getShell(true);
Command command = new Command(0,
"su\n",
"rm " + pathTarget + "\n",
"cat " + pathSource + " > " + pathTarget + "\n",
"chown root.root " + pathTarget + "\n",
"chmod 777 " + pathTarget + "\n");
command = shell.add(command);
int exitCode = command.getExitCode();
while (!command.isFinished()) {
Thread.sleep(50);
}
shell.close();
boolean success = exitCode == -1 && command.isFinished();
L.d(RootUtils.class, "copyDatabase: " + success);
return success;
}
编辑:我的新复制功能甚至将 Owner/Group 设置为我的应用程序进程 - 目前只收到一个反馈,但似乎没有解决问题
public final static boolean copyDatabase(String ownAppDatabase, String pathSource, String pathTarget) throws IOException, InterruptedException, TimeoutException, RootDeniedException
{
Shell shell = RootTools.getShell(true);
boolean getRealUser = true;
String owner = null;
String group = null;
if (getRealUser)
{
final StringHolder lsResult = new StringHolder("");
Command lsCommand = new Command(0,
"su\n",
"ls -ld " + ownAppDatabase + "\n")
{
public void commandOutput(int id, String line) {
super.commandOutput(id, line);
lsResult.set(line);
}
};
lsCommand = shell.add(lsCommand);
int lsExitCode = lsCommand.getExitCode();
while (!lsCommand.isFinished()) {
Thread.sleep(50);
}
L.d(RootUtils.class, "ls exit code: " + lsExitCode);
L.d(RootUtils.class, "ls result: " + lsResult.get());
String[] parts = lsResult.get().split("\s+");
if (parts.length > 3)
{
owner = parts[1];
group = parts[2];
}
}
if (owner == null || group == null)
{
L.d(RootUtils.class, "owner or group is NULL!");
getRealUser = false;
}
else
L.d(RootUtils.class, "owner=" + owner + " | group=" + group);
Command command = new Command(0,
"su\n",
"rm " + pathTarget + "\n",
"cat " + pathSource + " > " + pathTarget + "\n",
"chown " + (getRealUser ? (owner + "." + group + " ") : "chown root.root ") + pathTarget + "\n",
"chmod 777 " + pathTarget + "\n");
command = shell.add(command);
int exitCode = command.getExitCode();
while (!command.isFinished()) {
Thread.sleep(50);
}
shell.close();
boolean success = exitCode == -1 && command.isFinished();
L.d(RootUtils.class, "copyDatabase: " + success);
return success;
}
更新到 Android 6.0 后,我在尝试打开存储在内部存储器上的 SQLite 数据库时遇到了同样的问题。
这是由 Marshmallow 的新权限模型引起的。
作为解决方法,我必须转到我的应用程序的 "App Info" 系统设置页面并授予存储访问权限。
以下是我找到的唯一解决方案(它应该也适用于所有旧设备):
转储数据库!
复制数据库不行,读取数据库也行。可能是因为数据库被另一个进程打开了。但是转储数据库有效。您也可以转储单个 table,这是一个适用于所有设备的示例:
String dump = pathSQLite3 + " " + pathDB + " \".dump '" + tablename + "'\"\n";
唯一的缺点是,您必须将 sqlite executable 添加到您的应用程序中,并且您必须找出目标设备的架构,以便您知道您使用的是哪个 sqlite executable可以使用...
但至少这个工作正常。
您就快完成了 - 还需要在 chmod 之后执行此操作:"chcon u:object_r:app_data_file:s0:c512,c768 "。
以下内容适用于数千台设备,但最近我收到两次以下错误,均来自 android 6.0,因此它可能与新的 android 版本有关:
RootUtils.copyDatabase(path, pathApp);
if (new File(pathApp).exists())
{
L.d(this, "Database copied!"); // <= this is called, so copying file succeeds!!!
SQLiteDatabase db = SQLiteDatabase.openDatabase(pathApp, null, SQLiteDatabase.OPEN_READONLY);
final Cursor cursor = db.rawQuery("select * from contacts", new String[0]); // <= this line throws the exception
cursor.moveToFirst();
....
}
Log/Exception
c.m.s.utils.RootUtils [RootUtils-91] copyDatabase: true
c.m.s.networks.utils.Util [FUtil-105] Database copied!
c.m.s.networks.utils.Util [FUtil-185] unknown error (code 14): Could not open database
android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:207)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:191)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:806)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:791)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
...
我的复制功能如下:
public final static boolean copyDatabase(String pathSource, String pathTarget) throws IOException, InterruptedException, TimeoutException, RootDeniedException
{
Shell shell = RootTools.getShell(true);
Command command = new Command(0,
"su\n",
"rm " + pathTarget + "\n",
"cat " + pathSource + " > " + pathTarget + "\n",
"chown root.root " + pathTarget + "\n",
"chmod 777 " + pathTarget + "\n");
command = shell.add(command);
int exitCode = command.getExitCode();
while (!command.isFinished()) {
Thread.sleep(50);
}
shell.close();
boolean success = exitCode == -1 && command.isFinished();
L.d(RootUtils.class, "copyDatabase: " + success);
return success;
}
编辑:我的新复制功能甚至将 Owner/Group 设置为我的应用程序进程 - 目前只收到一个反馈,但似乎没有解决问题
public final static boolean copyDatabase(String ownAppDatabase, String pathSource, String pathTarget) throws IOException, InterruptedException, TimeoutException, RootDeniedException
{
Shell shell = RootTools.getShell(true);
boolean getRealUser = true;
String owner = null;
String group = null;
if (getRealUser)
{
final StringHolder lsResult = new StringHolder("");
Command lsCommand = new Command(0,
"su\n",
"ls -ld " + ownAppDatabase + "\n")
{
public void commandOutput(int id, String line) {
super.commandOutput(id, line);
lsResult.set(line);
}
};
lsCommand = shell.add(lsCommand);
int lsExitCode = lsCommand.getExitCode();
while (!lsCommand.isFinished()) {
Thread.sleep(50);
}
L.d(RootUtils.class, "ls exit code: " + lsExitCode);
L.d(RootUtils.class, "ls result: " + lsResult.get());
String[] parts = lsResult.get().split("\s+");
if (parts.length > 3)
{
owner = parts[1];
group = parts[2];
}
}
if (owner == null || group == null)
{
L.d(RootUtils.class, "owner or group is NULL!");
getRealUser = false;
}
else
L.d(RootUtils.class, "owner=" + owner + " | group=" + group);
Command command = new Command(0,
"su\n",
"rm " + pathTarget + "\n",
"cat " + pathSource + " > " + pathTarget + "\n",
"chown " + (getRealUser ? (owner + "." + group + " ") : "chown root.root ") + pathTarget + "\n",
"chmod 777 " + pathTarget + "\n");
command = shell.add(command);
int exitCode = command.getExitCode();
while (!command.isFinished()) {
Thread.sleep(50);
}
shell.close();
boolean success = exitCode == -1 && command.isFinished();
L.d(RootUtils.class, "copyDatabase: " + success);
return success;
}
更新到 Android 6.0 后,我在尝试打开存储在内部存储器上的 SQLite 数据库时遇到了同样的问题。
这是由 Marshmallow 的新权限模型引起的。
作为解决方法,我必须转到我的应用程序的 "App Info" 系统设置页面并授予存储访问权限。
以下是我找到的唯一解决方案(它应该也适用于所有旧设备):
转储数据库!
复制数据库不行,读取数据库也行。可能是因为数据库被另一个进程打开了。但是转储数据库有效。您也可以转储单个 table,这是一个适用于所有设备的示例:
String dump = pathSQLite3 + " " + pathDB + " \".dump '" + tablename + "'\"\n";
唯一的缺点是,您必须将 sqlite executable 添加到您的应用程序中,并且您必须找出目标设备的架构,以便您知道您使用的是哪个 sqlite executable可以使用...
但至少这个工作正常。
您就快完成了 - 还需要在 chmod 之后执行此操作:"chcon u:object_r:app_data_file:s0:c512,c768 "。