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"; 55cfb018ba6db78f2b33b54d4811f0bf166db29792Keisuke Kuroyanagi @UsedForTesting 56cfb018ba6db78f2b33b54d4811f0bf166db29792Keisuke Kuroyanagi public static final String MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT"; 57cfb018ba6db78f2b33b54d4811f0bf166db29792Keisuke Kuroyanagi @UsedForTesting 58cfb018ba6db78f2b33b54d4811f0bf166db29792Keisuke Kuroyanagi public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT"; 5931097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi 605fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka private long mNativeDict; 613979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka private final Locale mLocale; 624c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi private final long mDictSize; 63d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi private final String mDictFilePath; 64f3850e554389dc3012584f9d81a4f2d3d4c89e44Ken Wakasa private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; 651e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS]; 66f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa private final int[] mSpaceIndices = new int[MAX_RESULTS]; 676f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard private final int[] mOutputScores = new int[MAX_RESULTS]; 686931df9c17aaeb04288f937cabf956c1b9eb0cc9Jean Chalard private final int[] mOutputTypes = new int[MAX_RESULTS]; 69bb57090f1da9d1fc5a0eda9b627d3f8c8b25ab42Jean Chalard // Only one result is ever used 70bb57090f1da9d1fc5a0eda9b627d3f8c8b25ab42Jean Chalard private final int[] mOutputAutoCommitFirstWordConfidence = new int[1]; 71923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 7250db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(); 733979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka 743979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka private final SparseArray<DicTraverseSession> mDicTraverseSessions = 75ecfbf4625c8afd9cde7b79e0c7846b87e20f79e9Tadashi G. Takaoka CollectionUtils.newSparseArray(); 768ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka 778ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka // TODO: There should be a way to remove used DicTraverseSession objects from 788ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka // {@code mDicTraverseSessions}. 79bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka private DicTraverseSession getTraverseSession(final int traverseSessionId) { 808ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka synchronized(mDicTraverseSessions) { 818ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId); 828ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka if (traverseSession == null) { 833979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka traverseSession = mDicTraverseSessions.get(traverseSessionId); 843979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka if (traverseSession == null) { 854c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize); 863979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka mDicTraverseSessions.put(traverseSessionId, traverseSession); 873979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 883979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 898ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka return traverseSession; 903979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 913979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 92923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 93923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 944250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * Constructor for the binary dictionary. This is supposed to be called from the 954250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * dictionary factory. 964250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * @param filename the name of the file to read through native code. 974250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * @param offset the offset of the dictionary data within the file. 984250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * @param length the length of the binary data. 9924aee9100e92dc4c06cdb54487a4922420fa8660Jean Chalard * @param useFullEditDistance whether to use the full edit distance in suggestions 10005efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard * @param dictType the dictionary type, as a human-readable string 101981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi * @param isUpdatable whether to open the dictionary file in writable mode. 102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 103f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa public BinaryDictionary(final String filename, final long offset, final long length, 104981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi final boolean useFullEditDistance, final Locale locale, final String dictType, 105981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi final boolean isUpdatable) { 10605efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard super(dictType); 1073979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka mLocale = locale; 1084c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi mDictSize = length; 109d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi mDictFilePath = filename; 11050db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance); 111981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi loadDictionary(filename, offset, length, isUpdatable); 112979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 113979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 114eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa static { 115cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka JniUtils.loadNativeLibrary(); 116eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa } 117c2bbc6a4499a6da979381fa0e8e6e855a5ac6aa4Jean Chalard 1185ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi private static native boolean createEmptyDictFileNative(String filePath, long dictVersion, 1195ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi String[] attributeKeyStringArray, String[] attributeValueStringArray); 120f3b62900c7bcb0d6434f45ec7b467b7b4bad6f9aKeisuke Kuroynagi private static native long openNative(String sourceDir, long dictOffset, long dictSize, 121f3b62900c7bcb0d6434f45ec7b467b7b4bad6f9aKeisuke Kuroynagi boolean isUpdatable); 122d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi private static native void flushNative(long dict, String filePath); 123c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC); 124d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi private static native void flushWithGCNative(long dict, String filePath); 1255db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa private static native void closeNative(long dict); 126e0e67373735918c78eaeaf24f127e1d28816aa29Satoshi Kataoka private static native int getProbabilityNative(long dict, int[] word); 1274d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1); 1285db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa private static native int getSuggestionsNative(long dict, long proximityInfo, 1295db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, 1305db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint, 13150db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi int[] suggestOptions, int[] prevWordCodePointArray, 13224aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes, 13324aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard int[] outputAutoCommitFirstWordConfidence); 1341e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa private static native float calcNormalizedScoreNative(int[] before, int[] after, int score); 1351e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa private static native int editDistanceNative(int[] before, int[] after); 1365bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi private static native void addUnigramWordNative(long dict, int[] word, int probability); 1375bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi private static native void addBigramWordsNative(long dict, int[] word0, int[] word1, 1385bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi int probability); 1395bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1); 1404d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi private static native int calculateProbabilityNative(long dict, int unigramProbability, 1414d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi int bigramProbability); 14231097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi private static native String getPropertyNative(long dict, String query); 143979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 1445ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi @UsedForTesting 1455ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi public static boolean createEmptyDictFile(final String filePath, final long dictVersion, 1465ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi final Map<String, String> attributeMap) { 1475ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi final String[] keyArray = new String[attributeMap.size()]; 1485ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi final String[] valueArray = new String[attributeMap.size()]; 1495ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi int index = 0; 1505ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi for (final String key : attributeMap.keySet()) { 1515ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi keyArray[index] = key; 1525ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi valueArray[index] = attributeMap.get(key); 1535ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi index++; 1545ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi } 1555ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi return createEmptyDictFileNative(filePath, dictVersion, keyArray, valueArray); 1565ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi } 1575ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi 158e9f3e182e4b3217282831fd8805958270b4dbba3Satoshi Kataoka // TODO: Move native dict into session 159bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka private final void loadDictionary(final String path, final long startOffset, 160981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi final long length, final boolean isUpdatable) { 161981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi mNativeDict = openNative(path, startOffset, length, isUpdatable); 162979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 163979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 164979f8690967ff5409fe18f5085858ccdb8e0ccf1satok @Override 165b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, 1662dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final String prevWord, final ProximityInfo proximityInfo, 167fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { 1682dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, 169fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi additionalFeaturesOptions, 0 /* sessionId */); 1703979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 1713979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka 1723979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka @Override 1733979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, 1742dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final String prevWord, final ProximityInfo proximityInfo, 175fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, 176fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi final int sessionId) { 177b4b93dbf3e0912ba26c5c34abba7a5b94c74138cJean Chalard if (!isValidDictionary()) return null; 1783979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka 179ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); 180ea98e026f1ad7732279aec6d06107f46ea0af93dJean Chalard // TODO: toLowerCase in the native code 1815e21ea1a3553000529c288acdf6d6a4b165bedc5Tadashi Takaoka final int[] prevWordCodePointArray = (null == prevWord) 182bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka ? null : StringUtils.toCodePointArray(prevWord); 183860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard final int composerSize = composer.size(); 184860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard 185e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard final boolean isGesture = composer.isBatchMode(); 186e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard if (composerSize <= 1 || !isGesture) { 187860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard if (composerSize > MAX_WORD_LENGTH - 1) return null; 188860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard for (int i = 0; i < composerSize; i++) { 189f3850e554389dc3012584f9d81a4f2d3d4c89e44Ken Wakasa mInputCodePoints[i] = composer.getCodeAt(i); 190860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard } 191860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard } 192860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard 193251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard final InputPointers ips = composer.getInputPointers(); 1945db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa final int inputSize = isGesture ? ips.getPointerSize() : composerSize; 19550db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi mNativeSuggestOptions.setIsGesture(isGesture); 196fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); 197251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard // proximityInfo and/or prevWordForBigrams may not be null. 198f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), 199f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa getTraverseSession(sessionId).getSession(), ips.getXCoordinates(), 200f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints, 20150db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(), 20250db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices, 20324aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard mOutputTypes, mOutputAutoCommitFirstWordConfidence); 2045f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); 205f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok for (int j = 0; j < count; ++j) { 206f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok final int start = j * MAX_WORD_LENGTH; 207979f8690967ff5409fe18f5085858ccdb8e0ccf1satok int len = 0; 2081e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa while (len < MAX_WORD_LENGTH && mOutputCodePoints[start + len] != 0) { 209f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok ++len; 210979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 211979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if (len > 0) { 21299e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS; 2132dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard if (blockOffensiveWords 214bb61293675234f6e5e1312db70618be985b37a83Jean Chalard && 0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE) 21599e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) { 216bb61293675234f6e5e1312db70618be985b37a83Jean Chalard // If we block potentially offensive words, and if the word is possibly 217bb61293675234f6e5e1312db70618be985b37a83Jean Chalard // offensive, then we don't output it unless it's also an exact match. 21899e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard continue; 21999e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard } 22099e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard final int kind = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_KIND; 22199e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard final int score = SuggestedWordInfo.KIND_WHITELIST == kind 222926ef06a1ef8a0f8e01baa26c23172b93012d2b1Jean Chalard ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j]; 22399e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard // TODO: check that all users of the `kind' parameter are ready to accept 22499e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard // flags too and pass mOutputTypes[j] instead of kind 2251e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len), 226e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard score, kind, this /* sourceDict */, 227e8754aba1c8f217e7ca828de25e0506ac58daa99Keisuke Kuroyanagi mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */, 22824aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard mOutputAutoCommitFirstWordConfidence[0])); 229979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 230979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 231d82898c5a91f8aa69d5dc594b7a9290b8be1247aJean Chalard return suggestions; 232923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 233923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 23415f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka public boolean isValidDictionary() { 2356f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka return mNativeDict != 0; 2366f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka } 2376f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka 238bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public static float calcNormalizedScore(final String before, final String after, 239bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final int score) { 2401e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa return calcNormalizedScoreNative(StringUtils.toCodePointArray(before), 2411e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa StringUtils.toCodePointArray(after), score); 242be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok } 243be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok 244bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public static int editDistance(final String before, final String after) { 24566d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka if (before == null || after == null) { 24666d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka throw new IllegalArgumentException(); 24766d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka } 2481e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa return editDistanceNative(StringUtils.toCodePointArray(before), 2491e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa StringUtils.toCodePointArray(after)); 250be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok } 251be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok 252923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project @Override 253bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public boolean isValidWord(final String word) { 2544d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi return getFrequency(word) != NOT_A_PROBABILITY; 255c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka } 256c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka 257c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka @Override 258bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public int getFrequency(final String word) { 2594d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi if (word == null) return NOT_A_PROBABILITY; 260bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka int[] codePoints = StringUtils.toCodePointArray(word); 261e0e67373735918c78eaeaf24f127e1d28816aa29Satoshi Kataoka return getProbabilityNative(mNativeDict, codePoints); 262923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 263c3df2d6fd27f3a5b84040b59aece3367769f0cb6Amith Yamasani 2644d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni 2654d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // calls when checking for changes in an entire dictionary. 266f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi public boolean isValidBigram(final String word0, final String word1) { 2674d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi return getBigramProbability(word0, word1) != NOT_A_PROBABILITY; 2684d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi } 2694d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi 2704d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi public int getBigramProbability(final String word0, final String word1) { 2714d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY; 272f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints0 = StringUtils.toCodePointArray(word0); 2731e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa final int[] codePoints1 = StringUtils.toCodePointArray(word1); 2744d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1); 275f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 276f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi 277f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi // Add a unigram entry to binary dictionary in native code. 278f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi public void addUnigramWord(final String word, final int probability) { 279f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi if (TextUtils.isEmpty(word)) { 280f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi return; 281f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 282f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints = StringUtils.toCodePointArray(word); 283f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi addUnigramWordNative(mNativeDict, codePoints, probability); 284f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 285f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi 286f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi // Add a bigram entry to binary dictionary in native code. 287f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi public void addBigramWords(final String word0, final String word1, final int probability) { 288f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { 289f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi return; 290f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 291f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints0 = StringUtils.toCodePointArray(word0); 292f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints1 = StringUtils.toCodePointArray(word1); 293f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability); 294f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 295f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi 296f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi // Remove a bigram entry form binary dictionary in native code. 297f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi public void removeBigramWords(final String word0, final String word1) { 298f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { 299f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi return; 300f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 301f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints0 = StringUtils.toCodePointArray(word0); 302f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints1 = StringUtils.toCodePointArray(word1); 303f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi removeBigramWordsNative(mNativeDict, codePoints0, codePoints1); 3044d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 3054d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 3066142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi private void reopen() { 3076142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi close(); 3082e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi final File dictFile = new File(mDictFilePath); 3092e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */, 3102e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi dictFile.length(), true /* isUpdatable */); 311d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi } 312d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi 3136142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi public void flush() { 3146142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi if (!isValidDictionary()) return; 3156142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi flushNative(mNativeDict, mDictFilePath); 3166142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi reopen(); 3176142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi } 3186142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi 319d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi public void flushWithGC() { 320d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi if (!isValidDictionary()) return; 321d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi flushWithGCNative(mNativeDict, mDictFilePath); 3226142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi reopen(); 323d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi } 324d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi 325c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi /** 326c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi * Checks whether GC is needed to run or not. 327c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi * @param mindsBlockByGC Whether to mind operations blocked by GC. We don't need to care about 328c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi * the blocking in some situations such as in idle time or just before closing. 329c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi * @return whether GC is needed to run or not. 330c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi */ 331c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi public boolean needsToRunGC(final boolean mindsBlockByGC) { 332d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi if (!isValidDictionary()) return false; 333c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi return needsToRunGCNative(mNativeDict, mindsBlockByGC); 334d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi } 335d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi 3364d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi @UsedForTesting 3374d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi public int calculateProbability(final int unigramProbability, final int bigramProbability) { 3384d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi if (!isValidDictionary()) return NOT_A_PROBABILITY; 3394d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability); 3404d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi } 3414d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi 34231097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi @UsedForTesting 34331097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi public String getPropertyForTests(String query) { 34431097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi if (!isValidDictionary()) return ""; 34531097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi return getPropertyNative(mNativeDict, query); 34631097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi } 34731097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi 34836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani @Override 34924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard public boolean shouldAutoCommit(final SuggestedWordInfo candidate) { 350459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard return candidate.mAutoCommitFirstWordConfidence > CONFIDENCE_TO_AUTO_COMMIT; 35124aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard } 35224aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard 35324aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard @Override 3548ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka public void close() { 3558ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka synchronized (mDicTraverseSessions) { 3568ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka final int sessionsSize = mDicTraverseSessions.size(); 3578ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka for (int index = 0; index < sessionsSize; ++index) { 3588ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index); 3598ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka if (traverseSession != null) { 3608ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka traverseSession.close(); 3618ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka } 3623979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 3636142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi mDicTraverseSessions.clear(); 3643979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 3656142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi closeInternalLocked(); 366da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa } 367da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa 3686142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi private synchronized void closeInternalLocked() { 369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project if (mNativeDict != 0) { 370923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project closeNative(mNativeDict); 371923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project mNativeDict = 0; 372923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 373923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 374923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 3756142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi // TODO: Manage BinaryDictionary instances without using WeakReference or something. 376923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project @Override 377923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project protected void finalize() throws Throwable { 378da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa try { 3796142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi closeInternalLocked(); 380da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa } finally { 381da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa super.finalize(); 382da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa } 383923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 384923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 385