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