move notification of key changes into DAOs
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 + " = ?",
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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?");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user