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