10825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/*
20825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Copyright (C) 2006 The Android Open Source Project
30825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
40825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Licensed under the Apache License, Version 2.0 (the "License");
50825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * you may not use this file except in compliance with the License.
60825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * You may obtain a copy of the License at
70825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
80825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *      http://www.apache.org/licenses/LICENSE-2.0
90825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unless required by applicable law or agreed to in writing, software
110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * distributed under the License is distributed on an "AS IS" BASIS,
120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * See the License for the specific language governing permissions and
140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * limitations under the License.
150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
170825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepackage com.android.internal.telephony;
180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
19cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Savilleimport java.util.ArrayList;
200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.List;
210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2299c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Savilleimport android.telephony.Rlog;
230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/**
250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@hide}
260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
270825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic abstract class Call {
2822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    protected final String LOG_TAG = "Call";
2922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville
300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /* Enums */
310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public enum State {
330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;
340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public boolean isAlive() {
360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public boolean isRinging() {
400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return this == INCOMING || this == WAITING;
410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public boolean isDialing() {
440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return this == DIALING || this == ALERTING;
450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /* Instance Variables */
500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    public State mState = State.IDLE;
520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    public ArrayList<Connection> mConnections = new ArrayList<Connection>();
540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    // Flag to indicate if the current calling/caller information
560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    // is accurate. If false the information is known to be accurate.
570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    //
580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    // For CDMA, during call waiting/3 way, there is no network response
590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    // if call waiting is answered, network timed out, dropped, 3 way
600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    // merged, etc.
6122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    protected boolean mIsGeneric = false;
620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /* Instance Methods */
640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Do not modify the List result!!! This list is not yours to keep
660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *  It will change across event loop iterations            top
670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public abstract List<Connection> getConnections();
700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public abstract Phone getPhone();
710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public abstract boolean isMultiparty();
720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public abstract void hangup() throws CallStateException;
730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * hasConnection
770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param c a Connection object
790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if the call contains the connection object passed in
800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean hasConnection(Connection c) {
820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return c.getCall() == this;
830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * hasConnections
870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if the call contains one or more connections
880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean hasConnections() {
90cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        List<Connection> connections = getConnections();
910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (connections == null) {
930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return false;
940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return connections.size() > 0;
970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * getState
1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return state of class call
1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public State getState() {
10422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mState;
1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * isIdle
1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * FIXME rename
1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if the call contains only disconnected connections (if any)
1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean isIdle() {
1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return !getState().isAlive();
1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Returns the Connection associated with this Call that was created
1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * first, or null if there are no Connections in this Call
1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public Connection
1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    getEarliestConnection() {
123cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        List<Connection> l;
1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long time = Long.MAX_VALUE;
1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Connection c;
1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Connection earliest = null;
1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        l = getConnections();
1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (l.size() == 0) {
1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return null;
1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i = 0, s = l.size() ; i < s ; i++) {
135cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville            c = l.get(i);
1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            long t;
1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            t = c.getCreateTime();
1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (t < time) {
1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                earliest = c;
1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                time = t;
1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return earliest;
1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public long
1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    getEarliestCreateTime() {
151cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        List<Connection> l;
1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long time = Long.MAX_VALUE;
1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        l = getConnections();
1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (l.size() == 0) {
1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return 0;
1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i = 0, s = l.size() ; i < s ; i++) {
161cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville            Connection c = l.get(i);
1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            long t;
1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            t = c.getCreateTime();
1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            time = t < time ? t : time;
1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return time;
1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public long
1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    getEarliestConnectTime() {
1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long time = Long.MAX_VALUE;
175cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        List<Connection> l = getConnections();
1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (l.size() == 0) {
1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return 0;
1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i = 0, s = l.size() ; i < s ; i++) {
182cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville            Connection c = l.get(i);
1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            long t;
1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            t = c.getConnectTime();
1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            time = t < time ? t : time;
1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return time;
1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean
1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    isDialingOrAlerting() {
1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return getState().isDialing();
1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean
2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    isRinging() {
2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return getState().isRinging();
2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Returns the Connection associated with this Call that was created
2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * last, or null if there are no Connections in this Call
2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public Connection
2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    getLatestConnection() {
210cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        List<Connection> l = getConnections();
2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (l.size() == 0) {
2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return null;
2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long time = 0;
2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Connection latest = null;
2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i = 0, s = l.size() ; i < s ; i++) {
218cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville            Connection c = l.get(i);
2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            long t = c.getCreateTime();
2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (t > time) {
2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                latest = c;
2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                time = t;
2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return latest;
2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * To indicate if the connection information is accurate
2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * or not. false means accurate. Only used for CDMA.
2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean isGeneric() {
23522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mIsGeneric;
2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Set the generic instance variable
2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public void setGeneric(boolean generic) {
24222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mIsGeneric = generic;
2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Hangup call if it is alive
2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public void hangupIfAlive() {
2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (getState().isAlive()) {
2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            try {
2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                hangup();
2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } catch (CallStateException ex) {
25399c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                Rlog.w(LOG_TAG, " hangupIfActive: caught " + ex);
2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville}
258