move notification of key changes into DAOs

This commit is contained in:
Vincent Breitmoser
2018-06-14 15:23:20 +02:00
parent 10d3ca814c
commit 8adf4a8a64
11 changed files with 146 additions and 129 deletions

View File

@@ -72,10 +72,11 @@ public class AutocryptPeerDataAccessObject {
private final SimpleContentResolverInterface queryInterface; private final SimpleContentResolverInterface queryInterface;
private final String packageName; private final String packageName;
private final DatabaseNotifyManager databaseNotifyManager;
public AutocryptPeerDataAccessObject(Context context, String packageName) { public AutocryptPeerDataAccessObject(Context context, String packageName) {
this.packageName = packageName; this.packageName = packageName;
this.databaseNotifyManager = DatabaseNotifyManager.create(context);
final ContentResolver contentResolver = context.getContentResolver(); final ContentResolver contentResolver = context.getContentResolver();
queryInterface = new SimpleContentResolverInterface() { queryInterface = new SimpleContentResolverInterface() {
@@ -102,9 +103,11 @@ public class AutocryptPeerDataAccessObject {
}; };
} }
public AutocryptPeerDataAccessObject(SimpleContentResolverInterface queryInterface, String packageName) { public AutocryptPeerDataAccessObject(SimpleContentResolverInterface queryInterface, String packageName,
DatabaseNotifyManager databaseNotifyManager) {
this.queryInterface = queryInterface; this.queryInterface = queryInterface;
this.packageName = packageName; this.packageName = packageName;
this.databaseNotifyManager = databaseNotifyManager;
} }
public Long getMasterKeyIdForAutocryptPeer(String autocryptId) { public Long getMasterKeyIdForAutocryptPeer(String autocryptId) {
@@ -190,6 +193,7 @@ public class AutocryptPeerDataAccessObject {
cv.put(ApiAutocryptPeer.IS_MUTUAL, isMutual ? 1 : 0); cv.put(ApiAutocryptPeer.IS_MUTUAL, isMutual ? 1 : 0);
queryInterface queryInterface
.update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null); .update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null);
databaseNotifyManager.notifyAutocryptUpdate(autocryptId, masterKeyId);
} }
public void updateKeyGossipFromAutocrypt(String autocryptId, Date effectiveDate, long masterKeyId) { public void updateKeyGossipFromAutocrypt(String autocryptId, Date effectiveDate, long masterKeyId) {
@@ -211,10 +215,13 @@ public class AutocryptPeerDataAccessObject {
cv.put(ApiAutocryptPeer.GOSSIP_ORIGIN, origin); cv.put(ApiAutocryptPeer.GOSSIP_ORIGIN, origin);
queryInterface queryInterface
.update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null); .update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null);
databaseNotifyManager.notifyAutocryptUpdate(autocryptId, masterKeyId);
} }
public void delete(String autocryptId) { public void delete(String autocryptId) {
Long masterKeyId = getMasterKeyIdForAutocryptPeer(autocryptId);
queryInterface.delete(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), null, null); queryInterface.delete(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), null, null);
databaseNotifyManager.notifyAutocryptDelete(autocryptId, masterKeyId);
} }
public List<AutocryptRecommendationResult> determineAutocryptRecommendations(String... autocryptIds) { public List<AutocryptRecommendationResult> determineAutocryptRecommendations(String... autocryptIds) {

View File

@@ -0,0 +1,42 @@
package org.sufficientlysecure.keychain.provider;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
public class DatabaseNotifyManager {
private ContentResolver contentResolver;
public static DatabaseNotifyManager create(Context context) {
ContentResolver contentResolver = context.getContentResolver();
return new DatabaseNotifyManager(contentResolver);
}
private DatabaseNotifyManager(ContentResolver contentResolver) {
this.contentResolver = contentResolver;
}
public void notifyKeyChange(long masterKeyId) {
Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId);
contentResolver.notifyChange(uri, null);
}
public void notifyAutocryptDelete(String autocryptId, Long masterKeyId) {
Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId);
contentResolver.notifyChange(uri, null);
}
public void notifyAutocryptUpdate(String autocryptId, long masterKeyId) {
Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId);
contentResolver.notifyChange(uri, null);
}
public void notifyKeyserverStatusChange(long masterKeyId) {
Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId);
contentResolver.notifyChange(uri, null);
}
}

View File

