1、图形显示

挂断电话分为本地挂断和远程对方挂断

2、本地挂断

1)、点击按钮 

先看按键的监听事件 CallCardFragment.java 中有对按钮的监听事件

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);mPulseAnimation =AnimationUtils.loadAnimation(view.getContext(), R.anim.call_status_pulse);mPhoneNumber = (TextView) view.findViewById(R.id.phoneNumber);mConnectedAddress = (TextView) view.findViewById(R.id.connectedAddress);mPrimaryName = (TextView) view.findViewById(R.id.name);mNumberLabel = (TextView) view.findViewById(R.id.label);mPrimarySimIndication = (TextView) view.findViewById(R.id.simIndication);mPrimaryLocation = (TextView) view.findViewById(R.id.location);mSecondaryCallInfo = view.findViewById(R.id.secondary_call_info);mSecondaryCallProviderInfo = view.findViewById(R.id.secondary_call_provider_info);mCallCardContent = view.findViewById(R.id.call_card_content);mPhotoLarge = (ImageView) view.findViewById(R.id.photoLarge);mPhotoLarge.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {getPresenter().onContactPhotoClick();}});mContactContext = view.findViewById(R.id.contact_context);mContactContextTitle = (TextView) view.findViewById(R.id.contactContextTitle);mContactContextListView = (ListView) view.findViewById(R.id.contactContextInfo);// This layout stores all the list header layouts so they can be easily removed.mContactContextListHeaders = new LinearLayout(getView().getContext());mContactContextListView.addHeaderView(mContactContextListHeaders);mCallStateIcon = (ImageView) view.findViewById(R.id.callStateIcon);mCallStateVideoCallIcon = (ImageView) view.findViewById(R.id.videoCallIcon);mWorkProfileIcon = (ImageView) view.findViewById(R.id.workProfileIcon);mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel);mHdAudioIcon = (ImageView) view.findViewById(R.id.hdAudioIcon);mForwardIcon = (ImageView) view.findViewById(R.id.forwardIcon);mCallNumberAndLabel = view.findViewById(R.id.labelAndNumber);mCallLocationAndType = view.findViewById(R.id.locationAndType);mCallTypeLabel = (TextView) view.findViewById(R.id.callTypeLabel);mRecordingLabel = (TextView) view.findViewById(R.id.recordinglabel);mElapsedTime = (TextView) view.findViewById(R.id.elapsedTime);mPrimaryCallCardContainer = view.findViewById(R.id.primary_call_info_container);mPrimaryCallInfo = (ViewGroup) view.findViewById(R.id.primary_call_banner);mCallButtonsContainer = view.findViewById(R.id.callButtonFragment);mPhotoSmall = (ImageView) view.findViewById(R.id.photoSmall);mPhotoSmall.setVisibility(View.GONE);mInCallMessageLabel = (TextView) view.findViewById(R.id.connectionServiceMessage);mProgressSpinner = view.findViewById(R.id.progressSpinner);mFloatingActionButtonContainer = view.findViewById(R.id.floating_end_call_action_button_container);mFloatingActionButton = (ImageButton) view.findViewById(R.id.floating_end_call_action_button);mFloatingActionButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//调用的是这一个函数getPresenter().endCallClicked();}});mFloatingActionButtonController = new FloatingActionButtonController(getActivity(),mFloatingActionButtonContainer, mFloatingActionButton);mSecondaryCallInfo.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {getPresenter().secondaryInfoClicked();updateFabPositionForSecondaryCallInfo();}});mCallStateButton = view.findViewById(R.id.callStateButton);mCallStateButton.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {getPresenter().onCallStateButtonTouched();return false;}});mManageConferenceCallButton = view.findViewById(R.id.manage_conference_call_button);mManageConferenceCallButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {InCallActivity activity = (InCallActivity) getActivity();activity.showConferenceFragment(true);}});mPrimaryName.setElegantTextHeight(false);mCallStateLabel.setElegantTextHeight(false);mCallSubject = (TextView) view.findViewById(R.id.callSubject);}
2)、CallCardPresenter.java的endCallClicked()方法。
/*** endCallClicked.*/public void endCallClicked() {if (mPrimary == null) {Log.i(TAG, "(mPrimary == null)");return;}Log.i(TAG, "Disconnecting call: " + mPrimary);Log.i(TAG, "mPrimary.getDisconnectCause().getCode(): " + mPrimary.getDisconnectCause().getCode());if (mPrimary.getDisconnectCause().getCode() != DisconnectCause.BUSY) {final String callId = mPrimary.getId();mPrimary.setState(Call.State.DISCONNECTING);CallList.getInstance().onUpdate(mPrimary);TelecomAdapter.getInstance().disconnectCall(callId);} else {mPrimary.setState(Call.State.DISCONNECTED);CallList.getInstance().onDisconnect(mPrimary);//if has held call, unhold call./* if (mSecondary != null && mSecondary.getState() == Call.State.ONHOLD) {final String callId = mSecondary.getId();TelecomAdapter.getInstance().unholdCall(callId);}*/}}

这里先把call的状态设置成DISCONNECTING,然后通过CallList更新UI界面,最后继续挂断流程。

3)、TelecomAdapter.java文件的disconnectCall()方法
void disconnectCall(String callId) {android.telecom.Call call = getTelecomCallById(callId);if (call != null) {//执行这一句call.disconnect();} else {Log.e(TAG, "error disconnectCall, call not in call list " + callId);}}

通过Callid找到对应的Call对象(android.telephone.Call)

4)、调用到framework里call.java的disconnect()方法

    /*** Instructs this {@code Call} to disconnect.*/public void disconnect() {mInCallAdapter.disconnectCall(mTelecomCallId);}
5)、调用到framework里InCallAdapter.java的disconnectCall()方法
    /*** Instructs Telecom to disconnect the specified call.** @param callId The identifier of the call to disconnect.*/public void disconnectCall(String callId) {try {mAdapter.disconnectCall(callId);} catch (RemoteException e) {}}

mAdapter就是incallui与telecom通信的AIDL接口
跨进程调用,进入telecom进程,该AIDL接口具体实现类是InCallAdapter,相同的类名不一样的包名。

6)、调用到packages里InCallAdapter.java的disconnectCall()方法
    @Overridepublic void disconnectCall(String callId) {try {Log.startSession("ICA.dC", mOwnerComponentName);long token = Binder.clearCallingIdentity();try {synchronized (mLock) {Log.v(this, "disconnectCall: %s", callId);Call call = mCallIdMapper.getCall(callId);if (call != null) {//调用到这句话                  mCallsManager.disconnectCall(call);} else {Log.w(this, "disconnectCall, unknown call id: %s", callId);}}} finally {Binder.restoreCallingIdentity(token);}} finally {Log.endSession();}}

同样是根据callid取出对应的 Call(com.android.server.telecom.Call),最后调用CallsManager的disconnectCall方法传入call

7)、CallsManager.java文件的diaconnectCall()方法
    /*** Instructs Telecom to disconnect the specified call. Intended to be invoked by the* in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by* the user hitting the end-call button.*/@VisibleForTestingpublic void disconnectCall(Call call) {Log.v(this, "disconnectCall %s", call);if (!mCalls.contains(call)) {Log.w(this, "Unknown call (%s) asked to disconnect", call);} else {mLocallyDisconnectingCalls.add(call);call.disconnect();}}

将该call对象加入本地断开call列表,之后进入Call的disconnect方法。

8)、call.java的disconnect()方法
    @VisibleForTestingpublic void disconnect() {disconnect(false);}

继续调用

    /*** Attempts to disconnect the call through the connection service.*/@VisibleForTestingpublic void disconnect(boolean wasViaNewOutgoingCallBroadcaster) {Log.event(this, Log.Events.REQUEST_DISCONNECT);// Track that the call is now locally disconnecting.setLocallyDisconnecting(true);if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||mState == CallState.CONNECTING) {Log.v(this, "Aborting call %s", this);abort(wasViaNewOutgoingCallBroadcaster);} else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {if (mConnectionService == null) {Log.e(this, new Exception(), "disconnect() request on a call without a"+ " connection service.");} else {Log.i(this, "Send disconnect to connection service for call: %s", this);// The call isn't officially disconnected until the connection service// confirms that the call was actually disconnected. Only then is the// association between call and connection service severed, see// {@link CallsManager#markCallAsDisconnected}.//调用这个函数mConnectionService.disconnect(this);}}}

setLocallyDisconnecting(true); 先设置本地挂断标志为true
由于mState这个时候是CallState.ACTIVE状态,进入mConnectionService的disconnect方法。
这里的mConnectionService是ConnectionServiceWrapper类,是telecom与telephony通信的代理类

9)、ConnectionServiceWrapper.java的disconnect()方法
    /** @see IConnectionService#disconnect(String) */void disconnect(Call call) {final String callId = mCallIdMapper.getCallId(call);if (callId != null && isServiceValid("disconnect")) {try {logOutgoing("disconnect %s", callId);mServiceInterface.disconnect(callId);} catch (RemoteException e) {}}}

这里的mServiceInterface就是telephony提供给telecom调用的AIDL接口
跨进程进入telephony,AIDL接口具体实现是其父类ConnectionService的mBinder成员变量

10)、ConnectionService.java文件


/*** An abstract service that should be implemented by any apps which can make phone calls (VoIP or* otherwise) and want those calls to be integrated into the built-in phone app.* Once implemented, the {@code ConnectionService} needs two additional steps before it will be* integrated into the phone app:* <p>* 1. <i>Registration in AndroidManifest.xml</i>* <br/>* <pre>* &lt;service android:name="com.example.package.MyConnectionService"*    android:label="@string/some_label_for_my_connection_service"*    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"&gt;*  &lt;intent-filter&gt;*   &lt;action android:name="android.telecom.ConnectionService" /&gt;*  &lt;/intent-filter&gt;* &lt;/service&gt;* </pre>* <p>* 2. <i> Registration of {@link PhoneAccount} with {@link TelecomManager}.</i>* <br/>* See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information.* <p>* Once registered and enabled by the user in the phone app settings, telecom will bind to a* {@code ConnectionService} implementation when it wants that {@code ConnectionService} to place* a call or the service has indicated that is has an incoming call through* {@link TelecomManager#addNewIncomingCall}. The {@code ConnectionService} can then expect a call* to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection} wherein it* should provide a new instance of a {@link Connection} object.  It is through this* {@link Connection} object that telecom receives state updates and the {@code ConnectionService}* receives call-commands such as answer, reject, hold and disconnect.* <p>* When there are no more live calls, telecom will unbind from the {@code ConnectionService}.*/
public abstract class ConnectionService extends Service {/*** The {@link Intent} that must be declared as handled by the service.*/@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)public static final String SERVICE_INTERFACE = "android.telecom.ConnectionService";// Flag controlling whether PII is emitted into the logsprivate static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;private static final int MSG_CREATE_CONNECTION = 2;private static final int MSG_ABORT = 3;private static final int MSG_ANSWER = 4;private static final int MSG_REJECT = 5;private static final int MSG_DISCONNECT = 6;private static final int MSG_HOLD = 7;private static final int MSG_UNHOLD = 8;private static final int MSG_ON_CALL_AUDIO_STATE_CHANGED = 9;private static final int MSG_PLAY_DTMF_TONE = 10;private static final int MSG_STOP_DTMF_TONE = 11;private static final int MSG_CONFERENCE = 12;private static final int MSG_SPLIT_FROM_CONFERENCE = 13;private static final int MSG_ON_POST_DIAL_CONTINUE = 14;private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16;private static final int MSG_ANSWER_VIDEO = 17;private static final int MSG_MERGE_CONFERENCE = 18;private static final int MSG_SWAP_CONFERENCE = 19;private static final int MSG_REJECT_WITH_MESSAGE = 20;private static final int MSG_SILENCE = 21;private static final int MSG_PULL_EXTERNAL_CALL = 22;private static final int MSG_SEND_CALL_EVENT = 23;private static final int MSG_ON_EXTRAS_CHANGED = 24;private static Connection sNullConnection;private final Map<String, Connection> mConnectionById = new ConcurrentHashMap<>();private final Map<Connection, String> mIdByConnection = new ConcurrentHashMap<>();private final Map<String, Conference> mConferenceById = new ConcurrentHashMap<>();private final Map<Conference, String> mIdByConference = new ConcurrentHashMap<>();private final RemoteConnectionManager mRemoteConnectionManager =new RemoteConnectionManager(this);private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>();private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();private boolean mAreAccountsInitialized = false;private Conference sNullConference;private Object mIdSyncRoot = new Object();private int mId = 0;private final IBinder mBinder = new IConnectionService.Stub() {@Overridepublic void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();}public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();}@Overridepublic void createConnection(PhoneAccountHandle connectionManagerPhoneAccount,String id,ConnectionRequest request,boolean isIncoming,boolean isUnknown) {SomeArgs args = SomeArgs.obtain();args.arg1 = connectionManagerPhoneAccount;args.arg2 = id;args.arg3 = request;args.argi1 = isIncoming ? 1 : 0;args.argi2 = isUnknown ? 1 : 0;mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();}@Overridepublic void abort(String callId) {mHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();}@Overridepublic void answerVideo(String callId, int videoState) {SomeArgs args = SomeArgs.obtain();args.arg1 = callId;args.argi1 = videoState;mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget();}@Overridepublic void answer(String callId) {mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();}@Overridepublic void reject(String callId) {mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();}@Overridepublic void rejectWithMessage(String callId, String message) {SomeArgs args = SomeArgs.obtain();args.arg1 = callId;args.arg2 = message;mHandler.obtainMessage(MSG_REJECT_WITH_MESSAGE, args).sendToTarget();}@Overridepublic void silence(String callId) {mHandler.obtainMessage(MSG_SILENCE, callId).sendToTarget();}//调用这个函数@Overridepublic void disconnect(String callId) {mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();}@Overridepublic void hold(String callId) {mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();}@Overridepublic void unhold(String callId) {mHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();}@Overridepublic void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {SomeArgs args = SomeArgs.obtain();args.arg1 = callId;args.arg2 = callAudioState;mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget();}@Overridepublic void playDtmfTone(String callId, char digit) {mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();}@Overridepublic void stopDtmfTone(String callId) {mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();}@Overridepublic void conference(String callId1, String callId2) {SomeArgs args = SomeArgs.obtain();args.arg1 = callId1;args.arg2 = callId2;mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();}@Overridepublic void splitFromConference(String callId) {mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();}@Overridepublic void mergeConference(String callId) {mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();}@Overridepublic void swapConference(String callId) {mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();}@Overridepublic void onPostDialContinue(String callId, boolean proceed) {SomeArgs args = SomeArgs.obtain();args.arg1 = callId;args.argi1 = proceed ? 1 : 0;mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();}@Overridepublic void pullExternalCall(String callId) {mHandler.obtainMessage(MSG_PULL_EXTERNAL_CALL, callId).sendToTarget();}@Overridepublic void sendCallEvent(String callId, String event, Bundle extras) {SomeArgs args = SomeArgs.obtain();args.arg1 = callId;args.arg2 = event;args.arg3 = extras;mHandler.obtainMessage(MSG_SEND_CALL_EVENT, args).sendToTarget();}@Overridepublic void onExtrasChanged(String callId, Bundle extras) {SomeArgs args = SomeArgs.obtain();args.arg1 = callId;args.arg2 = extras;mHandler.obtainMessage(MSG_ON_EXTRAS_CHANGED, args).sendToTarget();}};private final Handler mHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_ADD_CONNECTION_SERVICE_ADAPTER:mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);onAdapterAttached();break;case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER:mAdapter.removeAdapter((IConnectionServiceAdapter) msg.obj);break;case MSG_CREATE_CONNECTION: {SomeArgs args = (SomeArgs) msg.obj;try {final PhoneAccountHandle connectionManagerPhoneAccount =(PhoneAccountHandle) args.arg1;final String id = (String) args.arg2;final ConnectionRequest request = (ConnectionRequest) args.arg3;final boolean isIncoming = args.argi1 == 1;final boolean isUnknown = args.argi2 == 1;if (!mAreAccountsInitialized) {Log.d(this, "Enqueueing pre-init request %s", id);mPreInitializationConnectionRequests.add(new Runnable() {@Overridepublic void run() {createConnection(connectionManagerPhoneAccount,id,request,isIncoming,isUnknown);}});} else {createConnection(connectionManagerPhoneAccount,id,request,isIncoming,isUnknown);}} finally {args.recycle();}break;}case MSG_ABORT:abort((String) msg.obj);break;case MSG_ANSWER:answer((String) msg.obj);break;case MSG_ANSWER_VIDEO: {SomeArgs args = (SomeArgs) msg.obj;try {String callId = (String) args.arg1;int videoState = args.argi1;answerVideo(callId, videoState);} finally {args.recycle();}break;}case MSG_REJECT:reject((String) msg.obj);break;case MSG_REJECT_WITH_MESSAGE: {SomeArgs args = (SomeArgs) msg.obj;try {reject((String) args.arg1, (String) args.arg2);} finally {args.recycle();}break;}case MSG_DISCONNECT:disconnect((String) msg.obj);break;case MSG_SILENCE:silence((String) msg.obj);break;case MSG_HOLD:hold((String) msg.obj);break;case MSG_UNHOLD:unhold((String) msg.obj);break;case MSG_ON_CALL_AUDIO_STATE_CHANGED: {SomeArgs args = (SomeArgs) msg.obj;try {String callId = (String) args.arg1;CallAudioState audioState = (CallAudioState) args.arg2;onCallAudioStateChanged(callId, new CallAudioState(audioState));} finally {args.recycle();}break;}case MSG_PLAY_DTMF_TONE:playDtmfTone((String) msg.obj, (char) msg.arg1);break;case MSG_STOP_DTMF_TONE:stopDtmfTone((String) msg.obj);break;case MSG_CONFERENCE: {SomeArgs args = (SomeArgs) msg.obj;try {String callId1 = (String) args.arg1;String callId2 = (String) args.arg2;conference(callId1, callId2);} finally {args.recycle();}break;}case MSG_SPLIT_FROM_CONFERENCE:splitFromConference((String) msg.obj);break;case MSG_MERGE_CONFERENCE:mergeConference((String) msg.obj);break;case MSG_SWAP_CONFERENCE:swapConference((String) msg.obj);break;case MSG_ON_POST_DIAL_CONTINUE: {SomeArgs args = (SomeArgs) msg.obj;try {String callId = (String) args.arg1;boolean proceed = (args.argi1 == 1);onPostDialContinue(callId, proceed);} finally {args.recycle();}break;}case MSG_PULL_EXTERNAL_CALL: {pullExternalCall((String) msg.obj);break;}case MSG_SEND_CALL_EVENT: {SomeArgs args = (SomeArgs) msg.obj;try {String callId = (String) args.arg1;String event = (String) args.arg2;Bundle extras = (Bundle) args.arg3;sendCallEvent(callId, event, extras);} finally {args.recycle();}break;}case MSG_ON_EXTRAS_CHANGED: {SomeArgs args = (SomeArgs) msg.obj;try {String callId = (String) args.arg1;Bundle extras = (Bundle) args.arg2;handleExtrasChanged(callId, extras);} finally {args.recycle();}break;}default:break;}}};private final Conference.Listener mConferenceListener = new Conference.Listener() {@Overridepublic void onStateChanged(Conference conference, int oldState, int newState) {String id = mIdByConference.get(conference);switch (newState) {case Connection.STATE_ACTIVE:mAdapter.setActive(id);break;case Connection.STATE_HOLDING:mAdapter.setOnHold(id);break;case Connection.STATE_DISCONNECTED:// handled by onDisconnectedbreak;}}@Overridepublic void onDisconnected(Conference conference, DisconnectCause disconnectCause) {String id = mIdByConference.get(conference);mAdapter.setDisconnected(id, disconnectCause);}@Overridepublic void onConnectionAdded(Conference conference, Connection connection) {}@Overridepublic void onConnectionRemoved(Conference conference, Connection connection) {}@Overridepublic void onConferenceableConnectionsChanged(Conference conference, List<Connection> conferenceableConnections) {mAdapter.setConferenceableConnections(mIdByConference.get(conference),createConnectionIdList(conferenceableConnections));}@Overridepublic void onDestroyed(Conference conference) {removeConference(conference);}@Overridepublic void onConnectionCapabilitiesChanged(Conference conference,int connectionCapabilities) {String id = mIdByConference.get(conference);Log.d(this, "call capabilities: conference: %s",Connection.capabilitiesToString(connectionCapabilities));mAdapter.setConnectionCapabilities(id, connectionCapabilities);}@Overridepublic void onConnectionPropertiesChanged(Conference conference,int connectionProperties) {String id = mIdByConference.get(conference);Log.d(this, "call capabilities: conference: %s",Connection.propertiesToString(connectionProperties));mAdapter.setConnectionProperties(id, connectionProperties);}@Overridepublic void onVideoStateChanged(Conference c, int videoState) {String id = mIdByConference.get(c);Log.d(this, "onVideoStateChanged set video state %d", videoState);mAdapter.setVideoState(id, videoState);}@Overridepublic void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {String id = mIdByConference.get(c);Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,videoProvider);mAdapter.setVideoProvider(id, videoProvider);}@Overridepublic void onStatusHintsChanged(Conference conference, StatusHints statusHints) {String id = mIdByConference.get(conference);if (id != null) {mAdapter.setStatusHints(id, statusHints);}}@Overridepublic void onExtrasChanged(Conference c, Bundle extras) {String id = mIdByConference.get(c);if (id != null) {mAdapter.putExtras(id, extras);}}@Overridepublic void onExtrasRemoved(Conference c, List<String> keys) {String id = mIdByConference.get(c);if (id != null) {mAdapter.removeExtras(id, keys);}}};private final Connection.Listener mConnectionListener = new Connection.Listener() {@Overridepublic void onStateChanged(Connection c, int state) {String id = mIdByConnection.get(c);Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state));switch (state) {case Connection.STATE_ACTIVE:mAdapter.setActive(id);break;case Connection.STATE_DIALING:mAdapter.setDialing(id);break;case Connection.STATE_DISCONNECTED:// Handled in onDisconnected()break;case Connection.STATE_HOLDING:mAdapter.setOnHold(id);break;case Connection.STATE_NEW:// Nothing to tell Telecombreak;case Connection.STATE_RINGING:mAdapter.setRinging(id);break;}}@Overridepublic void onDisconnected(Connection c, DisconnectCause disconnectCause) {String id = mIdByConnection.get(c);Log.d(this, "Adapter set disconnected %s", disconnectCause);mAdapter.setDisconnected(id, disconnectCause);}@Overridepublic void onVideoStateChanged(Connection c, int videoState) {String id = mIdByConnection.get(c);Log.d(this, "Adapter set video state %d", videoState);mAdapter.setVideoState(id, videoState);}@Overridepublic void onAddressChanged(Connection c, Uri address, int presentation) {String id = mIdByConnection.get(c);mAdapter.setAddress(id, address, presentation);}@Overridepublic void onConnectedAddressChanged(Connection c, Uri connectedAddress, int connectedAddressPresentation) {String id = mIdByConnection.get(c);mAdapter.setConnectedAddress(id, connectedAddress, connectedAddressPresentation);}@Overridepublic void onCallerDisplayNameChanged(Connection c, String callerDisplayName, int presentation) {String id = mIdByConnection.get(c);mAdapter.setCallerDisplayName(id, callerDisplayName, presentation);}@Overridepublic void onDestroyed(Connection c) {removeConnection(c);}@Overridepublic void onPostDialWait(Connection c, String remaining) {String id = mIdByConnection.get(c);Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining);mAdapter.onPostDialWait(id, remaining);}@Overridepublic void onPostDialChar(Connection c, char nextChar) {String id = mIdByConnection.get(c);Log.d(this, "Adapter onPostDialChar %s, %s", c, nextChar);mAdapter.onPostDialChar(id, nextChar);}@Overridepublic void onRingbackRequested(Connection c, boolean ringback) {String id = mIdByConnection.get(c);Log.d(this, "Adapter onRingback %b", ringback);mAdapter.setRingbackRequested(id, ringback);}@Overridepublic void onConnectionCapabilitiesChanged(Connection c, int capabilities) {String id = mIdByConnection.get(c);Log.d(this, "capabilities: parcelableconnection: %s",Connection.capabilitiesToString(capabilities));mAdapter.setConnectionCapabilities(id, capabilities);}@Overridepublic void onConnectionPropertiesChanged(Connection c, int properties) {String id = mIdByConnection.get(c);Log.d(this, "properties: parcelableconnection: %s",Connection.propertiesToString(properties));mAdapter.setConnectionProperties(id, properties);}@Overridepublic void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {String id = mIdByConnection.get(c);Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,videoProvider);mAdapter.setVideoProvider(id, videoProvider);}@Overridepublic void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {String id = mIdByConnection.get(c);mAdapter.setIsVoipAudioMode(id, isVoip);}@Overridepublic void onStatusHintsChanged(Connection c, StatusHints statusHints) {String id = mIdByConnection.get(c);mAdapter.setStatusHints(id, statusHints);}@Overridepublic void onConferenceablesChanged(Connection connection, List<Conferenceable> conferenceables) {mAdapter.setConferenceableConnections(mIdByConnection.get(connection),createIdList(conferenceables));}@Overridepublic void onConferenceChanged(Connection connection, Conference conference) {String id = mIdByConnection.get(connection);if (id != null) {String conferenceId = null;if (conference != null) {conferenceId = mIdByConference.get(conference);}mAdapter.setIsConferenced(id, conferenceId);}}@Overridepublic void onConferenceMergeFailed(Connection connection) {String id = mIdByConnection.get(connection);if (id != null) {mAdapter.onConferenceMergeFailed(id);}}@Overridepublic void onExtrasChanged(Connection c, Bundle extras) {String id = mIdByConnection.get(c);if (id != null) {mAdapter.putExtras(id, extras);}}public void onExtrasRemoved(Connection c, List<String> keys) {String id = mIdByConnection.get(c);if (id != null) {mAdapter.removeExtras(id, keys);}}@Overridepublic void onConnectionEvent(Connection connection, String event, Bundle extras) {String id = mIdByConnection.get(connection);if (id != null) {mAdapter.onConnectionEvent(id, event, extras);}}};/** {@inheritDoc} */@Overridepublic final IBinder onBind(Intent intent) {return mBinder;}/** {@inheritDoc} */@Overridepublic boolean onUnbind(Intent intent) {endAllConnections();return super.onUnbind(intent);}/*** This can be used by telecom to either create a new outgoing call or attach to an existing* incoming call. In either case, telecom will cycle through a set of services and call* createConnection util a connection service cancels the process or completes it successfully.*/private void createConnection(final PhoneAccountHandle callManagerAccount,final String callId,final ConnectionRequest request,boolean isIncoming,boolean isUnknown) {Log.d(this, "lum_1 createConnection, callManagerAccount: %s, callId: %s, request: %s, " +"isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,isIncoming,isUnknown);Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request): isIncoming ? onCreateIncomingConnection(callManagerAccount, request): onCreateOutgoingConnection(callManagerAccount, request);Log.d(this, "lum_2 createConnection, connection: %s", connection);if (connection == null) {Log.d(this, "lum_3");connection = Connection.createFailedConnection(new DisconnectCause(DisconnectCause.ERROR));}connection.setTelecomCallId(callId);if (connection.getState() != Connection.STATE_DISCONNECTED) {addConnection(callId, connection);}Uri address = connection.getAddress();String number = address == null ? "null" : address.getSchemeSpecificPart();Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",Connection.toLogSafePhoneNumber(number),Connection.stateToString(connection.getState()),Connection.capabilitiesToString(connection.getConnectionCapabilities()),Connection.propertiesToString(connection.getConnectionProperties()));Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);mAdapter.handleCreateConnectionComplete(callId,request,new ParcelableConnection(request.getAccountHandle(),connection.getState(),connection.getConnectionCapabilities(),connection.getConnectionProperties(),connection.getAddress(),connection.getAddressPresentation(),connection.getCallerDisplayName(),connection.getCallerDisplayNamePresentation(),connection.getVideoProvider() == null ?null : connection.getVideoProvider().getInterface(),connection.getVideoState(),connection.isRingbackRequested(),connection.getAudioModeIsVoip(),connection.getConnectTimeMillis(),connection.getStatusHints(),connection.getDisconnectCause(),createIdList(connection.getConferenceables()),connection.getExtras()));if (isUnknown) {triggerConferenceRecalculate();}}private void abort(String callId) {Log.d(this, "abort %s", callId);findConnectionForAction(callId, "abort").onAbort();}private void answerVideo(String callId, int videoState) {Log.d(this, "answerVideo %s", callId);findConnectionForAction(callId, "answer").onAnswer(videoState);}private void answer(String callId) {Log.d(this, "answer %s", callId);findConnectionForAction(callId, "answer").onAnswer();}private void reject(String callId) {Log.d(this, "reject %s", callId);findConnectionForAction(callId, "reject").onReject();}private void reject(String callId, String rejectWithMessage) {Log.d(this, "reject %s with message", callId);findConnectionForAction(callId, "reject").onReject(rejectWithMessage);}private void silence(String callId) {Log.d(this, "silence %s", callId);findConnectionForAction(callId, "silence").onSilence();}private void disconnect(String callId) {Log.d(this, "disconnect %s", callId);if (mConnectionById.containsKey(callId)) {findConnectionForAction(callId, "disconnect").onDisconnect();} else {findConferenceForAction(callId, "disconnect").onDisconnect();}}private void hold(String callId) {Log.d(this, "hold %s", callId);if (mConnectionById.containsKey(callId)) {findConnectionForAction(callId, "hold").onHold();} else {findConferenceForAction(callId, "hold").onHold();}}private void unhold(String callId) {Log.d(this, "unhold %s", callId);if (mConnectionById.containsKey(callId)) {findConnectionForAction(callId, "unhold").onUnhold();} else {findConferenceForAction(callId, "unhold").onUnhold();}}private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {Log.d(this, "onAudioStateChanged %s %s", callId, callAudioState);if (mConnectionById.containsKey(callId)) {findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState(callAudioState);} else {findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState(callAudioState);}}private void playDtmfTone(String callId, char digit) {Log.d(this, "playDtmfTone %s %c", callId, digit);if (mConnectionById.containsKey(callId)) {findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);} else {findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);}}private void stopDtmfTone(String callId) {Log.d(this, "stopDtmfTone %s", callId);if (mConnectionById.containsKey(callId)) {findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();} else {findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone();}}private void conference(String callId1, String callId2) {Log.d(this, "conference %s, %s", callId1, callId2);// Attempt to get second connection or conference.Connection connection2 = findConnectionForAction(callId2, "conference");Conference conference2 = getNullConference();if (connection2 == getNullConnection()) {conference2 = findConferenceForAction(callId2, "conference");if (conference2 == getNullConference()) {Log.w(this, "Connection2 or Conference2 missing in conference request %s.",callId2);return;}}// Attempt to get first connection or conference and perform merge.Connection connection1 = findConnectionForAction(callId1, "conference");if (connection1 == getNullConnection()) {Conference conference1 = findConferenceForAction(callId1, "addConnection");if (conference1 == getNullConference()) {Log.w(this,"Connection1 or Conference1 missing in conference request %s.",callId1);} else {// Call 1 is a conference.if (connection2 != getNullConnection()) {// Call 2 is a connection so merge via call 1 (conference).conference1.onMerge(connection2);} else {// Call 2 is ALSO a conference; this should never happen.Log.wtf(this, "There can only be one conference and an attempt was made to " +"merge two conferences.");return;}}} else {// Call 1 is a connection.if (conference2 != getNullConference()) {// Call 2 is a conference, so merge via call 2.conference2.onMerge(connection1);} else {// Call 2 is a connection, so merge together.onConference(connection1, connection2);}}}private void splitFromConference(String callId) {Log.d(this, "splitFromConference(%s)", callId);Connection connection = findConnectionForAction(callId, "splitFromConference");if (connection == getNullConnection()) {Log.w(this, "Connection missing in conference request %s.", callId);return;}Conference conference = connection.getConference();if (conference != null) {conference.onSeparate(connection);}}private void mergeConference(String callId) {Log.d(this, "mergeConference(%s)", callId);Conference conference = findConferenceForAction(callId, "mergeConference");if (conference != null) {conference.onMerge();}}private void swapConference(String callId) {Log.d(this, "swapConference(%s)", callId);Conference conference = findConferenceForAction(callId, "swapConference");if (conference != null) {conference.onSwap();}}/*** Notifies a {@link Connection} of a request to pull an external call.** See {@link Call#pullExternalCall()}.** @param callId The ID of the call to pull.*/private void pullExternalCall(String callId) {Log.d(this, "pullExternalCall(%s)", callId);Connection connection = findConnectionForAction(callId, "pullExternalCall");if (connection != null) {connection.onPullExternalCall();}}/*** Notifies a {@link Connection} of a call event.** See {@link Call#sendCallEvent(String, Bundle)}.** @param callId The ID of the call receiving the event.* @param event The event.* @param extras Extras associated with the event.*/private void sendCallEvent(String callId, String event, Bundle extras) {Log.d(this, "sendCallEvent(%s, %s)", callId, event);Connection connection = findConnectionForAction(callId, "sendCallEvent");if (connection != null) {connection.onCallEvent(event, extras);}}/*** Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom.* <p>* These extra changes can originate from Telecom itself, or from an {@link InCallService} via* the {@link android.telecom.Call#putExtra(String, boolean)},* {@link android.telecom.Call#putExtra(String, int)},* {@link android.telecom.Call#putExtra(String, String)},* {@link Call#removeExtras(List)}.** @param callId The ID of the call receiving the event.* @param extras The new extras bundle.*/private void handleExtrasChanged(String callId, Bundle extras) {Log.d(this, "handleExtrasChanged(%s, %s)", callId, extras);if (mConnectionById.containsKey(callId)) {findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);} else if (mConferenceById.containsKey(callId)) {findConferenceForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);}}private void onPostDialContinue(String callId, boolean proceed) {Log.d(this, "onPostDialContinue(%s)", callId);findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);}private void onAdapterAttached() {if (mAreAccountsInitialized) {// No need to query again if we already did it.return;}mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {@Overridepublic void onResult(final List<ComponentName> componentNames,final List<IBinder> services) {mHandler.post(new Runnable() {@Overridepublic void run() {for (int i = 0; i < componentNames.size() && i < services.size(); i++) {mRemoteConnectionManager.addConnectionService(componentNames.get(i),IConnectionService.Stub.asInterface(services.get(i)));}onAccountsInitialized();Log.d(this, "remote connection services found: " + services);}});}@Overridepublic void onError() {mHandler.post(new Runnable() {@Overridepublic void run() {mAreAccountsInitialized = true;}});}});}/*** Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an* incoming request. This is used by {@code ConnectionService}s that are registered with* {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage* SIM-based incoming calls.** @param connectionManagerPhoneAccount See description at*         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.* @param request Details about the incoming call.* @return The {@code Connection} object to satisfy this call, or {@code null} to*         not handle the call.*/public final RemoteConnection createRemoteIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,ConnectionRequest request) {return mRemoteConnectionManager.createRemoteConnection(connectionManagerPhoneAccount, request, true);}/*** Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an* outgoing request. This is used by {@code ConnectionService}s that are registered with* {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the* SIM-based {@code ConnectionService} to place its outgoing calls.** @param connectionManagerPhoneAccount See description at*         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.* @param request Details about the incoming call.* @return The {@code Connection} object to satisfy this call, or {@code null} to*         not handle the call.*/public final RemoteConnection createRemoteOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount,ConnectionRequest request) {return mRemoteConnectionManager.createRemoteConnection(connectionManagerPhoneAccount, request, false);}/*** Indicates to the relevant {@code RemoteConnectionService} that the specified* {@link RemoteConnection}s should be merged into a conference call.* <p>* If the conference request is successful, the method {@link #onRemoteConferenceAdded} will* be invoked.** @param remoteConnection1 The first of the remote connections to conference.* @param remoteConnection2 The second of the remote connections to conference.*/public final void conferenceRemoteConnections(RemoteConnection remoteConnection1,RemoteConnection remoteConnection2) {mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2);}/*** Adds a new conference call. When a conference call is created either as a result of an* explicit request via {@link #onConference} or otherwise, the connection service should supply* an instance of {@link Conference} by invoking this method. A conference call provided by this* method will persist until {@link Conference#destroy} is invoked on the conference instance.** @param conference The new conference object.*/public final void addConference(Conference conference) {Log.d(this, "addConference: conference=%s", conference);String id = addConferenceInternal(conference);if (id != null) {List<String> connectionIds = new ArrayList<>(2);for (Connection connection : conference.getConnections()) {if (mIdByConnection.containsKey(connection)) {connectionIds.add(mIdByConnection.get(connection));}}conference.setTelecomCallId(id);ParcelableConference parcelableConference = new ParcelableConference(conference.getPhoneAccountHandle(),conference.getState(),conference.getConnectionCapabilities(),conference.getConnectionProperties(),connectionIds,conference.getVideoProvider() == null ?null : conference.getVideoProvider().getInterface(),conference.getVideoState(),conference.getConnectTimeMillis(),conference.getStatusHints(),conference.getExtras());mAdapter.addConferenceCall(id, parcelableConference);mAdapter.setVideoProvider(id, conference.getVideoProvider());mAdapter.setVideoState(id, conference.getVideoState());// Go through any child calls and set the parent.for (Connection connection : conference.getConnections()) {String connectionId = mIdByConnection.get(connection);if (connectionId != null) {mAdapter.setIsConferenced(connectionId, id);}}}}/*** Adds a connection created by the {@link ConnectionService} and informs telecom of the new* connection.** @param phoneAccountHandle The phone account handle for the connection.* @param connection The connection to add.*/public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle,Connection connection) {String id = addExistingConnectionInternal(phoneAccountHandle, connection);if (id != null) {List<String> emptyList = new ArrayList<>(0);ParcelableConnection parcelableConnection = new ParcelableConnection(phoneAccountHandle,connection.getState(),connection.getConnectionCapabilities(),connection.getConnectionProperties(),connection.getAddress(),connection.getAddressPresentation(),connection.getCallerDisplayName(),connection.getCallerDisplayNamePresentation(),connection.getVideoProvider() == null ?null : connection.getVideoProvider().getInterface(),connection.getVideoState(),connection.isRingbackRequested(),connection.getAudioModeIsVoip(),connection.getConnectTimeMillis(),connection.getStatusHints(),connection.getDisconnectCause(),emptyList,connection.getExtras());mAdapter.addExistingConnection(id, parcelableConnection);}}/*** Returns all the active {@code Connection}s for which this {@code ConnectionService}* has taken responsibility.** @return A collection of {@code Connection}s created by this {@code ConnectionService}.*/public final Collection<Connection> getAllConnections() {return mConnectionById.values();}/*** Returns all the active {@code Conference}s for which this {@code ConnectionService}* has taken responsibility.** @return A collection of {@code Conference}s created by this {@code ConnectionService}.*/public final Collection<Conference> getAllConferences() {return mConferenceById.values();}/*** Create a {@code Connection} given an incoming request. This is used to attach to existing* incoming calls.** @param connectionManagerPhoneAccount See description at*         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.* @param request Details about the incoming call.* @return The {@code Connection} object to satisfy this call, or {@code null} to*         not handle the call.*/public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,ConnectionRequest request) {return null;}/*** Trigger recalculate functinality for conference calls. This is used when a Telephony* Connection is part of a conference controller but is not yet added to Connection* Service and hence cannot be added to the conference call.** @hide*/public void triggerConferenceRecalculate() {}/*** Create a {@code Connection} given an outgoing request. This is used to initiate new* outgoing calls.** @param connectionManagerPhoneAccount The connection manager account to use for managing*         this call.*         <p>*         If this parameter is not {@code null}, it means that this {@code ConnectionService}*         has registered one or more {@code PhoneAccount}s having*         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain*         one of these {@code PhoneAccount}s, while the {@code request} will contain another*         (usually but not always distinct) {@code PhoneAccount} to be used for actually*         making the connection.*         <p>*         If this parameter is {@code null}, it means that this {@code ConnectionService} is*         being asked to make a direct connection. The*         {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be*         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for*         making the connection.* @param request Details about the outgoing call.* @return The {@code Connection} object to satisfy this call, or the result of an invocation*         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.*/public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount,ConnectionRequest request) {return null;}/*** Create a {@code Connection} for a new unknown call. An unknown call is a call originating* from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming* call created using* {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}.** @param connectionManagerPhoneAccount* @param request* @return** @hide*/public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,ConnectionRequest request) {return null;}/*** Conference two specified connections. Invoked when the user has made a request to merge the* specified connections into a conference call. In response, the connection service should* create an instance of {@link Conference} and pass it into {@link #addConference}.** @param connection1 A connection to merge into a conference call.* @param connection2 A connection to merge into a conference call.*/public void onConference(Connection connection1, Connection connection2) {}/*** Indicates that a remote conference has been created for existing {@link RemoteConnection}s.* When this method is invoked, this {@link ConnectionService} should create its own* representation of the conference call and send it to telecom using {@link #addConference}.* <p>* This is only relevant to {@link ConnectionService}s which are registered with* {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.** @param conference The remote conference call.*/public void onRemoteConferenceAdded(RemoteConference conference) {}/*** Called when an existing connection is added remotely.* @param connection The existing connection which was added.*/public void onRemoteExistingConnectionAdded(RemoteConnection connection) {}/*** @hide*/public boolean containsConference(Conference conference) {return mIdByConference.containsKey(conference);}/** {@hide} */void addRemoteConference(RemoteConference remoteConference) {onRemoteConferenceAdded(remoteConference);}/** {@hide} */void addRemoteExistingConnection(RemoteConnection remoteConnection) {onRemoteExistingConnectionAdded(remoteConnection);}private void onAccountsInitialized() {mAreAccountsInitialized = true;for (Runnable r : mPreInitializationConnectionRequests) {r.run();}mPreInitializationConnectionRequests.clear();}/*** Adds an existing connection to the list of connections, identified by a new call ID unique* to this connection service.** @param connection The connection.* @return The ID of the connection (e.g. the call-id).*/private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) {String id;if (handle == null) {// If no phone account handle was provided, we cannot be sure the call ID is unique,// so just use a random UUID.id = UUID.randomUUID().toString();} else {// Phone account handle was provided, so use the ConnectionService class name as a// prefix for a unique incremental call ID.id = handle.getComponentName().getClassName() + "@" + getNextCallId();}addConnection(id, connection);return id;}private void addConnection(String callId, Connection connection) {connection.setTelecomCallId(callId);mConnectionById.put(callId, connection);mIdByConnection.put(connection, callId);connection.addConnectionListener(mConnectionListener);connection.setConnectionService(this);}/** {@hide} */protected void removeConnection(Connection connection) {connection.unsetConnectionService(this);connection.removeConnectionListener(mConnectionListener);String id = mIdByConnection.get(connection);if (id != null) {mConnectionById.remove(id);mIdByConnection.remove(connection);mAdapter.removeCall(id);}}private String addConferenceInternal(Conference conference) {if (mIdByConference.containsKey(conference)) {Log.w(this, "Re-adding an existing conference: %s.", conference);} else if (conference != null) {// Conferences do not (yet) have a PhoneAccountHandle associated with them, so we// cannot determine a ConnectionService class name to associate with the ID, so use// a unique UUID (for now).String id = UUID.randomUUID().toString();mConferenceById.put(id, conference);mIdByConference.put(conference, id);conference.addListener(mConferenceListener);return id;}return null;}private void removeConference(Conference conference) {if (mIdByConference.containsKey(conference)) {conference.removeListener(mConferenceListener);String id = mIdByConference.get(conference);mConferenceById.remove(id);mIdByConference.remove(conference);mAdapter.removeCall(id);}}private Connection findConnectionForAction(String callId, String action) {if (mConnectionById.containsKey(callId)) {return mConnectionById.get(callId);}Log.w(this, "%s - Cannot find Connection %s", action, callId);return getNullConnection();}static synchronized Connection getNullConnection() {if (sNullConnection == null) {sNullConnection = new Connection() {};}return sNullConnection;}private Conference findConferenceForAction(String conferenceId, String action) {if (mConferenceById.containsKey(conferenceId)) {return mConferenceById.get(conferenceId);}Log.w(this, "%s - Cannot find conference %s", action, conferenceId);return getNullConference();}private List<String> createConnectionIdList(List<Connection> connections) {List<String> ids = new ArrayList<>();for (Connection c : connections) {if (mIdByConnection.containsKey(c)) {ids.add(mIdByConnection.get(c));}}Collections.sort(ids);return ids;}/*** Builds a list of {@link Connection} and {@link Conference} IDs based on the list of* {@link Conferenceable}s passed in.** @param conferenceables The {@link Conferenceable} connections and conferences.* @return List of string conference and call Ids.*/private List<String> createIdList(List<Conferenceable> conferenceables) {List<String> ids = new ArrayList<>();for (Conferenceable c : conferenceables) {// Only allow Connection and Conference conferenceables.if (c instanceof Connection) {Connection connection = (Connection) c;if (mIdByConnection.containsKey(connection)) {ids.add(mIdByConnection.get(connection));}} else if (c instanceof Conference) {Conference conference = (Conference) c;if (mIdByConference.containsKey(conference)) {ids.add(mIdByConference.get(conference));}}}Collections.sort(ids);return ids;}private Conference getNullConference() {if (sNullConference == null) {sNullConference = new Conference(null) {};}return sNullConference;}private void endAllConnections() {// Unbound from telecomm.  We should end all connections and conferences.for (Connection connection : mIdByConnection.keySet()) {// only operate on top-level calls. Conference calls will be removed on their own.if (connection.getConference() == null) {connection.onDisconnect();}}for (Conference conference : mIdByConference.keySet()) {conference.onDisconnect();}}/*** Retrieves the next call ID as maintainted by the connection service.** @return The call ID.*/private int getNextCallId() {synchronized(mIdSyncRoot) {return ++mId;}}
}

发送MES_DISCONNECT消息到队列了处理

    private void disconnect(String callId) {Log.d(this, "disconnect %s", callId);if (mConnectionById.containsKey(callId)) {findConnectionForAction(callId, "disconnect").onDisconnect();} else {findConferenceForAction(callId, "disconnect").onDisconnect();}}

接着调用这个函数

    private Connection findConnectionForAction(String callId, String action) {if (mConnectionById.containsKey(callId)) {return mConnectionById.get(callId);}Log.w(this, "%s - Cannot find Connection %s", action, callId);return getNullConnection();}

根据callid找到对应的connection对象(android.telecom.Connection),调用onDisconnect()方法

11)、TelephonyConnection.java文件的onDisconnect()方法

    @Overridepublic void onDisconnect() {Log.v(this, "onDisconnect");hangup(android.telephony.DisconnectCause.LOCAL);}

