ImsPhoneCall.java revision 61952e69d543ddc7bffdefb89cb512e22b00bf49
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 } 241 } 242 243 ImsPhoneConnection[] cc = that.mConnections.toArray( 244 new ImsPhoneConnection[that.mConnections.size()]); 245 for (ImsPhoneConnection c : cc) { 246 c.update(null, state); 247 } 248 } 249 250 /** 251 * Retrieves the {@link ImsCall} for the current {@link ImsPhoneCall}. 252 * <p> 253 * Marked as {@code VisibleForTesting} so that the 254 * {@link com.android.internal.telephony.TelephonyTester} class can inject a test conference 255 * event package into a regular ongoing IMS call. 256 * 257 * @return The {@link ImsCall}. 258 */ 259 @VisibleForTesting 260 public ImsCall 261 getImsCall() { 262 return (getFirstConnection() == null) ? null : getFirstConnection().getImsCall(); 263 } 264 265 /*package*/ static boolean isLocalTone(ImsCall imsCall) { 266 if ((imsCall == null) || (imsCall.getCallProfile() == null) 267 || (imsCall.getCallProfile().mMediaProfile == null)) { 268 return false; 269 } 270 271 ImsStreamMediaProfile mediaProfile = imsCall.getCallProfile().mMediaProfile; 272 273 return (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE) 274 ? true : false; 275 } 276 277 /*package*/ boolean 278 update (ImsPhoneConnection conn, ImsCall imsCall, State state) { 279 State newState = state; 280 boolean changed = false; 281 282 //ImsCall.Listener.onCallProgressing can be invoked several times 283 //and ringback tone mode can be changed during the call setup procedure 284 if (state == State.ALERTING) { 285 if (mRingbackTonePlayed && !isLocalTone(imsCall)) { 286 mOwner.mPhone.stopRingbackTone(); 287 mRingbackTonePlayed = false; 288 } else if (!mRingbackTonePlayed && isLocalTone(imsCall)) { 289 mOwner.mPhone.startRingbackTone(); 290 mRingbackTonePlayed = true; 291 } 292 } else { 293 if (mRingbackTonePlayed) { 294 mOwner.mPhone.stopRingbackTone(); 295 mRingbackTonePlayed = false; 296 } 297 } 298 299 if ((newState != mState) && (state != State.DISCONNECTED)) { 300 mState = newState; 301 changed = true; 302 } else if (state == State.DISCONNECTED) { 303 changed = true; 304 } 305 306 return changed; 307 } 308 309 /* package */ ImsPhoneConnection 310 getHandoverConnection() { 311 return (ImsPhoneConnection) getEarliestConnection(); 312 } 313 314 void switchWith(ImsPhoneCall that) { 315 synchronized (ImsPhoneCall.class) { 316 ImsPhoneCall tmp = new ImsPhoneCall(); 317 tmp.takeOver(this); 318 this.takeOver(that); 319 that.takeOver(tmp); 320 } 321 } 322 323 private void takeOver(ImsPhoneCall that) { 324 mConnections = that.mConnections; 325 mState = that.mState; 326 for (Connection c : mConnections) { 327 ((ImsPhoneConnection) c).changeParent(this); 328 } 329 } 330} 331