@@ -53,7 +53,7 @@ public class KeyRepository {
public static final int FIELD_TYPE_STRING = 4; public static final int FIELD_TYPE_STRING = 4;
public static final int FIELD_TYPE_BLOB = 5; public static final int FIELD_TYPE_BLOB = 5;
final ContentResolver mContentResolver; final ContentResolver contentResolver;
final LocalPublicKeyStorage mLocalPublicKeyStorage; final LocalPublicKeyStorage mLocalPublicKeyStorage;
OperationLog mLog; OperationLog mLog;
int mIndent; int mIndent;
@@ -71,7 +71,7 @@ public class KeyRepository {
KeyRepository(ContentResolver contentResolver, LocalPublicKeyStorage localPublicKeyStorage, KeyRepository(ContentResolver contentResolver, LocalPublicKeyStorage localPublicKeyStorage,
OperationLog log, int indent) { OperationLog log, int indent) {
mContentResolver = contentResolver; this.contentResolver = contentResolver;
mLocalPublicKeyStorage = localPublicKeyStorage; mLocalPublicKeyStorage = localPublicKeyStorage;
mIndent = indent; mIndent = indent;
mLog = log; mLog = log;
@@ -121,7 +121,7 @@ public class KeyRepository {
private HashMap<String, Object> getGenericData(Uri uri, String[] proj, int[] types, String selection) private HashMap<String, Object> getGenericData(Uri uri, String[] proj, int[] types, String selection)
throws NotFoundException { throws NotFoundException {
Cursor cursor = mContentResolver.query(uri, proj, selection, null, null); Cursor cursor = contentResolver.query(uri, proj, selection, null, null);
try { try {
HashMap<String, Object> result = new HashMap<>(proj.length); HashMap<String, Object> result = new HashMap<>(proj.length);
@@ -184,7 +184,7 @@ public class KeyRepository {
} }
public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri queryUri) throws NotFoundException { public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri queryUri) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri, Cursor cursor = contentResolver.query(queryUri,
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.VERIFIED }, null, null, null); new String[] { KeyRings.MASTER_KEY_ID, KeyRings.VERIFIED }, null, null, null);
try { try {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
@@ -208,7 +208,7 @@ public class KeyRepository {
} }
public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri queryUri) throws NotFoundException { public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri queryUri) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri, Cursor cursor = contentResolver.query(queryUri,
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.VERIFIED, KeyRings.HAS_ANY_SECRET }, null, null, null); new String[] { KeyRings.MASTER_KEY_ID, KeyRings.VERIFIED, KeyRings.HAS_ANY_SECRET }, null, null, null);
try { try {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
@@ -232,7 +232,7 @@ public class KeyRepository {
} }
public ArrayList<String> getConfirmedUserIds(long masterKeyId) throws NotFoundException { public ArrayList<String> getConfirmedUserIds(long masterKeyId) throws NotFoundException {
Cursor cursor = mContentResolver.query(UserPackets.buildUserIdsUri(masterKeyId), Cursor cursor = contentResolver.query(UserPackets.buildUserIdsUri(masterKeyId),
new String[]{UserPackets.USER_ID}, UserPackets.VERIFIED + " = " + Certs.VERIFIED_SECRET, null, null new String[]{UserPackets.USER_ID}, UserPackets.VERIFIED + " = " + Certs.VERIFIED_SECRET, null, null
); );
if (cursor == null) { if (cursor == null) {
@@ -276,12 +276,12 @@ public class KeyRepository {
} }
public ContentResolver getContentResolver() { public ContentResolver getContentResolver() {
return mContentResolver; return contentResolver;
} }
@Nullable @Nullable
Long getLastUpdateTime(long masterKeyId) { Long getLastUpdateTime(long masterKeyId) {
Cursor lastUpdatedCursor = mContentResolver.query( Cursor lastUpdatedCursor = contentResolver.query(
UpdatedKeys.CONTENT_URI, UpdatedKeys.CONTENT_URI,
new String[] { UpdatedKeys.LAST_UPDATED }, new String[] { UpdatedKeys.LAST_UPDATED },
UpdatedKeys.MASTER_KEY_ID + " = ?", UpdatedKeys.MASTER_KEY_ID + " = ?",

View File

@@ -83,32 +83,38 @@ import timber.log.Timber;
public class KeyWritableRepository extends KeyRepository { public class KeyWritableRepository extends KeyRepository {
private static final int MAX_CACHED_KEY_SIZE = 1024 * 50; private static final int MAX_CACHED_KEY_SIZE = 1024 * 50;
private final Context mContext; private final Context context;
private final LastUpdateInteractor lastUpdateInteractor; private final LastUpdateInteractor lastUpdateInteractor;
private DatabaseNotifyManager databaseNotifyManager;
public static KeyWritableRepository create(Context context) { public static KeyWritableRepository create(Context context) {
LocalPublicKeyStorage localPublicKeyStorage = LocalPublicKeyStorage.getInstance(context); LocalPublicKeyStorage localPublicKeyStorage = LocalPublicKeyStorage.getInstance(context);
LastUpdateInteractor lastUpdateInteractor = LastUpdateInteractor.create(context); LastUpdateInteractor lastUpdateInteractor = LastUpdateInteractor.create(context);
DatabaseNotifyManager databaseNotifyManager = DatabaseNotifyManager.create(context);
return new KeyWritableRepository(context, localPublicKeyStorage, lastUpdateInteractor); return new KeyWritableRepository(context, localPublicKeyStorage, lastUpdateInteractor,
databaseNotifyManager);
} }
@VisibleForTesting @VisibleForTesting
KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage, KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage,
LastUpdateInteractor lastUpdateInteractor) { LastUpdateInteractor lastUpdateInteractor, DatabaseNotifyManager databaseNotifyManager) {
this(context, localPublicKeyStorage, lastUpdateInteractor, new OperationLog(), 0); this(context, localPublicKeyStorage, lastUpdateInteractor, new OperationLog(), 0,
databaseNotifyManager);
} }
private KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage, private KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage,
LastUpdateInteractor lastUpdateInteractor, OperationLog log, int indent) { LastUpdateInteractor lastUpdateInteractor, OperationLog log, int indent,
DatabaseNotifyManager databaseNotifyManager) {
super(context.getContentResolver(), localPublicKeyStorage, log, indent); super(context.getContentResolver(), localPublicKeyStorage, log, indent);
mContext = context; this.context = context;
this.databaseNotifyManager = databaseNotifyManager;
this.lastUpdateInteractor = lastUpdateInteractor; this.lastUpdateInteractor = lastUpdateInteractor;
} }
private LongSparseArray<CanonicalizedPublicKey> getTrustedMasterKeys() { private LongSparseArray<CanonicalizedPublicKey> getTrustedMasterKeys() {
Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { Cursor cursor = contentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] {
KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID,
// we pick from cache only information that is not easily available from keyrings // we pick from cache only information that is not easily available from keyrings
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED
@@ -530,7 +536,7 @@ public class KeyWritableRepository extends KeyRepository {
try { try {
// delete old version of this keyRing (from database only!), which also deletes all keys and userIds on cascade // delete old version of this keyRing (from database only!), which also deletes all keys and userIds on cascade
int deleted = mContentResolver.delete( int deleted = contentResolver.delete(
KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null); KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null);
if (deleted > 0) { if (deleted > 0) {
log(LogType.MSG_IP_DELETE_OLD_OK); log(LogType.MSG_IP_DELETE_OLD_OK);
@@ -540,7 +546,8 @@ public class KeyWritableRepository extends KeyRepository {
} }
log(LogType.MSG_IP_APPLY_BATCH); log(LogType.MSG_IP_APPLY_BATCH);
mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); contentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);
databaseNotifyManager.notifyKeyChange(masterKeyId);
log(LogType.MSG_IP_SUCCESS); log(LogType.MSG_IP_SUCCESS);
return result; return result;
@@ -603,7 +610,7 @@ public class KeyWritableRepository extends KeyRepository {
// insert new version of this keyRing // insert new version of this keyRing
Uri uri = KeyRingData.buildSecretKeyRingUri(masterKeyId); Uri uri = KeyRingData.buildSecretKeyRingUri(masterKeyId);
return mContentResolver.insert(uri, values); return contentResolver.insert(uri, values);
} }
public boolean deleteKeyRing(long masterKeyId) { public boolean deleteKeyRing(long masterKeyId) {
@@ -613,8 +620,11 @@ public class KeyWritableRepository extends KeyRepository {
Timber.e(e, "Could not delete file!"); Timber.e(e, "Could not delete file!");
return false; return false;
} }
mContentResolver.delete(ApiAutocryptPeer.buildByMasterKeyId(masterKeyId),null, null); contentResolver.delete(ApiAutocryptPeer.buildByMasterKeyId(masterKeyId),null, null);
int deletedRows = mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null); int deletedRows = contentResolver.delete(KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null);
databaseNotifyManager.notifyKeyChange(masterKeyId);
return deletedRows > 0; return deletedRows > 0;
} }
@@ -690,7 +700,7 @@ public class KeyWritableRepository extends KeyRepository {
// first, mark all keys as not available // first, mark all keys as not available
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(Keys.HAS_SECRET, SecretKeyType.GNU_DUMMY.getNum()); values.put(Keys.HAS_SECRET, SecretKeyType.GNU_DUMMY.getNum());
mContentResolver.update(uri, values, null, null); contentResolver.update(uri, values, null, null);
// then, mark exactly the keys we have available // then, mark exactly the keys we have available
log(LogType.MSG_IS_IMPORTING_SUBKEYS); log(LogType.MSG_IS_IMPORTING_SUBKEYS);
@@ -699,7 +709,7 @@ public class KeyWritableRepository extends KeyRepository {
long id = sub.getKeyId(); long id = sub.getKeyId();
SecretKeyType mode = sub.getSecretKeyTypeSuperExpensive(); SecretKeyType mode = sub.getSecretKeyTypeSuperExpensive();
values.put(Keys.HAS_SECRET, mode.getNum()); values.put(Keys.HAS_SECRET, mode.getNum());
int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", int upd = contentResolver.update(uri, values, Keys.KEY_ID + " = ?",
new String[]{Long.toString(id)}); new String[]{Long.toString(id)});
if (upd == 1) { if (upd == 1) {
switch (mode) { switch (mode) {
@@ -1030,11 +1040,11 @@ public class KeyWritableRepository extends KeyRepository {
log.add(LogType.MSG_TRUST, 0); log.add(LogType.MSG_TRUST, 0);
Cursor cursor; Cursor cursor;
Preferences preferences = Preferences.getPreferences(mContext); Preferences preferences = Preferences.getPreferences(context);
boolean isTrustDbInitialized = preferences.isKeySignaturesTableInitialized(); boolean isTrustDbInitialized = preferences.isKeySignaturesTableInitialized();
if (!isTrustDbInitialized) { if (!isTrustDbInitialized) {
log.add(LogType.MSG_TRUST_INITIALIZE, 1); log.add(LogType.MSG_TRUST_INITIALIZE, 1);
cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), cursor = contentResolver.query(KeyRings.buildUnifiedKeyRingsUri(),
new String[] { KeyRings.MASTER_KEY_ID }, null, null, null); new String[] { KeyRings.MASTER_KEY_ID }, null, null, null);
} else { } else {
String[] signerMasterKeyIdStrings = new String[signerMasterKeyIds.size()]; String[] signerMasterKeyIdStrings = new String[signerMasterKeyIds.size()];
@@ -1044,7 +1054,7 @@ public class KeyWritableRepository extends KeyRepository {
signerMasterKeyIdStrings[i++] = Long.toString(masterKeyId); signerMasterKeyIdStrings[i++] = Long.toString(masterKeyId);
} }
cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsFilterBySigner(), cursor = contentResolver.query(KeyRings.buildUnifiedKeyRingsFilterBySigner(),
new String[] { KeyRings.MASTER_KEY_ID }, null, signerMasterKeyIdStrings, null); new String[] { KeyRings.MASTER_KEY_ID }, null, signerMasterKeyIdStrings, null);
} }

View File

@@ -50,6 +50,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.DatabaseUtil;
import timber.log.Timber; import timber.log.Timber;
import static android.database.DatabaseUtils.dumpCursorToString; import static android.database.DatabaseUtils.dumpCursorToString;
@@ -101,7 +102,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
String authority = KeychainContract.CONTENT_AUTHORITY; String authority = KeychainContract.CONTENT_AUTHORITY;
/** /*
* list key_rings * list key_rings
* *
* <pre> * <pre>
@@ -124,7 +125,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
+ "/" + KeychainContract.PATH_USER_IDS, + "/" + KeychainContract.PATH_USER_IDS,
KEY_RINGS_USER_IDS); KEY_RINGS_USER_IDS);
/** /*
* find by criteria other than master key id * find by criteria other than master key id
* *
* key_rings/find/email/_ * key_rings/find/email/_
@@ -144,7 +145,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
+ KeychainContract.PATH_FILTER + "/" + KeychainContract.PATH_BY_SIGNER, + KeychainContract.PATH_FILTER + "/" + KeychainContract.PATH_BY_SIGNER,
KEY_RINGS_FILTER_BY_SIGNER); KEY_RINGS_FILTER_BY_SIGNER);
/** /*
* list key_ring specifics * list key_ring specifics
* *
* <pre> * <pre>
@@ -189,7 +190,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
+ KeychainContract.PATH_CERTS + "/*/*", + KeychainContract.PATH_CERTS + "/*/*",
KEY_RING_CERTS_SPECIFIC); KEY_RING_CERTS_SPECIFIC);
/** /*
* API apps * API apps
* *
* <pre> * <pre>
@@ -205,7 +206,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ KeychainContract.PATH_ALLOWED_KEYS, API_ALLOWED_KEYS); + KeychainContract.PATH_ALLOWED_KEYS, API_ALLOWED_KEYS);
/** /*
* Trust Identity access * Trust Identity access
* *
* <pre> * <pre>
@@ -221,7 +222,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
KeychainContract.PATH_BY_PACKAGE_NAME + "/*/*", AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID); KeychainContract.PATH_BY_PACKAGE_NAME + "/*/*", AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID);
/** /*
* to access table containing last updated dates of keys * to access table containing last updated dates of keys
*/ */
matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS, UPDATED_KEYS); matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS, UPDATED_KEYS);
@@ -835,7 +836,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
SQLiteDatabase db = getDb().getReadableDatabase(); SQLiteDatabase db = getDb().getReadableDatabase();
Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy); Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, null, orderBy);
if (cursor != null) { if (cursor != null) {
// Tell the cursor what uri to watch, so it knows when its source data changes // Tell the cursor what uri to watch, so it knows when its source data changes
cursor.setNotificationUri(getContext().getContentResolver(), uri); cursor.setNotificationUri(getContext().getContentResolver(), uri);
@@ -849,27 +850,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
if (Constants.DEBUG && Constants.DEBUG_EXPLAIN_QUERIES) { if (Constants.DEBUG && Constants.DEBUG_EXPLAIN_QUERIES) {
String rawQuery = qb.buildQuery(projection, selection, groupBy, having, orderBy, null); String rawQuery = qb.buildQuery(projection, selection, groupBy, having, orderBy, null);
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + rawQuery, selectionArgs); DatabaseUtil.explainQuery(db, rawQuery);
// this is a debugging feature, we can be a little careless
explainCursor.moveToFirst();
StringBuilder line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getColumnName(i)).append(", ");
}
Timber.d(line.toString());
while (!explainCursor.isAfterLast()) {
line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getString(i)).append(", ");
}
Timber.d(line.toString());
explainCursor.moveToNext();
}
explainCursor.close();
} }
return cursor; return cursor;
@@ -966,9 +947,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
rowUri = uri; rowUri = uri;
} }
// notify of changes in db
getContext().getContentResolver().notifyChange(uri, null);
} catch (SQLiteConstraintException e) { } catch (SQLiteConstraintException e) {
Timber.d(e, "Constraint exception on insert! Entry already existing?"); Timber.d(e, "Constraint exception on insert! Entry already existing?");
} }
@@ -1003,7 +981,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
} }
// corresponding keys and userIds are deleted by ON DELETE CASCADE // corresponding keys and userIds are deleted by ON DELETE CASCADE
count = db.delete(Tables.KEY_RINGS_PUBLIC, selection, selectionArgs); count = db.delete(Tables.KEY_RINGS_PUBLIC, selection, selectionArgs);
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1)), null);
break; break;
} }
case KEY_RING_SECRET: { case KEY_RING_SECRET: {
@@ -1013,7 +990,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
selection += " AND (" + additionalSelection + ")"; selection += " AND (" + additionalSelection + ")";
} }
count = db.delete(Tables.KEY_RINGS_SECRET, selection, selectionArgs); count = db.delete(Tables.KEY_RINGS_SECRET, selection, selectionArgs);
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1)), null);
break; break;
} }
@@ -1024,20 +1000,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
String selection = ApiAutocryptPeer.PACKAGE_NAME + " = ? AND " + ApiAutocryptPeer.IDENTIFIER + " = ?"; String selection = ApiAutocryptPeer.PACKAGE_NAME + " = ? AND " + ApiAutocryptPeer.IDENTIFIER + " = ?";
selectionArgs = new String[] { packageName, autocryptPeer }; selectionArgs = new String[] { packageName, autocryptPeer };
Cursor cursor = db.query(Tables.API_AUTOCRYPT_PEERS, new String[] { ApiAutocryptPeer.MASTER_KEY_ID },
selection, selectionArgs, null, null, null);
Long masterKeyId = null;
if (cursor != null && cursor.moveToNext() && !cursor.isNull(0)) {
masterKeyId = cursor.getLong(0);
}
count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs); count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs);
if (masterKeyId != null) {
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(masterKeyId), null);
}
contentResolver.notifyChange(
ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptPeer), null);
break; break;
} }
@@ -1047,7 +1010,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
selection += " AND (" + additionalSelection + ")"; selection += " AND (" + additionalSelection + ")";
} }
count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs); count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs);
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(uri.getLastPathSegment()), null);
break; break;
case API_APPS_BY_PACKAGE_NAME: { case API_APPS_BY_PACKAGE_NAME: {
@@ -1076,7 +1038,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
Timber.v("update(uri=" + uri + ", values=" + values.toString() + ")"); Timber.v("update(uri=" + uri + ", values=" + values.toString() + ")");
final SQLiteDatabase db = getDb().getWritableDatabase(); final SQLiteDatabase db = getDb().getWritableDatabase();
ContentResolver contentResolver = getContext().getContentResolver();
int count = 0; int count = 0;
try { try {
@@ -1127,25 +1088,12 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
db.insertOrThrow(Tables.API_AUTOCRYPT_PEERS, null, values); db.insertOrThrow(Tables.API_AUTOCRYPT_PEERS, null, values);
} }
Long masterKeyId = values.getAsLong(ApiAutocryptPeer.MASTER_KEY_ID);
if (masterKeyId != null) {
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(masterKeyId), null);
}
Long gossipMasterKeyId = values.getAsLong(ApiAutocryptPeer.GOSSIP_MASTER_KEY_ID);
if (gossipMasterKeyId != null && !gossipMasterKeyId.equals(masterKeyId)) {
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(gossipMasterKeyId), null);
}
break; break;
} }
default: { default: {
throw new UnsupportedOperationException("Unknown uri: " + uri); throw new UnsupportedOperationException("Unknown uri: " + uri);
} }
} }
// notify of changes in db
contentResolver.notifyChange(uri, null);
} catch (SQLiteConstraintException e) { } catch (SQLiteConstraintException e) {
Timber.d(e, "Constraint exception on update! Entry already existing?"); Timber.d(e, "Constraint exception on update! Entry already existing?");
} }

