1666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada/*
2666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * Copyright (C) 2012 The Android Open Source Project
3666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada *
4666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * use this file except in compliance with the License. You may obtain a copy of
6666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * the License at
7666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada *
8666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * http://www.apache.org/licenses/LICENSE-2.0
9666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada *
10666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * Unless required by applicable law or agreed to in writing, software
11666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * License for the specific language governing permissions and limitations under
14666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * the License.
15666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada */
16666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
17666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadapackage com.android.inputmethod.latin;
18666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
19666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport android.util.Log;
20666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
2165feee12e5889601e375d92dfdf5f8e8fbb05092Yuichiro Hanadaimport com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
22666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
23666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
241a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanadaimport com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
25666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport com.android.inputmethod.latin.makedict.FusionDictionary;
26666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport com.android.inputmethod.latin.makedict.FusionDictionary.Node;
27666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport com.android.inputmethod.latin.makedict.PendingAttribute;
28666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport com.android.inputmethod.latin.makedict.UnsupportedFormatException;
29666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
30666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport java.io.IOException;
31666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport java.io.OutputStream;
32666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport java.util.ArrayList;
33666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport java.util.HashMap;
34666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanadaimport java.util.Map;
35666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
36666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada/**
37666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * Reads and writes Binary files for a UserHistoryDictionary.
38666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada *
39666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada * All the methods in this class are static.
40666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada */
41a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class UserHistoryDictIOUtils {
42666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    private static final String TAG = UserHistoryDictIOUtils.class.getSimpleName();
43666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    private static final boolean DEBUG = false;
44666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
45666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    public interface OnAddWordListener {
46666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public void setUnigram(final String word, final String shortcutTarget, final int frequency);
47666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public void setBigram(final String word1, final String word2, final int frequency);
48666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    }
49666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
50666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    public interface BigramDictionaryInterface {
51666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public int getFrequency(final String word1, final String word2);
52666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    }
53666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
54666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    public static final class ByteArrayWrapper implements FusionDictionaryBufferInterface {
55666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        private byte[] mBuffer;
56666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        private int mPosition;
57666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
58be5db53a09a705575e3902769b44d687142f6a83Yuichiro Hanada        public ByteArrayWrapper(final byte[] buffer) {
59666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            mBuffer = buffer;
60666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            mPosition = 0;
61666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
62666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
63666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        @Override
64666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public int readUnsignedByte() {
65666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            return ((int)mBuffer[mPosition++]) & 0xFF;
66666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
67666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
68666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        @Override
69666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public int readUnsignedShort() {
70666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final int retval = readUnsignedByte();
71666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            return (retval << 8) + readUnsignedByte();
72666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
73666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
74666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        @Override
75666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public int readUnsignedInt24() {
76666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final int retval = readUnsignedShort();
77666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            return (retval << 8) + readUnsignedByte();
78666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
79666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
80666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        @Override
81666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public int readInt() {
82666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final int retval = readUnsignedShort();
83666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            return (retval << 16) + readUnsignedShort();
84666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
85666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
86666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        @Override
87666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public int position() {
88666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            return mPosition;
89666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
90666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
91666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        @Override
92666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        public void position(int position) {
93666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            mPosition = position;
94666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
958d031a63b4d59d4d8670b4310dd9e18a0e03435aYuichiro Hanada
968d031a63b4d59d4d8670b4310dd9e18a0e03435aYuichiro Hanada        @Override
978d031a63b4d59d4d8670b4310dd9e18a0e03435aYuichiro Hanada        public void put(final byte b) {
988d031a63b4d59d4d8670b4310dd9e18a0e03435aYuichiro Hanada            mBuffer[mPosition++] = b;
998d031a63b4d59d4d8670b4310dd9e18a0e03435aYuichiro Hanada        }
100a149c53c8ebe1b2acb7ee92eac51ccdc364162e7Yuichiro Hanada
101a149c53c8ebe1b2acb7ee92eac51ccdc364162e7Yuichiro Hanada        @Override
102a149c53c8ebe1b2acb7ee92eac51ccdc364162e7Yuichiro Hanada        public int limit() {
103a161bdac885fc8e5f0063d33b055b0a6ecdefbdbYuichiro Hanada            return mBuffer.length - 1;
104a161bdac885fc8e5f0063d33b055b0a6ecdefbdbYuichiro Hanada        }
105a161bdac885fc8e5f0063d33b055b0a6ecdefbdbYuichiro Hanada
106a161bdac885fc8e5f0063d33b055b0a6ecdefbdbYuichiro Hanada        @Override
107a161bdac885fc8e5f0063d33b055b0a6ecdefbdbYuichiro Hanada        public int capacity() {
108a149c53c8ebe1b2acb7ee92eac51ccdc364162e7Yuichiro Hanada            return mBuffer.length;
109a149c53c8ebe1b2acb7ee92eac51ccdc364162e7Yuichiro Hanada        }
110666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    }
111666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
112666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    /**
113666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada     * Writes dictionary to file.
114666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada     */
115666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    public static void writeDictionaryBinary(final OutputStream destination,
116666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams,
11783dfe0fd8c7e2bce2717930dbf8732f5414ee39dYuichiro Hanada            final FormatOptions formatOptions) {
118666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams);
119666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        try {
12083dfe0fd8c7e2bce2717930dbf8732f5414ee39dYuichiro Hanada            BinaryDictInputOutput.writeDictionaryBinary(destination, fusionDict, formatOptions);
12184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada            Log.d(TAG, "end writing");
122666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        } catch (IOException e) {
123666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            Log.e(TAG, "IO exception while writing file: " + e);
124666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        } catch (UnsupportedFormatException e) {
125666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            Log.e(TAG, "Unsupported fomat: " + e);
126666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
127666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    }
128666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
129666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    /**
130666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada     * Constructs a new FusionDictionary from BigramDictionaryInterface.
131666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada     */
132666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    /* packages for test */ static FusionDictionary constructFusionDictionary(
133666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) {
134666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        final FusionDictionary fusionDict = new FusionDictionary(new Node(),
13584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
13684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                        false));
13784d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        int profTotal = 0;
138666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        for (final String word1 : bigrams.keySet()) {
139666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final HashMap<String, Byte> word1Bigrams = bigrams.getBigrams(word1);
140666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            for (final String word2 : word1Bigrams.keySet()) {
141666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                final int freq = dict.getFrequency(word1, word2);
14284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                if (freq == -1) {
14384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                    // don't add this bigram.
14484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                    continue;
14584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                }
146666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                if (DEBUG) {
147666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                    if (word1 == null) {
148666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                        Log.d(TAG, "add unigram: " + word2 + "," + Integer.toString(freq));
149666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                    } else {
150666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                        Log.d(TAG, "add bigram: " + word1
151666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                                + "," + word2 + "," + Integer.toString(freq));
152666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                    }
15384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                    profTotal++;
154666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                }
155666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                if (word1 == null) { // unigram
1568251036616dc30db9ad1a981cc5af5843d474647Jean Chalard                    fusionDict.add(word2, freq, null, false /* isNotAWord */);
157666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                } else { // bigram
15884d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                    if (FusionDictionary.findWordInTree(fusionDict.mRoot, word1) == null) {
15984d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                        fusionDict.add(word1, 2, null, false /* isNotAWord */);
16084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada                    }
161666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                    fusionDict.setBigram(word1, word2, freq);
162666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                }
163666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                bigrams.updateBigram(word1, word2, (byte)freq);
164666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            }
165666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
16684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        if (DEBUG) {
16784d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada            Log.d(TAG, "add " + profTotal + "words");
16884d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        }
169666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        return fusionDict;
170666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    }
171666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
172666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    /**
173666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada     * Reads dictionary from file.
174666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada     */
175666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    public static void readDictionaryBinary(final FusionDictionaryBufferInterface buffer,
176666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final OnAddWordListener dict) {
177666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        final Map<Integer, String> unigrams = CollectionUtils.newTreeMap();
178666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        final Map<Integer, Integer> frequencies = CollectionUtils.newTreeMap();
179666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        final Map<Integer, ArrayList<PendingAttribute>> bigrams = CollectionUtils.newTreeMap();
180666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        try {
18165feee12e5889601e375d92dfdf5f8e8fbb05092Yuichiro Hanada            BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies,
182666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                    bigrams);
183666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        } catch (IOException e) {
184666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            Log.e(TAG, "IO exception while reading file: " + e);
185666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        } catch (UnsupportedFormatException e) {
186666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            Log.e(TAG, "Unsupported format: " + e);
1877a7d117aa7e7157ed429d68c4797218476151d8dYuichiro Hanada        } catch (ArrayIndexOutOfBoundsException e) {
1887a7d117aa7e7157ed429d68c4797218476151d8dYuichiro Hanada            Log.e(TAG, "ArrayIndexOutOfBoundsException while reading file: " + e);
189666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
1907a7d117aa7e7157ed429d68c4797218476151d8dYuichiro Hanada        addWordsFromWordMap(unigrams, frequencies, bigrams, dict);
191666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    }
192666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
193666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    /**
194666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada     * Adds all unigrams and bigrams in maps to OnAddWordListener.
195666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada     */
196666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    /* package for test */ static void addWordsFromWordMap(final Map<Integer, String> unigrams,
197666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final Map<Integer, Integer> frequencies,
198666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final Map<Integer, ArrayList<PendingAttribute>> bigrams, final OnAddWordListener to) {
199666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        for (Map.Entry<Integer, String> entry : unigrams.entrySet()) {
200666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final String word1 = entry.getValue();
201666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final int unigramFrequency = frequencies.get(entry.getKey());
202666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            to.setUnigram(word1, null, unigramFrequency);
203666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            final ArrayList<PendingAttribute> attrList = bigrams.get(entry.getKey());
204666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            if (attrList != null) {
205666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                for (final PendingAttribute attr : attrList) {
206666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                    to.setBigram(word1, unigrams.get(attr.mAddress),
207666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                            BinaryDictInputOutput.reconstructBigramFrequency(unigramFrequency,
208666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                                    attr.mFrequency));
209666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada                }
210666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada            }
211666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada        }
212666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada
213666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada    }
214666a4338026866df1f18dd6b3f968c3788943e4cYuichiro Hanada}