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