接着调用

protected void hangup(int telephonyDisconnectCode) {if (mOriginalConnection != null) {int connectionState = getState();Log.w(this, "hangup connection state: " + connectionState);try {// Hanging up a ringing call requires that we invoke call.hangup() as opposed to// connection.hangup(). Without this change, the party originating the call will not// get sent to voicemail if the user opts to reject the call.if (isValidRingingCall()) {Log.v(this, "The call is Valid Ringing call");Call call = getCall();if (call != null) {call.hangup();} else {Log.w(this, "Attempting to hangup a connection without backing call.");}} else if (isValidWaitingCall()) {Log.w(this, "isValidWaitingCall");if (connectionState == STATE_HOLDING) {mNeedAccept = true;mOriginalConnection.hangup();} else if (connectionState == STATE_ACTIVE) {Call call = getCall(); // this call is foreground callif (call != null) {//调用到这个函数call.hangup();} else {Log.w(this, "Foreground call does not exist.");}}} else if (isSrvccCompleted()) {Log.d(this, "hangup: After handover, trigger pending request");mPendingRequest = PENDING_REQUEST_TERMINATE;} else {// We still prefer to call connection.hangup() for non-ringing calls in order// to support hanging-up specific calls within a conference call. If we invoked// call.hangup() while in a conference, we would end up hanging up the entire// conference call instead of the specific connection.mOriginalConnection.hangup();}} catch (CallStateException e) {Log.e(this, e, "Call to Connection.hangup failed with exception");}}}

设置断开链接类型DisconnectCause.LOCAL,并调用mOriginalConnection的hangup方法,实际Connection类是GsmCdmaConnection

12)、GsmCdmaConnection.java文件的hangup()方法
    @Overridepublic void hangup() throws CallStateException {if (!mDisconnected) {mOwner.hangup(this);} else {throw new CallStateException ("disconnected");}}
13)、调用GsmCdmaCallTracker.java的hangup()方法
//***** Called from GsmCdmaConnectionpublic void hangup(GsmCdmaConnection conn) throws CallStateException {if (conn.mOwner != this) {throw new CallStateException ("GsmCdmaConnection " + conn+ "does not belong to GsmCdmaCallTracker " + this);}if (conn == mPendingMO) {// We're hanging up an outgoing call that doesn't have it's// GsmCdma index assigned yetif (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");mHangupPendingMO = true;} else if (!isPhoneTypeGsm()&& conn.getCall() == mRingingCall&& mRingingCall.getState() == GsmCdmaCall.State.WAITING) {// Handle call waiting hang up case.//// The ringingCall state will change to IDLE in GsmCdmaCall.detach// if the ringing call connection size is 0. We don't specifically// set the ringing call state to IDLE here to avoid a race condition// where a new call waiting could get a hang up from an old call// waiting ringingCall.//// PhoneApp does the call log itself since only PhoneApp knows// the hangup reason is user ignoring or timing out. So conn.onDisconnect()// is not called here. Instead, conn.onLocalDisconnect() is called.conn.onLocalDisconnect();updatePhoneState();mPhone.notifyPreciseCallStateChanged();return;} else {try {//调用这个函数(注意参数)mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage());} catch (CallStateException ex) {// Ignore "connection not found"// Call may have hung up alreadyRlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: hangup() on absent connection "+ conn);}}conn.onHangupLocal();}

由于是通话挂断,这里调用 mCi.hangupConnection(conn.getGsmCdmaIndex(),obtainCompleteMessage());
这里调用conn的getGsmCdmaIndex方法先索取索引

14)、GsmCdmaConnection.java中getGsmCdmaIndex()函数
/*package*/ intgetGsmCdmaIndex() throws CallStateException {if (mIndex >= 0) {return mIndex + 1;} else {throw new CallStateException ("GsmCdma index not yet assigned");}}

