diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
4 files changed, 37 insertions, 48 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 879f1db49..165116ae0 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -23,6 +23,7 @@ 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; @@ -95,23 +96,6 @@ public final class BinaryDictionaryFileDumper { } /** - * Finds out whether the dictionary pack is available on this device. - * @param context A context to get the content resolver. - * @return whether the dictionary pack is present or not. - */ - private static boolean isDictionaryPackPresent(final Context context) { - final ContentResolver cr = context.getContentResolver(); - final ContentProviderClient client = - cr.acquireContentProviderClient(getProviderUriBuilder("").build()); - if (client != null) { - client.release(); - return true; - } else { - return false; - } - } - - /** * Queries a content provider for the list of word lists for a specific locale * available to copy into Latin IME. */ @@ -128,15 +112,14 @@ public final class BinaryDictionaryFileDumper { } final Uri dictionaryPackUri = builder.build(); - final ContentResolver resolver = context.getContentResolver(); + final ContentProviderClient client = context.getContentResolver(). + acquireContentProviderClient(getProviderUriBuilder("").build()); + if (null == client) return Collections.<WordListInfo>emptyList(); try { - final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, + final Cursor c = client.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null); if (null == c) { - if (isDictionaryPackPresent(context)) { - reinitializeClientRecordInDictionaryContentProvider(context, resolver, - clientId); - } + reinitializeClientRecordInDictionaryContentProvider(context, client, clientId); return Collections.<WordListInfo>emptyList(); } if (c.getCount() <= 0 || !c.moveToFirst()) { @@ -152,21 +135,20 @@ public final class BinaryDictionaryFileDumper { } while (c.moveToNext()); c.close(); return list; - } catch (IllegalArgumentException e) { - // Any method call on the content resolver may unexpectedly crash without notice - // if the content provider is not present (for example, while crypting a device). - // Testing seems to indicate that ContentResolver#query() merely returns null - // while ContentResolver#delete throws IllegalArgumentException but this is - // undocumented, so all ContentResolver methods should be protected. 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 let's be as safe as possible. - Log.e(TAG, "IllegalArgumentException: the dictionary pack can't be contacted?", e); + } 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(); } } @@ -380,7 +362,7 @@ public final class BinaryDictionaryFileDumper { } private static void reinitializeClientRecordInDictionaryContentProvider(final Context context, - final ContentResolver resolver, final String clientId) { + 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 @@ -388,12 +370,12 @@ public final class BinaryDictionaryFileDumper { .appendPath(QUERY_PATH_METADATA) .appendQueryParameter(QUERY_PARAMETER_PROTOCOL, QUERY_PARAMETER_PROTOCOL_VALUE) .build(); - resolver.delete(metadataContentUri, null, null); + 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); - resolver.insert(metadataContentUri, metadataValues); + client.insert(metadataContentUri, metadataValues); // Update the dictionary list. final Uri dictionaryContentUriBase = getProviderUriBuilder(clientId) @@ -405,7 +387,7 @@ public final class BinaryDictionaryFileDumper { final int length = dictionaryList.size(); for (int i = 0; i < length; ++i) { final DictionaryInfo info = dictionaryList.get(i); - resolver.insert(Uri.withAppendedPath(dictionaryContentUriBase, info.mId), + client.insert(Uri.withAppendedPath(dictionaryContentUriBase, info.mId), info.toContentValues()); } } diff --git a/java/src/com/android/inputmethod/research/LogStatement.java b/java/src/com/android/inputmethod/research/LogStatement.java index 059146ae6..09b12fcfa 100644 --- a/java/src/com/android/inputmethod/research/LogStatement.java +++ b/java/src/com/android/inputmethod/research/LogStatement.java @@ -35,7 +35,7 @@ import java.io.IOException; * associated with the {@code String[] keys} are likely to reveal information about the user. The * actual values are stored separately. */ -class LogStatement { +public class LogStatement { private static final String TAG = LogStatement.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; @@ -166,6 +166,8 @@ class LogStatement { /** * Write the contents out through jsonWriter. * + * The JsonWriter class must have already had {@code JsonWriter.beginArray} called on it. + * * Note that this method is not thread safe for the same jsonWriter. Callers must ensure * thread safety. */ diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java index a584a3af6..1a9a720f3 100644 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -110,7 +110,13 @@ import java.util.List; } /** - * Publish the contents of this LogUnit to researchLog. + * Publish the contents of this LogUnit to {@code researchLog}. + * + * For each publishable {@code LogStatement}, invoke {@link LogStatement#outputToLocked}. + * + * @param researchLog where to publish the contents of this {@code LogUnit} + * @param canIncludePrivateData whether the private data in this {@code LogUnit} should be + * included */ public synchronized void publishTo(final ResearchLog researchLog, final boolean canIncludePrivateData) { diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 24bf7d15f..5114977d8 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -81,10 +81,7 @@ public class ResearchLog { } } - public ResearchLog(final File outputFile, Context context) { - if (outputFile == null) { - throw new IllegalArgumentException(); - } + public ResearchLog(final File outputFile, final Context context) { mExecutor = Executors.newSingleThreadScheduledExecutor(); mFile = outputFile; mContext = context; @@ -112,7 +109,7 @@ public class ResearchLog { Log.d(TAG, "error when closing ResearchLog:"); e.printStackTrace(); } finally { - if (mFile.exists()) { + if (mFile != null && mFile.exists()) { mFile.setWritable(false, false); } if (onClosed != null) { @@ -139,7 +136,9 @@ public class ResearchLog { mHasWrittenData = false; } } finally { - mIsAbortSuccessful = mFile.delete(); + if (mFile != null) { + mIsAbortSuccessful = mFile.delete(); + } } return null; } @@ -209,7 +208,7 @@ public class ResearchLog { */ public JsonWriter getValidJsonWriterLocked() { try { - if (mJsonWriter == NULL_JSON_WRITER) { + if (mJsonWriter == NULL_JSON_WRITER && mFile != null) { final FileOutputStream fos = mContext.openFileOutput(mFile.getName(), Context.MODE_PRIVATE); mJsonWriter = new JsonWriter(new BufferedWriter(new OutputStreamWriter(fos))); |