主要内容为MT收到视频升级的请求的过程,也是MT消息上传的过程。
流程图
可以看到信息上报跨了4个模块,没有经过service/Telecom
看关键log方便跟踪
06-19 19:05:41.437 3329-4362/com.android.phone V/ImsSenderRxr: Read packet: 24 bytes
06-19 19:05:41.437 3329-4362/com.android.phone V/ImsSenderRxr: processResponse[SUB1]
06-19 19:05:41.437 3329-4362/com.android.phone D/ImsSenderRxr: Response data: [12, 13, -1, -1, -1, -1, 16, 3, 24, -49, 1, 32, 0, 21, 1, 0, 0, 0, 26, 4, 8, 3, 16, 2][SUB1]
06-19 19:05:41.437 3329-4362/com.android.phone D/ImsSenderRxr: Tag -1 3 207 0[SUB1]
06-19 19:05:41.437 3329-4362/com.android.phone D/ImsSenderRxr: responseModifyCall 1 3 2 callSubState 0 videoPauseState2 mediaId-1 Local Ability Peer Ability Cause code 0 0[SUB1]
06-19 19:05:41.438 3329-4362/com.android.phone D/ImsSenderRxr: [UNSL]< UNSOL_MODIFY_CALL 1 3 2 callSubState 0 videoPauseState2 mediaId-1 Local Ability Peer Ability Cause code 0 0[SUB1]
06-19 19:05:41.438 3329-3329/com.android.phone D/ImsServiceSub: Message received: what = 13
06-19 19:05:41.438 3329-3329/com.android.phone D/ImsServiceSub: handleCallModifyRequest( 1 3 2 callSubState 0 videoPauseState2 mediaId-1 Local Ability Peer Ability Cause code 0 0)
06-19 19:05:41.438 3329-3329/com.android.phone D/VideoCall_ImsCallModification: onReceivedCallModify( 1 3 2 callSubState 0 videoPauseState2 mediaId-1 Local Ability Peer Ability Cause code 0 0)
06-19 19:05:41.438 3329-3329/com.android.phone D/VideoCall_ImsCallModification: validateIncomingModifyConnectionType newCallType = 3
06-19 19:05:41.438 3329-3329/com.android.phone D/VideoCall_ImsCallModification: validateIncomingModifyConnectionType modifyToCurrCallType = false isIndexValid = true isLowBattery = false
06-19 19:05:41.439 3329-3329/com.android.phone D/VideoCall_ImsVideoCallProviderImpl: (1) onCallTypeChanged session= callid= 1 mediaId=5
代码段
vendor/qcom/proprietary/telephony-apps/ims
高通私有代码,略。
frameworks/opt/net/ims
android/frameworks/
opt/net/ims/src/java/com/android/ims/internal/ImsVideoCallProvider.java
看到方法名带receive request字样,直译就是收到请求,在响应之后方法名中就会换上类似response。
/** @see Connection.VideoProvider#receiveSessionModifyRequest */
public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
if (mCallback != null) {
try {
mCallback.receiveSessionModifyRequest(VideoProfile);
} catch (RemoteException ignored) {
}
}
}
android/frameworks
/opt/net/ims/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java
/**
* IImsVideoCallCallback stub implementation.
*/
private final class ImsVideoCallCallback extends IImsVideoCallCallback.Stub {
@Override
public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST,
VideoProfile).sendToTarget();
}
/** Default handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
SomeArgs args;
switch (msg.what) {
case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
receiveSessionModifyRequest((VideoProfile) msg.obj);
break;
frameworks/base/telecomm
android/frameworks/
base/telecomm/java/android/telecom/Connection.java
/**
* Used to inform listening {@link InCallService} implementations when the
* {@link VideoProvider} receives a session modification request.
* <p>
* Received by the {@link InCallService} via
* {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)},
*
* @param videoProfile The requested video profile.
* @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
*/
public void receiveSessionModifyRequest(VideoProfile videoProfile) {
if (mVideoCallbacks != null) {
for (IVideoCallback callback : mVideoCallbacks.values()) {
try {
callback.receiveSessionModifyRequest(videoProfile);//多处调用
} catch (RemoteException ignored) {
Log.w(this, "receiveSessionModifyRequest callback failed", ignored);
}
}
}
}
android/frameworks/
base/telecomm/java/android/telecom/VideoCallImpl.java
/**
* IVideoCallback stub implementation.
*/
private final class VideoCallListenerBinder extends IVideoCallback.Stub {
@Override
public void receiveSessionModifyRequest(VideoProfile videoProfile) {
if (mHandler == null) {
return;
}
mHandler.obtainMessage(MessageHandler.MSG_RECEIVE_SESSION_MODIFY_REQUEST,
videoProfile).sendToTarget();
}
handleMessage
@Override
public void handleMessage(Message msg) {
if (mCallback == null) {
return;
}
SomeArgs args;
switch (msg.what) {
case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
mCallback.onSessionModifyRequestReceived((VideoProfile) msg.obj);
break;
前面几乎都没什么说的,走一条线就是了,然后
经InCallService接口回调到InCallUI
packages/apps/InCallUI
一般客户有需求,要修改界面的话也都是该InCallUI,下层的流程是不动的,因此下面一部分比较重要
android/packages/apps/
InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
目前为止传想来的消息都只是MODIFY CALL,并没有携带说是升级还是降级,从下面的方法中可以看到有分开两种情况了。
/**
* Handles an incoming session modification request.
*
* @param videoProfile The requested video call profile.
*/
@Override
public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
Log.d(this, " onSessionModifyRequestReceived videoProfile=" + videoProfile);
int previousVideoState = CallUtils.getUnPausedVideoState(mCall.getVideoState());
int newVideoState = CallUtils.getUnPausedVideoState(videoProfile.getVideoState());
InCallPresenter.getInstance().wakeUpScreen();
boolean wasVideoCall = CallUtils.isVideoCall(previousVideoState);
boolean isVideoCall = CallUtils.isVideoCall(newVideoState);
// Check for upgrades to video and downgrades to audio.
if (wasVideoCall && !isVideoCall) {
InCallVideoCallCallbackNotifier.getInstance().downgradeToAudio(mCall);
} else if (previousVideoState != newVideoState) {
InCallVideoCallCallbackNotifier.getInstance().upgradeToVideoRequest(mCall,
newVideoState);
}
}
downgradeToAudio()方法在后面会调用到VideoCallPresenter.java的onDowngradeToAudio()方法,进而调用exitVideoMode()退出视频模式。
upgradeToVideoRequest()升级视频的请求
/**
* Inform listeners of an upgrade to video request for a call.
* @param call The call.
* @param videoState The video state we want to upgrade to.
*/
public void upgradeToVideoRequest(Call call, int videoState) {
Log.d(this, "upgradeToVideoRequest call = " + call + " new video state = " + videoState);
for (SessionModificationListener listener : mSessionModificationListeners) {
listener.onUpgradeToVideoRequest(call, videoState);
}
}
InCallPresenter
@Override
public void onUpgradeToVideoRequest(Call call, int videoState) {
Log.d(this, "onUpgradeToVideoRequest call = " + call + " video state = " + videoState);
if (call == null) {
return;
}
call.setSessionModificationTo(videoState);
}
android/packages/apps/
InCallUI/src/com/android/incallui/Call.java
/**
* This method is called when we request for a video upgrade or downgrade. This handles the
* session modification state RECEIVED_UPGRADE_TO_VIDEO_REQUEST and sets the video state we
* want to upgrade/downgrade to.
*/
public void setSessionModificationTo(int videoState) {
Log.d(this, "setSessionModificationTo - video state= " + videoState);
if (videoState == getVideoState()) {
mSessionModificationState = Call.SessionModificationState.NO_REQUEST;
Log.w(this,"setSessionModificationTo - Clearing session modification state");
} else {
mSessionModificationState =
Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
setModifyToVideoState(videoState);
CallList.getInstance().onUpgradeToVideo(this);
}
Log.d(this, "setSessionModificationTo - mSessionModificationState="
+ mSessionModificationState + " video state= " + videoState);
update();
}
android/packages/apps/
InCallUI/src/com/android/incallui/CallList.java
public void onUpgradeToVideo(Call call){
Log.d(this, "onUpgradeToVideo call=" + call);
for (Listener listener : mListeners) {
listener.onUpgradeToVideo(call);
}
}
看前面的注释
这是modify call的唯一调用的方法,等下我们看下降级的
* Called when a new modify call request comes in
* This is the only method that gets called for modify requests.
*/
public void onUpgradeToVideo(Call call);
android/packages/apps/InCallUI/src/com/android/incallui/AnswerPresenter.java
界面显示,(在5.1上我们自己做过升级声音提示,在6.0上高通加了一个类实现这个功能,不知道AOSP上有没有那个提示音的功能)
@Override
public void onUpgradeToVideo(Call call) {
Log.d(this, "onUpgradeToVideo: " + this + " call=" + call);
showAnswerUi(true);//一次
boolean isUpgradePending = isVideoUpgradePending(call);
InCallPresenter inCallPresenter = InCallPresenter.getInstance();
if (isUpgradePending
&& inCallPresenter.getInCallState() == InCallPresenter.InCallState.INCOMING) {
Log.d(this, "declining upgrade request");
//If there is incoming call reject upgrade request
inCallPresenter.declineUpgradeRequest(getUi().getContext());
} else if (isUpgradePending) {
Log.d(this, "process upgrade request as no MT call");
processVideoUpgradeRequestCall(call);
}
}
private void processVideoUpgradeRequestCall(Call call) {
Log.d(this, " processVideoUpgradeRequestCall call=" + call);
int subId = call.getSubId();
int phoneId = mCalls.getPhoneId(subId);
mCallId[phoneId] = call.getId();
mCall[phoneId] = call;
// Listen for call updates for the current call.
CallList.getInstance().addCallUpdateListener(mCallId[phoneId], this);
final int currentVideoState = call.getVideoState();
final int modifyToVideoState = call.getModifyToVideoState();
if (currentVideoState == modifyToVideoState) {
Log.w(this, "processVideoUpgradeRequestCall: Video states are same. Return.");
return;
}
AnswerUi ui = getUi();
if (ui == null) {
Log.e(this, "Ui is null. Can't process upgrade request");
return;
}
showAnswerUi(true);//2次
ui.showTargets(QtiCallUtils.getSessionModificationOptions(getUi().getContext(),
currentVideoState, modifyToVideoState));
}
QtiCallUtils.java
根据实际情况,在界面AnswerFragment上显示不同选择。
/**
* Returns the session modification user options based on session modify request video states
* (current video state and modify request video state)
*/
public static int getSessionModificationOptions(Context context, int currentVideoState,
int modifyToVideoState) {
if (!useExt(context)) {
return AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST;
}
if (showVideoUpgradeOptions(currentVideoState, modifyToVideoState)) {
return AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_ACCEPT_REJECT_REQUEST;
} else if (isEnabled(VideoProfile.STATE_BIDIRECTIONAL, modifyToVideoState)) {
return AnswerFragment.TARGET_SET_FOR_QTI_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST;
} else if (isEnabled(VideoProfile.STATE_TX_ENABLED, modifyToVideoState)) {
return AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST;
} else if (isEnabled(VideoProfile.STATE_RX_ENABLED, modifyToVideoState)) {
return AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST;
}
return AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_ACCEPT_REJECT_REQUEST;
}
在我们的代码中,这个返回值后面被修改了,会根据运营商返回不同的值,
/**
* Checks the boolean flag in config file to figure out if we are going to use Qti extension or
* not
*/
public static boolean useExt(Context context) {
if (context == null) {
Log.w(context, "Context is null...");
}
return isConfigEnabled(context, R.bool.video_call_use_ext);//这个布尔值默认为true,但是针对mnc为405的都重写成了false
}
AnswerFragment.java
显示到界面上
/**
* Sets targets on the glowpad according to target set identified by the parameter.
* @param targetSet Integer identifying the set of targets to use.
*/
public void showTargets(int targetSet) {
showTargets(targetSet, VideoProfile.STATE_BIDIRECTIONAL);
}
/**
* Sets targets on the glowpad according to target set identified by the parameter.
* @param targetSet Integer identifying the set of targets to use.
*/
@Override
public void showTargets(int targetSet, int videoState) {
final int targetResourceId;
final int targetDescriptionsResourceId;
final int directionDescriptionsResourceId;
final int handleDrawableResourceId;
mGlowpad.setVideoState(videoState);
final boolean isEnhanceUIEnabled = getContext().getResources().getBoolean(
R.bool.config_enable_enhance_video_call_ui);
switch (targetSet) {
...
case TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST:
targetResourceId =
R.array.incoming_call_widget_video_request_targets;
targetDescriptionsResourceId =
R.array.incoming_call_widget_video_request_target_descriptions;
directionDescriptionsResourceId = R.array
.incoming_call_widget_video_request_target_direction_descriptions;
handleDrawableResourceId = R.drawable.ic_incall_video_handle;
break;
log
06-19 19:05:41.622 14182-14182/com.android.dialer I/InCall: StatusBarNotifier - Will show "dismiss upgrade" action in the incoming call Notification
06-19 19:05:41.624 14182-14182/com.android.dialer I/InCall: StatusBarNotifier - Will show "accept upgrade" action in the incoming call Notification
关键log
MT receive
收到视频升级请求
//receive
06-19 19:05:41.437 3329-4362/com.android.phone V/ImsSenderRxr: Read packet: 24 bytes
06-19 19:05:41.437 3329-4362/com.android.phone V/ImsSenderRxr: processResponse[SUB1]
06-19 19:05:41.437 3329-4362/com.android.phone D/ImsSenderRxr: Response data: [12, 13, -1, -1, -1, -1, 16, 3, 24, -49, 1, 32, 0, 21, 1, 0, 0, 0, 26, 4, 8, 3, 16, 2][SUB1]
06-19 19:05:41.437 3329-4362/com.android.phone D/ImsSenderRxr: Tag -1 3 207 0[SUB1]
06-19 19:05:41.437 3329-4362/com.android.phone D/ImsSenderRxr: responseModifyCall 1 3 2 callSubState 0 videoPauseState2 mediaId-1 Local Ability Peer Ability Cause code 0 0[SUB1]
06-19 19:05:41.438 3329-4362/com.android.phone D/ImsSenderRxr: [UNSL]< UNSOL_MODIFY_CALL 1 3 2 callSubState 0 videoPauseState2 mediaId-1 Local Ability Peer Ability Cause code 0 0[SUB1]
MO send request, accepted, rejected, time out
MT接受升级请求后MO的log
//accepted response
06-25 21:37:21.793 3289-4294/com.android.phone V/ImsSenderRxr: Read packet: 12 bytes
06-25 21:37:21.793 3289-4294/com.android.phone V/ImsSenderRxr: processResponse[SUB1]
06-25 21:37:21.793 3289-4294/com.android.phone D/ImsSenderRxr: Response data: [11, 13, 110, 0, 0, 0, 16, 2, 24, 17, 32, 0][SUB1]
06-25 21:37:21.793 3289-4294/com.android.phone D/ImsSenderRxr: Tag 110 2 17 0[SUB1]
06-25 21:37:21.793 3289-4294/com.android.phone D/ImsSenderRxr: [0110]< MODIFY_CALL_INITIATE [SUB1]
06-25 21:37:21.793 3289-3289/com.android.phone D/VideoCall_ImsCallModification: EVENT_MODIFY_CALL_INITIATE_DONE received
升级请求被拒绝和超时,可以看到两者的log是一样的
//send request
06-21 16:00:12.167 13623-13623/com.android.phone D/ImsSenderRxr: [0021]> MODIFY_CALL_INITIATE[SUB0]
//reject
06-21 16:00:19.129 13623-14690/com.android.phone D/ImsSenderRxr: [0021]< MODIFY_CALL_INITIATE error: 28
06-21 16:00:19.129 13623-13623/com.android.phone D/VideoCall_ImsCallModification: EVENT_MODIFY_CALL_INITIATE_DONE received
/
/send request07-17 10:00:11.655 2915-2915/com.android.phone D/ImsSenderRxr: [0028]> MODIFY_CALL_INITIATE[SUB1]
//time out07-17 10:00:27.733 2915-4049/com.android.phone D/ImsSenderRxr: [0028]< MODIFY_CALL_INITIATE error: 28 07-17 10:00:27.733 2915-2915/com.android.phone D/VideoCall_ImsCallModification: EVENT_MODIFY_CALL_INITIATE_DONE received
07-17 10:00:27.748 13872-13872/com.android.dialer D/InCall: InCallVideoCallCallback - onSessionModifyResponseReceived status=5 requestedProfile=[VideoProfile videoState = Audio Tx Rx
MT不做任何处理的话15秒后(运营商决定)MO收到运营商发来的响应消息,其中包括
InCallVideoCallCallback - onSessionModifyResponseReceived status=5,这个5的定义为
VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE(5)意为被远端拒绝了可见对于MO来说超时和reject是一样的,而在MT收到的则是
VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT(4),所以在MT方 reject和time out是区分的。
然后这个error: 28的定义
在imsIF.proto中查到,也证明了timeout和reject的。
E_REJECTED_BY_REMOTE = 28; /* Remote end rejected a change started by
REQUEST_MODIFY_CALL_INITIATE */
小结
整个流程中的log不多,主要是底层ImsSenderRxr.java中的log多些,UNSOL_MODIFY_CALL (未处理的新消息),MODIFY_CALL_CONFIRM(MT界面应答),MODIFY_CALL_INITIATE(MO发起),然后InCall中在一些场景中有些log,AnswerPresenter和CallCardPresenter的log。
如果出问题的话也基本上在从底层和上层判断,中间应该不会有什么问题,底层看消息首发,上层看信息显示。Modify Call已经包括upgrade和downgrade,流程整体也是一样,在需要区分的时候会通过callType(CallMofidy)来区分。