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