diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java | 86 |
1 files changed, 71 insertions, 15 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index d4cdc6c5c..165116ae0 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -16,14 +16,19 @@ package com.android.inputmethod.latin; +import android.content.ContentProviderClient; import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; +import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.latin.DictionaryInfoUtils.DictionaryInfo; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -32,6 +37,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -68,6 +74,10 @@ public final class BinaryDictionaryFileDumper { // The path fragment to append after the client ID for dictionary info requests. private static final String QUERY_PATH_DICT_INFO = "dict"; + // The path fragment to append after the client ID for updating the metadata URI. + private static final String QUERY_PATH_METADATA = "metadata"; + private static final String INSERT_METADATA_CLIENT_ID_COLUMN = "clientid"; + private static final String INSERT_METADATA_METADATA_URI_COLUMN = "uri"; // Prevents this class to be accidentally instantiated. private BinaryDictionaryFileDumper() { @@ -91,25 +101,31 @@ public final class BinaryDictionaryFileDumper { */ private static List<WordListInfo> getWordListWordListInfos(final Locale locale, final Context context, final boolean hasDefaultWordList) { - final ContentResolver resolver = context.getContentResolver(); final String clientId = context.getString(R.string.dictionary_pack_client_id); final Uri.Builder builder = getProviderUriBuilder(clientId); builder.appendPath(QUERY_PATH_DICT_INFO); builder.appendPath(locale.toString()); builder.appendQueryParameter(QUERY_PARAMETER_PROTOCOL, QUERY_PARAMETER_PROTOCOL_VALUE); if (!hasDefaultWordList) { - builder.appendQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER, QUERY_PARAMETER_TRUE); + builder.appendQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER, + QUERY_PARAMETER_TRUE); } final Uri dictionaryPackUri = builder.build(); - final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null); - if (null == c) return Collections.<WordListInfo>emptyList(); - if (c.getCount() <= 0 || !c.moveToFirst()) { - c.close(); - return Collections.<WordListInfo>emptyList(); - } - + final ContentProviderClient client = context.getContentResolver(). + acquireContentProviderClient(getProviderUriBuilder("").build()); + if (null == client) return Collections.<WordListInfo>emptyList(); try { + final Cursor c = client.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, + null); + if (null == c) { + reinitializeClientRecordInDictionaryContentProvider(context, client, clientId); + return Collections.<WordListInfo>emptyList(); + } + if (c.getCount() <= 0 || !c.moveToFirst()) { + c.close(); + return Collections.<WordListInfo>emptyList(); + } final List<WordListInfo> list = CollectionUtils.newArrayList(); do { final String wordListId = c.getString(0); @@ -119,11 +135,20 @@ public final class BinaryDictionaryFileDumper { } while (c.moveToNext()); c.close(); return list; + } catch (RemoteException e) { + // The documentation is unclear as to in which cases this may happen, but it probably + // happens when the content provider got suddenly killed because it crashed or because + // the user disabled it through Settings. + Log.e(TAG, "RemoteException: communication with the dictionary pack cut", e); + return Collections.<WordListInfo>emptyList(); } catch (Exception e) { - // Just in case we hit a problem in communication with the dictionary pack. - // We don't want to die. - Log.e(TAG, "Exception communicating with the dictionary pack : " + e); + // A crash here is dangerous because crashing here would brick any encrypted device - + // we need the keyboard to be up and working to enter the password, so we don't want + // to die no matter what. So let's be as safe as possible. + Log.e(TAG, "Unexpected exception communicating with the dictionary pack", e); return Collections.<WordListInfo>emptyList(); + } finally { + client.release(); } } @@ -237,7 +262,7 @@ public final class BinaryDictionaryFileDumper { return AssetFileAddress.makeFromFileName(finalFileName); } catch (Exception e) { if (DEBUG) { - Log.i(TAG, "Can't open word list in mode " + mode + " : " + e); + Log.i(TAG, "Can't open word list in mode " + mode, e); } if (null != outputFile) { // This may or may not fail. The file may not have been created if the @@ -255,12 +280,12 @@ public final class BinaryDictionaryFileDumper { if (null != decryptedStream) decryptedStream.close(); if (null != bufferedInputStream) bufferedInputStream.close(); } catch (Exception e) { - Log.e(TAG, "Exception while closing a file descriptor : " + e); + Log.e(TAG, "Exception while closing a file descriptor", e); } try { if (null != bufferedOutputStream) bufferedOutputStream.close(); } catch (Exception e) { - Log.e(TAG, "Exception while closing a file : " + e); + Log.e(TAG, "Exception while closing a file", e); } } } @@ -335,4 +360,35 @@ public final class BinaryDictionaryFileDumper { output.write(buffer, 0, readBytes); input.close(); } + + private static void reinitializeClientRecordInDictionaryContentProvider(final Context context, + final ContentProviderClient client, final String clientId) throws RemoteException { + final String metadataFileUri = context.getString(R.string.dictionary_pack_metadata_uri); + if (TextUtils.isEmpty(metadataFileUri)) return; + // Tell the content provider to reset all information about this client id + final Uri metadataContentUri = getProviderUriBuilder(clientId) + .appendPath(QUERY_PATH_METADATA) + .appendQueryParameter(QUERY_PARAMETER_PROTOCOL, QUERY_PARAMETER_PROTOCOL_VALUE) + .build(); + client.delete(metadataContentUri, null, null); + // Update the metadata URI + final ContentValues metadataValues = new ContentValues(); + metadataValues.put(INSERT_METADATA_CLIENT_ID_COLUMN, clientId); + metadataValues.put(INSERT_METADATA_METADATA_URI_COLUMN, metadataFileUri); + client.insert(metadataContentUri, metadataValues); + + // Update the dictionary list. + final Uri dictionaryContentUriBase = getProviderUriBuilder(clientId) + .appendPath(QUERY_PATH_DICT_INFO) + .appendQueryParameter(QUERY_PARAMETER_PROTOCOL, QUERY_PARAMETER_PROTOCOL_VALUE) + .build(); + final ArrayList<DictionaryInfo> dictionaryList = + DictionaryInfoUtils.getCurrentDictionaryFileNameAndVersionInfo(context); + final int length = dictionaryList.size(); + for (int i = 0; i < length; ++i) { + final DictionaryInfo info = dictionaryList.get(i); + client.insert(Uri.withAppendedPath(dictionaryContentUriBase, info.mId), + info.toContentValues()); + } + } } |