BinaryDictionary.java revision 459cd6f8ef3eaa561e47dd996ce537770ea8b37a
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
3e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
7e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa *
10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin;
18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
194d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyangimport android.text.TextUtils;
203979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataokaimport android.util.SparseArray;
21fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka
22d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagiimport com.android.inputmethod.annotations.UsedForTesting;
23ab72a97d7ce44230a0c824797d1675a5ca354a56Tadashi G. Takaokaimport com.android.inputmethod.keyboard.ProximityInfo;
24bda7eaa63aace64f3d40eae3affaf281591ffa66Jean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
25a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.settings.NativeSuggestOptions;
26e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils;
27e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.JniUtils;
28e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.StringUtils;
29ab72a97d7ce44230a0c824797d1675a5ca354a56Tadashi G. Takaoka
302e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagiimport java.io.File;
31bda7eaa63aace64f3d40eae3affaf281591ffa66Jean Chalardimport java.util.ArrayList;
32923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport java.util.Arrays;
3343ebd8a035af31244a2d54fce5d8000a1fbada4csatokimport java.util.Locale;
345ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagiimport java.util.Map;
35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Implements a static, compacted, binary dictionary of standard words.
38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
396142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi// TODO: All methods which should be locked need to have a suffix "Locked".
40a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class BinaryDictionary extends Dictionary {
41b02ee3d67a1884b6ff59cc16c29a476845c0694fKen Wakasa    private static final String TAG = BinaryDictionary.class.getSimpleName();
42cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
436c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa    // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
44ffcbbaf12788a9fc9398607a548e552d7d2bf05eSatoshi Kataoka    private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
456c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa    // Must be equal to MAX_RESULTS in native/jni/src/defines.h
465db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa    private static final int MAX_RESULTS = 18;
47459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard    // The cutoff returned by native for auto-commit confidence.
48459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard    // Must be equal to CONFIDENCE_TO_AUTO_COMMIT in native/jni/src/defines.h
49459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard    private static final int CONFIDENCE_TO_AUTO_COMMIT = 1000000;
50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
5131097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    @UsedForTesting
5231097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    public static final String UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT";
5331097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    @UsedForTesting
5431097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    public static final String BIGRAM_COUNT_QUERY = "BIGRAM_COUNT";
5531097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi
565fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka    private long mNativeDict;
573979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    private final Locale mLocale;
584c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi    private final long mDictSize;
59d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    private final String mDictFilePath;
60f3850e554389dc3012584f9d81a4f2d3d4c89e44Ken Wakasa    private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
611e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS];
62f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa    private final int[] mSpaceIndices = new int[MAX_RESULTS];
636f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard    private final int[] mOutputScores = new int[MAX_RESULTS];
646931df9c17aaeb04288f937cabf956c1b9eb0cc9Jean Chalard    private final int[] mOutputTypes = new int[MAX_RESULTS];
65bb57090f1da9d1fc5a0eda9b627d3f8c8b25ab42Jean Chalard    // Only one result is ever used
66bb57090f1da9d1fc5a0eda9b627d3f8c8b25ab42Jean Chalard    private final int[] mOutputAutoCommitFirstWordConfidence = new int[1];
67923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
6850db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi    private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
693979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka
703979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    private final SparseArray<DicTraverseSession> mDicTraverseSessions =
71ecfbf4625c8afd9cde7b79e0c7846b87e20f79e9Tadashi G. Takaoka            CollectionUtils.newSparseArray();
728ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka
738ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka    // TODO: There should be a way to remove used DicTraverseSession objects from
748ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka    // {@code mDicTraverseSessions}.
75bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private DicTraverseSession getTraverseSession(final int traverseSessionId) {
768ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka        synchronized(mDicTraverseSessions) {
778ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
788ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            if (traverseSession == null) {
793979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka                traverseSession = mDicTraverseSessions.get(traverseSessionId);
803979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka                if (traverseSession == null) {
814c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi                    traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize);
823979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka                    mDicTraverseSessions.put(traverseSessionId, traverseSession);
833979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka                }
843979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka            }
858ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            return traverseSession;
863979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka        }
873979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    }
88923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
89923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
904250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * Constructor for the binary dictionary. This is supposed to be called from the
914250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * dictionary factory.
924250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param filename the name of the file to read through native code.
934250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param offset the offset of the dictionary data within the file.
944250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param length the length of the binary data.
9524aee9100e92dc4c06cdb54487a4922420fa8660Jean Chalard     * @param useFullEditDistance whether to use the full edit distance in suggestions
9605efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard     * @param dictType the dictionary type, as a human-readable string
97981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi     * @param isUpdatable whether to open the dictionary file in writable mode.
98923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
99f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa    public BinaryDictionary(final String filename, final long offset, final long length,
100981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi            final boolean useFullEditDistance, final Locale locale, final String dictType,
101981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi            final boolean isUpdatable) {
10205efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard        super(dictType);
1033979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka        mLocale = locale;
1044c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi        mDictSize = length;
105d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi        mDictFilePath = filename;
10650db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi        mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance);
107981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi        loadDictionary(filename, offset, length, isUpdatable);
108979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
109979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
110eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa    static {
111cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        JniUtils.loadNativeLibrary();
112eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa    }
113c2bbc6a4499a6da979381fa0e8e6e855a5ac6aa4Jean Chalard
1145ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi    private static native boolean createEmptyDictFileNative(String filePath, long dictVersion,
1155ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            String[] attributeKeyStringArray, String[] attributeValueStringArray);
116f3b62900c7bcb0d6434f45ec7b467b7b4bad6f9aKeisuke Kuroynagi    private static native long openNative(String sourceDir, long dictOffset, long dictSize,
117f3b62900c7bcb0d6434f45ec7b467b7b4bad6f9aKeisuke Kuroynagi            boolean isUpdatable);
118d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    private static native void flushNative(long dict, String filePath);
119c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi    private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC);
120d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    private static native void flushWithGCNative(long dict, String filePath);
1215db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa    private static native void closeNative(long dict);
122e0e67373735918c78eaeaf24f127e1d28816aa29Satoshi Kataoka    private static native int getProbabilityNative(long dict, int[] word);
1234d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1);
1245db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa    private static native int getSuggestionsNative(long dict, long proximityInfo,
1255db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa            long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
1265db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa            int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
12750db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi            int[] suggestOptions, int[] prevWordCodePointArray,
12824aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard            int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes,
12924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard            int[] outputAutoCommitFirstWordConfidence);
1301e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
1311e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    private static native int editDistanceNative(int[] before, int[] after);
1325bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi    private static native void addUnigramWordNative(long dict, int[] word, int probability);
1335bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi    private static native void addBigramWordsNative(long dict, int[] word0, int[] word1,
1345bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi            int probability);
1355bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi    private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1);
1364d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    private static native int calculateProbabilityNative(long dict, int unigramProbability,
1374d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi            int bigramProbability);
13831097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    private static native String getPropertyNative(long dict, String query);
139979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1405ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi    @UsedForTesting
1415ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi    public static boolean createEmptyDictFile(final String filePath, final long dictVersion,
1425ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            final Map<String, String> attributeMap) {
1435ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        final String[] keyArray = new String[attributeMap.size()];
1445ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        final String[] valueArray = new String[attributeMap.size()];
1455ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        int index = 0;
1465ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        for (final String key : attributeMap.keySet()) {
1475ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            keyArray[index] = key;
1485ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            valueArray[index] = attributeMap.get(key);
1495ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            index++;
1505ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        }
1515ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        return createEmptyDictFileNative(filePath, dictVersion, keyArray, valueArray);
1525ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi    }
1535ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi
154e9f3e182e4b3217282831fd8805958270b4dbba3Satoshi Kataoka    // TODO: Move native dict into session
155bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private final void loadDictionary(final String path, final long startOffset,
156981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi            final long length, final boolean isUpdatable) {
157981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi        mNativeDict = openNative(path, startOffset, length, isUpdatable);
158979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
159979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
160979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    @Override
161b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
1622dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard            final String prevWord, final ProximityInfo proximityInfo,
163fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
1642dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard        return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords,
165fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi                additionalFeaturesOptions, 0 /* sessionId */);
1663979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    }
1673979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka
1683979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    @Override
1693979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
1702dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard            final String prevWord, final ProximityInfo proximityInfo,
171fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
172fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi            final int sessionId) {
173b4b93dbf3e0912ba26c5c34abba7a5b94c74138cJean Chalard        if (!isValidDictionary()) return null;
1743979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka
175ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka        Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
176ea98e026f1ad7732279aec6d06107f46ea0af93dJean Chalard        // TODO: toLowerCase in the native code
1775e21ea1a3553000529c288acdf6d6a4b165bedc5Tadashi Takaoka        final int[] prevWordCodePointArray = (null == prevWord)
178bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka                ? null : StringUtils.toCodePointArray(prevWord);
179860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard        final int composerSize = composer.size();
180860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard
181e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard        final boolean isGesture = composer.isBatchMode();
182e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard        if (composerSize <= 1 || !isGesture) {
183860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            if (composerSize > MAX_WORD_LENGTH - 1) return null;
184860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            for (int i = 0; i < composerSize; i++) {
185f3850e554389dc3012584f9d81a4f2d3d4c89e44Ken Wakasa                mInputCodePoints[i] = composer.getCodeAt(i);
186860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            }
187860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard        }
188860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard
189251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        final InputPointers ips = composer.getInputPointers();
1905db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa        final int inputSize = isGesture ? ips.getPointerSize() : composerSize;
19150db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi        mNativeSuggestOptions.setIsGesture(isGesture);
192fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi        mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions);
193251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        // proximityInfo and/or prevWordForBigrams may not be null.
194f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa        final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
195f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa                getTraverseSession(sessionId).getSession(), ips.getXCoordinates(),
196f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa                ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints,
19750db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi                inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(),
19850db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi                prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices,
19924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard                mOutputTypes, mOutputAutoCommitFirstWordConfidence);
2005f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka        final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
201f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok        for (int j = 0; j < count; ++j) {
202f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok            final int start = j * MAX_WORD_LENGTH;
203979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            int len = 0;
2041e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa            while (len < MAX_WORD_LENGTH && mOutputCodePoints[start + len] != 0) {
205f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok                ++len;
206979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
207979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (len > 0) {
20899e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS;
2092dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard                if (blockOffensiveWords
210bb61293675234f6e5e1312db70618be985b37a83Jean Chalard                        && 0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE)
21199e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                        && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) {
212bb61293675234f6e5e1312db70618be985b37a83Jean Chalard                    // If we block potentially offensive words, and if the word is possibly
213bb61293675234f6e5e1312db70618be985b37a83Jean Chalard                    // offensive, then we don't output it unless it's also an exact match.
21499e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                    continue;
21599e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                }
21699e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                final int kind = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_KIND;
21799e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                final int score = SuggestedWordInfo.KIND_WHITELIST == kind
218926ef06a1ef8a0f8e01baa26c23172b93012d2b1Jean Chalard                        ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
21999e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                // TODO: check that all users of the `kind' parameter are ready to accept
22099e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                // flags too and pass mOutputTypes[j] instead of kind
2211e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
222e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard                        score, kind, this /* sourceDict */,
223e8754aba1c8f217e7ca828de25e0506ac58daa99Keisuke Kuroyanagi                        mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
22424aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard                        mOutputAutoCommitFirstWordConfidence[0]));
225979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
226979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
227d82898c5a91f8aa69d5dc594b7a9290b8be1247aJean Chalard        return suggestions;
228923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
229923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
23015f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    public boolean isValidDictionary() {
2316f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka        return mNativeDict != 0;
2326f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka    }
2336f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka
234bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public static float calcNormalizedScore(final String before, final String after,
235bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final int score) {
2361e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa        return calcNormalizedScoreNative(StringUtils.toCodePointArray(before),
2371e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                StringUtils.toCodePointArray(after), score);
238be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok    }
239be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok
240bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public static int editDistance(final String before, final String after) {
24166d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka        if (before == null || after == null) {
24266d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka            throw new IllegalArgumentException();
24366d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka        }
2441e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa        return editDistanceNative(StringUtils.toCodePointArray(before),
2451e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                StringUtils.toCodePointArray(after));
246be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok    }
247be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok
248923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
249bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public boolean isValidWord(final String word) {
2504d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        return getFrequency(word) != NOT_A_PROBABILITY;
251c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka    }
252c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka
253c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka    @Override
254bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public int getFrequency(final String word) {
2554d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        if (word == null) return NOT_A_PROBABILITY;
256bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        int[] codePoints = StringUtils.toCodePointArray(word);
257e0e67373735918c78eaeaf24f127e1d28816aa29Satoshi Kataoka        return getProbabilityNative(mNativeDict, codePoints);
258923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
259c3df2d6fd27f3a5b84040b59aece3367769f0cb6Amith Yamasani
2604d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
2614d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    // calls when checking for changes in an entire dictionary.
262f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    public boolean isValidBigram(final String word0, final String word1) {
2634d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        return getBigramProbability(word0, word1) != NOT_A_PROBABILITY;
2644d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    }
2654d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi
2664d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    public int getBigramProbability(final String word0, final String word1) {
2674d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY;
268f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints0 = StringUtils.toCodePointArray(word0);
2691e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa        final int[] codePoints1 = StringUtils.toCodePointArray(word1);
2704d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
271f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    }
272f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi
273f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    // Add a unigram entry to binary dictionary in native code.
274f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    public void addUnigramWord(final String word, final int probability) {
275f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        if (TextUtils.isEmpty(word)) {
276f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi            return;
277f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        }
278f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints = StringUtils.toCodePointArray(word);
279f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        addUnigramWordNative(mNativeDict, codePoints, probability);
280f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    }
281f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi
282f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    // Add a bigram entry to binary dictionary in native code.
283f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    public void addBigramWords(final String word0, final String word1, final int probability) {
284f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
285f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi            return;
286f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        }
287f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints0 = StringUtils.toCodePointArray(word0);
288f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints1 = StringUtils.toCodePointArray(word1);
289f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability);
290f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    }
291f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi
292f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    // Remove a bigram entry form binary dictionary in native code.
293f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    public void removeBigramWords(final String word0, final String word1) {
294f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
295f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi            return;
296f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        }
297f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints0 = StringUtils.toCodePointArray(word0);
298f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints1 = StringUtils.toCodePointArray(word1);
299f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        removeBigramWordsNative(mNativeDict, codePoints0, codePoints1);
3004d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    }
3014d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang
3026142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    private void reopen() {
3036142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        close();
3042e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi        final File dictFile = new File(mDictFilePath);
3052e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi        mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */,
3062e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi                dictFile.length(), true /* isUpdatable */);
307d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    }
308d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi
3096142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    public void flush() {
3106142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        if (!isValidDictionary()) return;
3116142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        flushNative(mNativeDict, mDictFilePath);
3126142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        reopen();
3136142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    }
3146142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi
315d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    public void flushWithGC() {
316d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi        if (!isValidDictionary()) return;
317d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi        flushWithGCNative(mNativeDict, mDictFilePath);
3186142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        reopen();
319d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    }
320d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi
321c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi    /**
322c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     * Checks whether GC is needed to run or not.
323c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     * @param mindsBlockByGC Whether to mind operations blocked by GC. We don't need to care about
324c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     * the blocking in some situations such as in idle time or just before closing.
325c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     * @return whether GC is needed to run or not.
326c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     */
327c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi    public boolean needsToRunGC(final boolean mindsBlockByGC) {
328d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi        if (!isValidDictionary()) return false;
329c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi        return needsToRunGCNative(mNativeDict, mindsBlockByGC);
330d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    }
331d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi
3324d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    @UsedForTesting
3334d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    public int calculateProbability(final int unigramProbability, final int bigramProbability) {
3344d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        if (!isValidDictionary()) return NOT_A_PROBABILITY;
3354d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability);
3364d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    }
3374d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi
33831097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    @UsedForTesting
33931097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    public String getPropertyForTests(String query) {
34031097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi        if (!isValidDictionary()) return "";
34131097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi        return getPropertyNative(mNativeDict, query);
34231097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    }
34331097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi
34436fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    @Override
34524aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard    public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
346459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard        return candidate.mAutoCommitFirstWordConfidence > CONFIDENCE_TO_AUTO_COMMIT;
34724aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard    }
34824aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard
34924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard    @Override
3508ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka    public void close() {
3518ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka        synchronized (mDicTraverseSessions) {
3528ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            final int sessionsSize = mDicTraverseSessions.size();
3538ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            for (int index = 0; index < sessionsSize; ++index) {
3548ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka                final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index);
3558ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka                if (traverseSession != null) {
3568ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka                    traverseSession.close();
3578ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka                }
3583979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka            }
3596142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi            mDicTraverseSessions.clear();
3603979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka        }
3616142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        closeInternalLocked();
362da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa    }
363da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa
3646142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    private synchronized void closeInternalLocked() {
365923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (mNativeDict != 0) {
366923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            closeNative(mNativeDict);
367923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mNativeDict = 0;
368923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
370923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
3716142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    // TODO: Manage BinaryDictionary instances without using WeakReference or something.
372923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
373923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    protected void finalize() throws Throwable {
374da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        try {
3756142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi            closeInternalLocked();
376da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        } finally {
377da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa            super.finalize();
378da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        }
379923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
380923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
381