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