这个索引对应的就是DriveCall里面的index的值,匹配的modem里CallList里对于Call对象

mCi是CommandsInterface即RILJ接口,包装了一个EVENT_OPERATION_COMPLETE回调的消息,发送给RIL

15)、RIL.java 文件的hangupConnection()方法
    @Overridepublic voidhangupConnection (int gsmIndex, Message result) {if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex);RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result);if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " +gsmIndex);mEventLog.writeRilHangup(rr.mSerial, RIL_REQUEST_HANGUP, gsmIndex);rr.mParcel.writeInt(1);rr.mParcel.writeInt(gsmIndex);Rlog.d(RILJ_LOG_TAG, "lum_52");send(rr);}

给RIL层发送RIL_REQUEST_HANGUP消息并附带index参数

收到RIL层的回应消息并处理,最后发送回调消息 EVENT_OPERATION_COMPLETE给GsmCdmaCallTracker
GsmCdmaCallTracker处理回调消息EVENT_OPERATION_COMPLETE

16)、GsmCdmaCallTracker.java文件operationComplete()方法
    private void operationComplete() {mPendingOperations--;if (DBG_POLL) log("operationComplete: pendingOperations=" +mPendingOperations + ", needsPoll=" + mNeedsPoll);if (mPendingOperations == 0 && mNeedsPoll) {mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);mCi.getCurrentCalls(mLastRelevantPoll);} else if (mPendingOperations < 0) {// this should never happenRlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0");mPendingOperations = 0;}}

这里再次向RIL发送消息主动获取当前call状态,包装的回调消息为EVENT_POLL_CALLS_RESULT

RIL 返回消息,GsmCdmaCallTracker接受EVENT_POLL_CALLS_RESULT消息并处理

// ***** Overwritten from CallTracker@Overrideprotected synchronized void handlePollCalls(AsyncResult ar) {List polledCalls;if (VDBG) log("handlePollCalls");if (ar.exception == null) {polledCalls = (List)ar.result;} else if (isCommandExceptionRadioNotAvailable(ar.exception)) {// just a dummy empty ArrayList to cause the loop// to hang up all the callspolledCalls = new ArrayList();} else {// Radio probably wasn't ready--try again in a bit// But don't keep polling if the channel is closedpollCallsAfterDelay();return;}Connection newRinging = null; //or waitingArrayList<Connection> newUnknownConnectionsGsm = new ArrayList<Connection>();Connection newUnknownConnectionCdma = null;boolean hasNonHangupStateChanged = false;   // Any change besides// a dropped connectionboolean hasAnyCallDisconnected = false;boolean needsPollDelay = false;boolean unknownConnectionAppeared = false;int ignoredUpdateRequest = ImsCall.UPDATE_NONE;// countOfIgnoredRequest can be 0 ~ 3.// 0 means there's no ignored request.// 1 means there's hangup or hold or resume or accept.// 2 means there's hangup active or hold and accept waiting.// 3 means there's hangup active and hangup hold and accept waiting.int countOfIgnoredRequest = 0;GsmCdmaConnection pendingConnection = null;// pendingHoldConnection uses to distinguish whether to hangup active or hold ignored request,// when countOfIgnoredRequest is 2.GsmCdmaConnection pendingHoldConnection = null;//CDMAboolean noConnectionExists = true;for (int i = 0, curDC = 0, dcSize = polledCalls.size(); i < mConnections.length; i++) {GsmCdmaConnection conn = mConnections[i];DriverCall dc = null;// polledCall list is sparseif (curDC < dcSize) {dc = (DriverCall) polledCalls.get(curDC);if (dc.index == i+1) {curDC++;} else {dc = null;}}//CDMAif (conn != null || dc != null) {noConnectionExists = false;}if (DBG_POLL) log("poll: conn[i=" + i + "]=" +conn+", dc=" + dc);if (conn == null && dc != null) {// Connection appeared in CLCC response that we don't know aboutif (mPendingMO != null && mPendingMO.compareTo(dc)) {if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);// It's our pending mobile originating callmConnections[i] = mPendingMO;mPendingMO.mIndex = i;mPendingMO.update(dc);mPendingMO = null;// Someone has already asked to hangup this callif (mHangupPendingMO) {mHangupPendingMO = false;// Re-start Ecm timer when an uncompleted emergency call endsif (!isPhoneTypeGsm() && mIsEcmTimerCanceled) {handleEcmTimer(GsmCdmaPhone.RESTART_ECM_TIMER);}try {if (Phone.DEBUG_PHONE) log("poll: hangupPendingMO, hangup conn " + i);hangup(mConnections[i]);} catch (CallStateException ex) {Rlog.e(LOG_TAG, "unexpected error on hangup");}// Do not continue processing this poll// Wait for hangup and repollreturn;}// Check update state of foreground ImsCall.// If it is UPDATE_TERMINATE, it means user wanted hangup the call.// But there was no response. it may be ignored by silent redial or SRVCC.// In here, handing silent redial case.ImsPhone imsPhone = (ImsPhone)mPhone.getImsPhone();if (imsPhone != null) {ImsCall imsCall = imsPhone.getForegroundCall().getImsCall();if (imsCall != null && imsCall.getUpdateRequest() == ImsCall.UPDATE_TERMINATE) {try {Rlog.e(LOG_TAG, "hangup after silent redial");hangup(mConnections[i]);} catch (CallStateException ex) {Rlog.e(LOG_TAG, "unexpected error on hangup");}// clear ImsCall update state.// set ImsReasonInfo as CODE_UNSPECIFIED.imsCall.clear(new ImsReasonInfo());// Do not continue processing this poll// Wait for hangup and repollreturn;}}} else {if (Phone.DEBUG_PHONE) {log("pendingMo=" + mPendingMO + ", dc=" + dc);}mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i);Connection hoConnection = getHoConnection(dc);if (hoConnection != null) {// Single Radio Voice Call Continuity (SRVCC) completedmConnections[i].migrateFrom(hoConnection);// Updating connect time for silent redial cases (ex: Calls are transferred// from DIALING/ALERTING/INCOMING/WAITING to ACTIVE)if (hoConnection.mPreHandoverState != GsmCdmaCall.State.ACTIVE &&hoConnection.mPreHandoverState != GsmCdmaCall.State.HOLDING &&dc.state == DriverCall.State.ACTIVE) {mConnections[i].onConnectedInOrOut();}if (hoConnection instanceof ImsPhoneConnection) {ImsCall imsCall = ((ImsPhoneConnection)hoConnection).getImsCall();// Find ignored request. (HOLD, RESUME, TERMINATED, ACCEPT)if (imsCall != null &&findIgnoredRequest(imsCall.getUpdateRequest(), mConnections[i].getState())) {ignoredUpdateRequest = imsCall.getUpdateRequest();pendingConnection = mConnections[i];if (mConnections[i].getState() == Call.State.HOLDING) {pendingHoldConnection = mConnections[i];}countOfIgnoredRequest++;}}mHandoverConnections.remove(hoConnection);if (isPhoneTypeGsm()) {for (Iterator<Connection> it = mHandoverConnections.iterator();it.hasNext(); ) {Connection c = it.next();Rlog.i(LOG_TAG, "HO Conn state is " + c.mPreHandoverState);if (c.mPreHandoverState == mConnections[i].getState()) {Rlog.i(LOG_TAG, "Removing HO conn "+ hoConnection + c.mPreHandoverState);it.remove();}}}mPhone.notifyHandoverStateChanged(mConnections[i]);} else {// find if the MT call is a new ring or unknown connectionnewRinging = checkMtFindNewRinging(dc,i);if (newRinging == null) {unknownConnectionAppeared = true;if (isPhoneTypeGsm()) {newUnknownConnectionsGsm.add(mConnections[i]);} else {newUnknownConnectionCdma = mConnections[i];}}}}hasNonHangupStateChanged = true;} else if (conn != null && dc == null) {if (isPhoneTypeGsm()) {// Connection missing in CLCC response that we were// tracking.mDroppedDuringPoll.add(conn);// Dropped connections are removed from the CallTracker// list but kept in the GsmCdmaCall listmConnections[i] = null;} else {// This case means the RIL has no more active call anymore and// we need to clean up the foregroundCall and ringingCall.// Loop through foreground call connections as// it contains the known logical connections.int count = mForegroundCall.mConnections.size();for (int n = 0; n < count; n++) {if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");GsmCdmaConnection cn = (GsmCdmaConnection)mForegroundCall.mConnections.get(n);mDroppedDuringPoll.add(cn);}count = mRingingCall.mConnections.size();// Loop through ringing call connections as// it may contain the known logical connections.for (int n = 0; n < count; n++) {if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");GsmCdmaConnection cn = (GsmCdmaConnection)mRingingCall.mConnections.get(n);mDroppedDuringPoll.add(cn);}// Re-start Ecm timer when the connected emergency call endsif (mIsEcmTimerCanceled) {handleEcmTimer(GsmCdmaPhone.RESTART_ECM_TIMER);}// If emergency call is not going through while dialingcheckAndEnableDataCallAfterEmergencyCallDropped();// Dropped connections are removed from the CallTracker// list but kept in the Call listmConnections[i] = null;}} else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) {// Connection in CLCC response does not match what// we were tracking. Assume dropped call and new callmDroppedDuringPoll.add(conn);mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i);if (mConnections[i].getCall() == mRingingCall) {newRinging = mConnections[i];} // else something strange happenedhasNonHangupStateChanged = true;} else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */// Call collision caseif (!isPhoneTypeGsm() && conn.isIncoming() != dc.isMT) {if (dc.isMT == true) {// Mt call takes precedence than Mo,drops MomDroppedDuringPoll.add(conn);// find if the MT call is a new ring or unknown connectionnewRinging = checkMtFindNewRinging(dc,i);if (newRinging == null) {unknownConnectionAppeared = true;newUnknownConnectionCdma = conn;}checkAndEnableDataCallAfterEmergencyCallDropped();} else {// Call info stored in conn is not consistent with the call info from dc.// We should follow the rule of MT calls taking precedence over MO calls// when there is conflict, so here we drop the call info from dc and// continue to use the call info from conn, and only take a log.Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);}} else {boolean changed;changed = conn.update(dc);hasNonHangupStateChanged = hasNonHangupStateChanged || changed;}}if (REPEAT_POLLING) {if (dc != null) {// FIXME with RIL, we should not need this anymoreif ((dc.state == DriverCall.State.DIALING/*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)|| (dc.state == DriverCall.State.ALERTING/*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)|| (dc.state == DriverCall.State.INCOMING/*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)|| (dc.state == DriverCall.State.WAITING/*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)) {// Sometimes there's no unsolicited notification// for state transitionsneedsPollDelay = true;}}}}// SRVCC SolutionRlog.d(LOG_TAG, "countOfIgnoredRequest is " + countOfIgnoredRequest);// this means hangup active and hangup hold and accept waiting.if (countOfIgnoredRequest == 3) {if (pendingHoldConnection != null) {try {Rlog.d(LOG_TAG, "Trigger ignored hangup active, accept waiting call request");hangup(pendingHoldConnection);hangupForegroundResumeBackground();// Do not continue processing this poll// Wait for hangup and repollreturn;} catch (CallStateException ex) {Rlog.d(LOG_TAG, "hangup failed with exception: " + ex);}} else {Rlog.d(LOG_TAG, "pendingHoldConnection is null.");}// this means hangup active or hold and accept waiting.} else if (countOfIgnoredRequest == 2) {if (pendingHoldConnection != null) {try {Rlog.d(LOG_TAG, "Trigger ignored hangup hold, accept waiting call request");hangup(pendingHoldConnection);switchWaitingOrHoldingAndActive();// Do not continue processing this poll// Wait for hangup and repollreturn;} catch (CallStateException ex) {Rlog.d(LOG_TAG, "hangup or switching failed with exception: " + ex);}} else {Rlog.d(LOG_TAG, "Trigger ignored hangup active, accept waiting call request");hangupForegroundResumeBackground();// Do not continue processing this poll// Wait for hangup and repollreturn;}} else if (countOfIgnoredRequest == 1) { // other cases.if (ignoredUpdateRequest == ImsCall.UPDATE_TERMINATE) {if (pendingConnection != null) {try {Rlog.d(LOG_TAG, "Trigger ignored terminate request.");pendingConnection.hangup();// Do not continue processing this poll// Wait for hangup and repollreturn;} catch (CallStateException ex) {Rlog.d(LOG_TAG, "hangup failed with exception: " + ex);}}} else if (ignoredUpdateRequest == ImsCall.UPDATE_HOLD ||ignoredUpdateRequest == ImsCall.UPDATE_RESUME) {try {Rlog.d(LOG_TAG, "Trigger ignored hold/resume request.");switchWaitingOrHoldingAndActive();// Do not continue processing this poll// Wait for switching and repollreturn;} catch (CallStateException ex) {Rlog.d(LOG_TAG, "switching failed with exception: " + ex);}} else if (ignoredUpdateRequest == ImsCall.UPDATE_ACCEPT) {try {Rlog.d(LOG_TAG, "Trigger ignored accept request.");acceptCall();// Do not continue processing this poll// Wait for accept and repollreturn;} catch (CallStateException ex) {Rlog.d(LOG_TAG, "accept failed with exception: " + ex);}}}// Safety check so that obj is not stuck with mIsInEmergencyCall set to true (and data// disabled). This should never happen though.if (!isPhoneTypeGsm() && noConnectionExists) {checkAndEnableDataCallAfterEmergencyCallDropped();}// This is the first poll after an ATD.// We expect the pending call to appear in the list// If it does not, we land hereif (mPendingMO != null) {Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:"+ mForegroundCall.getState());mDroppedDuringPoll.add(mPendingMO);mPendingMO = null;mHangupPendingMO = false;if (!isPhoneTypeGsm()) {if( mPendingCallInEcm) {mPendingCallInEcm = false;}checkAndEnableDataCallAfterEmergencyCallDropped();}}if (newRinging != null) {mPhone.notifyNewRingingConnection(newRinging);}// clear the "local hangup" and "missed/rejected call"// cases from the "dropped during poll" list// These cases need no "last call fail" reasonfor (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {GsmCdmaConnection conn = mDroppedDuringPoll.get(i);//CDMAboolean wasDisconnected = false;if (conn.isIncoming() && conn.getConnectTime() == 0) {// Missed or rejected callint cause;if (conn.mCause == DisconnectCause.LOCAL) {cause = DisconnectCause.INCOMING_REJECTED;} else {cause = DisconnectCause.INCOMING_MISSED;}if (Phone.DEBUG_PHONE) {log("missed/rejected call, conn.cause=" + conn.mCause);log("setting cause to " + cause);}mDroppedDuringPoll.remove(i);hasAnyCallDisconnected |= conn.onDisconnect(cause);wasDisconnected = true;} else if (conn.mCause == DisconnectCause.LOCAL|| conn.mCause == DisconnectCause.INVALID_NUMBER) {mDroppedDuringPoll.remove(i);hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);wasDisconnected = true;}if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared&& conn == newUnknownConnectionCdma) {unknownConnectionAppeared = false;newUnknownConnectionCdma = null;}}/* Disconnect any pending Handover connections */for (Iterator<Connection> it = mHandoverConnections.iterator();it.hasNext();) {Connection hoConnection = it.next();log("handlePollCalls - disconnect hoConn= " + hoConnection +" hoConn.State= " + hoConnection.getState());if (hoConnection.getState().isRinging()) {hoConnection.onDisconnect(DisconnectCause.INCOMING_MISSED);} else {hoConnection.onDisconnect(DisconnectCause.NOT_VALID);}it.remove();}// for STK function SET_UP_CALLIntent intent = new Intent("com.android.call.CALL_STATE_CHANGED");// Any non-local disconnects: determine causeif (mDroppedDuringPoll.size() > 0) {mCi.getLastCallFailCause(obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));intent.putExtra("CALL_SUCCESSFUL", false);} else {intent.putExtra("CALL_SUCCESSFUL", true);}mPhone.getContext().sendBroadcast(intent);if (needsPollDelay) {pollCallsAfterDelay();}if (mDroppedDuringPoll.size() > 0 || hasAnyCallDisconnected) {processEmcStatusAfterCsEnd(EmcStatus.CALL_END);}// Cases when we can no longer keep disconnected Connection's// with their previous calls// 1) the phone has started to ring// 2) A Call/Connection object has changed state...//    we may have switched or held or answered (but not hung up)if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {internalClearDisconnected();}if (VDBG) log("handlePollCalls calling updatePhoneState()");updatePhoneState();if (unknownConnectionAppeared) {if (isPhoneTypeGsm()) {for (Connection c : newUnknownConnectionsGsm) {log("Notify unknown for " + c);mPhone.notifyUnknownConnection(c);}} else {mPhone.notifyUnknownConnection(newUnknownConnectionCdma);}}if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {mPhone.notifyPreciseCallStateChanged();}//dumpState();}

这里是通话断开事件,将connection放入mDroppedDurungPoll列表,由于断开类型DisconnectCause.LOCAL,直接调用GsmCdmaConnection的onDisconnect方法传入cause参数

16)、GsmCdmaConnection.java的onDisconnect()方法
/*** Called when the radio indicates the connection has been disconnected.* @param cause call disconnect cause; values are defined in {@link DisconnectCause}*/@Overridepublic boolean onDisconnect(int cause) {boolean changed = false;mCause = cause;if (!mDisconnected) {doDisconnect();if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);mOwner.getPhone().notifyDisconnect(this);if (mParent != null) {changed = mParent.connectionDisconnected(this);}mOrigConnection = null;}clearPostDialListeners();releaseWakeLock();return changed;}

