@Override public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode()); ImsPhoneCall.State oldState = mForegroundCall.getState(); int cause = getDisconnectCauseFromReasonInfo(reasonInfo); ImsPhoneConnection conn = findConnection(imsCall); if (DBG) log("cause = " + cause + " conn = " + conn); if (conn != null && conn.isIncoming() && conn.getConnectTime() == 0) { // Missed if (cause == DisconnectCause.NORMAL) { cause = DisconnectCause.INCOMING_MISSED; } if (DBG) log("Incoming connection of 0 connect time detected - translated cause = " + cause); } if (cause == DisconnectCause.NORMAL && conn != null && conn.getImsCall().isMerged()) { // Call was terminated while it is merged instead of a remote disconnect. cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY; } processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); }
private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) { if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause); if (imsCall == null) return; boolean changed = false; ImsPhoneConnection conn = findConnection(imsCall); if (conn == null) { // TODO : what should be done? return; } changed = conn.update(imsCall, state); if (state == ImsPhoneCall.State.DISCONNECTED) { changed = conn.onDisconnect(cause) || changed; // detach the disconnected connections conn.getCall().detach(conn); removeConnection(conn); } if (changed) { if (conn.getCall() == mHandoverCall) return; updatePhoneState(); mPhone.notifyPreciseCallStateChanged(); } }
private void transferHandoverConnections(ImsPhoneCall call) { if (call.mConnections != null) { for (Connection c : call.mConnections) { c.mPreHandoverState = call.mState; log("Connection state before handover is " + c.getStateBeforeHandover()); } } if (mHandoverCall.mConnections == null) { mHandoverCall.mConnections = call.mConnections; } else { // Multi-call SRVCC mHandoverCall.mConnections.addAll(call.mConnections); } if (mHandoverCall.mConnections != null) { if (call.getImsCall() != null) { call.getImsCall().close(); } for (Connection c : mHandoverCall.mConnections) { ((ImsPhoneConnection) c).changeParent(mHandoverCall); ((ImsPhoneConnection) c).releaseWakeLock(); } } if (call.getState().isAlive()) { log("Call is alive and state is " + call.mState); mHandoverCall.mState = call.mState; } call.mConnections.clear(); call.mState = ImsPhoneCall.State.IDLE; }
void conference() { if (DBG) log("conference"); ImsCall fgImsCall = mForegroundCall.getImsCall(); if (fgImsCall == null) { log("conference no foreground ims call"); return; } ImsCall bgImsCall = mBackgroundCall.getImsCall(); if (bgImsCall == null) { log("conference no background ims call"); return; } // Keep track of the connect time of the earliest call so that it can be set on the // {@code ImsConference} when it is created. long conferenceConnectTime = Math.min( mForegroundCall.getEarliestConnectTime(), mBackgroundCall.getEarliestConnectTime()); ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection(); if (foregroundConnection != null) { foregroundConnection.setConferenceConnectTime(conferenceConnectTime); } try { fgImsCall.merge(bgImsCall); } catch (ImsException e) { log("conference " + e.getMessage()); } }
private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) { for (ImsPhoneConnection conn : mConnections) { if (conn.getImsCall() == imsCall) { return conn; } } return null; }
/*package*/ void hangup(ImsPhoneConnection conn) throws CallStateException { if (DBG) log("hangup connection"); if (conn.getOwner() != this) { throw new CallStateException( "ImsPhoneConnection " + conn + "does not belong to ImsPhoneCallTracker " + this); } hangup(conn.getCall()); }
/** * Called when the state of IMS conference participant(s) has changed. * * @param call the call object that carries out the IMS call. * @param participants the participant(s) and their new state information. */ @Override public void onConferenceParticipantsStateChanged( ImsCall call, List<ConferenceParticipant> participants) { if (DBG) log("onConferenceParticipantsStateChanged"); ImsPhoneConnection conn = findConnection(call); if (conn != null) { conn.updateConferenceParticipants(participants); } }
/* package */ void hangup(ImsPhoneCall call) throws CallStateException { if (DBG) log("hangup call"); if (call.getConnections().size() == 0) { throw new CallStateException("no connections"); } ImsCall imsCall = call.getImsCall(); boolean rejectCall = false; if (call == mRingingCall) { if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming"); rejectCall = true; } else if (call == mForegroundCall) { if (call.isDialingOrAlerting()) { if (Phone.DEBUG_PHONE) { log("(foregnd) hangup dialing or alerting..."); } } else { if (Phone.DEBUG_PHONE) { log("(foregnd) hangup foreground"); } // held call will be resumed by onCallTerminated } } else if (call == mBackgroundCall) { if (Phone.DEBUG_PHONE) { log("(backgnd) hangup waiting or background"); } } else { throw new CallStateException( "ImsPhoneCall " + call + "does not belong to ImsPhoneCallTracker " + this); } call.onHangupLocal(); try { if (imsCall != null) { if (rejectCall) imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE); else imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED); } else if (mPendingMO != null && call == mForegroundCall) { // is holding a foreground call mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED); mPendingMO.onDisconnect(); removeConnection(mPendingMO); mPendingMO = null; updatePhoneState(); removeMessages(EVENT_DIAL_PENDINGMO); } } catch (ImsException e) { throw new CallStateException(e.getMessage()); } mPhone.notifyPreciseCallStateChanged(); }
@Override public void onCallHoldReceived(ImsCall imsCall) { if (DBG) log("onCallHoldReceived"); ImsPhoneConnection conn = findConnection(imsCall); if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) { if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) { mPhone.startOnHoldTone(); mOnHoldToneStarted = true; } } }
@Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) { if (DBG) log("onReceive : incoming call intent"); if (mImsManager == null) return; if (mServiceId < 0) return; try { // Network initiated USSD will be treated by mImsUssdListener boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false); if (isUssd) { if (DBG) log("onReceive : USSD"); mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener); if (mUssdSession != null) { mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE); } return; } // Normal MT call ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener); ImsPhoneConnection conn = new ImsPhoneConnection( mPhone.getContext(), imsCall, ImsPhoneCallTracker.this, mRingingCall); addConnection(conn); IImsVideoCallProvider imsVideoCallProvider = imsCall.getCallSession().getVideoCallProvider(); if (imsVideoCallProvider != null) { ImsVideoCallProviderWrapper imsVideoCallProviderWrapper = new ImsVideoCallProviderWrapper(imsVideoCallProvider); conn.setVideoProvider(imsVideoCallProviderWrapper); } if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) || (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) { conn.update(imsCall, ImsPhoneCall.State.WAITING); } mPhone.notifyNewRingingConnection(conn); mPhone.notifyIncomingRing(); updatePhoneState(); mPhone.notifyPreciseCallStateChanged(); } catch (ImsException e) { loge("onReceive : exception " + e); } catch (RemoteException e) { } } }
private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState) { if (conn == null) { return; } if (conn.getAddress() == null || conn.getAddress().length() == 0 || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) { // Phone number is invalid conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER); sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); return; } // Always unmute when initiating a new call setMute(false); int serviceType = PhoneNumberUtils.isEmergencyNumber(conn.getAddress()) ? ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL; int callType = ImsCallProfile.getCallTypeFromVideoState(videoState); // TODO(vt): Is this sufficient? At what point do we know the video state of the call? conn.setVideoState(videoState); try { String[] callees = new String[] {conn.getAddress()}; ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, serviceType, callType); profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode); ImsCall imsCall = mImsManager.makeCall(mServiceId, profile, callees, mImsCallListener); conn.setImsCall(imsCall); IImsVideoCallProvider imsVideoCallProvider = imsCall.getCallSession().getVideoCallProvider(); if (imsVideoCallProvider != null) { ImsVideoCallProviderWrapper imsVideoCallProviderWrapper = new ImsVideoCallProviderWrapper(imsVideoCallProvider); conn.setVideoProvider(imsVideoCallProviderWrapper); } } catch (ImsException e) { loge("dialInternal : " + e); conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); } catch (RemoteException e) { } }
@Override public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode()); synchronized (mSyncHold) { ImsPhoneCall.State bgState = mBackgroundCall.getState(); if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) { // disconnected while processing hold if (mPendingMO != null) { sendEmptyMessage(EVENT_DIAL_PENDINGMO); } } else if (bgState == ImsPhoneCall.State.ACTIVE) { mForegroundCall.switchWith(mBackgroundCall); if (mPendingMO != null) { mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); } } } }
/** * onCallStartFailed will be invoked when: case 1) Dialing fails case 2) Ringing call is * disconnected by local or remote user */ @Override public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode()); if (mPendingMO != null) { // To initiate dialing circuit-switched call if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE && mRingingCall.getState() == ImsPhoneCall.State.IDLE) { mForegroundCall.detach(mPendingMO); removeConnection(mPendingMO); mPendingMO.finalize(); mPendingMO = null; mPhone.initiateSilentRedial(); return; } else { int cause = getDisconnectCauseFromReasonInfo(reasonInfo); processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); } mPendingMO = null; } }
@Override public void handleMessage(Message msg) { AsyncResult ar; if (DBG) log("handleMessage what=" + msg.what); switch (msg.what) { case EVENT_HANGUP_PENDINGMO: if (mPendingMO != null) { mPendingMO.onDisconnect(); removeConnection(mPendingMO); mPendingMO = null; } updatePhoneState(); mPhone.notifyPreciseCallStateChanged(); break; case EVENT_RESUME_BACKGROUND: try { resumeWaitingOrHolding(); } catch (CallStateException e) { if (Phone.DEBUG_PHONE) { loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e); } } break; case EVENT_DIAL_PENDINGMO: dialInternal(mPendingMO, mClirMode, VideoProfile.VideoState.AUDIO_ONLY); break; case EVENT_EXIT_ECM_RESPONSE_CDMA: // no matter the result, we still do the same here if (pendingCallInEcm) { dialInternal(mPendingMO, pendingCallClirMode, pendingCallVideoState); pendingCallInEcm = false; } mPhone.unsetOnEcbModeExitResponse(this); break; } }