View File

@@ -15,14 +15,16 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
public class LastUpdateInteractor { public class LastUpdateInteractor {
private final ContentResolver contentResolver; private final ContentResolver contentResolver;
private final DatabaseNotifyManager databaseNotifyManager;
public static LastUpdateInteractor create(Context context) { public static LastUpdateInteractor create(Context context) {
return new LastUpdateInteractor(context.getContentResolver()); return new LastUpdateInteractor(context.getContentResolver(), DatabaseNotifyManager.create(context));
} }
private LastUpdateInteractor(ContentResolver contentResolver) { private LastUpdateInteractor(ContentResolver contentResolver, DatabaseNotifyManager databaseNotifyManager) {
this.contentResolver = contentResolver; this.contentResolver = contentResolver;
this.databaseNotifyManager = databaseNotifyManager;
} }
@Nullable @Nullable
@@ -69,6 +71,8 @@ public class LastUpdateInteractor {
// this will actually update/replace, doing the right thing™ for seenOnKeyservers value // this will actually update/replace, doing the right thing™ for seenOnKeyservers value
// see `KeychainProvider.insert()` // see `KeychainProvider.insert()`
return contentResolver.insert(UpdatedKeys.CONTENT_URI, values); Uri insert = contentResolver.insert(UpdatedKeys.CONTENT_URI, values);
databaseNotifyManager.notifyKeyserverStatusChange(masterKeyId);
return insert;
} }
} }