doDisconnect()方法 设置断开时间以及通话时长

    private voiddoDisconnect() {mIndex = -1;mDisconnectTime = System.currentTimeMillis();mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;mDisconnected = true;clearPostDialListeners();}

最后通知注册者断开事件mOwner.getPhone().notifyDisconnect(this);

之后的流程就跟上篇讲解phone拒接流程一样,这里就不重复描述了

详见http://www.cnblogs.com/lance2016/p/6391096.html

文章参考:
Android7.0 Phone应用源码分析(三) phone拒接流程分析

http://www.cnblogs.com/lance2016/p/6582858.html

Android 7.0 挂断电话流程分析相关推荐

  1. android6.0 挂断电话流程分析(一)

    下面是android 6.0挂断电话的流程分析图: 后继续更新挂断回调...........................!

  2. 远程挂断电话流程分析

    3,远程挂断电话流程分析 3.1 services Telephony 当远程挂断/拒接电话时,GsmCallTracker的handlePollCalls 方法有关代码如下, if (mDroppe ...

  3. Android 5.1 Phone 挂断电话流程分析

    写在前面的话 本文主要分析Android挂断电话的流程,研究的代码是Android 5.1的,以CDMA为例,GSM同理. 挂断电话主要分两种情况: 本地主动挂断电话 \color{red}{本地主动 ...

  4. android蓝牙耳机来电铃声,Android蓝牙耳机接听挂断电话流程

    一.alps/packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java image.png proc ...

  5. Android挂断电话流程

    近期在友盟上看到许多关于挂断电话导致崩溃的问题,如下异常 java.lang.NoSuchMethodError: No interface method endCall()Z in class Lc ...

  6. Android之——自动挂断电话的实现

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/47072451 通过<Android之--AIDL小结>与<And ...

  7. Android 9.0 开关机动画流程分析

    Android开机动画流程的启动主要是在Surfaseflinger里面完成的,具体代码如下: /frameworks/native/services/surfaceflinger/StartProp ...

  8. Android 7.0 APN 拨号上网流程分析

    1.前言 在前段时间的项目中遇到客户的设备出现APN断开的情况没有自动连接,后来折腾了一段时间解决了这个问题.现在用这篇博客记录一下APN的选择和连接流程. 2.名词解析 APN:APN指一种网络接入 ...

  9. Android 9.0 SystemUI 锁屏流程分析

    1.锁屏界面显示的流程 2.按键灭屏 -> 按键亮屏 对于Key事件,InputDispatcher在分发之前会先将事件上发到PhoneWindowManager中,可以进行拦截,故从Phone ...

最新文章

  1. 政府免费WiFi遭吐槽:近七成网友表示从未用过
  2. 详解.NET中容易混淆的委托与接口
  3. MySQL 传统复制与 GTID 复制原理及操作详解
  4. ImageButton 无法显示
  5. 内含干货PPT下载|一站式数据管理 DMS 关键技术解读
  6. C语言联合体基本内容
  7. 【CASS精品教程】CASS9.1查询功能大全(坐标、长度、面积、方位角)
  8. 7-3 人民币与美元汇率兑换程序 (10分)
  9. C语言lseek()函数和 fseek()函数 rewind函数
  10. 学习java被虐千百遍
  11. 某工厂配电线路及变电所设计
  12. rabbitMQ概述/在springboot下测试五种模式
  13. html标签栏logo怎么加,在htmltitle/title标签添加图标,网页title左边显示网页的logo图标...
  14. ${pageContext.request.contextPath}的作用
  15. C语言--使用指针实现删除字符串中的空格
  16. java植物大战僵尸小游戏
  17. ETL工具Informatica开发流程 综合应用 电信通话计费系统开发项目案例10
  18. 外贸开发信标题如何写?7个例子
  19. windows下安装php相关
  20. 计算机登录忘记密码怎么办,电脑登录密码忘记了怎么办

热门文章

  1. Vuex中的mapGetters
  2. 修改OnlyOffice的连接数限制
  3. 打印耗材,原配的才是真正贴心的
  4. linux下如何使用 tcpdump 进行抓包详细教程
  5. node.js 如何读取json文件内容
  6. HTTP常见状态码(404、400、500)等错误
  7. 白领 放下你的“包袱”
  8. “1024”征文活动结果新鲜出炉!快来看看是否榜上有名?~~
  9. 计算两个经纬度的距离
  10. python 枪表情包_Python自动生成表情包,python在手,从此斗图无敌手