ImsPhoneCall.java revision 7ee3f31d9670662a3d4a652860396340b386c29e
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.telecom.ConferenceParticipant; 20import android.telephony.Rlog; 21import android.telephony.DisconnectCause; 22 23import com.android.internal.annotations.VisibleForTesting; 24import com.android.internal.telephony.Call; 25import com.android.internal.telephony.CallStateException; 26import com.android.internal.telephony.Connection; 27import com.android.internal.telephony.Phone; 28import com.android.ims.ImsCall; 29import com.android.ims.ImsException; 30import com.android.ims.ImsStreamMediaProfile; 31 32import java.util.List; 33 34/** 35 * {@hide} 36 */ 37public class ImsPhoneCall extends Call { 38 /*************************** Instance Variables **************************/ 39 40 private static final String LOG_TAG = "ImsPhoneCall"; 41 42 /*package*/ ImsPhoneCallTracker mOwner; 43 44 private boolean mRingbackTonePlayed = false; 45 46 /****************************** Constructors *****************************/ 47 /*package*/ 48 ImsPhoneCall() { 49 } 50 51 /*package*/ 52 ImsPhoneCall(ImsPhoneCallTracker owner) { 53 mOwner = owner; 54 } 55 56 public void dispose() { 57 try { 58 mOwner.hangup(this); 59 } catch (CallStateException ex) { 60 //Rlog.e(LOG_TAG, "dispose: unexpected error on hangup", ex); 61 //while disposing, ignore the exception and clean the connections 62 } finally { 63 for(int i = 0, s = mConnections.size(); i < s; i++) { 64 ImsPhoneConnection c = (ImsPhoneConnection) mConnections.get(i); 65 c.onDisconnect(DisconnectCause.LOST_SIGNAL); 66 } 67 } 68 } 69 70 /************************** Overridden from Call *************************/ 71 72 @Override 73 public List<Connection> 74 getConnections() { 75 return mConnections; 76 } 77 78 @Override 79 public Phone 80 getPhone() { 81 return mOwner.mPhone; 82 } 83 84 @Override 85 public boolean 86 isMultiparty() { 87 ImsCall imsCall = getImsCall(); 88 if (imsCall == null) { 89 return false; 90 } 91 92 return imsCall.isMultiparty(); 93 } 94 95 /** Please note: if this is the foreground call and a 96 * background call exists, the background call will be resumed. 97 */ 98 @Override 99 public void 100 hangup() throws CallStateException { 101 mOwner.hangup(this); 102 } 103 104 @Override 105 public String 106 toString() { 107 return mState.toString(); 108 } 109 110 @Override 111 public List<ConferenceParticipant> getConferenceParticipants() { 112 ImsCall call = getImsCall(); 113 if (call == null) { 114 return null; 115 } 116 return call.getConferenceParticipants(); 117 } 118 119 //***** Called from ImsPhoneConnection 120 121 /*package*/ void 122 attach(Connection conn) { 123 clearDisconnected(); 124 mConnections.add(conn); 125 } 126 127 /*package*/ void 128 attach(Connection conn, State state) { 129 this.attach(conn); 130 mState = state; 131 } 132 133 /*package*/ void 134 attachFake(Connection conn, State state) { 135 attach(conn, state); 136 } 137 138 /** 139 * Called by ImsPhoneConnection when it has disconnected 140 */ 141 boolean 142 connectionDisconnected(ImsPhoneConnection conn) { 143 if (mState != State.DISCONNECTED) { 144 /* If only disconnected connections remain, we are disconnected*/ 145 146 boolean hasOnlyDisconnectedConnections = true; 147 148 for (int i = 0, s = mConnections.size() ; i < s; i ++) { 149 if (mConnections.get(i).getState() != State.DISCONNECTED) { 150 hasOnlyDisconnectedConnections = false; 151 break; 152 } 153 } 154 155 if (hasOnlyDisconnectedConnections) { 156 mState = State.DISCONNECTED; 157 return true; 158 } 159 } 160 161 return false; 162 } 163 164 /*package*/ void 165 detach(ImsPhoneConnection conn) { 166 mConnections.remove(conn); 167 clearDisconnected(); 168 } 169 170 /** 171 * @return true if there's no space in this call for additional 172 * connections to be added via "conference" 173 */ 174 /*package*/ boolean 175 isFull() { 176 return mConnections.size() == ImsPhoneCallTracker.MAX_CONNECTIONS_PER_CALL; 177 } 178 179 //***** Called from ImsPhoneCallTracker 180 /** 181 * Called when this Call is being hung up locally (eg, user pressed "end") 182 */ 183 void 184 onHangupLocal() { 185 for (int i = 0, s = mConnections.size(); i < s; i++) { 186 ImsPhoneConnection cn = (ImsPhoneConnection)mConnections.get(i); 187 cn.onHangupLocal(); 188 } 189 mState = State.DISCONNECTING; 190 } 191 192 /** 193 * Called when it's time to clean up disconnected Connection objects 194 */ 195 void 196 clearDisconnected() { 197 for (int i = mConnections.size() - 1 ; i >= 0 ; i--) { 198 ImsPhoneConnection cn = (ImsPhoneConnection)mConnections.get(i); 199 200 if (cn.getState() == State.DISCONNECTED) { 201 mConnections.remove(i); 202 } 203 } 204 205 if (mConnections.size() == 0) { 206 mState = State.IDLE; 207 } 208 } 209 210 /*package*/ ImsPhoneConnection 211 getFirstConnection() { 212 if (mConnections.size() == 0) return null; 213 214 return (ImsPhoneConnection) mConnections.get(0); 215 } 216 217 /*package*/ void 218 setMute(boolean mute) { 219 ImsCall imsCall = getFirstConnection() == null ? 220 null : getFirstConnection().getImsCall(); 221 if (imsCall != null) { 222 try { 223 imsCall.setMute(mute); 224 } catch (ImsException e) { 225 Rlog.e(LOG_TAG, "setMute failed : " + e.getMessage()); 226 } 227 } 228 } 229 230 /* package */ void 231 merge(ImsPhoneCall that, State state) { 232 // This call is the conference host and the "that" call is the one being merged in. 233 // Set the connect time for the conference; this will have been determined when the 234 // conference was initially created. 235 ImsPhoneConnection imsPhoneConnection = getFirstConnection(); 236 if (imsPhoneConnection != null) { 237 long conferenceConnectTime = imsPhoneConnection.getConferenceConnectTime(); 238 if (conferenceConnectTime > 0) { 239 imsPhoneConnection.setConnectTime(conferenceConnectTime); 240 } else { 241 Rlog.d(LOG_TAG, "merge: conference connect time is 0"); 242 } 243 } 244 Rlog.d(LOG_TAG, "merge: " + that + "state = " + state); 245 } 246 247 /** 248 * Retrieves the {@link ImsCall} for the current {@link ImsPhoneCall}. 249 * <p> 250 * Marked as {@code VisibleForTesting} so that the 251 * {@link com.android.internal.telephony.TelephonyTester} class can inject a test conference 252 * event package into a regular ongoing IMS call. 253 * 254 * @return The {@link ImsCall}. 255 */ 256 @VisibleForTesting 257 public ImsCall 258 getImsCall() { 259 return (getFirstConnection() == null) ? null : getFirstConnection().getImsCall(); 260 } 261 262 /*package*/ static boolean isLocalTone(ImsCall imsCall) { 263 if ((imsCall == null) || (imsCall.getCallProfile() == null) 264 || (imsCall.getCallProfile().mMediaProfile == null)) { 265 return false; 266 } 267 268 ImsStreamMediaProfile mediaProfile = imsCall.getCallProfile().mMediaProfile; 269 270 return (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE) 271 ? true : false; 272 } 273 274 /*package*/ boolean 275 update (ImsPhoneConnection conn, ImsCall imsCall, State state) { 276 State newState = state; 277 boolean changed = false; 278 279 //ImsCall.Listener.onCallProgressing can be invoked several times 280 //and ringback tone mode can be changed during the call setup procedure 281 if (state == State.ALERTING) { 282 if (mRingbackTonePlayed && !isLocalTone(imsCall)) { 283 mOwner.mPhone.stopRingbackTone(); 284 mRingbackTonePlayed = false; 285 } else if (!mRingbackTonePlayed && isLocalTone(imsCall)) { 286 mOwner.mPhone.startRingbackTone(); 287 mRingbackTonePlayed = true; 288 } 289 } else { 290 if (mRingbackTonePlayed) { 291 mOwner.mPhone.stopRingbackTone(); 292 mRingbackTonePlayed = false; 293 } 294 } 295 296 if ((newState != mState) && (state != State.DISCONNECTED)) { 297 mState = newState; 298 changed = true; 299 } else if (state == State.DISCONNECTED) { 300 changed = true; 301 } 302 303 return changed; 304 } 305 306 /* package */ ImsPhoneConnection 307 getHandoverConnection() { 308 return (ImsPhoneConnection) getEarliestConnection(); 309 } 310 311 void switchWith(ImsPhoneCall that) { 312 synchronized (ImsPhoneCall.class) { 313 ImsPhoneCall tmp = new ImsPhoneCall(); 314 tmp.takeOver(this); 315 this.takeOver(that); 316 that.takeOver(tmp); 317 } 318 } 319 320 private void takeOver(ImsPhoneCall that) { 321 mConnections = that.mConnections; 322 mState = that.mState; 323 for (Connection c : mConnections) { 324 ((ImsPhoneConnection) c).changeParent(this); 325 } 326 } 327} 328