View File

@@ -25,6 +25,7 @@ import java.util.List;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher; import android.content.UriMatcher;
import android.database.Cursor; import android.database.Cursor;
import android.database.DatabaseUtils; import android.database.DatabaseUtils;
@@ -40,6 +41,7 @@ import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptRecommendationResult; import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptRecommendationResult;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptState; import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptState;
import org.sufficientlysecure.keychain.provider.DatabaseNotifyManager;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -72,6 +74,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
private UriMatcher uriMatcher; private UriMatcher uriMatcher;
private ApiPermissionHelper apiPermissionHelper; private ApiPermissionHelper apiPermissionHelper;
private KeychainProvider internalKeychainProvider; private KeychainProvider internalKeychainProvider;
private DatabaseNotifyManager databaseNotifyManager;
/** /**
@@ -103,9 +106,15 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
public boolean onCreate() { public boolean onCreate() {
uriMatcher = buildUriMatcher(); uriMatcher = buildUriMatcher();
Context context = getContext();
if (context == null) {
throw new NullPointerException("Context can't be null during onCreate!");
}
internalKeychainProvider = new KeychainProvider(); internalKeychainProvider = new KeychainProvider();
internalKeychainProvider.attachInfo(getContext(), null); internalKeychainProvider.attachInfo(context, null);
apiPermissionHelper = new ApiPermissionHelper(getContext(), new ApiDataAccessObject(internalKeychainProvider)); apiPermissionHelper = new ApiPermissionHelper(context, new ApiDataAccessObject(internalKeychainProvider));
databaseNotifyManager = DatabaseNotifyManager.create(context);
return true; return true;
} }
@@ -270,7 +279,8 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
} }
if (!isWildcardSelector && queriesAutocryptResult) { if (!isWildcardSelector && queriesAutocryptResult) {
AutocryptPeerDataAccessObject autocryptPeerDao = AutocryptPeerDataAccessObject autocryptPeerDao =
new AutocryptPeerDataAccessObject(internalKeychainProvider, callingPackageName); new AutocryptPeerDataAccessObject(internalKeychainProvider, callingPackageName,
databaseNotifyManager);
fillTempTableWithAutocryptRecommendations(db, autocryptPeerDao, selectionArgs); fillTempTableWithAutocryptRecommendations(db, autocryptPeerDao, selectionArgs);
} }

View File

@@ -17,8 +17,13 @@
package org.sufficientlysecure.keychain.util; package org.sufficientlysecure.keychain.util;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils; import android.text.TextUtils;
import timber.log.Timber;
/** /**
* Shamelessly copied from android.database.DatabaseUtils * Shamelessly copied from android.database.DatabaseUtils
*/ */
@@ -50,4 +55,28 @@ public class DatabaseUtil {
System.arraycopy(newValues, 0, result, originalValues.length, newValues.length); System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
return result; return result;
} }
public static void explainQuery(SQLiteDatabase db, String sql) {
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + sql, new String[0]);
// this is a debugging feature, we can be a little careless
explainCursor.moveToFirst();
StringBuilder line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getColumnName(i)).append(", ");
}
Timber.d(line.toString());
while (!explainCursor.isAfterLast()) {
line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getString(i)).append(", ");
}
Timber.d(line.toString());
explainCursor.moveToNext();
}
explainCursor.close();
}
} }

View File

@@ -1,34 +0,0 @@
package org.sufficientlysecure.keychain.util;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import timber.log.Timber;
public class DatabaseUtils {
public static void explainQuery(SQLiteDatabase db, String sql) {
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + sql, new String[0]);
// this is a debugging feature, we can be a little careless
explainCursor.moveToFirst();
StringBuilder line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getColumnName(i)).append(", ");
}
Timber.d(line.toString());
while (!explainCursor.isAfterLast()) {
line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getString(i)).append(", ");
}
Timber.d(line.toString());
explainCursor.moveToNext();
}
explainCursor.close();
}
}

View File

@@ -244,7 +244,8 @@ public class InteropTest {
KeyWritableRepository helper = new KeyWritableRepository(RuntimeEnvironment.application, KeyWritableRepository helper = new KeyWritableRepository(RuntimeEnvironment.application,
LocalPublicKeyStorage.getInstance(RuntimeEnvironment.application), LocalPublicKeyStorage.getInstance(RuntimeEnvironment.application),
LastUpdateInteractor.create(RuntimeEnvironment.application)) { LastUpdateInteractor.create(RuntimeEnvironment.application),
DatabaseNotifyManager.create(RuntimeEnvironment.application)) {
@Override @Override
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException { public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException {