1363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang/* 2363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Copyright (C) 2010 The Android Open Source Project 3363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 4363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * you may not use this file except in compliance with the License. 6363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * You may obtain a copy of the License at 7363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 8363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * http://www.apache.org/licenses/LICENSE-2.0 9363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 10363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Unless required by applicable law or agreed to in writing, software 11363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS, 12363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * See the License for the specific language governing permissions and 14363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * limitations under the License. 15363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 16363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 17363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wangpackage android.net.rtp; 18363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 19c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yehimport android.media.AudioManager; 20c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh 21363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wangimport java.util.HashMap; 22363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wangimport java.util.Map; 23363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 24363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang/** 25c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * An AudioGroup is an audio hub for the speaker, the microphone, and 26c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * {@link AudioStream}s. Each of these components can be logically turned on 27c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * or off by calling {@link #setMode(int)} or {@link RtpStream#setMode(int)}. 28c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * The AudioGroup will go through these components and process them one by one 29c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * within its execution loop. The loop consists of four steps. First, for each 30c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming 31c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * packets and stores in its buffer. Then, if the microphone is enabled, 32c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * processes the recorded audio and stores in its buffer. Third, if the speaker 33c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * is enabled, mixes all AudioStream buffers and plays back. Finally, for each 34c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other 35c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * buffers and sends back the encoded packets. An AudioGroup does nothing if 36c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * there is no AudioStream in it. 3732e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * 3832e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * <p>Few things must be noticed before using these classes. The performance is 3932e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * highly related to the system load and the network bandwidth. Usually a 4032e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network 41c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * bandwidth, and vise versa. Using two AudioStreams at the same time doubles 42c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * not only the load but also the bandwidth. The condition varies from one 43c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * device to another, and developers should choose the right combination in 44c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * order to get the best result.</p> 4532e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * 4632e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For 4732e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * example, a Voice over IP (VoIP) application might want to put a conference 4832e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * call on hold in order to make a new call but still allow people in the 49c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * conference call talking to each other. This can be done easily using two 5032e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * AudioGroups, but there are some limitations. Since the speaker and the 51c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * microphone are globally shared resources, only one AudioGroup at a time is 52c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * allowed to run in a mode other than {@link #MODE_ON_HOLD}. The others will 53c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * be unable to acquire these resources and fail silently.</p> 5453aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh * 5553aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh * <p class="note">Using this class requires 56c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * {@link android.Manifest.permission#RECORD_AUDIO} permission. Developers 57c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * should set the audio mode to {@link AudioManager#MODE_IN_COMMUNICATION} 58c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * using {@link AudioManager#setMode(int)} and change it back when none of 59c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * the AudioGroups is in use.</p> 6053aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh * 6153aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh * @see AudioStream 62363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 63363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wangpublic class AudioGroup { 6432e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh /** 6532e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * This mode is similar to {@link #MODE_NORMAL} except the speaker and 66c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * the microphone are both disabled. 6732e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh */ 68363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang public static final int MODE_ON_HOLD = 0; 6932e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh 7032e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh /** 7132e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * This mode is similar to {@link #MODE_NORMAL} except the microphone is 72c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh * disabled. 7332e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh */ 74363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang public static final int MODE_MUTED = 1; 7532e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh 7632e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh /** 7732e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * This mode indicates that the speaker, the microphone, and all 7832e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * {@link AudioStream}s in the group are enabled. First, the packets 7932e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * received from the streams are decoded and mixed with the audio recorded 8032e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * from the microphone. Then, the results are played back to the speaker, 8132e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * encoded and sent back to each stream. 8232e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh */ 83363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang public static final int MODE_NORMAL = 2; 8432e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh 8532e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh /** 8632e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * This mode is similar to {@link #MODE_NORMAL} except the echo suppression 8732e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * is enabled. It should be only used when the speaker phone is on. 8832e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh */ 8932e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh public static final int MODE_ECHO_SUPPRESSION = 3; 90363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 9153aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh private static final int MODE_LAST = 3; 9253aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh 93363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang private final Map<AudioStream, Integer> mStreams; 94363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang private int mMode = MODE_ON_HOLD; 95363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 96363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang private int mNative; 97363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang static { 98363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang System.loadLibrary("rtp_jni"); 99363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 100363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 10132e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh /** 10232e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * Creates an empty AudioGroup. 10332e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh */ 104363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang public AudioGroup() { 105363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang mStreams = new HashMap<AudioStream, Integer>(); 106363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 107363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 10832e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh /** 10953aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh * Returns the {@link AudioStream}s in this group. 11053aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh */ 11153aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh public AudioStream[] getStreams() { 11253aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh synchronized (this) { 11353aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh return mStreams.keySet().toArray(new AudioStream[mStreams.size()]); 11453aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 11553aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 11653aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh 11753aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh /** 11832e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * Returns the current mode. 11932e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh */ 120363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang public int getMode() { 121363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang return mMode; 122363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 123363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 12432e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh /** 12532e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * Changes the current mode. It must be one of {@link #MODE_ON_HOLD}, 12632e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and 12732e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * {@link #MODE_ECHO_SUPPRESSION}. 12832e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * 12932e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * @param mode The mode to change to. 13032e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * @throws IllegalArgumentException if the mode is invalid. 13132e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh */ 13253aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh public void setMode(int mode) { 13353aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh if (mode < 0 || mode > MODE_LAST) { 13453aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh throw new IllegalArgumentException("Invalid mode"); 13553aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 13653aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh synchronized (this) { 13753aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh nativeSetMode(mode); 13853aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh mMode = mode; 13953aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 14053aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 14132e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh 14253aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh private native void nativeSetMode(int mode); 14353aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh 14453aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh // Package-private method used by AudioStream.join(). 145e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh synchronized void add(AudioStream stream) { 146c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh if (!mStreams.containsKey(stream)) { 147c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh try { 148e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh AudioCodec codec = stream.getCodec(); 149c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh String codecSpec = String.format("%d %s %s", codec.type, 150c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh codec.rtpmap, codec.fmtp); 151e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh int id = nativeAdd(stream.getMode(), stream.getSocket(), 152c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh stream.getRemoteAddress().getHostAddress(), 153e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh stream.getRemotePort(), codecSpec, stream.getDtmfType()); 154e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh mStreams.put(stream, id); 155c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh } catch (NullPointerException e) { 156c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh throw new IllegalStateException(e); 157363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 158363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 159363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 160363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 161e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh private native int nativeAdd(int mode, int socket, String remoteAddress, 16253aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh int remotePort, String codecSpec, int dtmfType); 163363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 16453aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh // Package-private method used by AudioStream.join(). 165c52f5b2ec5e13ab3d9ab016e6cab757d4ecb45c7Chia-chi Yeh synchronized void remove(AudioStream stream) { 166e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh Integer id = mStreams.remove(stream); 167e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh if (id != null) { 168e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh nativeRemove(id); 169363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 170363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 171363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 172e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh private native void nativeRemove(int id); 17353aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh 174363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 175363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Sends a DTMF digit to every {@link AudioStream} in this group. Currently 176363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * only event {@code 0} to {@code 15} are supported. 177363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 178363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @throws IllegalArgumentException if the event is invalid. 179363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 18053aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh public void sendDtmf(int event) { 18153aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh if (event < 0 || event > 15) { 18253aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh throw new IllegalArgumentException("Invalid event"); 18353aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 18453aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh synchronized (this) { 18553aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh nativeSendDtmf(event); 18653aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 18753aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 18853aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh 18953aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh private native void nativeSendDtmf(int event); 190363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 19132e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh /** 19232e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh * Removes every {@link AudioStream} in this group. 19332e106b7bdd57c82ee67705871f6116d92bce79bChia-chi Yeh */ 19453aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh public void clear() { 195e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh for (AudioStream stream : getStreams()) { 196e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh stream.join(null); 19753aa6ef70d8692277f9403f94d43918ad9712dd0Chia-chi Yeh } 198363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 199363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 200363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang @Override 201363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang protected void finalize() throws Throwable { 202e66950506c473e660f2e5762d7a71e13808be387Chia-chi Yeh nativeRemove(0); 203363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang super.finalize(); 204363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 205363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang} 206