SipConnection.java revision 2093a451b17c26f4341e9565b65dcaa0e20bbd7d
1/*
2 * Copyright (C) 2014 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.services.telephony.sip;
18
19import android.os.Handler;
20import android.os.Message;
21import android.telecomm.CallAudioState;
22import android.telecomm.CallCapabilities;
23import android.telecomm.Connection;
24import android.util.Log;
25
26import com.android.internal.telephony.Call;
27import com.android.internal.telephony.CallStateException;
28import com.android.internal.telephony.sip.SipPhone;
29
30import java.util.List;
31
32final class SipConnection extends Connection {
33    private static final String PREFIX = "[SipConnection] ";
34    private static final boolean VERBOSE = true; /* STOP SHIP if true */
35
36    private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1;
37
38    private final Handler mHandler = new Handler() {
39        @Override
40        public void handleMessage(Message msg) {
41            switch (msg.what) {
42                case MSG_PRECISE_CALL_STATE_CHANGED:
43                    updateState();
44                    break;
45            }
46        }
47    };
48
49    private com.android.internal.telephony.Connection mOriginalConnection;
50    private Call.State mOriginalConnectionState = Call.State.IDLE;
51
52    SipConnection(com.android.internal.telephony.Connection connection) {
53        if (VERBOSE) log("new SipConnection, connection: " + connection);
54        mOriginalConnection = connection;
55        if (getPhone() != null) {
56            getPhone().registerForPreciseCallStateChanged(mHandler, MSG_PRECISE_CALL_STATE_CHANGED,
57                    null);
58        }
59    }
60
61    @Override
62    protected void onSetAudioState(CallAudioState state) {
63        if (VERBOSE) log("onSetAudioState: " + state);
64        if (getPhone() != null) {
65            getPhone().setEchoSuppressionEnabled();
66        }
67    }
68
69    @Override
70    protected void onSetState(int state) {
71        if (VERBOSE) log("onSetState, state: " + Connection.stateToString(state));
72    }
73
74    @Override
75    protected void onPlayDtmfTone(char c) {
76        if (VERBOSE) log("onPlayDtmfTone");
77        if (getPhone() != null) {
78            getPhone().startDtmf(c);
79        }
80    }
81
82    @Override
83    protected void onStopDtmfTone() {
84        if (VERBOSE) log("onStopDtmfTone");
85        if (getPhone() != null) {
86            getPhone().stopDtmf();
87        }
88    }
89
90    @Override
91    protected void onDisconnect() {
92        if (VERBOSE) log("onDisconnect");
93        try {
94            if (getCall() != null && !getCall().isMultiparty()) {
95                getCall().hangup();
96            } else if (mOriginalConnection != null) {
97                mOriginalConnection.hangup();
98            }
99        } catch (CallStateException e) {
100            log("onDisconnect, exception: " + e);
101        }
102    }
103
104    @Override
105    protected void onSeparate() {
106        if (VERBOSE) log("onSeparate");
107        try {
108            if (mOriginalConnection != null) {
109                mOriginalConnection.separate();
110            }
111        } catch (CallStateException e) {
112            log("onSeparate, exception: " + e);
113        }
114    }
115
116    @Override
117    protected void onAbort() {
118        if (VERBOSE) log("onAbort");
119        onDisconnect();
120    }
121
122    @Override
123    protected void onHold() {
124        if (VERBOSE) log("onHold");
125        try {
126            if (getPhone() != null && getState() == State.ACTIVE) {
127                getPhone().switchHoldingAndActive();
128            }
129        } catch (CallStateException e) {
130            log("onHold, exception: " + e);
131        }
132    }
133
134    @Override
135    protected void onUnhold() {
136        if (VERBOSE) log("onUnhold");
137        try {
138            if (getPhone() != null && getState() == State.HOLDING) {
139                getPhone().switchHoldingAndActive();
140            }
141        } catch (CallStateException e) {
142            log("onUnhold, exception: " + e);
143        }
144    }
145
146    @Override
147    protected void onAnswer() {
148        if (VERBOSE) log("onAnswer");
149        try {
150            if (isValidRingingCall() && getPhone() != null) {
151                getPhone().acceptCall();
152            }
153        } catch (CallStateException e) {
154            log("onAnswer, exception: " + e);
155        }
156    }
157
158    @Override
159    protected void onReject() {
160        if (VERBOSE) log("onReject");
161        try {
162            if (isValidRingingCall() && getPhone() != null) {
163                getPhone().rejectCall();
164            }
165        } catch (CallStateException e) {
166            log("onReject, exception: " + e);
167        }
168    }
169
170    @Override
171    protected void onPostDialContinue(boolean proceed) {
172        if (VERBOSE) log("onPostDialContinue, proceed: " + proceed);
173        // SIP doesn't have post dial support.
174    }
175
176    @Override
177    protected void onSwapWithBackgroundCall() {
178        if (VERBOSE) log("onSwapWithBackgroundCall");
179        // TODO(sail): Implement swap.
180    }
181
182    @Override
183    protected void onChildrenChanged(List<Connection> children) {
184        if (VERBOSE) log("onChildrenChanged, children: " + children);
185    }
186
187    @Override
188    protected void onPhoneAccountClicked() {
189        if (VERBOSE) log("onPhoneAccountClicked");
190    }
191
192    private Call getCall() {
193        if (mOriginalConnection != null) {
194            return mOriginalConnection.getCall();
195        }
196        return null;
197    }
198
199    SipPhone getPhone() {
200        Call call = getCall();
201        if (call != null) {
202            return (SipPhone) call.getPhone();
203        }
204        return null;
205    }
206
207    private boolean isValidRingingCall() {
208        Call call = getCall();
209        return call != null && call.getState().isRinging() &&
210                call.getEarliestConnection() == mOriginalConnection;
211    }
212
213    private void updateState() {
214        if (mOriginalConnection == null) {
215            return;
216        }
217
218        Call.State newState = mOriginalConnection.getState();
219        if (VERBOSE) log("updateState, " + mOriginalConnectionState + " -> " + newState);
220        if (mOriginalConnectionState != newState) {
221            mOriginalConnectionState = newState;
222            switch (newState) {
223                case IDLE:
224                    break;
225                case ACTIVE:
226                    setActive();
227                    break;
228                case HOLDING:
229                    setOnHold();
230                    break;
231                case DIALING:
232                case ALERTING:
233                    setDialing();
234                    break;
235                case INCOMING:
236                case WAITING:
237                    setRinging();
238                    break;
239                case DISCONNECTED:
240                    setDisconnected(mOriginalConnection.getDisconnectCause(), null);
241                    close();
242                    break;
243                case DISCONNECTING:
244                    break;
245            }
246            updateCallCapabilities();
247        }
248    }
249
250    private int buildCallCapabilities() {
251        int capabilities = CallCapabilities.MUTE | CallCapabilities.SUPPORT_HOLD;
252        if (getState() == State.ACTIVE || getState() == State.HOLDING) {
253            capabilities |= CallCapabilities.HOLD;
254        }
255        return capabilities;
256    }
257
258    void updateCallCapabilities() {
259        int newCallCapabilities = buildCallCapabilities();
260        if (getCallCapabilities() != newCallCapabilities) {
261            setCallCapabilities(newCallCapabilities);
262        }
263    }
264
265    void onAddedToCallService() {
266        if (VERBOSE) log("onAddedToCallService");
267        updateCallCapabilities();
268        setAudioModeIsVoip(true);
269        if (mOriginalConnection != null) {
270            setCallerDisplayName(mOriginalConnection.getCnapName(),
271                    mOriginalConnection.getCnapNamePresentation());
272        }
273    }
274
275    private void close() {
276        if (getPhone() != null) {
277            getPhone().unregisterForPreciseCallStateChanged(mHandler);
278        }
279        mOriginalConnection = null;
280        setDestroyed();
281    }
282
283    private static void log(String msg) {
284        Log.d(SipUtil.LOG_TAG, PREFIX + msg);
285    }
286}
287