Android 7.0 挂断电话流程分析
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>* <service android:name="com.example.package.MyConnectionService"* android:label="@string/some_label_for_my_connection_service"* android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">* <intent-filter>* <action android:name="android.telecom.ConnectionService" />* </intent-filter>* </service>* </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 挂断电话流程分析相关推荐
- android6.0 挂断电话流程分析(一)
下面是android 6.0挂断电话的流程分析图: 后继续更新挂断回调...........................!
- 远程挂断电话流程分析
3,远程挂断电话流程分析 3.1 services Telephony 当远程挂断/拒接电话时,GsmCallTracker的handlePollCalls 方法有关代码如下, if (mDroppe ...
- Android 5.1 Phone 挂断电话流程分析
写在前面的话 本文主要分析Android挂断电话的流程,研究的代码是Android 5.1的,以CDMA为例,GSM同理. 挂断电话主要分两种情况: 本地主动挂断电话 \color{red}{本地主动 ...
- android蓝牙耳机来电铃声,Android蓝牙耳机接听挂断电话流程
一.alps/packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java image.png proc ...
- Android挂断电话流程
近期在友盟上看到许多关于挂断电话导致崩溃的问题,如下异常 java.lang.NoSuchMethodError: No interface method endCall()Z in class Lc ...
- Android之——自动挂断电话的实现
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/47072451 通过<Android之--AIDL小结>与<And ...
- Android 9.0 开关机动画流程分析
Android开机动画流程的启动主要是在Surfaseflinger里面完成的,具体代码如下: /frameworks/native/services/surfaceflinger/StartProp ...
- Android 7.0 APN 拨号上网流程分析
1.前言 在前段时间的项目中遇到客户的设备出现APN断开的情况没有自动连接,后来折腾了一段时间解决了这个问题.现在用这篇博客记录一下APN的选择和连接流程. 2.名词解析 APN:APN指一种网络接入 ...
- Android 9.0 SystemUI 锁屏流程分析
1.锁屏界面显示的流程 2.按键灭屏 -> 按键亮屏 对于Key事件,InputDispatcher在分发之前会先将事件上发到PhoneWindowManager中,可以进行拦截,故从Phone ...
最新文章
- 政府免费WiFi遭吐槽:近七成网友表示从未用过
- 详解.NET中容易混淆的委托与接口
- MySQL 传统复制与 GTID 复制原理及操作详解
- ImageButton 无法显示
- 内含干货PPT下载|一站式数据管理 DMS 关键技术解读
- C语言联合体基本内容
- 【CASS精品教程】CASS9.1查询功能大全(坐标、长度、面积、方位角)
- 7-3 人民币与美元汇率兑换程序 (10分)
- C语言lseek()函数和 fseek()函数 rewind函数
- 学习java被虐千百遍
- 某工厂配电线路及变电所设计
- rabbitMQ概述/在springboot下测试五种模式
- html标签栏logo怎么加,在htmltitle/title标签添加图标,网页title左边显示网页的logo图标...
- ${pageContext.request.contextPath}的作用
- C语言--使用指针实现删除字符串中的空格
- java植物大战僵尸小游戏
- ETL工具Informatica开发流程 综合应用 电信通话计费系统开发项目案例10
- 外贸开发信标题如何写?7个例子
- windows下安装php相关
- 计算机登录忘记密码怎么办,电脑登录密码忘记了怎么办