use SQLDelight, remove ApiApps access from KeychainProvider
This commit is contained in:
@@ -20,179 +20,97 @@ package org.sufficientlysecure.keychain.provider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.arch.persistence.db.SupportSQLiteDatabase;
|
||||
import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
||||
import org.sufficientlysecure.keychain.remote.AppSettings;
|
||||
import com.squareup.sqldelight.SqlDelightQuery;
|
||||
import org.sufficientlysecure.keychain.ApiAllowedKeysModel.InsertAllowedKey;
|
||||
import org.sufficientlysecure.keychain.ApiAppsModel;
|
||||
import org.sufficientlysecure.keychain.ApiAppsModel.DeleteByPackageName;
|
||||
import org.sufficientlysecure.keychain.ApiAppsModel.InsertApiApp;
|
||||
import org.sufficientlysecure.keychain.model.ApiAllowedKey;
|
||||
import org.sufficientlysecure.keychain.model.ApiApp;
|
||||
|
||||
|
||||
public class ApiDataAccessObject {
|
||||
|
||||
private final SimpleContentResolverInterface mQueryInterface;
|
||||
private final SupportSQLiteDatabase db;
|
||||
|
||||
public ApiDataAccessObject(Context context) {
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
mQueryInterface = new SimpleContentResolverInterface() {
|
||||
@Override
|
||||
public Cursor query(Uri contentUri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
return contentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri contentUri, ContentValues values) {
|
||||
return contentResolver.insert(contentUri, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri contentUri, ContentValues values, String where, String[] selectionArgs) {
|
||||
return contentResolver.update(contentUri, values, where, selectionArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri contentUri, String where, String[] selectionArgs) {
|
||||
return contentResolver.delete(contentUri, where, selectionArgs);
|
||||
}
|
||||
};
|
||||
KeychainDatabase keychainDatabase = new KeychainDatabase(context);
|
||||
db = keychainDatabase.getWritableDatabase();
|
||||
}
|
||||
|
||||
public ApiDataAccessObject(SimpleContentResolverInterface queryInterface) {
|
||||
mQueryInterface = queryInterface;
|
||||
}
|
||||
|
||||
public ArrayList<String> getRegisteredApiApps() {
|
||||
Cursor cursor = mQueryInterface.query(ApiApps.CONTENT_URI, null, null, null, null);
|
||||
|
||||
ArrayList<String> packageNames = new ArrayList<>();
|
||||
try {
|
||||
if (cursor != null) {
|
||||
int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
packageNames.add(cursor.getString(packageNameCol));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
public ApiApp getApiApp(String packageName) {
|
||||
try (Cursor cursor = db.query(ApiApp.FACTORY.selectByPackageName(packageName))) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return ApiApp.FACTORY.selectByPackageNameMapper().map(cursor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return packageNames;
|
||||
}
|
||||
|
||||
private ContentValues contentValueForApiApps(AppSettings appSettings) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
|
||||
values.put(ApiApps.PACKAGE_CERTIFICATE, appSettings.getPackageCertificate());
|
||||
return values;
|
||||
}
|
||||
|
||||
public void insertApiApp(AppSettings appSettings) {
|
||||
mQueryInterface.insert(ApiApps.CONTENT_URI, contentValueForApiApps(appSettings));
|
||||
}
|
||||
|
||||
public void deleteApiApp(String packageName) {
|
||||
mQueryInterface.delete(ApiApps.buildByPackageNameUri(packageName), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be an uri pointing to an account
|
||||
*/
|
||||
public AppSettings getApiAppSettings(Uri uri) {
|
||||
AppSettings settings = null;
|
||||
|
||||
Cursor cursor = mQueryInterface.query(uri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
settings = new AppSettings();
|
||||
settings.setPackageName(cursor.getString(
|
||||
cursor.getColumnIndex(ApiApps.PACKAGE_NAME)));
|
||||
settings.setPackageCertificate(cursor.getBlob(
|
||||
cursor.getColumnIndex(ApiApps.PACKAGE_CERTIFICATE)));
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public HashSet<Long> getAllowedKeyIdsForApp(Uri uri) {
|
||||
HashSet<Long> keyIds = new HashSet<>();
|
||||
|
||||
Cursor cursor = mQueryInterface.query(uri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null) {
|
||||
int keyIdColumn = cursor.getColumnIndex(ApiAllowedKeys.KEY_ID);
|
||||
while (cursor.moveToNext()) {
|
||||
keyIds.add(cursor.getLong(keyIdColumn));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
return keyIds;
|
||||
}
|
||||
|
||||
public void saveAllowedKeyIdsForApp(Uri uri, Set<Long> allowedKeyIds)
|
||||
throws RemoteException, OperationApplicationException {
|
||||
// wipe whole table of allowed keys for this account
|
||||
mQueryInterface.delete(uri, null, null);
|
||||
|
||||
// re-insert allowed key ids
|
||||
for (Long keyId : allowedKeyIds) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ApiAllowedKeys.KEY_ID, keyId);
|
||||
mQueryInterface.insert(uri, values);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAllowedKeyIdForApp(Uri uri, long allowedKeyId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ApiAllowedKeys.KEY_ID, allowedKeyId);
|
||||
mQueryInterface.insert(uri, values);
|
||||
}
|
||||
|
||||
public void addAllowedKeyIdForApp(String packageName, long allowedKeyId) {
|
||||
Uri uri = ApiAllowedKeys.buildBaseUri(packageName);
|
||||
addAllowedKeyIdForApp(uri, allowedKeyId);
|
||||
}
|
||||
|
||||
public byte[] getApiAppCertificate(String packageName) {
|
||||
Uri queryUri = ApiApps.buildByPackageNameUri(packageName);
|
||||
Cursor cursor = db.query(ApiApp.FACTORY.getCertificate(packageName));
|
||||
return ApiApp.FACTORY.getCertificateMapper().map(cursor);
|
||||
}
|
||||
|
||||
String[] projection = new String[]{ApiApps.PACKAGE_CERTIFICATE};
|
||||
public void insertApiApp(ApiApp apiApp) {
|
||||
InsertApiApp statement = new ApiAppsModel.InsertApiApp(db);
|
||||
statement.bind(apiApp.package_name(), apiApp.package_signature());
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
Cursor cursor = mQueryInterface.query(queryUri, projection, null, null, null);
|
||||
try {
|
||||
byte[] signature = null;
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int signatureCol = 0;
|
||||
public void deleteApiApp(String packageName) {
|
||||
DeleteByPackageName deleteByPackageName = new DeleteByPackageName(db);
|
||||
deleteByPackageName.bind(packageName);
|
||||
deleteByPackageName.executeUpdateDelete();
|
||||
}
|
||||
|
||||
signature = cursor.getBlob(signatureCol);
|
||||
}
|
||||
return signature;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
public HashSet<Long> getAllowedKeyIdsForApp(String packageName) {
|
||||
SqlDelightQuery allowedKeys = ApiAllowedKey.FACTORY.getAllowedKeys(packageName);
|
||||
HashSet<Long> keyIds = new HashSet<>();
|
||||
try (Cursor cursor = db.query(allowedKeys)) {
|
||||
while (cursor.moveToNext()) {
|
||||
long allowedKeyId = ApiAllowedKey.FACTORY.getAllowedKeysMapper().map(cursor);
|
||||
keyIds.add(allowedKeyId);
|
||||
}
|
||||
}
|
||||
return keyIds;
|
||||
}
|
||||
|
||||
public void saveAllowedKeyIdsForApp(String packageName, Set<Long> allowedKeyIds) {
|
||||
ApiAllowedKey.DeleteByPackageName deleteByPackageName = new ApiAllowedKey.DeleteByPackageName(db);
|
||||
deleteByPackageName.bind(packageName);
|
||||
deleteByPackageName.executeUpdateDelete();
|
||||
|
||||
InsertAllowedKey statement = new InsertAllowedKey(db);
|
||||
for (Long keyId : allowedKeyIds) {
|
||||
statement.bind(packageName, keyId);
|
||||
statement.execute();
|
||||
}
|
||||
}
|
||||
|
||||
public void addAllowedKeyIdForApp(String packageName, long allowedKeyId) {
|
||||
InsertAllowedKey statement = new InsertAllowedKey(db);
|
||||
statement.bind(packageName, allowedKeyId);
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
public List<ApiApp> getAllApiApps() {
|
||||
SqlDelightQuery query = ApiApp.FACTORY.selectAll();
|
||||
|
||||
ArrayList<ApiApp> result = new ArrayList<>();
|
||||
try (Cursor cursor = db.query(query)) {
|
||||
while (cursor.moveToNext()) {
|
||||
ApiApp apiApp = ApiApp.FACTORY.selectAllMapper().map(cursor);
|
||||
result.add(apiApp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ public class KeyRepository {
|
||||
try {
|
||||
return localSecretKeyStorage.readSecretKey(masterKeyId);
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "Error reading public key from storage!");
|
||||
Timber.e(e, "Error reading secret key from storage!");
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,9 +141,6 @@ public class KeychainContract {
|
||||
public static final String PATH_KEYS = "keys";
|
||||
public static final String PATH_CERTS = "certs";
|
||||
|
||||
public static final String BASE_API_APPS = "api_apps";
|
||||
public static final String PATH_ALLOWED_KEYS = "allowed_keys";
|
||||
|
||||
public static final String PATH_BY_PACKAGE_NAME = "by_package_name";
|
||||
public static final String PATH_BY_KEY_ID = "by_key_id";
|
||||
|
||||
@@ -323,43 +320,6 @@ public class KeychainContract {
|
||||
|
||||
}
|
||||
|
||||
public static class ApiApps implements ApiAppsColumns, BaseColumns {
|
||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||
.appendPath(BASE_API_APPS).build();
|
||||
|
||||
/**
|
||||
* Use if multiple items get returned
|
||||
*/
|
||||
public static final String CONTENT_TYPE
|
||||
= "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.api_apps";
|
||||
|
||||
/**
|
||||
* Use if a single item is returned
|
||||
*/
|
||||
public static final String CONTENT_ITEM_TYPE
|
||||
= "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.api_apps";
|
||||
|
||||
public static Uri buildByPackageNameUri(String packageName) {
|
||||
return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApiAllowedKeys implements ApiAppsAllowedKeysColumns, BaseColumns {
|
||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||
.appendPath(BASE_API_APPS).build();
|
||||
|
||||
/**
|
||||
* Use if multiple items get returned
|
||||
*/
|
||||
public static final String CONTENT_TYPE
|
||||
= "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.api_apps.allowed_keys";
|
||||
|
||||
public static Uri buildBaseUri(String packageName) {
|
||||
return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ALLOWED_KEYS)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApiAutocryptPeer implements ApiAutocryptPeerColumns, BaseColumns {
|
||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||
.appendPath(BASE_AUTOCRYPT_PEERS).build();
|
||||
|
||||
@@ -23,16 +23,18 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteDatabase;
|
||||
import android.arch.persistence.db.SupportSQLiteOpenHelper;
|
||||
import android.arch.persistence.db.SupportSQLiteOpenHelper.Callback;
|
||||
import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration;
|
||||
import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAllowedKeysColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeerColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
|
||||
@@ -53,10 +55,11 @@ import timber.log.Timber;
|
||||
* - TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
|
||||
* - BLOB. The value is a blob of data, stored exactly as it was input.
|
||||
*/
|
||||
public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
public class KeychainDatabase {
|
||||
private static final String DATABASE_NAME = "openkeychain.db";
|
||||
private static final int DATABASE_VERSION = 26;
|
||||
private Context mContext;
|
||||
private final SupportSQLiteOpenHelper supportSQLiteOpenHelper;
|
||||
private Context context;
|
||||
|
||||
public interface Tables {
|
||||
String KEY_RINGS_PUBLIC = "keyrings_public";
|
||||
@@ -65,18 +68,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
String KEY_SIGNATURES = "key_signatures";
|
||||
String USER_PACKETS = "user_packets";
|
||||
String CERTS = "certs";
|
||||
String API_APPS = "api_apps";
|
||||
String API_ALLOWED_KEYS = "api_allowed_keys";
|
||||
String OVERRIDDEN_WARNINGS = "overridden_warnings";
|
||||
String API_AUTOCRYPT_PEERS = "api_autocrypt_peers";
|
||||
}
|
||||
|
||||
private static final String CREATE_KEYRINGS_PUBLIC =
|
||||
"CREATE TABLE IF NOT EXISTS keyrings_public ("
|
||||
+ KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY,"
|
||||
+ KeyRingsColumns.KEY_RING_DATA + " BLOB"
|
||||
+ ")";
|
||||
|
||||
private static final String CREATE_KEYS =
|
||||
"CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " ("
|
||||
+ KeysColumns.MASTER_KEY_ID + " INTEGER, "
|
||||
@@ -143,15 +139,6 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
+ Tables.USER_PACKETS + "(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + ") ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
private static final String CREATE_UPDATE_KEYS =
|
||||
"CREATE TABLE IF NOT EXISTS " + Tables.UPDATED_KEYS + " ("
|
||||
+ UpdatedKeysColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY, "
|
||||
+ UpdatedKeysColumns.LAST_UPDATED + " INTEGER, "
|
||||
+ UpdatedKeysColumns.SEEN_ON_KEYSERVERS + " INTEGER, "
|
||||
+ "FOREIGN KEY(" + UpdatedKeysColumns.MASTER_KEY_ID + ") REFERENCES "
|
||||
+ Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
private static final String CREATE_KEY_SIGNATURES =
|
||||
"CREATE TABLE IF NOT EXISTS " + Tables.KEY_SIGNATURES + " ("
|
||||
+ KeySignaturesColumns.MASTER_KEY_ID + " INTEGER NOT NULL, "
|
||||
@@ -175,16 +162,9 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
+ "PRIMARY KEY(" + ApiAutocryptPeerColumns.PACKAGE_NAME + ", "
|
||||
+ ApiAutocryptPeerColumns.IDENTIFIER + "), "
|
||||
+ "FOREIGN KEY(" + ApiAutocryptPeerColumns.PACKAGE_NAME + ") REFERENCES "
|
||||
+ Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE"
|
||||
+ "api_apps (package_signature) ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
private static final String CREATE_API_APPS =
|
||||
"CREATE TABLE IF NOT EXISTS " + Tables.API_APPS + " ("
|
||||
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ ApiAppsColumns.PACKAGE_NAME + " TEXT NOT NULL UNIQUE, "
|
||||
+ ApiAppsColumns.PACKAGE_CERTIFICATE + " BLOB"
|
||||
+ ")";
|
||||
|
||||
private static final String CREATE_API_APPS_ALLOWED_KEYS =
|
||||
"CREATE TABLE IF NOT EXISTS " + Tables.API_ALLOWED_KEYS + " ("
|
||||
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
@@ -194,7 +174,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
+ "UNIQUE(" + ApiAppsAllowedKeysColumns.KEY_ID + ", "
|
||||
+ ApiAppsAllowedKeysColumns.PACKAGE_NAME + "), "
|
||||
+ "FOREIGN KEY(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") REFERENCES "
|
||||
+ Tables.API_APPS + "(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE"
|
||||
+ "api_apps (" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
private static final String CREATE_OVERRIDDEN_WARNINGS =
|
||||
@@ -204,12 +184,46 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
+ ")";
|
||||
|
||||
public KeychainDatabase(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
this.context = context;
|
||||
supportSQLiteOpenHelper =
|
||||
new FrameworkSQLiteOpenHelperFactory()
|
||||
.create(Configuration.builder(context).name(DATABASE_NAME).callback(
|
||||
new Callback(DATABASE_VERSION) {
|
||||
@Override
|
||||
public void onCreate(SupportSQLiteDatabase db) {
|
||||
KeychainDatabase.this.onCreate(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
KeychainDatabase.this.onUpgrade(db, oldVersion, newVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
KeychainDatabase.this.onDowngrade(db, oldVersion, newVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(SupportSQLiteDatabase db) {
|
||||
super.onOpen(db);
|
||||
if (!db.isReadOnly()) {
|
||||
// Enable foreign key constraints
|
||||
db.execSQL("PRAGMA foreign_keys=ON;");
|
||||
}
|
||||
}
|
||||
}).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
public SupportSQLiteDatabase getReadableDatabase() {
|
||||
return supportSQLiteOpenHelper.getReadableDatabase();
|
||||
}
|
||||
|
||||
public SupportSQLiteDatabase getWritableDatabase() {
|
||||
return supportSQLiteOpenHelper.getWritableDatabase();
|
||||
}
|
||||
|
||||
private void onCreate(SupportSQLiteDatabase db) {
|
||||
Timber.w("Creating database...");
|
||||
|
||||
db.execSQL(CREATE_KEYRINGS_PUBLIC);
|
||||
@@ -218,7 +232,6 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
db.execSQL(CREATE_CERTS);
|
||||
db.execSQL(CREATE_UPDATE_KEYS);
|
||||
db.execSQL(CREATE_KEY_SIGNATURES);
|
||||
db.execSQL(CREATE_API_APPS);
|
||||
db.execSQL(CREATE_API_APPS_ALLOWED_KEYS);
|
||||
db.execSQL(CREATE_OVERRIDDEN_WARNINGS);
|
||||
db.execSQL(CREATE_API_AUTOCRYPT_PEERS);
|
||||
@@ -231,20 +244,10 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
db.execSQL("CREATE INDEX uids_by_email ON user_packets ("
|
||||
+ UserPacketsColumns.EMAIL + ");");
|
||||
|
||||
Preferences.getPreferences(mContext).setKeySignaturesTableInitialized();
|
||||
Preferences.getPreferences(context).setKeySignaturesTableInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(SQLiteDatabase db) {
|
||||
super.onOpen(db);
|
||||
if (!db.isReadOnly()) {
|
||||
// Enable foreign key constraints
|
||||
db.execSQL("PRAGMA foreign_keys=ON;");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
private void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Timber.d("Upgrading db from " + oldVersion + " to " + newVersion);
|
||||
|
||||
switch (oldVersion) {
|
||||
@@ -459,9 +462,9 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateSecretKeysFromDbToLocalStorage(SQLiteDatabase db) throws IOException {
|
||||
LocalSecretKeyStorage localSecretKeyStorage = LocalSecretKeyStorage.getInstance(mContext);
|
||||
Cursor cursor = db.rawQuery("SELECT master_key_id, key_ring_data FROM keyrings_secret", null);
|
||||
private void migrateSecretKeysFromDbToLocalStorage(SupportSQLiteDatabase db) throws IOException {
|
||||
LocalSecretKeyStorage localSecretKeyStorage = LocalSecretKeyStorage.getInstance(context);
|
||||
Cursor cursor = db.query("SELECT master_key_id, key_ring_data FROM keyrings_secret");
|
||||
while (cursor.moveToNext()) {
|
||||
long masterKeyId = cursor.getLong(0);
|
||||
byte[] secretKeyBlob = cursor.getBlob(1);
|
||||
@@ -473,8 +476,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
// db.execSQL("DROP TABLE keyrings_secret");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// Downgrade is ok for the debug version, makes it easier to work with branches
|
||||
if (Constants.DEBUG) {
|
||||
return;
|
||||
@@ -528,7 +530,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
public void clearDatabase() {
|
||||
getWritableDatabase().execSQL("delete from " + Tables.KEY_RINGS_PUBLIC);
|
||||
getWritableDatabase().execSQL("delete from " + Tables.API_ALLOWED_KEYS);
|
||||
getWritableDatabase().execSQL("delete from " + Tables.API_APPS);
|
||||
getWritableDatabase().execSQL("delete from api_apps");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteDatabase;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
@@ -38,8 +39,6 @@ import android.text.TextUtils;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
|
||||
@@ -71,10 +70,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
private static final int KEY_RING_LINKED_IDS = 207;
|
||||
private static final int KEY_RING_LINKED_ID_CERTS = 208;
|
||||
|
||||
private static final int API_APPS = 301;
|
||||
private static final int API_APPS_BY_PACKAGE_NAME = 302;
|
||||
private static final int API_ALLOWED_KEYS = 305;
|
||||
|
||||
private static final int KEY_RINGS_FIND_BY_EMAIL = 400;
|
||||
private static final int KEY_RINGS_FIND_BY_SUBKEY = 401;
|
||||
private static final int KEY_RINGS_FIND_BY_USER_ID = 402;
|
||||
@@ -182,22 +177,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
+ KeychainContract.PATH_CERTS + "/*/*",
|
||||
KEY_RING_CERTS_SPECIFIC);
|
||||
|
||||
/*
|
||||
* API apps
|
||||
*
|
||||
* <pre>
|
||||
* api_apps
|
||||
* api_apps/_ (package name)
|
||||
*
|
||||
* api_apps/_/allowed_keys
|
||||
* </pre>
|
||||
*/
|
||||
matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
|
||||
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
|
||||
|
||||
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
|
||||
+ KeychainContract.PATH_ALLOWED_KEYS, API_ALLOWED_KEYS);
|
||||
|
||||
/*
|
||||
* Trust Identity access
|
||||
*
|
||||
@@ -269,15 +248,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
case KEY_SIGNATURES:
|
||||
return KeySignatures.CONTENT_TYPE;
|
||||
|
||||
case API_APPS:
|
||||
return ApiApps.CONTENT_TYPE;
|
||||
|
||||
case API_APPS_BY_PACKAGE_NAME:
|
||||
return ApiApps.CONTENT_ITEM_TYPE;
|
||||
|
||||
case API_ALLOWED_KEYS:
|
||||
return ApiAllowedKeys.CONTENT_TYPE;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown uri: " + uri);
|
||||
}
|
||||
@@ -781,26 +751,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case API_APPS: {
|
||||
qb.setTables(Tables.API_APPS);
|
||||
|
||||
break;
|
||||
}
|
||||
case API_APPS_BY_PACKAGE_NAME: {
|
||||
qb.setTables(Tables.API_APPS);
|
||||
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
|
||||
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
||||
|
||||
break;
|
||||
}
|
||||
case API_ALLOWED_KEYS: {
|
||||
qb.setTables(Tables.API_ALLOWED_KEYS);
|
||||
qb.appendWhere(Tables.API_ALLOWED_KEYS + "." + ApiAllowedKeys.PACKAGE_NAME + " = ");
|
||||
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
|
||||
}
|
||||
@@ -815,9 +765,10 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
orderBy = sortOrder;
|
||||
}
|
||||
|
||||
SQLiteDatabase db = getDb().getReadableDatabase();
|
||||
SupportSQLiteDatabase db = getDb().getReadableDatabase();
|
||||
|
||||
Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, null, orderBy);
|
||||
String query = qb.buildQuery(projection, selection, groupBy, null, orderBy, null);
|
||||
Cursor cursor = db.query(query, selectionArgs);
|
||||
if (cursor != null) {
|
||||
// Tell the cursor what uri to watch, so it knows when its source data changes
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
@@ -844,7 +795,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
Timber.d("insert(uri=" + uri + ", values=" + values.toString() + ")");
|
||||
|
||||
final SQLiteDatabase db = getDb().getWritableDatabase();
|
||||
final SupportSQLiteDatabase db = getDb().getWritableDatabase();
|
||||
|
||||
Uri rowUri = null;
|
||||
Long keyId = null;
|
||||
@@ -853,12 +804,12 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
|
||||
switch (match) {
|
||||
case KEY_RING_PUBLIC: {
|
||||
db.insertOrThrow(Tables.KEY_RINGS_PUBLIC, null, values);
|
||||
db.insert(Tables.KEY_RINGS_PUBLIC, SQLiteDatabase.CONFLICT_FAIL, values);
|
||||
keyId = values.getAsLong(KeyRings.MASTER_KEY_ID);
|
||||
break;
|
||||
}
|
||||
case KEY_RING_KEYS: {
|
||||
db.insertOrThrow(Tables.KEYS, null, values);
|
||||
db.insert(Tables.KEYS, SQLiteDatabase.CONFLICT_FAIL, values);
|
||||
keyId = values.getAsLong(Keys.MASTER_KEY_ID);
|
||||
break;
|
||||
}
|
||||
@@ -873,46 +824,33 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
if (((Number) values.get(UserPacketsColumns.RANK)).intValue() == 0 && values.get(UserPacketsColumns.USER_ID) == null) {
|
||||
throw new AssertionError("Rank 0 user packet must be a user id!");
|
||||
}
|
||||
db.insertOrThrow(Tables.USER_PACKETS, null, values);
|
||||
db.insert(Tables.USER_PACKETS, SQLiteDatabase.CONFLICT_FAIL, values);
|
||||
keyId = values.getAsLong(UserPackets.MASTER_KEY_ID);
|
||||
break;
|
||||
}
|
||||
case KEY_RING_CERTS: {
|
||||
// we replace here, keeping only the latest signature
|
||||
// TODO this would be better handled in savePublicKeyRing directly!
|
||||
db.replaceOrThrow(Tables.CERTS, null, values);
|
||||
db.insert(Tables.CERTS, SQLiteDatabase.CONFLICT_FAIL, values);
|
||||
keyId = values.getAsLong(Certs.MASTER_KEY_ID);
|
||||
break;
|
||||
}
|
||||
case UPDATED_KEYS: {
|
||||
keyId = values.getAsLong(UpdatedKeys.MASTER_KEY_ID);
|
||||
try {
|
||||
db.insertOrThrow(Tables.UPDATED_KEYS, null, values);
|
||||
db.insert(Tables.UPDATED_KEYS, SQLiteDatabase.CONFLICT_FAIL, values);
|
||||
} catch (SQLiteConstraintException e) {
|
||||
db.update(Tables.UPDATED_KEYS, values,
|
||||
db.update(Tables.UPDATED_KEYS, SQLiteDatabase.CONFLICT_IGNORE, values,
|
||||
UpdatedKeys.MASTER_KEY_ID + " = ?", new String[] { Long.toString(keyId) });
|
||||
}
|
||||
rowUri = UpdatedKeys.CONTENT_URI;
|
||||
break;
|
||||
}
|
||||
case KEY_SIGNATURES: {
|
||||
db.insert(Tables.KEY_SIGNATURES, null, values);
|
||||
db.insert(Tables.KEY_SIGNATURES, SQLiteDatabase.CONFLICT_FAIL, values);
|
||||
rowUri = KeySignatures.CONTENT_URI;
|
||||
break;
|
||||
}
|
||||
case API_APPS: {
|
||||
db.insert(Tables.API_APPS, null, values);
|
||||
break;
|
||||
}
|
||||
case API_ALLOWED_KEYS: {
|
||||
// set foreign key automatically based on given uri
|
||||
// e.g., api_apps/com.example.app/allowed_keys/
|
||||
String packageName = uri.getPathSegments().get(1);
|
||||
values.put(ApiAllowedKeys.PACKAGE_NAME, packageName);
|
||||
|
||||
db.insert(Tables.API_ALLOWED_KEYS, null, values);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new UnsupportedOperationException("Unknown uri: " + uri);
|
||||
}
|
||||
@@ -937,7 +875,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
public int delete(Uri uri, String additionalSelection, String[] selectionArgs) {
|
||||
Timber.v("delete(uri=" + uri + ")");
|
||||
|
||||
final SQLiteDatabase db = getDb().getWritableDatabase();
|
||||
final SupportSQLiteDatabase db = getDb().getWritableDatabase();
|
||||
|
||||
int count;
|
||||
final int match = mUriMatcher.match(uri);
|
||||
@@ -980,16 +918,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs);
|
||||
break;
|
||||
|
||||
case API_APPS_BY_PACKAGE_NAME: {
|
||||
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, additionalSelection),
|
||||
selectionArgs);
|
||||
break;
|
||||
}
|
||||
case API_ALLOWED_KEYS: {
|
||||
count = db.delete(Tables.API_ALLOWED_KEYS, buildDefaultApiAllowedKeysSelection(uri, additionalSelection),
|
||||
selectionArgs);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new UnsupportedOperationException("Unknown uri: " + uri);
|
||||
}
|
||||
@@ -1005,7 +933,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
Timber.v("update(uri=" + uri + ", values=" + values.toString() + ")");
|
||||
|
||||
final SQLiteDatabase db = getDb().getWritableDatabase();
|
||||
final SupportSQLiteDatabase db = getDb().getWritableDatabase();
|
||||
|
||||
int count = 0;
|
||||
try {
|
||||
@@ -1022,12 +950,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
if (!TextUtils.isEmpty(selection)) {
|
||||
actualSelection += " AND (" + selection + ")";
|
||||
}
|
||||
count = db.update(Tables.KEYS, values, actualSelection, selectionArgs);
|
||||
break;
|
||||
}
|
||||
case API_APPS_BY_PACKAGE_NAME: {
|
||||
count = db.update(Tables.API_APPS, values,
|
||||
buildDefaultApiAppsSelection(uri, selection), selectionArgs);
|
||||
count = db.update(Tables.KEYS, SQLiteDatabase.CONFLICT_FAIL, values, actualSelection, selectionArgs);
|
||||
break;
|
||||
}
|
||||
case UPDATED_KEYS: {
|
||||
@@ -1040,7 +963,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
throw new UnsupportedOperationException("can only reset all keys");
|
||||
}
|
||||
|
||||
db.update(Tables.UPDATED_KEYS, values, null, null);
|
||||
db.update(Tables.UPDATED_KEYS, SQLiteDatabase.CONFLICT_FAIL, values, null, null);
|
||||
break;
|
||||
}
|
||||
case AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID: {
|
||||
@@ -1049,11 +972,11 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
values.put(ApiAutocryptPeer.PACKAGE_NAME, packageName);
|
||||
values.put(ApiAutocryptPeer.IDENTIFIER, identifier);
|
||||
|
||||
int updated = db.update(Tables.API_AUTOCRYPT_PEERS, values,
|
||||
int updated = db.update(Tables.API_AUTOCRYPT_PEERS, SQLiteDatabase.CONFLICT_IGNORE, values,
|
||||
ApiAutocryptPeer.PACKAGE_NAME + "=? AND " + ApiAutocryptPeer.IDENTIFIER + "=?",
|
||||
new String[] { packageName, identifier });
|
||||
if (updated == 0) {
|
||||
db.insertOrThrow(Tables.API_AUTOCRYPT_PEERS, null, values);
|
||||
db.insert(Tables.API_AUTOCRYPT_PEERS, SQLiteDatabase.CONFLICT_FAIL,values);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1068,35 +991,4 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build default selection statement for API apps. If no extra selection is specified only build
|
||||
* where clause with rowId
|
||||
*
|
||||
* @param uri
|
||||
* @param selection
|
||||
* @return
|
||||
*/
|
||||
private String buildDefaultApiAppsSelection(Uri uri, String selection) {
|
||||
String packageName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
|
||||
|
||||
String andSelection = "";
|
||||
if (!TextUtils.isEmpty(selection)) {
|
||||
andSelection = " AND (" + selection + ")";
|
||||
}
|
||||
|
||||
return ApiApps.PACKAGE_NAME + "=" + packageName + andSelection;
|
||||
}
|
||||
|
||||
private String buildDefaultApiAllowedKeysSelection(Uri uri, String selection) {
|
||||
String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1));
|
||||
|
||||
String andSelection = "";
|
||||
if (!TextUtils.isEmpty(selection)) {
|
||||
andSelection = " AND (" + selection + ")";
|
||||
}
|
||||
|
||||
return ApiAllowedKeys.PACKAGE_NAME + "=" + packageName + andSelection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.provider;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import okhttp3.internal.Util;
|
||||
|
||||
|
||||
class LocalSecretKeyStorage {
|
||||
private static final String FORMAT_STR_SECRET_KEY = "0x%016x.sec";
|
||||
private static final String SECRET_KEYS_DIR_NAME = "secret_keys";
|
||||
|
||||
|
||||
private final File localSecretKeysDir;
|
||||
|
||||
|
||||
public static LocalSecretKeyStorage getInstance(Context context) {
|
||||
File localSecretKeysDir = new File(context.getFilesDir(), SECRET_KEYS_DIR_NAME);
|
||||
return new LocalSecretKeyStorage(localSecretKeysDir);
|
||||
}
|
||||
|
||||
private LocalSecretKeyStorage(File localSecretKeysDir) {
|
||||
this.localSecretKeysDir = localSecretKeysDir;
|
||||
}
|
||||
|
||||
private File getSecretKeyFile(long masterKeyId) throws IOException {
|
||||
if (!localSecretKeysDir.exists()) {
|
||||
localSecretKeysDir.mkdir();
|
||||
}
|
||||
if (!localSecretKeysDir.isDirectory()) {
|
||||
throw new IOException("Failed creating public key directory!");
|
||||
}
|
||||
|
||||
String keyFilename = String.format(FORMAT_STR_SECRET_KEY, masterKeyId);
|
||||
return new File(localSecretKeysDir, keyFilename);
|
||||
}
|
||||
|
||||
void writeSecretKey(long masterKeyId, byte[] encoded) throws IOException {
|
||||
File publicKeyFile = getSecretKeyFile(masterKeyId);
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(publicKeyFile);
|
||||
try {
|
||||
fileOutputStream.write(encoded);
|
||||
} finally {
|
||||
Util.closeQuietly(fileOutputStream);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] readSecretKey(long masterKeyId) throws IOException {
|
||||
File publicKeyFile = getSecretKeyFile(masterKeyId);
|
||||
|
||||
try {
|
||||
FileInputStream fileInputStream = new FileInputStream(publicKeyFile);
|
||||
return readIntoByteArray(fileInputStream);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] readIntoByteArray(FileInputStream fileInputStream) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
byte[] buf = new byte[128];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fileInputStream.read(buf)) != -1) {
|
||||
baos.write(buf, 0, bytesRead);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
void deleteSecretKey(long masterKeyId) throws IOException {
|
||||
File publicKeyFile = getSecretKeyFile(masterKeyId);
|
||||
if (publicKeyFile.exists()) {
|
||||
boolean deleteSuccess = publicKeyFile.delete();
|
||||
if (!deleteSuccess) {
|
||||
throw new IOException("File exists, but could not be deleted!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,9 @@
|
||||
package org.sufficientlysecure.keychain.provider;
|
||||
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteDatabase;
|
||||
import android.arch.persistence.db.SupportSQLiteQuery;
|
||||
import android.arch.persistence.db.SupportSQLiteQueryBuilder;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
@@ -47,34 +50,31 @@ public class OverriddenWarningsRepository {
|
||||
}
|
||||
|
||||
public boolean isWarningOverridden(String identifier) {
|
||||
SQLiteDatabase db = getDb().getReadableDatabase();
|
||||
Cursor cursor = db.query(
|
||||
Tables.OVERRIDDEN_WARNINGS,
|
||||
new String[] { "COUNT(*)" },
|
||||
OverriddenWarnings.IDENTIFIER + " = ?",
|
||||
new String[] { identifier },
|
||||
null, null, null);
|
||||
SupportSQLiteDatabase db = getDb().getReadableDatabase();
|
||||
SupportSQLiteQuery query = SupportSQLiteQueryBuilder
|
||||
.builder(Tables.OVERRIDDEN_WARNINGS)
|
||||
.columns(new String[] { "COUNT(*) FROM " })
|
||||
.selection(OverriddenWarnings.IDENTIFIER + " = ?", new String[] { identifier })
|
||||
.create();
|
||||
Cursor cursor = db.query(query);
|
||||
|
||||
try {
|
||||
cursor.moveToFirst();
|
||||
return cursor.getInt(0) > 0;
|
||||
} finally {
|
||||
cursor.close();
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void putOverride(String identifier) {
|
||||
SQLiteDatabase db = getDb().getWritableDatabase();
|
||||
SupportSQLiteDatabase db = getDb().getWritableDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(OverriddenWarnings.IDENTIFIER, identifier);
|
||||
db.replace(Tables.OVERRIDDEN_WARNINGS, null, cv);
|
||||
db.close();
|
||||
db.insert(Tables.OVERRIDDEN_WARNINGS, SQLiteDatabase.CONFLICT_REPLACE, cv);
|
||||
}
|
||||
|
||||
public void deleteOverride(String identifier) {
|
||||
SQLiteDatabase db = getDb().getWritableDatabase();
|
||||
SupportSQLiteDatabase db = getDb().getWritableDatabase();
|
||||
db.delete(Tables.OVERRIDDEN_WARNINGS, OverriddenWarnings.IDENTIFIER + " = ?", new String[] { identifier });
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user