ImsPhoneCall.java revision 77c77080eaf88cfc55eb16bff6fd2788817a6896
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony.imsphone;
18
19import android.telephony.Rlog;
20import android.telephony.DisconnectCause;
21
22import com.android.internal.telephony.Call;
23import com.android.internal.telephony.CallStateException;
24import com.android.internal.telephony.Connection;
25import com.android.internal.telephony.Phone;
26import com.android.ims.ImsCall;
27import com.android.ims.ImsException;
28import com.android.ims.ImsStreamMediaProfile;
29
30import java.util.List;
31
32/**
33 * {@hide}
34 */
35public class ImsPhoneCall extends Call {
36    /*************************** Instance Variables **************************/
37
38    private static final String LOG_TAG = "ImsPhoneCall";
39
40    /*package*/ ImsPhoneCallTracker mOwner;
41
42    private boolean mRingbackTonePlayed = false;
43
44    /****************************** Constructors *****************************/
45    /*package*/
46    ImsPhoneCall() {
47    }
48
49    /*package*/
50    ImsPhoneCall(ImsPhoneCallTracker owner) {
51        mOwner = owner;
52    }
53
54    public void dispose() {
55        try {
56            mOwner.hangup(this);
57        } catch (CallStateException ex) {
58            //Rlog.e(LOG_TAG, "dispose: unexpected error on hangup", ex);
59            //while disposing, ignore the exception and clean the connections
60        } finally {
61            for(int i = 0, s = mConnections.size(); i < s; i++) {
62                ImsPhoneConnection c = (ImsPhoneConnection) mConnections.get(i);
63                c.onDisconnect(DisconnectCause.LOST_SIGNAL);
64            }
65        }
66    }
67
68    /************************** Overridden from Call *************************/
69
70    @Override
71    public List<Connection>
72    getConnections() {
73        return mConnections;
74    }
75
76    @Override
77    public Phone
78    getPhone() {
79        return mOwner.mPhone;
80    }
81
82    @Override
83    public boolean
84    isMultiparty() {
85        return mConnections.size() > 1;
86    }
87
88    /** Please note: if this is the foreground call and a
89     *  background call exists, the background call will be resumed.
90     */
91    @Override
92    public void
93    hangup() throws CallStateException {
94        mOwner.hangup(this);
95    }
96
97    @Override
98    public String
99    toString() {
100        return mState.toString();
101    }
102
103    //***** Called from ImsPhoneConnection
104
105    /*package*/ void
106    attach(Connection conn) {
107        clearDisconnected();
108        mConnections.add(conn);
109    }
110
111    /*package*/ void
112    attach(Connection conn, State state) {
113        this.attach(conn);
114        mState = state;
115    }
116
117    /*package*/ void
118    attachFake(Connection conn, State state) {
119        attach(conn, state);
120    }
121
122    /**
123     * Called by ImsPhoneConnection when it has disconnected
124     */
125    boolean
126    connectionDisconnected(ImsPhoneConnection conn) {
127        if (mState != State.DISCONNECTED) {
128            /* If only disconnected connections remain, we are disconnected*/
129
130            boolean hasOnlyDisconnectedConnections = true;
131
132            for (int i = 0, s = mConnections.size()  ; i < s; i ++) {
133                if (mConnections.get(i).getState() != State.DISCONNECTED) {
134                    hasOnlyDisconnectedConnections = false;
135                    break;
136                }
137            }
138
139            if (hasOnlyDisconnectedConnections) {
140                mState = State.DISCONNECTED;
141                return true;
142            }
143        }
144
145        return false;
146    }
147
148    /*package*/ void
149    detach(ImsPhoneConnection conn) {
150        mConnections.remove(conn);
151
152        if (mConnections.size() == 0) {
153            mState = State.IDLE;
154        }
155    }
156
157    /**
158     * @return true if there's no space in this call for additional
159     * connections to be added via "conference"
160     */
161    /*package*/ boolean
162    isFull() {
163        return mConnections.size() == ImsPhoneCallTracker.MAX_CONNECTIONS_PER_CALL;
164    }
165
166    //***** Called from ImsPhoneCallTracker
167    /**
168     * Called when this Call is being hung up locally (eg, user pressed "end")
169     */
170    void
171    onHangupLocal() {
172        for (int i = 0, s = mConnections.size(); i < s; i++) {
173            ImsPhoneConnection cn = (ImsPhoneConnection)mConnections.get(i);
174            cn.onHangupLocal();
175        }
176        mState = State.DISCONNECTING;
177    }
178
179    /**
180     * Called when it's time to clean up disconnected Connection objects
181     */
182    void
183    clearDisconnected() {
184        for (int i = mConnections.size() - 1 ; i >= 0 ; i--) {
185            ImsPhoneConnection cn = (ImsPhoneConnection)mConnections.get(i);
186
187            if (cn.getState() == State.DISCONNECTED) {
188                mConnections.remove(i);
189            }
190        }
191
192        if (mConnections.size() == 0) {
193            mState = State.IDLE;
194        }
195    }
196
197    /*package*/ ImsPhoneConnection
198    getFirstConnection() {
199        if (mConnections.size() == 0) return null;
200
201        return (ImsPhoneConnection) mConnections.get(0);
202    }
203
204    /*package*/ void
205    setMute(boolean mute) {
206        ImsCall imsCall = getFirstConnection() == null ?
207                null : getFirstConnection().getImsCall();
208        if (imsCall != null) {
209            try {
210                imsCall.setMute(mute);
211            } catch (ImsException e) {
212                Rlog.e(LOG_TAG, "setMute failed : " + e.getMessage());
213            }
214        }
215    }
216
217    /* package */ void
218    merge(ImsPhoneCall that, State state) {
219        ImsPhoneConnection[] cc = that.mConnections.toArray(
220                new ImsPhoneConnection[that.mConnections.size()]);
221        for (ImsPhoneConnection c : cc) {
222            c.update(null, state);
223        }
224    }
225
226    /*package*/ ImsCall
227    getImsCall() {
228        return (getFirstConnection() == null) ? null : getFirstConnection().getImsCall();
229    }
230
231    /*package*/ static boolean isLocalTone(ImsCall imsCall) {
232        if ((imsCall == null) || (imsCall.getCallProfile() == null)
233                || (imsCall.getCallProfile().mMediaProfile == null)) {
234            return false;
235        }
236
237        ImsStreamMediaProfile mediaProfile = imsCall.getCallProfile().mMediaProfile;
238
239        return (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE)
240                ? true : false;
241    }
242
243    /*package*/ boolean
244    update (ImsPhoneConnection conn, ImsCall imsCall, State state) {
245        State newState = state;
246        boolean changed = false;
247
248        //ImsCall.Listener.onCallProgressing can be invoked several times
249        //and ringback tone mode can be changed during the call setup procedure
250        if (state == State.ALERTING) {
251            if (mRingbackTonePlayed && !isLocalTone(imsCall)) {
252                mOwner.mPhone.stopRingbackTone();
253                mRingbackTonePlayed = false;
254            } else if (!mRingbackTonePlayed && isLocalTone(imsCall)) {
255                mOwner.mPhone.startRingbackTone();
256                mRingbackTonePlayed = true;
257            }
258        } else {
259            if (mRingbackTonePlayed) {
260                mOwner.mPhone.stopRingbackTone();
261                mRingbackTonePlayed = false;
262            }
263        }
264
265        if ((newState != mState) && (state != State.DISCONNECTED)) {
266            mState = newState;
267            changed = true;
268        } else if (state == State.DISCONNECTED) {
269            changed = true;
270        }
271
272        return changed;
273    }
274
275    /* package */ ImsPhoneConnection
276    getHandoverConnection() {
277        ImsPhoneConnection conn = (ImsPhoneConnection) getEarliestConnection();
278        if (conn != null) {
279            conn.setMultiparty(isMultiparty());
280        }
281        return conn;
282    }
283
284    void switchWith(ImsPhoneCall that) {
285        synchronized (ImsPhoneCall.class) {
286            ImsPhoneCall tmp = new ImsPhoneCall();
287            tmp.takeOver(this);
288            this.takeOver(that);
289            that.takeOver(tmp);
290        }
291    }
292
293    private void takeOver(ImsPhoneCall that) {
294        mConnections = that.mConnections;
295        mState = that.mState;
296        for (Connection c : mConnections) {
297            ((ImsPhoneConnection) c).changeParent(this);
298        }
299    }
300}
301