Reorganise database access to prevent locking issues

This commit is contained in:
Ciaran Gultnieks 2012-09-10 21:01:39 +01:00
parent 568b615ecf
commit 0623801474
7 changed files with 135 additions and 65 deletions

View File

@ -23,6 +23,8 @@ import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.fdroid.fdroid.DB.Apk.CompatibilityChecker;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ListActivity; import android.app.ListActivity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
@ -157,13 +159,12 @@ public class AppDetails extends ListActivity {
private static final int MARKET = Menu.FIRST + 5; private static final int MARKET = Menu.FIRST + 5;
private static final int DONATE = Menu.FIRST + 6; private static final int DONATE = Menu.FIRST + 6;
private DB db;
private DB.App app; private DB.App app;
private int app_currentvercode; private int app_currentvercode;
private DB.Apk curapk; private DB.Apk curapk;
private String appid; private String appid;
private CompatibilityChecker compatChecker;
private PackageManager mPm; private PackageManager mPm;
private DB.Apk.CompatibilityChecker compatChecker;
private DownloadHandler downloadHandler; private DownloadHandler downloadHandler;
private boolean stateRetained; private boolean stateRetained;
@ -197,8 +198,6 @@ public class AppDetails extends ListActivity {
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
db = new DB(this);
compatChecker = db.getCompatibilityChecker();
mPm = getPackageManager(); mPm = getPackageManager();
// Get the preferences we're going to use in this Activity... // Get the preferences we're going to use in this Activity...
SharedPreferences prefs = PreferenceManager SharedPreferences prefs = PreferenceManager
@ -235,8 +234,6 @@ public class AppDetails extends ListActivity {
@Override @Override
protected void onStop() { protected void onStop() {
db.close();
db = null;
super.onStop(); super.onStop();
} }
@ -275,8 +272,15 @@ public class AppDetails extends ListActivity {
// also when something has been installed/uninstalled. // also when something has been installed/uninstalled.
private void reset() { private void reset() {
Log.d("FDroid", "Getting application details for " + appid); Log.d("FDroid", "Getting application details for " + appid);
app = db.getApps(appid, null, true, false).get(0); DB.Apk curver;
DB.Apk curver = app.getCurrentVersion(compatChecker); try {
DB db = DB.getDB();
compatChecker = db.getCompatibilityChecker();
app = db.getApps(appid, null, true, false).get(0);
curver = app.getCurrentVersion(compatChecker);
} finally {
DB.releaseDB();
}
app_currentvercode = curver == null ? 0 : curver.vercode; app_currentvercode = curver == null ? 0 : curver.vercode;
// Get the signature of the installed package... // Get the signature of the installed package...

View File

@ -27,6 +27,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Vector; import java.util.Vector;
import java.util.concurrent.Semaphore;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
@ -44,6 +45,31 @@ import android.util.Log;
public class DB { public class DB {
private static Semaphore dbSync = new Semaphore(1, true);
static DB dbInstance = null;
// Initialise the database. Called once when the application starts up.
static void initDB(Context ctx) {
dbInstance = new DB(ctx);
}
// Get access to the database. Must be called before any database activity,
// and releaseDB must be called subsequently. Returns null in the event of
// failure.
static DB getDB() {
try {
dbSync.acquire();
return dbInstance;
} catch (InterruptedException e) {
return null;
}
}
// Release database access lock acquired via getDB().
static void releaseDB() {
dbSync.release();
}
private static final String DATABASE_NAME = "fdroid"; private static final String DATABASE_NAME = "fdroid";
// Possible values of the SQLite flag "synchronous" // Possible values of the SQLite flag "synchronous"
@ -131,7 +157,7 @@ public class DB {
// To skip compatibility checks, pass null as the checker. // To skip compatibility checks, pass null as the checker.
public Apk getCurrentVersion(DB.Apk.CompatibilityChecker checker) { public Apk getCurrentVersion(DB.Apk.CompatibilityChecker checker) {
// Try and return the version that's in Google's market first... // Try and return the real current version first...
if (marketVersion != null && marketVercode > 0) { if (marketVersion != null && marketVercode > 0) {
for (Apk apk : apks) { for (Apk apk : apks) {
if (apk.vercode == marketVercode if (apk.vercode == marketVercode
@ -140,7 +166,7 @@ public class DB {
} }
} }
// If we don't know the market version, or we don't have it, we // If we don't know the current version, or we don't have it, we
// return the most recent compatible version we have... // return the most recent compatible version we have...
int latestcode = -1; int latestcode = -1;
Apk latestapk = null; Apk latestapk = null;
@ -396,7 +422,7 @@ public class DB {
// database. // database.
private SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd"); private SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd");
public DB(Context ctx) { private DB(Context ctx) {
mContext = ctx; mContext = ctx;
DBHelper h = new DBHelper(ctx); DBHelper h = new DBHelper(ctx);
@ -568,12 +594,13 @@ public class DB {
app.marketVercode = c.getInt(c app.marketVercode = c.getInt(c
.getColumnIndex("marketVercode")); .getColumnIndex("marketVercode"));
String sAdded = c.getString(c.getColumnIndex("added")); String sAdded = c.getString(c.getColumnIndex("added"));
app.added = (sAdded == null || sAdded.length() == 0) ? null : mDateFormat app.added = (sAdded == null || sAdded.length() == 0) ? null
.parse(sAdded); : mDateFormat.parse(sAdded);
String sLastUpdated = c.getString(c String sLastUpdated = c.getString(c
.getColumnIndex("lastUpdated")); .getColumnIndex("lastUpdated"));
app.lastUpdated = (sLastUpdated == null || sLastUpdated.length() == 0) ? null app.lastUpdated = (sLastUpdated == null || sLastUpdated
: mDateFormat.parse(sLastUpdated); .length() == 0) ? null : mDateFormat
.parse(sLastUpdated);
app.hasUpdates = false; app.hasUpdates = false;
c2 = db.rawQuery("select * from " + TABLE_APK c2 = db.rawQuery("select * from " + TABLE_APK
@ -873,8 +900,12 @@ public class DB {
values.put("trackerURL", upapp.trackerURL); values.put("trackerURL", upapp.trackerURL);
values.put("sourceURL", upapp.sourceURL); values.put("sourceURL", upapp.sourceURL);
values.put("donateURL", upapp.donateURL); values.put("donateURL", upapp.donateURL);
values.put("added", upapp.added == null ? "" : mDateFormat.format(upapp.added)); values.put("added",
values.put("lastUpdated", upapp.added == null ? "" : mDateFormat.format(upapp.lastUpdated)); upapp.added == null ? "" : mDateFormat.format(upapp.added));
values.put(
"lastUpdated",
upapp.added == null ? "" : mDateFormat
.format(upapp.lastUpdated));
values.put("marketVersion", upapp.marketVersion); values.put("marketVersion", upapp.marketVersion);
values.put("marketVercode", upapp.marketVercode); values.put("marketVercode", upapp.marketVercode);
values.put("antiFeatures", CommaSeparatedList.str(upapp.antiFeatures)); values.put("antiFeatures", CommaSeparatedList.str(upapp.antiFeatures));
@ -907,7 +938,8 @@ public class DB {
values.put("apkName", upapk.apkName); values.put("apkName", upapk.apkName);
values.put("apkSource", upapk.apkSource); values.put("apkSource", upapk.apkSource);
values.put("minSdkVersion", upapk.minSdkVersion); values.put("minSdkVersion", upapk.minSdkVersion);
values.put("added", upapk.added == null ? "" : mDateFormat.format(upapk.added)); values.put("added",
upapk.added == null ? "" : mDateFormat.format(upapk.added));
values.put("permissions", CommaSeparatedList.str(upapk.permissions)); values.put("permissions", CommaSeparatedList.str(upapk.permissions));
values.put("features", CommaSeparatedList.str(upapk.features)); values.put("features", CommaSeparatedList.str(upapk.features));
if (oldapk != null) { if (oldapk != null) {

View File

@ -70,8 +70,6 @@ public class FDroid extends TabActivity implements OnItemClickListener,
private static final int ABOUT = Menu.FIRST + 3; private static final int ABOUT = Menu.FIRST + 3;
private static final int SEARCH = Menu.FIRST + 4; private static final int SEARCH = Menu.FIRST + 4;
private DB db = null;
// Apps that are available to be installed // Apps that are available to be installed
private AppListAdapter apps_av = new AppListAdapter(this); private AppListAdapter apps_av = new AppListAdapter(this);
@ -135,14 +133,12 @@ public class FDroid extends TabActivity implements OnItemClickListener,
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
db = new DB(this);
triedEmptyUpdate = false; triedEmptyUpdate = false;
populateLists(true); populateLists(true);
} }
@Override @Override
protected void onStop() { protected void onStop() {
db.close();
super.onStop(); super.onStop();
} }
@ -328,23 +324,34 @@ public class FDroid extends TabActivity implements OnItemClickListener,
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// Populate the category list with the real categories, and the locally DB db;
// generated meta-categories for "All", "What's New" and "Recently Vector<DB.App> apps;
// Updated"... String cat_all, cat_whatsnew, cat_recentlyupdated;
String cat_all = getString(R.string.category_all); try {
String cat_whatsnew = getString(R.string.category_whatsnew); db = DB.getDB();
String cat_recentlyupdated = getString(R.string.category_recentlyupdated);
categories.add(cat_all); // Populate the category list with the real categories, and the
for (String s : db.getCategories()) { // locally
Log.d("FDroid", "s: " + s); // generated meta-categories for "All", "What's New" and "Recently
categories.add(s); // Updated"...
} cat_all = getString(R.string.category_all);
categories.add(cat_whatsnew); cat_whatsnew = getString(R.string.category_whatsnew);
categories.add(cat_recentlyupdated); cat_recentlyupdated = getString(R.string.category_recentlyupdated);
if (currentCategory == null) categories.add(cat_all);
currentCategory = cat_all; for (String s : db.getCategories()) {
Log.d("FDroid", "s: " + s);
categories.add(s);
}
categories.add(cat_whatsnew);
categories.add(cat_recentlyupdated);
if (currentCategory == null)
currentCategory = cat_all;
apps = db.getApps(null, null, update, true);
} finally {
DB.releaseDB();
}
Vector<DB.App> apps = db.getApps(null, null, update, true);
if (apps.isEmpty()) { if (apps.isEmpty()) {
// Don't attempt this more than once - we may have invalid // Don't attempt this more than once - we may have invalid
// repositories. // repositories.
@ -419,8 +426,9 @@ public class FDroid extends TabActivity implements OnItemClickListener,
public UpdateReceiver(Handler handler) { public UpdateReceiver(Handler handler) {
super(handler); super(handler);
} }
@Override @Override
protected void onReceiveResult (int resultCode, Bundle resultData) { protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 1) { if (resultCode == 1) {
Toast.makeText(FDroid.this, Toast.makeText(FDroid.this,
getString(R.string.connection_error_msg), getString(R.string.connection_error_msg),
@ -432,6 +440,7 @@ public class FDroid extends TabActivity implements OnItemClickListener,
pd.dismiss(); pd.dismiss();
} }
} }
private UpdateReceiver mUpdateReceiver; private UpdateReceiver mUpdateReceiver;
// Force a repo update now. A progress dialog is shown and the UpdateService // Force a repo update now. A progress dialog is shown and the UpdateService

View File

@ -22,5 +22,13 @@ import android.app.Application;
public class FDroidApp extends Application { public class FDroidApp extends Application {
@Override
public void onCreate() {
super.onCreate();
DB.initDB(getApplicationContext());
}
} }

View File

@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2010-12 Ciaran Gultnieks, ciaran@ciarang.com
* Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt * Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt
* Copyright (C) 2010 Ciaran Gultnieks, ciaran@ciarang.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -44,8 +44,6 @@ import android.widget.SimpleAdapter;
public class ManageRepo extends ListActivity { public class ManageRepo extends ListActivity {
private DB db = null;
private final int ADD_REPO = 1; private final int ADD_REPO = 1;
private final int REM_REPO = 2; private final int REM_REPO = 2;
@ -59,8 +57,6 @@ public class ManageRepo extends ListActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.repolist); setContentView(R.layout.repolist);
db = new DB(this);
} }
@Override @Override
@ -71,7 +67,12 @@ public class ManageRepo extends ListActivity {
} }
private void redraw() { private void redraw() {
repos = db.getRepos(); try {
DB db = DB.getDB();
repos = db.getRepos();
} finally {
DB.releaseDB();
}
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(); List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
Map<String, Object> server_line; Map<String, Object> server_line;
@ -91,22 +92,22 @@ public class ManageRepo extends ListActivity {
byte[] fingerprint = digest.digest(); byte[] fingerprint = digest.digest();
Formatter formatter = new Formatter(new StringBuilder()); Formatter formatter = new Formatter(new StringBuilder());
formatter.format("%02X", fingerprint[0]); formatter.format("%02X", fingerprint[0]);
for (int i=1; i < fingerprint.length; i++) { for (int i = 1; i < fingerprint.length; i++) {
formatter.format(i % 5 == 0 ? " %02X" : ":%02X", formatter.format(i % 5 == 0 ? " %02X" : ":%02X",
fingerprint[i]); fingerprint[i]);
} }
server_line.put("fingerprint", formatter.toString()); server_line.put("fingerprint", formatter.toString());
} catch (Exception e) { } catch (Exception e) {
Log.w("FDroid", "Unable to get certificate fingerprint.\n" Log.w("FDroid", "Unable to get certificate fingerprint.\n"
+ Log.getStackTraceString(e)); + Log.getStackTraceString(e));
} }
} }
result.add(server_line); result.add(server_line);
} }
SimpleAdapter show_out = new SimpleAdapter(this, result, SimpleAdapter show_out = new SimpleAdapter(this, result,
R.layout.repolisticons, R.layout.repolisticons, new String[] { "address", "inuse",
new String[] { "address", "inuse", "fingerprint" }, "fingerprint" }, new int[] { R.id.uri, R.id.img,
new int[] { R.id.uri, R.id.img, R.id.fingerprint }); R.id.fingerprint });
setListAdapter(show_out); setListAdapter(show_out);
} }
@ -115,7 +116,12 @@ public class ManageRepo extends ListActivity {
protected void onListItemClick(ListView l, View v, int position, long id) { protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id); super.onListItemClick(l, v, position, id);
db.changeServerStatus(repos.get(position).address); try {
DB db = DB.getDB();
db.changeServerStatus(repos.get(position).address);
} finally {
DB.releaseDB();
}
changed = true; changed = true;
redraw(); redraw();
} }
@ -150,7 +156,12 @@ public class ManageRepo extends ListActivity {
EditText uri = (EditText) alrt EditText uri = (EditText) alrt
.findViewById(R.id.edit_uri); .findViewById(R.id.edit_uri);
String uri_str = uri.getText().toString(); String uri_str = uri.getText().toString();
db.addServer(uri_str, 10, null); try {
DB db = DB.getDB();
db.addServer(uri_str, 10, null);
} finally {
DB.releaseDB();
}
changed = true; changed = true;
redraw(); redraw();
} }
@ -180,11 +191,9 @@ public class ManageRepo extends ListActivity {
public void onClick(DialogInterface dialog, public void onClick(DialogInterface dialog,
int whichButton, boolean isChecked) { int whichButton, boolean isChecked) {
if (isChecked) { if (isChecked) {
rem_lst rem_lst.addElement(repos.get(whichButton).address);
.addElement(repos.get(whichButton).address);
} else { } else {
rem_lst rem_lst.removeElement(repos.get(whichButton).address);
.removeElement(repos.get(whichButton).address);
} }
} }
}); });
@ -192,7 +201,12 @@ public class ManageRepo extends ListActivity {
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, public void onClick(DialogInterface dialog,
int whichButton) { int whichButton) {
db.removeServers(rem_lst); try {
DB db = DB.getDB();
db.removeServers(rem_lst);
} finally {
DB.releaseDB();
}
changed = true; changed = true;
redraw(); redraw();
} }
@ -217,7 +231,6 @@ public class ManageRepo extends ListActivity {
if (changed) if (changed)
ret.putExtra("update", true); ret.putExtra("update", true);
this.setResult(RESULT_OK, ret); this.setResult(RESULT_OK, ret);
db.close();
super.finish(); super.finish();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2011 Ciaran Gultnieks, ciaran@ciarang.com * Copyright (C) 2011-12 Ciaran Gultnieks, ciaran@ciarang.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -67,8 +67,13 @@ public class SearchResults extends ListActivity {
} }
private void updateView() { private void updateView() {
DB db = new DB(this); Vector<DB.App> apps;
Vector<DB.App> apps = db.getApps(null, mQuery, false, true); try {
DB db = DB.getDB();
apps = db.getApps(null, mQuery, false, true);
} finally {
DB.releaseDB();
}
TextView tv = (TextView) findViewById(R.id.description); TextView tv = (TextView) findViewById(R.id.description);
String headertext; String headertext;
if(apps.size()==0) if(apps.size()==0)
@ -86,7 +91,6 @@ public class SearchResults extends ListActivity {
} }
applist.notifyDataSetChanged(); applist.notifyDataSetChanged();
setListAdapter(applist); setListAdapter(applist);
db.close();
} }

View File

@ -89,7 +89,7 @@ public class UpdateService extends IntentService {
// Do the update... // Do the update...
DB db = null; DB db = null;
try { try {
db = new DB(getBaseContext()); db = DB.getDB();
boolean notify = prefs.getBoolean("updateNotify", false); boolean notify = prefs.getBoolean("updateNotify", false);
// Get the number of updates available before we // Get the number of updates available before we
@ -141,7 +141,7 @@ public class UpdateService extends IntentService {
} }
} finally { } finally {
if (db != null) if (db != null)
db.close(); DB.releaseDB();
} }
} }