Bug 713228 - Add bridge to access our own SQLite libraries from Java. r=blassey
This commit is contained in:
@@ -113,7 +113,8 @@ public class GeckoAppShell
|
|||||||
public static native void onLowMemory();
|
public static native void onLowMemory();
|
||||||
public static native void callObserver(String observerKey, String topic, String data);
|
public static native void callObserver(String observerKey, String topic, String data);
|
||||||
public static native void removeObserver(String observerKey);
|
public static native void removeObserver(String observerKey);
|
||||||
public static native void loadLibs(String apkName, boolean shouldExtract);
|
public static native void loadGeckoLibsNative(String apkName);
|
||||||
|
public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
|
||||||
public static native void onChangeNetworkLinkStatus(String status);
|
public static native void onChangeNetworkLinkStatus(String status);
|
||||||
public static native void reportJavaCrash(String stack);
|
public static native void reportJavaCrash(String stack);
|
||||||
|
|
||||||
@@ -396,7 +397,8 @@ public class GeckoAppShell
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadLibs(apkName, extractLibs);
|
loadSQLiteLibsNative(apkName, extractLibs);
|
||||||
|
loadGeckoLibsNative(apkName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void putLocaleEnv() {
|
private static void putLocaleEnv() {
|
||||||
|
|||||||
@@ -2101,9 +2101,10 @@ abstract public class GeckoApp
|
|||||||
File profileDir = getProfileDir();
|
File profileDir = getProfileDir();
|
||||||
if (profileDir != null) {
|
if (profileDir != null) {
|
||||||
Log.i(LOGTAG, "checking profile migration in: " + profileDir.getAbsolutePath());
|
Log.i(LOGTAG, "checking profile migration in: " + profileDir.getAbsolutePath());
|
||||||
|
final GeckoApp app = GeckoApp.mAppContext;
|
||||||
|
GeckoAppShell.ensureSQLiteLibsLoaded(app.getApplication().getPackageResourcePath());
|
||||||
ProfileMigrator profileMigrator =
|
ProfileMigrator profileMigrator =
|
||||||
new ProfileMigrator(GeckoApp.mAppContext.getContentResolver(),
|
new ProfileMigrator(app.getContentResolver(), profileDir);
|
||||||
profileDir);
|
|
||||||
profileMigrator.launchBackground();
|
profileMigrator.launchBackground();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public class GeckoAppShell
|
|||||||
static private int sFreeSpace = -1;
|
static private int sFreeSpace = -1;
|
||||||
static File sHomeDir = null;
|
static File sHomeDir = null;
|
||||||
static private int sDensityDpi = 0;
|
static private int sDensityDpi = 0;
|
||||||
|
private static Boolean sSQLiteLibsLoaded = false;
|
||||||
|
|
||||||
private static HashMap<String, ArrayList<GeckoEventListener>> mEventListeners;
|
private static HashMap<String, ArrayList<GeckoEventListener>> mEventListeners;
|
||||||
|
|
||||||
@@ -129,7 +130,8 @@ public class GeckoAppShell
|
|||||||
public static native void onLowMemory();
|
public static native void onLowMemory();
|
||||||
public static native void callObserver(String observerKey, String topic, String data);
|
public static native void callObserver(String observerKey, String topic, String data);
|
||||||
public static native void removeObserver(String observerKey);
|
public static native void removeObserver(String observerKey);
|
||||||
public static native void loadLibs(String apkName, boolean shouldExtract);
|
public static native void loadGeckoLibsNative(String apkName);
|
||||||
|
public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
|
||||||
public static native void onChangeNetworkLinkStatus(String status);
|
public static native void onChangeNetworkLinkStatus(String status);
|
||||||
public static native void reportJavaCrash(String stack);
|
public static native void reportJavaCrash(String stack);
|
||||||
public static void notifyUriVisited(String uri) {
|
public static void notifyUriVisited(String uri) {
|
||||||
@@ -310,7 +312,7 @@ public class GeckoAppShell
|
|||||||
}
|
}
|
||||||
|
|
||||||
// java-side stuff
|
// java-side stuff
|
||||||
public static void loadGeckoLibs(String apkName) {
|
public static boolean loadLibsSetup(String apkName) {
|
||||||
// The package data lib directory isn't placed in ld.so's
|
// The package data lib directory isn't placed in ld.so's
|
||||||
// search path, so we have to manually load libraries that
|
// search path, so we have to manually load libraries that
|
||||||
// libxul will depend on. Not ideal.
|
// libxul will depend on. Not ideal.
|
||||||
@@ -415,7 +417,23 @@ public class GeckoAppShell
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadLibs(apkName, extractLibs);
|
return extractLibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ensureSQLiteLibsLoaded(String apkName) {
|
||||||
|
if (sSQLiteLibsLoaded)
|
||||||
|
return;
|
||||||
|
synchronized(sSQLiteLibsLoaded) {
|
||||||
|
if (sSQLiteLibsLoaded)
|
||||||
|
return;
|
||||||
|
loadSQLiteLibsNative(apkName, loadLibsSetup(apkName));
|
||||||
|
sSQLiteLibsLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadGeckoLibs(String apkName) {
|
||||||
|
boolean extractLibs = loadLibsSetup(apkName);
|
||||||
|
loadGeckoLibsNative(apkName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void putLocaleEnv() {
|
private static void putLocaleEnv() {
|
||||||
|
|||||||
@@ -86,8 +86,9 @@ public class GeckoThread extends Thread {
|
|||||||
// At some point while loading the gecko libs our default locale gets set
|
// At some point while loading the gecko libs our default locale gets set
|
||||||
// so just save it to locale here and reset it as default after the join
|
// so just save it to locale here and reset it as default after the join
|
||||||
Locale locale = Locale.getDefault();
|
Locale locale = Locale.getDefault();
|
||||||
GeckoAppShell.loadGeckoLibs(
|
String resourcePath = app.getApplication().getPackageResourcePath();
|
||||||
app.getApplication().getPackageResourcePath());
|
GeckoAppShell.ensureSQLiteLibsLoaded(resourcePath);
|
||||||
|
GeckoAppShell.loadGeckoLibs(resourcePath);
|
||||||
Locale.setDefault(locale);
|
Locale.setDefault(locale);
|
||||||
Resources res = app.getBaseContext().getResources();
|
Resources res = app.getBaseContext().getResources();
|
||||||
Configuration config = res.getConfiguration();
|
Configuration config = res.getConfiguration();
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ FENNEC_JAVA_FILES = \
|
|||||||
LinkPreference.java \
|
LinkPreference.java \
|
||||||
ProfileMigrator.java \
|
ProfileMigrator.java \
|
||||||
PromptService.java \
|
PromptService.java \
|
||||||
|
sqlite/ByteBufferInputStream.java \
|
||||||
|
sqlite/SQLiteBridge.java \
|
||||||
|
sqlite/SQLiteBridgeException.java \
|
||||||
SurfaceLockInfo.java \
|
SurfaceLockInfo.java \
|
||||||
Tab.java \
|
Tab.java \
|
||||||
Tabs.java \
|
Tabs.java \
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
* The Original Code is Mozilla Android code.
|
* The Original Code is Mozilla Android code.
|
||||||
*
|
*
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
* Portions created by the Initial Developer are Copyright (C) 2011-2012
|
||||||
* the Initial Developer. All Rights Reserved.
|
* the Initial Developer. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
@@ -38,24 +38,22 @@
|
|||||||
package org.mozilla.gecko;
|
package org.mozilla.gecko;
|
||||||
|
|
||||||
import org.mozilla.gecko.db.BrowserDB;
|
import org.mozilla.gecko.db.BrowserDB;
|
||||||
|
import org.mozilla.gecko.sqlite.ByteBufferInputStream;
|
||||||
|
import org.mozilla.gecko.sqlite.SQLiteBridge;
|
||||||
|
import org.mozilla.gecko.sqlite.SQLiteBridgeException;
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.database.sqlite.SQLiteException;
|
|
||||||
import android.database.sqlite.SQLiteStatement;
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.database.SQLException;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.provider.Browser;
|
import android.provider.Browser;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.webkit.WebIconDatabase;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -70,7 +68,6 @@ public class ProfileMigrator {
|
|||||||
private static final String LOGTAG = "ProfMigr";
|
private static final String LOGTAG = "ProfMigr";
|
||||||
private File mProfileDir;
|
private File mProfileDir;
|
||||||
private ContentResolver mCr;
|
private ContentResolver mCr;
|
||||||
private SQLiteDatabase mDb;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Amount of Android history entries we will remember
|
Amount of Android history entries we will remember
|
||||||
@@ -82,32 +79,32 @@ public class ProfileMigrator {
|
|||||||
These queries are derived from the low-level Places schema
|
These queries are derived from the low-level Places schema
|
||||||
https://developer.mozilla.org/en/The_Places_database
|
https://developer.mozilla.org/en/The_Places_database
|
||||||
*/
|
*/
|
||||||
final String bookmarkQuery = "SELECT places.url AS a_url, "
|
private final String bookmarkQuery = "SELECT places.url AS a_url, "
|
||||||
+ "places.title AS a_title FROM "
|
+ "places.title AS a_title FROM "
|
||||||
+ "(moz_places as places JOIN moz_bookmarks as bookmarks ON "
|
+ "(moz_places as places JOIN moz_bookmarks as bookmarks ON "
|
||||||
+ "places.id = bookmarks.fk) WHERE places.hidden <> 1 "
|
+ "places.id = bookmarks.fk) WHERE places.hidden <> 1 "
|
||||||
+ "ORDER BY bookmarks.dateAdded";
|
+ "ORDER BY bookmarks.dateAdded";
|
||||||
// Don't ask why. Just curse along at the Android devs.
|
// Result column of relevant data
|
||||||
final String bookmarkUrl = "a_url";
|
private final String bookmarkUrl = "a_url";
|
||||||
final String bookmarkTitle = "a_title";
|
private final String bookmarkTitle = "a_title";
|
||||||
|
|
||||||
final String historyQuery =
|
private final String historyQuery =
|
||||||
"SELECT places.url AS a_url, places.title AS a_title, "
|
"SELECT places.url AS a_url, places.title AS a_title, "
|
||||||
+ "history.visit_date AS a_date FROM "
|
+ "history.visit_date AS a_date FROM "
|
||||||
+ "(moz_historyvisits AS history JOIN moz_places AS places ON "
|
+ "(moz_historyvisits AS history JOIN moz_places AS places ON "
|
||||||
+ "places.id = history.place_id) WHERE places.hidden <> 1 "
|
+ "places.id = history.place_id) WHERE places.hidden <> 1 "
|
||||||
+ "ORDER BY history.visit_date DESC";
|
+ "ORDER BY history.visit_date DESC";
|
||||||
final String historyUrl = "a_url";
|
private final String historyUrl = "a_url";
|
||||||
final String historyTitle = "a_title";
|
private final String historyTitle = "a_title";
|
||||||
final String historyDate = "a_date";
|
private final String historyDate = "a_date";
|
||||||
|
|
||||||
final String faviconQuery =
|
private final String faviconQuery =
|
||||||
"SELECT places.url AS a_url, favicon.data AS a_data, "
|
"SELECT places.url AS a_url, favicon.data AS a_data, "
|
||||||
+ "favicon.mime_type AS a_mime FROM (moz_places AS places JOIN "
|
+ "favicon.mime_type AS a_mime FROM (moz_places AS places JOIN "
|
||||||
+ "moz_favicons AS favicon ON places.favicon_id = favicon.id)";
|
+ "moz_favicons AS favicon ON places.favicon_id = favicon.id)";
|
||||||
final String faviconUrl = "a_url";
|
private final String faviconUrl = "a_url";
|
||||||
final String faviconData = "a_data";
|
private final String faviconData = "a_data";
|
||||||
final String faviconMime = "a_mime";
|
private final String faviconMime = "a_mime";
|
||||||
|
|
||||||
public ProfileMigrator(ContentResolver cr, File profileDir) {
|
public ProfileMigrator(ContentResolver cr, File profileDir) {
|
||||||
mProfileDir = profileDir;
|
mProfileDir = profileDir;
|
||||||
@@ -116,12 +113,8 @@ public class ProfileMigrator {
|
|||||||
|
|
||||||
public void launchBackground() {
|
public void launchBackground() {
|
||||||
// Work around http://code.google.com/p/android/issues/detail?id=11291
|
// Work around http://code.google.com/p/android/issues/detail?id=11291
|
||||||
// The WebIconDatabase needs to be initialized within the UI thread so
|
// WebIconDatabase needs to be initialized within a looper thread.
|
||||||
// just request the instance here.
|
GeckoAppShell.getHandler().post(new PlacesTask());
|
||||||
WebIconDatabase.getInstance();
|
|
||||||
|
|
||||||
PlacesTask placesTask = new PlacesTask();
|
|
||||||
new Thread(placesTask).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PlacesTask implements Runnable {
|
private class PlacesTask implements Runnable {
|
||||||
@@ -177,37 +170,24 @@ public class ProfileMigrator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void migrateHistory(SQLiteDatabase db) {
|
protected void migrateHistory(SQLiteBridge db) {
|
||||||
Map<String, Long> androidHistory = gatherAndroidHistory();
|
Map<String, Long> androidHistory = gatherAndroidHistory();
|
||||||
final ArrayList<String> placesHistory = new ArrayList<String>();
|
final ArrayList<String> placesHistory = new ArrayList<String>();
|
||||||
|
|
||||||
Cursor cursor = null;
|
|
||||||
try {
|
try {
|
||||||
cursor =
|
ArrayList<Object[]> queryResult = db.query(historyQuery);
|
||||||
db.rawQuery(historyQuery, new String[] { });
|
final int urlCol = db.getColumnIndex(historyUrl);
|
||||||
final int urlCol =
|
final int titleCol = db.getColumnIndex(historyTitle);
|
||||||
cursor.getColumnIndexOrThrow(historyUrl);
|
final int dateCol = db.getColumnIndex(historyDate);
|
||||||
final int titleCol =
|
|
||||||
cursor.getColumnIndexOrThrow(historyTitle);
|
|
||||||
final int dateCol =
|
|
||||||
cursor.getColumnIndexOrThrow(historyDate);
|
|
||||||
|
|
||||||
cursor.moveToFirst();
|
for (Object[] resultRow: queryResult) {
|
||||||
while (!cursor.isAfterLast()) {
|
String url = (String)resultRow[urlCol];
|
||||||
String url = cursor.getString(urlCol);
|
String title = (String)resultRow[titleCol];
|
||||||
String title = cursor.getString(titleCol);
|
long date = Long.parseLong((String)(resultRow[dateCol])) / (long)1000;
|
||||||
// Convert from us (Places) to ms (Java, Android)
|
|
||||||
long date = cursor.getLong(dateCol) / (long)1000;
|
|
||||||
addHistory(androidHistory, url, title, date);
|
addHistory(androidHistory, url, title, date);
|
||||||
placesHistory.add(url);
|
placesHistory.add(url);
|
||||||
cursor.moveToNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor.close();
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
}
|
||||||
|
} catch (SQLiteBridgeException e) {
|
||||||
Log.i(LOGTAG, "Failed to get bookmarks: " + e.getMessage());
|
Log.i(LOGTAG, "Failed to get bookmarks: " + e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -231,90 +211,55 @@ public class ProfileMigrator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void migrateBookmarks(SQLiteDatabase db) {
|
protected void migrateBookmarks(SQLiteBridge db) {
|
||||||
Cursor cursor = null;
|
|
||||||
try {
|
try {
|
||||||
cursor = db.rawQuery(bookmarkQuery,
|
ArrayList<Object[]> queryResult = db.query(bookmarkQuery);
|
||||||
new String[] {});
|
final int urlCol = db.getColumnIndex(bookmarkUrl);
|
||||||
if (cursor.getCount() > 0) {
|
final int titleCol = db.getColumnIndex(bookmarkTitle);
|
||||||
final int urlCol =
|
|
||||||
cursor.getColumnIndexOrThrow(bookmarkUrl);
|
|
||||||
final int titleCol =
|
|
||||||
cursor.getColumnIndexOrThrow(bookmarkTitle);
|
|
||||||
|
|
||||||
cursor.moveToFirst();
|
for (Object[] resultRow: queryResult) {
|
||||||
while (!cursor.isAfterLast()) {
|
String url = (String)resultRow[urlCol];
|
||||||
String url = cursor.getString(urlCol);
|
String title = (String)resultRow[titleCol];
|
||||||
String title = cursor.getString(titleCol);
|
addBookmark(url, title);
|
||||||
addBookmark(url, title);
|
|
||||||
cursor.moveToNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
}
|
||||||
|
} catch (SQLiteBridgeException e) {
|
||||||
Log.i(LOGTAG, "Failed to get bookmarks: " + e.getMessage());
|
Log.i(LOGTAG, "Failed to get bookmarks: " + e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addFavicon(String url, String mime, byte[] data) {
|
protected void addFavicon(String url, String mime, ByteBuffer data) {
|
||||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
|
ByteBufferInputStream byteStream = new ByteBufferInputStream(data);
|
||||||
BitmapDrawable image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src");
|
BitmapDrawable image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src");
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
try {
|
try {
|
||||||
BrowserDB.updateFaviconForUrl(mCr, url, image);
|
BrowserDB.updateFaviconForUrl(mCr, url, image);
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLException e) {
|
||||||
Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url
|
Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url
|
||||||
+ " error:" + e.getMessage());
|
+ " error:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void migrateFavicons(SQLiteDatabase db) {
|
protected void migrateFavicons(SQLiteBridge db) {
|
||||||
Cursor cursor = null;
|
|
||||||
try {
|
try {
|
||||||
cursor = db.rawQuery(faviconQuery,
|
ArrayList<Object[]> queryResult = db.query(faviconQuery);
|
||||||
new String[] {});
|
final int urlCol = db.getColumnIndex(faviconUrl);
|
||||||
if (cursor.getCount() > 0) {
|
final int mimeCol = db.getColumnIndex(faviconMime);
|
||||||
final int urlCol =
|
final int dataCol = db.getColumnIndex(faviconData);
|
||||||
cursor.getColumnIndexOrThrow(faviconUrl);
|
|
||||||
final int dataCol =
|
|
||||||
cursor.getColumnIndexOrThrow(faviconData);
|
|
||||||
final int mimeCol =
|
|
||||||
cursor.getColumnIndexOrThrow(faviconMime);
|
|
||||||
|
|
||||||
cursor.moveToFirst();
|
for (Object[] resultRow: queryResult) {
|
||||||
while (!cursor.isAfterLast()) {
|
String url = (String)resultRow[urlCol];
|
||||||
String url = cursor.getString(urlCol);
|
String mime = (String)resultRow[mimeCol];
|
||||||
String mime = cursor.getString(mimeCol);
|
ByteBuffer dataBuff = (ByteBuffer)resultRow[dataCol];
|
||||||
byte[] data = cursor.getBlob(dataCol);
|
addFavicon(url, mime, dataBuff);
|
||||||
addFavicon(url, mime, data);
|
|
||||||
cursor.moveToNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
}
|
||||||
|
} catch (SQLiteBridgeException e) {
|
||||||
Log.i(LOGTAG, "Failed to get favicons: " + e.getMessage());
|
Log.i(LOGTAG, "Failed to get favicons: " + e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLiteDatabase openPlaces(String dbPath) throws SQLiteException {
|
|
||||||
/* http://stackoverflow.com/questions/2528489/no-such-table-android-metadata-whats-the-problem */
|
|
||||||
SQLiteDatabase db = SQLiteDatabase.openDatabase(dbPath,
|
|
||||||
null,
|
|
||||||
SQLiteDatabase.OPEN_READONLY |
|
|
||||||
SQLiteDatabase.NO_LOCALIZED_COLLATORS);
|
|
||||||
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void migratePlaces(File aFile) {
|
protected void migratePlaces(File aFile) {
|
||||||
String dbPath = aFile.getPath() + "/places.sqlite";
|
String dbPath = aFile.getPath() + "/places.sqlite";
|
||||||
String dbPathWal = aFile.getPath() + "/places.sqlite-wal";
|
String dbPathWal = aFile.getPath() + "/places.sqlite-wal";
|
||||||
@@ -329,9 +274,9 @@ public class ProfileMigrator {
|
|||||||
File dbFileWal = new File(dbPathWal);
|
File dbFileWal = new File(dbPathWal);
|
||||||
File dbFileShm = new File(dbPathShm);
|
File dbFileShm = new File(dbPathShm);
|
||||||
|
|
||||||
SQLiteDatabase db = null;
|
SQLiteBridge db = null;
|
||||||
try {
|
try {
|
||||||
db = openPlaces(dbPath);
|
db = new SQLiteBridge(dbPath);
|
||||||
migrateBookmarks(db);
|
migrateBookmarks(db);
|
||||||
migrateHistory(db);
|
migrateHistory(db);
|
||||||
migrateFavicons(db);
|
migrateFavicons(db);
|
||||||
@@ -343,7 +288,7 @@ public class ProfileMigrator {
|
|||||||
dbFileShm.delete();
|
dbFileShm.delete();
|
||||||
|
|
||||||
Log.i(LOGTAG, "Profile migration finished");
|
Log.i(LOGTAG, "Profile migration finished");
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLiteBridgeException e) {
|
||||||
if (db != null) {
|
if (db != null) {
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|||||||
71
mobile/android/base/sqlite/ByteBufferInputStream.java
Normal file
71
mobile/android/base/sqlite/ByteBufferInputStream.java
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Android code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011-2012
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sqlite;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper class to make the ByteBuffers returned by SQLite BLOB
|
||||||
|
* easier to use.
|
||||||
|
*/
|
||||||
|
public class ByteBufferInputStream extends InputStream {
|
||||||
|
private ByteBuffer mByteBuffer;
|
||||||
|
|
||||||
|
public ByteBufferInputStream(ByteBuffer aByteBuffer) {
|
||||||
|
mByteBuffer = aByteBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int read() throws IOException {
|
||||||
|
if (!mByteBuffer.hasRemaining()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return mByteBuffer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int read(byte[] aBytes, int aOffset, int aLen)
|
||||||
|
throws IOException {
|
||||||
|
int toRead = Math.min(aLen, mByteBuffer.remaining());
|
||||||
|
mByteBuffer.get(aBytes, aOffset, toRead);
|
||||||
|
return toRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
101
mobile/android/base/sqlite/SQLiteBridge.java
Normal file
101
mobile/android/base/sqlite/SQLiteBridge.java
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Android code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sqlite;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.sqlite.SQLiteBridgeException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.lang.String;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This class allows using the mozsqlite3 library included with Firefox
|
||||||
|
* to read SQLite databases, instead of the Android SQLiteDataBase API,
|
||||||
|
* which might use whatever outdated DB is present on the Android system.
|
||||||
|
*/
|
||||||
|
public class SQLiteBridge {
|
||||||
|
private static final String LOGTAG = "SQLiteBridge";
|
||||||
|
// Path to the database. We reopen it every query.
|
||||||
|
private String mDb;
|
||||||
|
// Remember column names from last query result.
|
||||||
|
private ArrayList<String> mColumns;
|
||||||
|
|
||||||
|
// JNI code in $(topdir)/storage/android/..
|
||||||
|
private static native void sqliteCall(String aDb, String aQuery,
|
||||||
|
String[] aParams,
|
||||||
|
ArrayList<String> aColumns,
|
||||||
|
ArrayList<Object[]> aRes)
|
||||||
|
throws SQLiteBridgeException;
|
||||||
|
|
||||||
|
// Takes the path to the database we want to access.
|
||||||
|
public SQLiteBridge(String aDb) throws SQLiteBridgeException {
|
||||||
|
mDb = aDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do an SQL query without parameters
|
||||||
|
public ArrayList<Object[]> query(String aQuery) throws SQLiteBridgeException {
|
||||||
|
String[] params = new String[0];
|
||||||
|
return query(aQuery, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do an SQL query, substituting the parameters in the query with the passed
|
||||||
|
// parameters. The parameters are subsituded in order, so named parameters
|
||||||
|
// are not supported.
|
||||||
|
// The result is returned as an ArrayList<Object[]>, with each
|
||||||
|
// row being an entry in the ArrayList, and each column being one Object
|
||||||
|
// in the Object[] array. The columns are of type null,
|
||||||
|
// direct ByteBuffer (BLOB), or String (everything else).
|
||||||
|
public ArrayList<Object[]> query(String aQuery, String[] aParams)
|
||||||
|
throws SQLiteBridgeException {
|
||||||
|
ArrayList<Object[]> result = new ArrayList<Object[]>();
|
||||||
|
mColumns = new ArrayList<String>();
|
||||||
|
sqliteCall(mDb, aQuery, aParams, mColumns, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the index in the row Object[] for the given column name.
|
||||||
|
// Returns -1 if not found.
|
||||||
|
public int getColumnIndex(String aColumnName) {
|
||||||
|
return mColumns.lastIndexOf(aColumnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
// nop, provided for API compatibility with SQLiteDatabase.
|
||||||
|
}
|
||||||
|
}
|
||||||
47
mobile/android/base/sqlite/SQLiteBridgeException.java
Normal file
47
mobile/android/base/sqlite/SQLiteBridgeException.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Android code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sqlite;
|
||||||
|
|
||||||
|
public class SQLiteBridgeException extends Exception {
|
||||||
|
static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public SQLiteBridgeException() {}
|
||||||
|
public SQLiteBridgeException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,8 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include "Zip.h"
|
#include "Zip.h"
|
||||||
|
#include "sqlite3.h"
|
||||||
|
#include "SQLiteBridge.h"
|
||||||
|
|
||||||
/* Android headers don't define RUSAGE_THREAD */
|
/* Android headers don't define RUSAGE_THREAD */
|
||||||
#ifndef RUSAGE_THREAD
|
#ifndef RUSAGE_THREAD
|
||||||
@@ -301,6 +303,7 @@ SHELL_WRAPPER7(notifyGotNextMessage, jint, jstring, jstring, jstring, jlong, jin
|
|||||||
SHELL_WRAPPER3(notifyReadingMessageListFailed, jint, jint, jlong);
|
SHELL_WRAPPER3(notifyReadingMessageListFailed, jint, jint, jlong);
|
||||||
|
|
||||||
static void * xul_handle = NULL;
|
static void * xul_handle = NULL;
|
||||||
|
static void * sqlite_handle = NULL;
|
||||||
static time_t apk_mtime = 0;
|
static time_t apk_mtime = 0;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
extern "C" int extractLibs = 1;
|
extern "C" int extractLibs = 1;
|
||||||
@@ -618,12 +621,10 @@ report_mapping(char *name, void *base, uint32_t len, uint32_t offset)
|
|||||||
extern "C" void simple_linker_init(void);
|
extern "C" void simple_linker_init(void);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
loadLibs(const char *apkName)
|
loadGeckoLibs(const char *apkName)
|
||||||
{
|
{
|
||||||
chdir(getenv("GRE_HOME"));
|
chdir(getenv("GRE_HOME"));
|
||||||
|
|
||||||
simple_linker_init();
|
|
||||||
|
|
||||||
struct stat status;
|
struct stat status;
|
||||||
if (!stat(apkName, &status))
|
if (!stat(apkName, &status))
|
||||||
apk_mtime = status.st_mtime;
|
apk_mtime = status.st_mtime;
|
||||||
@@ -635,7 +636,6 @@ loadLibs(const char *apkName)
|
|||||||
|
|
||||||
Zip *zip = new Zip(apkName);
|
Zip *zip = new Zip(apkName);
|
||||||
|
|
||||||
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
|
|
||||||
#ifdef MOZ_CRASHREPORTER
|
#ifdef MOZ_CRASHREPORTER
|
||||||
file_ids = (char *)extractBuf("lib.id", zip);
|
file_ids = (char *)extractBuf("lib.id", zip);
|
||||||
#endif
|
#endif
|
||||||
@@ -645,7 +645,6 @@ loadLibs(const char *apkName)
|
|||||||
MOZLOAD("nspr4");
|
MOZLOAD("nspr4");
|
||||||
MOZLOAD("plc4");
|
MOZLOAD("plc4");
|
||||||
MOZLOAD("plds4");
|
MOZLOAD("plds4");
|
||||||
MOZLOAD("mozsqlite3");
|
|
||||||
MOZLOAD("nssutil3");
|
MOZLOAD("nssutil3");
|
||||||
MOZLOAD("nss3");
|
MOZLOAD("nss3");
|
||||||
MOZLOAD("ssl3");
|
MOZLOAD("ssl3");
|
||||||
@@ -712,20 +711,67 @@ loadLibs(const char *apkName)
|
|||||||
StartupTimeline_Record(LIBRARIES_LOADED, &t1);
|
StartupTimeline_Record(LIBRARIES_LOADED, &t1);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" NS_EXPORT void JNICALL
|
static void loadSQLiteLibs(const char *apkName)
|
||||||
Java_org_mozilla_gecko_GeckoAppShell_loadLibs(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract)
|
|
||||||
{
|
{
|
||||||
if (jShouldExtract)
|
chdir(getenv("GRE_HOME"));
|
||||||
extractLibs = 1;
|
|
||||||
|
|
||||||
|
simple_linker_init();
|
||||||
|
|
||||||
|
struct stat status;
|
||||||
|
if (!stat(apkName, &status))
|
||||||
|
apk_mtime = status.st_mtime;
|
||||||
|
|
||||||
|
Zip *zip = new Zip(apkName);
|
||||||
|
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
|
||||||
|
|
||||||
|
#ifdef MOZ_CRASHREPORTER
|
||||||
|
file_ids = (char *)extractBuf("lib.id", zip);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MOZLOAD(name) mozload("lib" name ".so", zip)
|
||||||
|
sqlite_handle = MOZLOAD("mozsqlite3");
|
||||||
|
#undef MOZLOAD
|
||||||
|
|
||||||
|
delete zip;
|
||||||
|
|
||||||
|
#ifdef MOZ_CRASHREPORTER
|
||||||
|
free(file_ids);
|
||||||
|
file_ids = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!sqlite_handle)
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!");
|
||||||
|
|
||||||
|
setup_sqlite_functions(sqlite_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" NS_EXPORT void JNICALL
|
||||||
|
Java_org_mozilla_gecko_GeckoAppShell_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName)
|
||||||
|
{
|
||||||
const char* str;
|
const char* str;
|
||||||
// XXX: java doesn't give us true UTF8, we should figure out something
|
// XXX: java doesn't give us true UTF8, we should figure out something
|
||||||
// better to do here
|
// better to do here
|
||||||
str = jenv->GetStringUTFChars(jApkName, NULL);
|
str = jenv->GetStringUTFChars(jApkName, NULL);
|
||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
loadLibs(str);
|
loadGeckoLibs(str);
|
||||||
|
jenv->ReleaseStringUTFChars(jApkName, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" NS_EXPORT void JNICALL
|
||||||
|
Java_org_mozilla_gecko_GeckoAppShell_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) {
|
||||||
|
if (jShouldExtract)
|
||||||
|
extractLibs = 1;
|
||||||
|
|
||||||
|
const char* str;
|
||||||
|
// XXX: java doesn't give us true UTF8, we should figure out something
|
||||||
|
// better to do here
|
||||||
|
str = jenv->GetStringUTFChars(jApkName, NULL);
|
||||||
|
if (str == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
loadSQLiteLibs(str);
|
||||||
jenv->ReleaseStringUTFChars(jApkName, str);
|
jenv->ReleaseStringUTFChars(jApkName, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -745,7 +791,8 @@ ChildProcessInit(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
fillLibCache(argv[argc - 1]);
|
fillLibCache(argv[argc - 1]);
|
||||||
loadLibs(argv[i]);
|
loadSQLiteLibs(argv[i]);
|
||||||
|
loadGeckoLibs(argv[i]);
|
||||||
|
|
||||||
// don't pass the last arg - it's only recognized by the lib cache
|
// don't pass the last arg - it's only recognized by the lib cache
|
||||||
argc--;
|
argc--;
|
||||||
|
|||||||
@@ -53,10 +53,12 @@ DEFINES += \
|
|||||||
CPPSRCS = \
|
CPPSRCS = \
|
||||||
nsGeckoUtils.cpp \
|
nsGeckoUtils.cpp \
|
||||||
APKOpen.cpp \
|
APKOpen.cpp \
|
||||||
|
SQLiteBridge.cpp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
LOCAL_INCLUDES += -I$(srcdir)/../linker
|
LOCAL_INCLUDES += -I$(srcdir)/../linker
|
||||||
LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/components/startup
|
LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/components/startup
|
||||||
|
LOCAL_INCLUDES += -I$(topsrcdir)/db/sqlite3/src
|
||||||
ifdef MOZ_OLD_LINKER
|
ifdef MOZ_OLD_LINKER
|
||||||
LOCAL_INCLUDES += -I$(topsrcdir)/other-licenses/android
|
LOCAL_INCLUDES += -I$(topsrcdir)/other-licenses/android
|
||||||
ifeq ($(CPU_ARCH),arm)
|
ifeq ($(CPU_ARCH),arm)
|
||||||
|
|||||||
312
mozglue/android/SQLiteBridge.cpp
Normal file
312
mozglue/android/SQLiteBridge.cpp
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Android code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include "dlfcn.h"
|
||||||
|
#include "APKOpen.h"
|
||||||
|
#include "SQLiteBridge.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
|
||||||
|
#else
|
||||||
|
#define LOG(x...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name;
|
||||||
|
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_open)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_errmsg)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_prepare_v2)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_bind_text)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_step)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_column_count)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_finalize)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_close)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_column_name)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_column_type)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_column_blob)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_column_bytes)
|
||||||
|
SQLITE_WRAPPER_INT(sqlite3_column_text)
|
||||||
|
|
||||||
|
void setup_sqlite_functions(void *sqlite_handle)
|
||||||
|
{
|
||||||
|
#define GETFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(sqlite_handle, #name)
|
||||||
|
GETFUNC(sqlite3_open);
|
||||||
|
GETFUNC(sqlite3_errmsg);
|
||||||
|
GETFUNC(sqlite3_prepare_v2);
|
||||||
|
GETFUNC(sqlite3_bind_parameter_count);
|
||||||
|
GETFUNC(sqlite3_bind_text);
|
||||||
|
GETFUNC(sqlite3_step);
|
||||||
|
GETFUNC(sqlite3_column_count);
|
||||||
|
GETFUNC(sqlite3_finalize);
|
||||||
|
GETFUNC(sqlite3_close);
|
||||||
|
GETFUNC(sqlite3_column_name);
|
||||||
|
GETFUNC(sqlite3_column_type);
|
||||||
|
GETFUNC(sqlite3_column_blob);
|
||||||
|
GETFUNC(sqlite3_column_bytes);
|
||||||
|
GETFUNC(sqlite3_column_text);
|
||||||
|
#undef GETFUNC
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool initialized = false;
|
||||||
|
static jclass stringClass;
|
||||||
|
static jclass objectClass;
|
||||||
|
static jclass byteBufferClass;
|
||||||
|
static jclass arrayListClass;
|
||||||
|
static jmethodID jByteBufferAllocateDirect;
|
||||||
|
static jmethodID jArrayListAdd;
|
||||||
|
static jobject jNull;
|
||||||
|
|
||||||
|
static void
|
||||||
|
JNI_Throw(JNIEnv* jenv, const char* name, const char* msg)
|
||||||
|
{
|
||||||
|
jclass cls = jenv->FindClass(name);
|
||||||
|
if (cls == NULL) {
|
||||||
|
LOG("Couldn't find exception class (or exception pending)\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int rc = jenv->ThrowNew(cls, msg);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOG("Error throwing exception\n");
|
||||||
|
}
|
||||||
|
jenv->DeleteLocalRef(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
JNI_Setup(JNIEnv* jenv)
|
||||||
|
{
|
||||||
|
if (initialized) return;
|
||||||
|
|
||||||
|
objectClass = jenv->FindClass("java/lang/Object");
|
||||||
|
stringClass = jenv->FindClass("java/lang/String");
|
||||||
|
byteBufferClass = jenv->FindClass("java/nio/ByteBuffer");
|
||||||
|
arrayListClass = jenv->FindClass("java/util/ArrayList");
|
||||||
|
jNull = jenv->NewGlobalRef(NULL);
|
||||||
|
|
||||||
|
if (stringClass == NULL || objectClass == NULL
|
||||||
|
|| byteBufferClass == NULL || arrayListClass == NULL) {
|
||||||
|
LOG("Error finding classes");
|
||||||
|
JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException",
|
||||||
|
"FindClass error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public static ByteBuffer allocateDirect(int capacity)
|
||||||
|
jByteBufferAllocateDirect =
|
||||||
|
jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
|
||||||
|
// boolean add(Object o)
|
||||||
|
jArrayListAdd =
|
||||||
|
jenv->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
|
||||||
|
|
||||||
|
if (jByteBufferAllocateDirect == NULL || jArrayListAdd == NULL) {
|
||||||
|
LOG("Error finding methods");
|
||||||
|
JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException",
|
||||||
|
"GetMethodId error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" NS_EXPORT void JNICALL
|
||||||
|
Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
|
||||||
|
jstring jDb,
|
||||||
|
jstring jQuery,
|
||||||
|
jobjectArray jParams,
|
||||||
|
jobject jColumns,
|
||||||
|
jobject jArrayList)
|
||||||
|
{
|
||||||
|
JNI_Setup(jenv);
|
||||||
|
|
||||||
|
const char* queryStr;
|
||||||
|
queryStr = jenv->GetStringUTFChars(jQuery, NULL);
|
||||||
|
|
||||||
|
const char* dbPath;
|
||||||
|
dbPath = jenv->GetStringUTFChars(jDb, NULL);
|
||||||
|
|
||||||
|
const char *pzTail;
|
||||||
|
sqlite3_stmt *ppStmt;
|
||||||
|
sqlite3 *db;
|
||||||
|
int rc;
|
||||||
|
rc = f_sqlite3_open(dbPath, &db);
|
||||||
|
jenv->ReleaseStringUTFChars(jDb, dbPath);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
LOG("Can't open database: %s\n", f_sqlite3_errmsg(db));
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = f_sqlite3_prepare_v2(db, queryStr, -1, &ppStmt, &pzTail);
|
||||||
|
if (rc != SQLITE_OK || ppStmt == NULL) {
|
||||||
|
LOG("Can't prepare statement: %s\n", f_sqlite3_errmsg(db));
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
jenv->ReleaseStringUTFChars(jQuery, queryStr);
|
||||||
|
|
||||||
|
// Check if number of parameters matches
|
||||||
|
jsize numPars;
|
||||||
|
numPars = jenv->GetArrayLength(jParams);
|
||||||
|
int sqlNumPars;
|
||||||
|
sqlNumPars = f_sqlite3_bind_parameter_count(ppStmt);
|
||||||
|
if (numPars != sqlNumPars) {
|
||||||
|
LOG("Passed parameter count (%d) doesn't match SQL parameter count (%d)\n",
|
||||||
|
numPars, sqlNumPars);
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
// Bind parameters, if any
|
||||||
|
if (numPars > 0) {
|
||||||
|
for (int i = 0; i < numPars; i++) {
|
||||||
|
jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i);
|
||||||
|
// IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf
|
||||||
|
// should be OK.
|
||||||
|
jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass);
|
||||||
|
if (isString != JNI_TRUE) {
|
||||||
|
LOG("Parameter is not of String type");
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
jstring jStringParam = (jstring)jObjectParam;
|
||||||
|
const char* paramStr = jenv->GetStringUTFChars(jStringParam, NULL);
|
||||||
|
// SQLite parameters index from 1.
|
||||||
|
rc = f_sqlite3_bind_text(ppStmt, i + 1, paramStr, -1, SQLITE_TRANSIENT);
|
||||||
|
jenv->ReleaseStringUTFChars(jStringParam, paramStr);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
LOG("Error binding query parameter");
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the query and step through the results
|
||||||
|
rc = f_sqlite3_step(ppStmt);
|
||||||
|
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
|
||||||
|
LOG("Can't step statement: (%d) %s\n", rc, f_sqlite3_errmsg(db));
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the column names
|
||||||
|
int cols;
|
||||||
|
cols = f_sqlite3_column_count(ppStmt);
|
||||||
|
for (int i = 0; i < cols; i++) {
|
||||||
|
const char* colName = f_sqlite3_column_name(ppStmt, i);
|
||||||
|
jstring jStr = jenv->NewStringUTF(colName);
|
||||||
|
jenv->CallBooleanMethod(jColumns, jArrayListAdd, jStr);
|
||||||
|
jenv->DeleteLocalRef(jStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each row, add an Object[] to the passed ArrayList,
|
||||||
|
// with that containing either String or ByteArray objects
|
||||||
|
// containing the columns
|
||||||
|
do {
|
||||||
|
// Process row
|
||||||
|
// Construct Object[]
|
||||||
|
jobjectArray jRow = jenv->NewObjectArray(cols,
|
||||||
|
objectClass,
|
||||||
|
NULL);
|
||||||
|
if (jRow == NULL) {
|
||||||
|
LOG("Can't allocate jRow Object[]\n");
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cols; i++) {
|
||||||
|
int colType = f_sqlite3_column_type(ppStmt, i);
|
||||||
|
if (colType == SQLITE_BLOB) {
|
||||||
|
// Treat as blob
|
||||||
|
const void* blob = f_sqlite3_column_blob(ppStmt, i);
|
||||||
|
int colLen = f_sqlite3_column_bytes(ppStmt, i);
|
||||||
|
|
||||||
|
// Construct ByteBuffer of correct size
|
||||||
|
jobject jByteBuffer =
|
||||||
|
jenv->CallStaticObjectMethod(byteBufferClass,
|
||||||
|
jByteBufferAllocateDirect,
|
||||||
|
colLen);
|
||||||
|
if (jByteBuffer == NULL) {
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get its backing array
|
||||||
|
void* bufferArray = jenv->GetDirectBufferAddress(jByteBuffer);
|
||||||
|
if (bufferArray == NULL) {
|
||||||
|
LOG("Failure calling GetDirectBufferAddress\n");
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
memcpy(bufferArray, blob, colLen);
|
||||||
|
|
||||||
|
jenv->SetObjectArrayElement(jRow, i, jByteBuffer);
|
||||||
|
jenv->DeleteLocalRef(jByteBuffer);
|
||||||
|
} else if (colType == SQLITE_NULL) {
|
||||||
|
jenv->SetObjectArrayElement(jRow, i, jNull);
|
||||||
|
} else {
|
||||||
|
// Treat everything else as text
|
||||||
|
const char* txt = (const char*)f_sqlite3_column_text(ppStmt, i);
|
||||||
|
jstring jStr = jenv->NewStringUTF(txt);
|
||||||
|
jenv->SetObjectArrayElement(jRow, i, jStr);
|
||||||
|
jenv->DeleteLocalRef(jStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append Object[] to ArrayList<Object[]>
|
||||||
|
// JNI doesn't know about the generic, so use Object[] as Object
|
||||||
|
jenv->CallBooleanMethod(jArrayList, jArrayListAdd, jRow);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
jenv->DeleteLocalRef(jRow);
|
||||||
|
|
||||||
|
// Get next row
|
||||||
|
rc = f_sqlite3_step(ppStmt);
|
||||||
|
// Real error?
|
||||||
|
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
|
||||||
|
LOG("Can't re-step statement:(%d) %s\n", rc, f_sqlite3_errmsg(db));
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
} while (rc != SQLITE_DONE);
|
||||||
|
|
||||||
|
rc = f_sqlite3_finalize(ppStmt);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
LOG("Can't finalize statement: %s\n", f_sqlite3_errmsg(db));
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
f_sqlite3_close(db);
|
||||||
|
return;
|
||||||
|
|
||||||
|
error_close:
|
||||||
|
f_sqlite3_close(db);
|
||||||
|
JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", "SQLite error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
63
mozglue/android/SQLiteBridge.h
Normal file
63
mozglue/android/SQLiteBridge.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Android code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#ifndef SQLiteBridge_h
|
||||||
|
#define SQLiteBridge_h
|
||||||
|
|
||||||
|
#include "sqlite3.h"
|
||||||
|
|
||||||
|
void setup_sqlite_functions(void *sqlite_handle);
|
||||||
|
|
||||||
|
#define SQLITE_WRAPPER(name, return_type, args...) \
|
||||||
|
typedef return_type (*name ## _t)(args); \
|
||||||
|
extern name ## _t f_ ## name;
|
||||||
|
|
||||||
|
SQLITE_WRAPPER(sqlite3_open, int, const char*, sqlite3**)
|
||||||
|
SQLITE_WRAPPER(sqlite3_errmsg, const char*, sqlite3*)
|
||||||
|
SQLITE_WRAPPER(sqlite3_prepare_v2, int, sqlite3*, const char*, int, sqlite3_stmt**, const char**)
|
||||||
|
SQLITE_WRAPPER(sqlite3_bind_parameter_count, int, sqlite3_stmt*)
|
||||||
|
SQLITE_WRAPPER(sqlite3_bind_text, int, sqlite3_stmt*, int, const char*, int, void(*)(void*))
|
||||||
|
SQLITE_WRAPPER(sqlite3_step, int, sqlite3_stmt*)
|
||||||
|
SQLITE_WRAPPER(sqlite3_column_count, int, sqlite3_stmt*)
|
||||||
|
SQLITE_WRAPPER(sqlite3_finalize, int, sqlite3_stmt*)
|
||||||
|
SQLITE_WRAPPER(sqlite3_close, int, sqlite3*)
|
||||||
|
SQLITE_WRAPPER(sqlite3_column_name, const char*, sqlite3_stmt*, int)
|
||||||
|
SQLITE_WRAPPER(sqlite3_column_type, int, sqlite3_stmt*, int)
|
||||||
|
SQLITE_WRAPPER(sqlite3_column_blob, const void*, sqlite3_stmt*, int)
|
||||||
|
SQLITE_WRAPPER(sqlite3_column_bytes, int, sqlite3_stmt*, int)
|
||||||
|
SQLITE_WRAPPER(sqlite3_column_text, const unsigned char*, sqlite3_stmt*, int)
|
||||||
|
|
||||||
|
#endif /* SQLiteBridge_h */
|
||||||
Reference in New Issue
Block a user