BinaryDictionary.java revision 251f302985bc491f4dd54983e9c69c5dc76cb834
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
3e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa *
4923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of
6923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License at
7e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa *
8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * 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
11923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * License for the specific language governing permissions and limitations under
14923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * 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
19fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport android.content.Context;
204d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyangimport android.text.TextUtils;
21fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka
22ab72a97d7ce44230a0c824797d1675a5ca354a56Tadashi G. Takaokaimport com.android.inputmethod.keyboard.ProximityInfo;
23bda7eaa63aace64f3d40eae3affaf281591ffa66Jean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
24ab72a97d7ce44230a0c824797d1675a5ca354a56Tadashi G. Takaoka
25bda7eaa63aace64f3d40eae3affaf281591ffa66Jean Chalardimport java.util.ArrayList;
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport java.util.Arrays;
2743ebd8a035af31244a2d54fce5d8000a1fbada4csatokimport java.util.Locale;
28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
30923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Implements a static, compacted, binary dictionary of standard words.
31923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
32923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpublic class BinaryDictionary extends Dictionary {
33923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
34cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    public static final String DICTIONARY_PACK_AUTHORITY =
35cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            "com.android.inputmethod.latin.dictionarypack";
36cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
37979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
38cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * There is a difference between what java and native code can handle.
39979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * This value should only be used in BinaryDictionary.java
40979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * It is necessary to keep it at this value because some languages e.g. German have
41979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * really long words.
42979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
438fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public static final int MAX_WORD_LENGTH = 48;
442e04770adfc16344f69d316efd3ed0a617ede330Tadashi G. Takaoka    public static final int MAX_WORDS = 18;
4573680097996ea2ddbca3f84144a00ce3ba66b763Satoshi Kataoka    public static final int MAX_SPACES = 16;
46979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
47979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private static final String TAG = "BinaryDictionary";
48b7d7c5a369cef80d4319de8e433501ab25b49615Jean Chalard    private static final int MAX_PREDICTIONS = 60;
49b7d7c5a369cef80d4319de8e433501ab25b49615Jean Chalard    private static final int MAX_RESULTS = Math.max(MAX_PREDICTIONS, MAX_WORDS);
50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
51923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int TYPED_LETTER_MULTIPLIER = 2;
52923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
535fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka    private long mNativeDict;
546ba8de2a608dfe4865b0b59a753f2d2abbedeeffsatok    private final int[] mInputCodes = new int[MAX_WORD_LENGTH];
556f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard    private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS];
5673680097996ea2ddbca3f84144a00ce3ba66b763Satoshi Kataoka    private final int[] mSpaceIndices = new int[MAX_SPACES];
576f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard    private final int[] mOutputScores = new int[MAX_RESULTS];
58923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
59338d3ec725a952cbe603ac8b2d49c337463f4093Jean Chalard    private final boolean mUseFullEditDistance;
60923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
61923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
624250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * Constructor for the binary dictionary. This is supposed to be called from the
634250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * dictionary factory.
644250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * All implementations should pass null into flagArray, except for testing purposes.
654250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param context the context to access the environment from.
664250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param filename the name of the file to read through native code.
674250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param offset the offset of the dictionary data within the file.
684250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param length the length of the binary data.
6924aee9100e92dc4c06cdb54487a4922420fa8660Jean Chalard     * @param useFullEditDistance whether to use the full edit distance in suggestions
7005efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard     * @param dictType the dictionary type, as a human-readable string
71923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
724250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard    public BinaryDictionary(final Context context,
7324aee9100e92dc4c06cdb54487a4922420fa8660Jean Chalard            final String filename, final long offset, final long length,
7405efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard            final boolean useFullEditDistance, final Locale locale, final String dictType) {
7505efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard        super(dictType);
76338d3ec725a952cbe603ac8b2d49c337463f4093Jean Chalard        mUseFullEditDistance = useFullEditDistance;
774250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard        loadDictionary(filename, offset, length);
78979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
79979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
80eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa    static {
81cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        JniUtils.loadNativeLibrary();
82eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa    }
83c2bbc6a4499a6da979381fa0e8e6e855a5ac6aa4Jean Chalard
845fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka    private native long openNative(String sourceDir, long dictOffset, long dictSize,
85b7d7c5a369cef80d4319de8e433501ab25b49615Jean Chalard            int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords,
86b7d7c5a369cef80d4319de8e433501ab25b49615Jean Chalard            int maxPredictions);
875fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka    private native void closeNative(long dict);
882f854e170c9fde47cae804145f90d164cdb5ceb8Satoshi Kataoka    private native int getFrequencyNative(long dict, int[] word, int wordLength);
894d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    private native boolean isValidBigramNative(long dict, int[] word1, int[] word2);
905fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka    private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates,
9173680097996ea2ddbca3f84144a00ce3ba66b763Satoshi Kataoka            int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodes, int codesSize,
9205efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard            int commitPoint, boolean isGesture,
9373680097996ea2ddbca3f84144a00ce3ba66b763Satoshi Kataoka            int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars,
9473680097996ea2ddbca3f84144a00ce3ba66b763Satoshi Kataoka            int[] scores, int[] outputIndices);
95522a04ea5b249d0af556647d2abcad57e5b99b4fJean Chalard    private native int getBigramsNative(long dict, int[] prevWord, int prevWordLength,
966a5d17cd2f55cdab01900af8933cb71b97b73a29Jean Chalard            int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores);
970028ed3627ff4f37a62a80f3b2c857e373cd5090satok    private static native float calcNormalizedScoreNative(
98be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok            char[] before, int beforeLength, char[] after, int afterLength, int score);
99be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok    private static native int editDistanceNative(
100be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok            char[] before, int beforeLength, char[] after, int afterLength);
101979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
10233e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka    private final void loadDictionary(String path, long startOffset, long length) {
103b7d7c5a369cef80d4319de8e433501ab25b49615Jean Chalard        mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER,
104b7d7c5a369cef80d4319de8e433501ab25b49615Jean Chalard                FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS);
105979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
106979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
107979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    @Override
108b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
109b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard            final CharSequence prevWord, final ProximityInfo proximityInfo) {
110b4b93dbf3e0912ba26c5c34abba7a5b94c74138cJean Chalard        if (!isValidDictionary()) return null;
111b4b93dbf3e0912ba26c5c34abba7a5b94c74138cJean Chalard        Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE);
112b4b93dbf3e0912ba26c5c34abba7a5b94c74138cJean Chalard        Arrays.fill(mOutputChars, (char) 0);
113b4b93dbf3e0912ba26c5c34abba7a5b94c74138cJean Chalard        Arrays.fill(mOutputScores, 0);
114ea98e026f1ad7732279aec6d06107f46ea0af93dJean Chalard        // TODO: toLowerCase in the native code
1155e21ea1a3553000529c288acdf6d6a4b165bedc5Tadashi Takaoka        final int[] prevWordCodePointArray = (null == prevWord)
1165e21ea1a3553000529c288acdf6d6a4b165bedc5Tadashi Takaoka                ? null : StringUtils.toCodePointArray(prevWord.toString());
117860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard        final int composerSize = composer.size();
118860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard
119e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard        final boolean isGesture = composer.isBatchMode();
120e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard        if (composerSize <= 1 || !isGesture) {
121860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            if (composerSize > MAX_WORD_LENGTH - 1) return null;
122860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            for (int i = 0; i < composerSize; i++) {
123860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard                mInputCodes[i] = composer.getCodeAt(i);
124860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            }
125860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard        }
126860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard
127251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        // TODO: move this test to native code.
128251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        if (composerSize <= 1 && TextUtils.isEmpty(prevWord)) return null;
129251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        final InputPointers ips = composer.getInputPointers();
130251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        final int codesSize = isGesture ? ips.getPointerSize() : composerSize;
131251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        // proximityInfo and/or prevWordForBigrams may not be null.
132251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        final int tmpCount = getSuggestionsNative(mNativeDict,
133251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard                proximityInfo.getNativeProximityInfo(), ips.getXCoordinates(),
134251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard                ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
1359c09fd02eb98ebb938e8033371c7e0d4c5ce2f52Jean Chalard                mInputCodes, codesSize, 0 /* unused */, isGesture, prevWordCodePointArray,
1369c09fd02eb98ebb938e8033371c7e0d4c5ce2f52Jean Chalard                mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices);
137251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        final int count = Math.min(tmpCount, MAX_PREDICTIONS);
138e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard
139bda7eaa63aace64f3d40eae3affaf281591ffa66Jean Chalard        final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
140f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok        for (int j = 0; j < count; ++j) {
141e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard            if (composerSize > 0 && mOutputScores[j] < 1) break;
142f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok            final int start = j * MAX_WORD_LENGTH;
143979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            int len = 0;
1446f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard            while (len <  MAX_WORD_LENGTH && mOutputChars[start + len] != 0) {
145f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok                ++len;
146979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
147979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (len > 0) {
148bda7eaa63aace64f3d40eae3affaf281591ffa66Jean Chalard                suggestions.add(new SuggestedWordInfo(
1496f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard                        new String(mOutputChars, start, len),
1506f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard                        mOutputScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType));
151979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
152979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
153d82898c5a91f8aa69d5dc594b7a9290b8be1247aJean Chalard        return suggestions;
154923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
155923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1566f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka    /* package for test */ boolean isValidDictionary() {
1576f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka        return mNativeDict != 0;
1586f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka    }
1596f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka
1600028ed3627ff4f37a62a80f3b2c857e373cd5090satok    public static float calcNormalizedScore(String before, String after, int score) {
161be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok        return calcNormalizedScoreNative(before.toCharArray(), before.length(),
162be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok                after.toCharArray(), after.length(), score);
163be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok    }
164be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok
165be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok    public static int editDistance(String before, String after) {
166be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok        return editDistanceNative(
167be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok                before.toCharArray(), before.length(), after.toCharArray(), after.length());
168be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok    }
169be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok
170923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
171923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean isValidWord(CharSequence word) {
172c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka        return getFrequency(word) >= 0;
173c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka    }
174c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka
175c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka    @Override
176c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka    public int getFrequency(CharSequence word) {
177c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka        if (word == null) return -1;
178522a04ea5b249d0af556647d2abcad57e5b99b4fJean Chalard        int[] chars = StringUtils.toCodePointArray(word.toString());
179c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka        return getFrequencyNative(mNativeDict, chars, chars.length);
180923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
181c3df2d6fd27f3a5b84040b59aece3367769f0cb6Amith Yamasani
1824d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
1834d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    // calls when checking for changes in an entire dictionary.
1844d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    public boolean isValidBigram(CharSequence word1, CharSequence word2) {
1854d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang        if (TextUtils.isEmpty(word1) || TextUtils.isEmpty(word2)) return false;
1864d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang        int[] chars1 = StringUtils.toCodePointArray(word1.toString());
1874d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang        int[] chars2 = StringUtils.toCodePointArray(word2.toString());
1884d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang        return isValidBigramNative(mNativeDict, chars1, chars2);
1894d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    }
1904d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang
19136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    @Override
192923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public synchronized void close() {
193da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        closeInternal();
194da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa    }
195da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa
196da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa    private void closeInternal() {
197923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (mNativeDict != 0) {
198923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            closeNative(mNativeDict);
199923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mNativeDict = 0;
200923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
201923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
202923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
203923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
204923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    protected void finalize() throws Throwable {
205da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        try {
206da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa            closeInternal();
207da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        } finally {
208da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa            super.finalize();
209da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        }
210923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
211923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
212