aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java173
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java12
-rw-r--r--java/src/com/android/inputmethod/latin/FileTransforms.java40
3 files changed, 169 insertions, 56 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index ed5f83b3b..24bb7b78a 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -63,10 +63,10 @@ public class BinaryDictionaryFileDumper {
}
/**
- * Queries a content provider for the list of dictionaries for a specific locale
+ * Queries a content provider for the list of word lists for a specific locale
* available to copy into Latin IME.
*/
- private static List<String> getDictIdList(final Locale locale, final Context context) {
+ private static List<String> getWordListIds(final Locale locale, final Context context) {
final ContentResolver resolver = context.getContentResolver();
final Uri dictionaryPackUri = getProviderUri(locale.toString());
@@ -87,77 +87,150 @@ public class BinaryDictionaryFileDumper {
return list;
}
+
/**
- * Queries a content provider for dictionary data for some locale and cache the returned files
- *
- * This will query a content provider for dictionary data for a given locale, and copy the
- * files locally so that they can be mmap'ed. This may overwrite previously cached dictionaries
- * with newer versions if a newer version is made available by the content provider.
- * @returns the addresses of the files, or null if no data could be obtained.
- * @throw FileNotFoundException if the provider returns non-existent data.
- * @throw IOException if the provider-returned data could not be read.
+ * Helper method to encapsulate exception handling.
*/
- public static List<AssetFileAddress> cacheDictionariesFromContentProvider(final Locale locale,
- final Context context) {
- final ContentResolver resolver = context.getContentResolver();
- final List<String> idList = getDictIdList(locale, context);
- final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
- for (String id : idList) {
- final Uri wordListUri = getProviderUri(id);
+ private static AssetFileDescriptor openAssetFileDescriptor(final ContentResolver resolver,
+ final Uri uri) {
+ try {
+ return resolver.openAssetFileDescriptor(uri, "r");
+ } catch (FileNotFoundException e) {
+ // I don't want to log the word list URI here for security concerns
+ Log.e(TAG, "Could not find a word list from the dictionary provider.");
+ return null;
+ }
+ }
+
+ /**
+ * Caches a word list the id of which is passed as an argument. This will write the file
+ * to the cache file name designated by its id and locale, overwriting it if already present
+ * and creating it (and its containing directory) if necessary.
+ */
+ private static AssetFileAddress cacheWordList(final String id, final Locale locale,
+ final ContentResolver resolver, final Context context) {
+
+ final int COMPRESSED_CRYPTED_COMPRESSED = 0;
+ final int CRYPTED_COMPRESSED = 1;
+ final int COMPRESSED_CRYPTED = 2;
+ final int COMPRESSED_ONLY = 3;
+ final int CRYPTED_ONLY = 4;
+ final int NONE = 5;
+ final int MODE_MIN = COMPRESSED_CRYPTED_COMPRESSED;
+ final int MODE_MAX = NONE;
+
+ final Uri wordListUri = getProviderUri(id);
+ final String outputFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context);
+
+ for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) {
+ InputStream originalSourceStream = null;
+ InputStream inputStream = null;
+ FileOutputStream outputStream = null;
AssetFileDescriptor afd = null;
try {
- afd = resolver.openAssetFileDescriptor(wordListUri, "r");
- } catch (FileNotFoundException e) {
- // leave null inside afd and continue
- }
- if (null == afd) continue;
- try {
- final String fileName = copyFileTo(afd.createInputStream(),
- BinaryDictionaryGetter.getCacheFileName(id, locale, context));
- afd.close();
+ // Open input.
+ afd = openAssetFileDescriptor(resolver, wordListUri);
+ // If we can't open it at all, don't even try a number of times.
+ if (null == afd) return null;
+ originalSourceStream = afd.createInputStream();
+ // Open output.
+ outputStream = new FileOutputStream(outputFileName);
+ // Get the appropriate decryption method for this try
+ switch (mode) {
+ case COMPRESSED_CRYPTED_COMPRESSED:
+ inputStream = FileTransforms.getUncompressedStream(
+ FileTransforms.getDecryptedStream(
+ FileTransforms.getUncompressedStream(
+ originalSourceStream)));
+ break;
+ case CRYPTED_COMPRESSED:
+ inputStream = FileTransforms.getUncompressedStream(
+ FileTransforms.getDecryptedStream(originalSourceStream));
+ break;
+ case COMPRESSED_CRYPTED:
+ inputStream = FileTransforms.getDecryptedStream(
+ FileTransforms.getUncompressedStream(originalSourceStream));
+ break;
+ case COMPRESSED_ONLY:
+ inputStream = FileTransforms.getUncompressedStream(originalSourceStream);
+ break;
+ case CRYPTED_ONLY:
+ inputStream = FileTransforms.getDecryptedStream(originalSourceStream);
+ break;
+ case NONE:
+ inputStream = originalSourceStream;
+ break;
+ }
+ copyFileTo(inputStream, outputStream);
if (0 >= resolver.delete(wordListUri, null, null)) {
- // I'd rather not print the word list ID to the log out of security concerns
Log.e(TAG, "Could not have the dictionary pack delete a word list");
}
- fileAddressList.add(AssetFileAddress.makeFromFileName(fileName));
- } catch (IOException e) {
- // Can't read the file for some reason. Continue onto the next file.
- Log.e(TAG, "Cannot read a word list from the dictionary pack : " + e);
+ // Success! Close files (through the finally{} clause) and return.
+ return AssetFileAddress.makeFromFileName(outputFileName);
+ } catch (Exception e) {
+ Log.e(TAG, "Can't open word list in mode " + mode + " : " + e);
+ // Try the next method.
+ } finally {
+ // Ignore exceptions while closing files.
+ try {
+ // afd.close() will close inputStream, we should not call inputStream.close().
+ if (null != afd) afd.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while closing a cross-process file descriptor : " + e);
+ }
+ try {
+ if (null != outputStream) outputStream.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while closing a file : " + e);
+ }
}
}
- return fileAddressList;
+
+ // We could not copy the file at all. This is very unexpected.
+ // I'd rather not print the word list ID to the log out of security concerns
+ Log.e(TAG, "Could not copy a word list. Will not be able to use it.");
+ // If we can't copy it we should probably delete it to avoid trying to copy it over
+ // and over each time we open LatinIME.
+ if (0 >= resolver.delete(wordListUri, null, null)) {
+ Log.e(TAG, "In addition, we were unable to delete it.");
+ }
+ return null;
}
/**
- * Accepts a resource number as dictionary data for some locale and returns the name of a file.
+ * Queries a content provider for word list data for some locale and cache the returned files
*
- * This will make the resource the cached dictionary for this locale, overwriting any previous
- * cached data.
+ * This will query a content provider for word list data for a given locale, and copy the
+ * files locally so that they can be mmap'ed. This may overwrite previously cached word lists
+ * with newer versions if a newer version is made available by the content provider.
+ * @returns the addresses of the word list files, or null if no data could be obtained.
+ * @throw FileNotFoundException if the provider returns non-existent data.
+ * @throw IOException if the provider-returned data could not be read.
*/
- public static String getDictionaryFileFromResource(int resource, Locale locale,
- Context context) throws FileNotFoundException, IOException {
- final Resources res = context.getResources();
- final Locale savedLocale = Utils.setSystemLocale(res, locale);
- final InputStream stream = res.openRawResource(resource);
- Utils.setSystemLocale(res, savedLocale);
- return copyFileTo(stream,
- BinaryDictionaryGetter.getCacheFileName(Integer.toString(resource),
- locale, context));
+ public static List<AssetFileAddress> cacheWordListsFromContentProvider(final Locale locale,
+ final Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ final List<String> idList = getWordListIds(locale, context);
+ final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
+ for (String id : idList) {
+ final AssetFileAddress afd = cacheWordList(id, locale, resolver, context);
+ if (null != afd) {
+ fileAddressList.add(afd);
+ }
+ }
+ return fileAddressList;
}
/**
- * Copies the data in an input stream to a target file, creating the file if necessary and
- * overwriting it if it already exists.
+ * Copies the data in an input stream to a target file.
* @param input the stream to be copied.
- * @param outputFileName the name of a file to copy the data to. It is created if necessary.
+ * @param outputFile an outputstream to copy the data to.
*/
- private static String copyFileTo(final InputStream input, final String outputFileName)
+ private static void copyFileTo(final InputStream input, final FileOutputStream output)
throws FileNotFoundException, IOException {
final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE];
- final FileOutputStream output = new FileOutputStream(outputFileName);
for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer))
output.write(buffer, 0, readBytes);
input.close();
- return outputFileName;
}
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 5d2dab0a9..38344300c 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -205,7 +205,7 @@ class BinaryDictionaryGetter {
* @param context the context on which to open the files upon.
* @return an array of binary dictionary files, which may be empty but may not be null.
*/
- private static File[] getCachedDictionaryList(final Locale locale,
+ private static File[] getCachedWordLists(final Locale locale,
final Context context) {
final String directoryName = getCacheDirectoryForLocale(locale, context);
final File[] cacheFiles = new File(directoryName).listFiles();
@@ -235,11 +235,11 @@ class BinaryDictionaryGetter {
public static List<AssetFileAddress> getDictionaryFiles(final Locale locale,
final Context context, final int fallbackResId) {
- // cacheDictionariesFromContentProvider returns the list of files it copied to local
+ // cacheWordListsFromContentProvider returns the list of files it copied to local
// storage, but we don't really care about what was copied NOW: what we want is the
// list of everything we ever cached, so we ignore the return value.
- BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context);
- final File[] cachedDictionaryList = getCachedDictionaryList(locale, context);
+ BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context);
+ final File[] cachedWordLists = getCachedWordLists(locale, context);
final String mainDictId = getMainDictId(locale);
@@ -247,8 +247,8 @@ class BinaryDictionaryGetter {
boolean foundMainDict = false;
final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>();
- // cachedDictionaryList may not be null, see doc for getCachedDictionaryList
- for (final File f : cachedDictionaryList) {
+ // cachedWordLists may not be null, see doc for getCachedDictionaryList
+ for (final File f : cachedWordLists) {
final String wordListId = getWordListIdFromFileName(f.getName());
if (wordListId.equals(mainDictId)) {
foundMainDict = true;
diff --git a/java/src/com/android/inputmethod/latin/FileTransforms.java b/java/src/com/android/inputmethod/latin/FileTransforms.java
new file mode 100644
index 000000000..d0374e01e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/FileTransforms.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+
+public class FileTransforms {
+ public static OutputStream getCryptedStream(OutputStream out) {
+ // Crypt the stream.
+ return out;
+ }
+
+ public static InputStream getDecryptedStream(InputStream in) {
+ // Decrypt the stream.
+ return in;
+ }
+
+ public static InputStream getUncompressedStream(InputStream in) throws IOException {
+ return new GZIPInputStream(in);
+ }
+}