mirror of
https://github.com/moparisthebest/k-9
synced 2024-12-25 00:58:50 -05:00
Factor out store locking code in an intermediate DB access layer.
This commit is contained in:
parent
c5f7dbf028
commit
ab5caba92c
@ -35,18 +35,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
public class Account implements BaseAccount
|
||||
{
|
||||
/**
|
||||
* @see Account#setLocalStoreMigrationListener(LocalStoreMigrationListener)
|
||||
*
|
||||
*/
|
||||
public interface LocalStoreMigrationListener
|
||||
{
|
||||
|
||||
void onLocalStoreMigration(String oldStoreUri,
|
||||
String newStoreUri) throws MessagingException;
|
||||
|
||||
}
|
||||
|
||||
public static final String EXPUNGE_IMMEDIATELY = "EXPUNGE_IMMEDIATELY";
|
||||
public static final String EXPUNGE_MANUALLY = "EXPUNGE_MANUALLY";
|
||||
public static final String EXPUNGE_ON_POLL = "EXPUNGE_ON_POLL";
|
||||
@ -89,7 +77,6 @@ public class Account implements BaseAccount
|
||||
* the moment.
|
||||
*/
|
||||
private final boolean mIsInUse = false;
|
||||
private LocalStoreMigrationListener mLocalStoreMigrationListener;
|
||||
private String mTransportUri;
|
||||
private String mDescription;
|
||||
private String mAlwaysBcc;
|
||||
@ -1389,12 +1376,11 @@ public class Account implements BaseAccount
|
||||
* Never <code>null</code>.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public void switchLocalStorage(String newStorageProviderId) throws MessagingException
|
||||
public void switchLocalStorage(final String newStorageProviderId) throws MessagingException
|
||||
{
|
||||
if (this.mLocalStoreMigrationListener != null && !mLocalStorageProviderId.equals(newStorageProviderId))
|
||||
if (!mLocalStorageProviderId.equals(newStorageProviderId))
|
||||
{
|
||||
mLocalStoreMigrationListener.onLocalStoreMigration(mLocalStorageProviderId,
|
||||
newStorageProviderId);
|
||||
getLocalStore().switchLocalStorage(newStorageProviderId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1555,19 +1541,6 @@ public class Account implements BaseAccount
|
||||
return mIsInUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a listener to be informed when the underlying {@link StorageProvider}
|
||||
* of the {@link LocalStore} of this account changes. (e.g. via
|
||||
* {@link #switchLocalStorage(Context, String)})
|
||||
*
|
||||
* @param listener
|
||||
* @see #switchLocalStorage(Context, String)
|
||||
*/
|
||||
public void setLocalStoreMigrationListener(LocalStoreMigrationListener listener)
|
||||
{
|
||||
this.mLocalStoreMigrationListener = listener;
|
||||
}
|
||||
|
||||
public synchronized CryptoProvider getCryptoProvider()
|
||||
{
|
||||
if (mCryptoProvider == null)
|
||||
|
@ -2,10 +2,16 @@
|
||||
package com.fsck.k9.helper;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.util.Log;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.mail.filter.Base64;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@ -496,4 +502,126 @@ public class Utility
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parentDir
|
||||
* @param name
|
||||
* Never <code>null</code>.
|
||||
*/
|
||||
public static void touchFile(final File parentDir, final String name)
|
||||
{
|
||||
final File file = new File(parentDir, name);
|
||||
try
|
||||
{
|
||||
if (!file.exists())
|
||||
{
|
||||
file.createNewFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
file.setLastModified(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.d(K9.LOG_TAG, "Unable to touch file: " + file.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from
|
||||
* @param to
|
||||
* @return
|
||||
*/
|
||||
public static boolean move(final File from, final File to)
|
||||
{
|
||||
if (to.exists())
|
||||
{
|
||||
to.delete();
|
||||
}
|
||||
to.getParentFile().mkdirs();
|
||||
|
||||
try
|
||||
{
|
||||
FileInputStream in = new FileInputStream(from);
|
||||
FileOutputStream out = new FileOutputStream(to);
|
||||
byte[] buffer = new byte[1024];
|
||||
int count = -1;
|
||||
while ((count = in.read(buffer)) > 0)
|
||||
{
|
||||
out.write(buffer, 0, count);
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
from.delete();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.w(K9.LOG_TAG, "cannot move " + from.getAbsolutePath() + " to " + to.getAbsolutePath(), e);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fromDir
|
||||
* @param toDir
|
||||
*/
|
||||
public static void moveRecursive(final File fromDir, final File toDir)
|
||||
{
|
||||
if (!fromDir.exists())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!fromDir.isDirectory())
|
||||
{
|
||||
if (toDir.exists())
|
||||
{
|
||||
if (!toDir.delete())
|
||||
{
|
||||
Log.w(K9.LOG_TAG, "cannot delete already existing file/directory " + toDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
if (!fromDir.renameTo(toDir))
|
||||
{
|
||||
Log.w(K9.LOG_TAG, "cannot rename " + fromDir.getAbsolutePath() + " to " + toDir.getAbsolutePath() + " - moving instead");
|
||||
move(fromDir, toDir);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!toDir.exists() || !toDir.isDirectory())
|
||||
{
|
||||
if (toDir.exists() )
|
||||
{
|
||||
toDir.delete();
|
||||
}
|
||||
if (!toDir.mkdirs())
|
||||
{
|
||||
Log.w(K9.LOG_TAG, "cannot create directory " + toDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
File[] files = fromDir.listFiles();
|
||||
for (File file : files)
|
||||
{
|
||||
if (file.isDirectory())
|
||||
{
|
||||
moveRecursive(file, new File(toDir, file.getName()));
|
||||
file.delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
File target = new File(toDir, file.getName());
|
||||
if (!file.renameTo(target))
|
||||
{
|
||||
Log.w(K9.LOG_TAG, "cannot rename " + file.getAbsolutePath() + " to " + target.getAbsolutePath() + " - moving instead");
|
||||
move(file, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fromDir.delete())
|
||||
{
|
||||
Log.w(K9.LOG_TAG, "cannot delete " + fromDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
591
src/com/fsck/k9/mail/store/LockableDatabase.java
Normal file
591
src/com/fsck/k9/mail/store/LockableDatabase.java
Normal file
@ -0,0 +1,591 @@
|
||||
package com.fsck.k9.mail.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import android.app.Application;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
|
||||
public class LockableDatabase
|
||||
{
|
||||
|
||||
/**
|
||||
* Callback interface for DB operations. Concept is similar to Spring
|
||||
* HibernateCallback.
|
||||
*
|
||||
* @param <T>
|
||||
* Return value type for {@link #doDbWork(SQLiteDatabase)}
|
||||
*/
|
||||
public static interface DbCallback<T>
|
||||
{
|
||||
/**
|
||||
* @param db
|
||||
* The locked database on which the work should occur. Never
|
||||
* <code>null</code>.
|
||||
* @return Any relevant data. Can be <code>null</code>.
|
||||
* @throws WrappedException
|
||||
* @throws UnavailableStorageException
|
||||
*/
|
||||
T doDbWork(SQLiteDatabase db) throws WrappedException, UnavailableStorageException;
|
||||
}
|
||||
|
||||
public static interface SchemaDefinition
|
||||
{
|
||||
int getVersion();
|
||||
|
||||
/**
|
||||
* @param db Never <code>null</code>.
|
||||
*/
|
||||
void doDbUpgrade(SQLiteDatabase db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround exception wrapper used to keep the inner exception generated
|
||||
* in a {@link DbCallback}.
|
||||
*/
|
||||
protected static class WrappedException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 8184421232587399369L;
|
||||
|
||||
public WrappedException(final Exception cause)
|
||||
{
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the DB on mount and close the DB on unmount
|
||||
*/
|
||||
private class StorageListener implements StorageManager.StorageListener
|
||||
{
|
||||
@Override
|
||||
public void onUnmount(final String providerId)
|
||||
{
|
||||
if (!providerId.equals(mStorageProviderId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (K9.DEBUG)
|
||||
{
|
||||
Log.d(K9.LOG_TAG, "LockableDatabase: Closing DB " + uUid + " due to unmount event on StorageProvider: " + providerId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
lockWrite();
|
||||
try
|
||||
{
|
||||
mDb.close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
unlockWrite();
|
||||
}
|
||||
}
|
||||
catch (UnavailableStorageException e)
|
||||
{
|
||||
Log.w(K9.LOG_TAG, "Unable to writelock on unmount", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMount(final String providerId)
|
||||
{
|
||||
if (!providerId.equals(mStorageProviderId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (K9.DEBUG)
|
||||
{
|
||||
Log.d(K9.LOG_TAG, "LockableDatabase: Opening DB " + uUid + " due to mount event on StorageProvider: " + providerId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
openOrCreateDataspace(mApplication);
|
||||
}
|
||||
catch (UnavailableStorageException e)
|
||||
{
|
||||
Log.e(K9.LOG_TAG, "Unable to open DB on mount", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String mStorageProviderId;
|
||||
|
||||
private SQLiteDatabase mDb;
|
||||
/**
|
||||
* Reentrant read lock
|
||||
*/
|
||||
private final Lock mReadLock;
|
||||
/**
|
||||
* Reentrant write lock (if you lock it 2x from the same thread, you have to
|
||||
* unlock it 2x to release it)
|
||||
*/
|
||||
private final Lock mWriteLock;
|
||||
|
||||
{
|
||||
final ReadWriteLock lock = new ReentrantReadWriteLock(true);
|
||||
mReadLock = lock.readLock();
|
||||
mWriteLock = lock.writeLock();
|
||||
}
|
||||
|
||||
private final StorageListener mStorageListener = new StorageListener();
|
||||
|
||||
private Application mApplication;
|
||||
|
||||
/**
|
||||
* {@link ThreadLocal} to check whether a DB transaction is occuring in the
|
||||
* current {@link Thread}.
|
||||
*
|
||||
* @see #execute(boolean, DbCallback)
|
||||
*/
|
||||
private ThreadLocal<Boolean> inTransaction = new ThreadLocal<Boolean>();
|
||||
|
||||
private SchemaDefinition mSchemaDefinition;
|
||||
|
||||
private String uUid;
|
||||
|
||||
/**
|
||||
* @param application
|
||||
* Never <code>null</code>.
|
||||
* @param uUid
|
||||
* Never <code>null</code>.
|
||||
* @param schemaDefinition
|
||||
* Never <code>null</code
|
||||
*/
|
||||
public LockableDatabase(final Application application, final String uUid, final SchemaDefinition schemaDefinition)
|
||||
{
|
||||
this.mApplication = application;
|
||||
this.uUid = uUid;
|
||||
this.mSchemaDefinition = schemaDefinition;
|
||||
}
|
||||
|
||||
public void setStorageProviderId(String mStorageProviderId)
|
||||
{
|
||||
this.mStorageProviderId = mStorageProviderId;
|
||||
}
|
||||
|
||||
public String getStorageProviderId()
|
||||
{
|
||||
return mStorageProviderId;
|
||||
}
|
||||
|
||||
private StorageManager getStorageManager()
|
||||
{
|
||||
return StorageManager.getInstance(mApplication);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the storage for shared operations (concurrent threads are allowed to
|
||||
* run simultaneously).
|
||||
*
|
||||
* <p>
|
||||
* You <strong>have to</strong> invoke {@link #unlockRead()} when you're
|
||||
* done with the storage.
|
||||
* </p>
|
||||
*
|
||||
* @throws UnavailableStorageException
|
||||
* If storage can't be locked because it is not available
|
||||
*/
|
||||
protected void lockRead() throws UnavailableStorageException
|
||||
{
|
||||
mReadLock.lock();
|
||||
try
|
||||
{
|
||||
getStorageManager().lockProvider(mStorageProviderId);
|
||||
}
|
||||
catch (UnavailableStorageException e)
|
||||
{
|
||||
mReadLock.unlock();
|
||||
throw e;
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
mReadLock.unlock();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected void unlockRead()
|
||||
{
|
||||
getStorageManager().unlockProvider(mStorageProviderId);
|
||||
mReadLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the storage for exclusive access (other threads aren't allowed to
|
||||
* run simultaneously)
|
||||
*
|
||||
* <p>
|
||||
* You <strong>have to</strong> invoke {@link #unlockWrite()} when you're
|
||||
* done with the storage.
|
||||
* </p>
|
||||
*
|
||||
* @throws UnavailableStorageException
|
||||
* If storage can't be locked because it is not available.
|
||||
*/
|
||||
protected void lockWrite() throws UnavailableStorageException
|
||||
{
|
||||
lockWrite(mStorageProviderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the storage for exclusive access (other threads aren't allowed to
|
||||
* run simultaneously)
|
||||
*
|
||||
* <p>
|
||||
* You <strong>have to</strong> invoke {@link #unlockWrite()} when you're
|
||||
* done with the storage.
|
||||
* </p>
|
||||
*
|
||||
* @param providerId
|
||||
* Never <code>null</code>.
|
||||
*
|
||||
* @throws UnavailableStorageException
|
||||
* If storage can't be locked because it is not available.
|
||||
*/
|
||||
protected void lockWrite(final String providerId) throws UnavailableStorageException
|
||||
{
|
||||
mWriteLock.lock();
|
||||
try
|
||||
{
|
||||
getStorageManager().lockProvider(providerId);
|
||||
}
|
||||
catch (UnavailableStorageException e)
|
||||
{
|
||||
mWriteLock.unlock();
|
||||
throw e;
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
mWriteLock.unlock();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected void unlockWrite()
|
||||
{
|
||||
unlockWrite(mStorageProviderId);
|
||||
}
|
||||
|
||||
protected void unlockWrite(final String providerId)
|
||||
{
|
||||
getStorageManager().unlockProvider(providerId);
|
||||
mWriteLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a DB callback in a shared context (doesn't prevent concurrent
|
||||
* shared executions), taking care of locking the DB storage.
|
||||
*
|
||||
* <p>
|
||||
* Can be instructed to start a transaction if none is currently active in
|
||||
* the current thread. Callback will participe in any active transaction (no
|
||||
* inner transaction created).
|
||||
* </p>
|
||||
*
|
||||
* @param transactional
|
||||
* <code>true</code> the callback must be executed in a
|
||||
* transactional context.
|
||||
* @param callback
|
||||
* Never <code>null</code>.
|
||||
*
|
||||
* @param <T>
|
||||
* @return Whatever {@link DbCallback#doDbWork(SQLiteDatabase)} returns.
|
||||
* @throws UnavailableStorageException
|
||||
*/
|
||||
public <T> T execute(final boolean transactional, final DbCallback<T> callback) throws UnavailableStorageException
|
||||
{
|
||||
lockRead();
|
||||
final boolean doTransaction = transactional && inTransaction.get() == null;
|
||||
try
|
||||
{
|
||||
final boolean debug = K9.DEBUG;
|
||||
if (doTransaction)
|
||||
{
|
||||
inTransaction.set(Boolean.TRUE);
|
||||
mDb.beginTransaction();
|
||||
}
|
||||
try
|
||||
{
|
||||
final T result = callback.doDbWork(mDb);
|
||||
if (doTransaction)
|
||||
{
|
||||
mDb.setTransactionSuccessful();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (doTransaction)
|
||||
{
|
||||
final long begin;
|
||||
if (debug)
|
||||
{
|
||||
begin = System.currentTimeMillis();
|
||||
}
|
||||
else
|
||||
{
|
||||
begin = 0l;
|
||||
}
|
||||
// not doing endTransaction in the same 'finally' block of unlockRead() because endTransaction() may throw an exception
|
||||
mDb.endTransaction();
|
||||
if (debug)
|
||||
{
|
||||
Log.v(K9.LOG_TAG, "LockableDatabase: Transaction ended, took " + Long.toString(System.currentTimeMillis() - begin) + "ms / " + new Exception().getStackTrace()[1].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (doTransaction)
|
||||
{
|
||||
inTransaction.set(null);
|
||||
}
|
||||
unlockRead();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newProviderId
|
||||
* Never <code>null</code>.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public void switchProvider(final String newProviderId) throws MessagingException
|
||||
{
|
||||
if (newProviderId.equals(mStorageProviderId))
|
||||
{
|
||||
Log.v(K9.LOG_TAG, "LockableDatabase: Ignoring provider switch request as they are equal: " + newProviderId);
|
||||
return;
|
||||
}
|
||||
|
||||
final String oldProviderId = mStorageProviderId;
|
||||
lockWrite(oldProviderId);
|
||||
try
|
||||
{
|
||||
lockWrite(newProviderId);
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
mDb.close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.i(K9.LOG_TAG, "Unable to close DB on local store migration", e);
|
||||
}
|
||||
|
||||
final StorageManager storageManager = getStorageManager();
|
||||
|
||||
// create new path
|
||||
prepareStorage(newProviderId);
|
||||
|
||||
// move all database files
|
||||
Utility.moveRecursive(storageManager.getDatabase(uUid, oldProviderId), storageManager.getDatabase(uUid, newProviderId));
|
||||
// move all attachment files
|
||||
Utility.moveRecursive(storageManager.getAttachmentDirectory(uUid, oldProviderId), storageManager.getAttachmentDirectory(uUid, newProviderId));
|
||||
|
||||
mStorageProviderId = newProviderId;
|
||||
|
||||
// re-initialize this class with the new Uri
|
||||
openOrCreateDataspace(mApplication);
|
||||
}
|
||||
finally
|
||||
{
|
||||
unlockWrite(newProviderId);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
unlockWrite(oldProviderId);
|
||||
}
|
||||
}
|
||||
|
||||
public void open() throws UnavailableStorageException
|
||||
{
|
||||
lockWrite();
|
||||
try
|
||||
{
|
||||
openOrCreateDataspace(mApplication);
|
||||
}
|
||||
finally
|
||||
{
|
||||
unlockWrite();
|
||||
}
|
||||
StorageManager.getInstance(mApplication).addListener(mStorageListener);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param application
|
||||
* @throws UnavailableStorageException
|
||||
*/
|
||||
protected void openOrCreateDataspace(final Application application) throws UnavailableStorageException
|
||||
{
|
||||
|
||||
lockWrite();
|
||||
try
|
||||
{
|
||||
final File databaseFile = prepareStorage(mStorageProviderId);
|
||||
try
|
||||
{
|
||||
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
|
||||
}
|
||||
catch (SQLiteException e)
|
||||
{
|
||||
// try to gracefully handle DB corruption - see issue 2537
|
||||
Log.w(K9.LOG_TAG, "Unable to open DB " + databaseFile + " - removing file and retrying", e);
|
||||
databaseFile.delete();
|
||||
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
|
||||
}
|
||||
if (mDb.getVersion() != mSchemaDefinition.getVersion())
|
||||
{
|
||||
mSchemaDefinition.doDbUpgrade(mDb);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
unlockWrite();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param providerId
|
||||
* Never <code>null</code>.
|
||||
* @return DB file.
|
||||
* @throws UnavailableStorageException
|
||||
*/
|
||||
protected File prepareStorage(final String providerId) throws UnavailableStorageException
|
||||
{
|
||||
final StorageManager storageManager = getStorageManager();
|
||||
|
||||
final File databaseFile;
|
||||
final File databaseParentDir;
|
||||
databaseFile = storageManager.getDatabase(uUid, providerId);
|
||||
databaseParentDir = databaseFile.getParentFile();
|
||||
if (databaseParentDir.isFile())
|
||||
{
|
||||
// should be safe to inconditionally delete clashing file: user is not supposed to mess with our directory
|
||||
databaseParentDir.delete();
|
||||
}
|
||||
if (!databaseParentDir.exists())
|
||||
{
|
||||
if (!databaseParentDir.mkdirs())
|
||||
{
|
||||
// Android seems to be unmounting the storage...
|
||||
throw new UnavailableStorageException("Unable to access: " + databaseParentDir);
|
||||
}
|
||||
Utility.touchFile(databaseParentDir, ".nomedia");
|
||||
}
|
||||
|
||||
final File attachmentDir;
|
||||
final File attachmentParentDir;
|
||||
attachmentDir = storageManager
|
||||
.getAttachmentDirectory(uUid, providerId);
|
||||
attachmentParentDir = attachmentDir.getParentFile();
|
||||
if (!attachmentParentDir.exists())
|
||||
{
|
||||
attachmentParentDir.mkdirs();
|
||||
Utility.touchFile(attachmentParentDir, ".nomedia");
|
||||
}
|
||||
if (!attachmentDir.exists())
|
||||
{
|
||||
attachmentDir.mkdirs();
|
||||
}
|
||||
return databaseFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the backing database.
|
||||
*
|
||||
* @throws UnavailableStorageException
|
||||
*/
|
||||
public void delete() throws UnavailableStorageException
|
||||
{
|
||||
delete(false);
|
||||
}
|
||||
|
||||
public void recreate() throws UnavailableStorageException
|
||||
{
|
||||
delete(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recreate
|
||||
* <code>true</code> if the DB should be recreated after delete
|
||||
* @throws UnavailableStorageException
|
||||
*/
|
||||
private void delete(final boolean recreate) throws UnavailableStorageException
|
||||
{
|
||||
lockWrite();
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
mDb.close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
}
|
||||
final StorageManager storageManager = getStorageManager();
|
||||
try
|
||||
{
|
||||
final File attachmentDirectory = storageManager.getAttachmentDirectory(uUid, mStorageProviderId);
|
||||
final File[] attachments = attachmentDirectory.listFiles();
|
||||
for (File attachment : attachments)
|
||||
{
|
||||
if (attachment.exists())
|
||||
{
|
||||
attachment.delete();
|
||||
}
|
||||
}
|
||||
if (attachmentDirectory.exists())
|
||||
{
|
||||
attachmentDirectory.delete();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
storageManager.getDatabase(uUid, mStorageProviderId).delete();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.i(K9.LOG_TAG, "LockableDatabase: delete(): Unable to delete backing DB file", e);
|
||||
}
|
||||
|
||||
if (recreate)
|
||||
{
|
||||
openOrCreateDataspace(mApplication);
|
||||
}
|
||||
else
|
||||
{
|
||||
// stop waiting for mount/unmount events
|
||||
getStorageManager().removeListener(mStorageListener);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
unlockWrite();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user