236 lines
10 KiB
Java
236 lines
10 KiB
Java
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License
|
|
*/
|
|
|
|
package com.android.incallui.videotech.ims;
|
|
|
|
import android.content.Context;
|
|
import android.os.Handler;
|
|
import android.telecom.Call;
|
|
import android.telecom.Connection;
|
|
import android.telecom.Connection.VideoProvider;
|
|
import android.telecom.InCallService.VideoCall;
|
|
import android.telecom.VideoProfile;
|
|
import android.telecom.VideoProfile.CameraCapabilities;
|
|
import com.android.dialer.common.LogUtil;
|
|
import com.android.dialer.logging.DialerImpression;
|
|
import com.android.dialer.logging.LoggingBindings;
|
|
import com.android.incallui.videotech.VideoTech.VideoTechListener;
|
|
import com.android.incallui.videotech.utils.SessionModificationState;
|
|
|
|
/** Receives IMS video call state updates. */
|
|
public class ImsVideoCallCallback extends VideoCall.Callback {
|
|
private static final int CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS = 4000;
|
|
private final Handler handler = new Handler();
|
|
private final LoggingBindings logger;
|
|
private final Call call;
|
|
private final ImsVideoTech videoTech;
|
|
private final VideoTechListener listener;
|
|
private final Context context;
|
|
private int requestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
|
|
|
|
ImsVideoCallCallback(
|
|
final LoggingBindings logger,
|
|
final Call call,
|
|
ImsVideoTech videoTech,
|
|
VideoTechListener listener,
|
|
Context context) {
|
|
this.logger = logger;
|
|
this.call = call;
|
|
this.videoTech = videoTech;
|
|
this.listener = listener;
|
|
this.context = context;
|
|
}
|
|
|
|
@Override
|
|
public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
|
|
LogUtil.i(
|
|
"ImsVideoCallCallback.onSessionModifyRequestReceived", "videoProfile: " + videoProfile);
|
|
|
|
int previousVideoState = ImsVideoTech.getUnpausedVideoState(call.getDetails().getVideoState());
|
|
int newVideoState = ImsVideoTech.getUnpausedVideoState(videoProfile.getVideoState());
|
|
|
|
boolean wasVideoCall = VideoProfile.isVideo(previousVideoState);
|
|
boolean isVideoCall = VideoProfile.isVideo(newVideoState);
|
|
|
|
if (wasVideoCall && !isVideoCall) {
|
|
LogUtil.i(
|
|
"ImsVideoTech.onSessionModifyRequestReceived", "call downgraded to %d", newVideoState);
|
|
} else if (previousVideoState != newVideoState) {
|
|
requestedVideoState = newVideoState;
|
|
if (!wasVideoCall) {
|
|
videoTech.setSessionModificationState(
|
|
SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST);
|
|
listener.onVideoUpgradeRequestReceived();
|
|
logger.logImpression(DialerImpression.Type.IMS_VIDEO_REQUEST_RECEIVED);
|
|
} else {
|
|
LogUtil.i(
|
|
"ImsVideoTech.onSessionModifyRequestReceived", "call updated to %d", newVideoState);
|
|
videoTech.acceptVideoRequest(context);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param status Status of the session modify request. Valid values are {@link
|
|
* Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS}, {@link
|
|
* Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL}, {@link
|
|
* Connection.VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
|
|
* @param responseProfile The actual profile changes made by the peer device.
|
|
*/
|
|
@Override
|
|
public void onSessionModifyResponseReceived(
|
|
int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
|
|
LogUtil.i(
|
|
"ImsVideoCallCallback.onSessionModifyResponseReceived",
|
|
"status: %d, requestedProfile: %s, responseProfile: %s, session modification state: %d",
|
|
status,
|
|
requestedProfile,
|
|
responseProfile,
|
|
videoTech.getSessionModificationState());
|
|
|
|
if (videoTech.getSessionModificationState()
|
|
== SessionModificationState.WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE) {
|
|
final int newSessionModificationState = getSessionModificationStateFromTelecomStatus(status);
|
|
if (status == VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
|
|
// Telecom manages audio route for us
|
|
listener.onUpgradedToVideo(false /* switchToSpeaker */);
|
|
} else {
|
|
// This will update the video UI to display the error message.
|
|
videoTech.setSessionModificationState(newSessionModificationState);
|
|
}
|
|
|
|
// If the other person accepted the upgrade request then this will keep the video UI up until
|
|
// the call's video state change. Without this we would switch to the voice call and then
|
|
// switch back to video UI.
|
|
clearFailedResponseState(newSessionModificationState);
|
|
} else if (videoTech.getSessionModificationState()
|
|
== SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
|
|
requestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
|
|
videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST);
|
|
} else if (videoTech.getSessionModificationState()
|
|
== SessionModificationState.WAITING_FOR_RESPONSE) {
|
|
final int newSessionModificationState = getSessionModificationStateFromTelecomStatus(status);
|
|
videoTech.setSessionModificationState(newSessionModificationState);
|
|
if (status != VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
|
|
clearFailedResponseState(newSessionModificationState);
|
|
}
|
|
} else {
|
|
LogUtil.i(
|
|
"ImsVideoCallCallback.onSessionModifyResponseReceived",
|
|
"call is not waiting for response, doing nothing");
|
|
}
|
|
}
|
|
|
|
private void clearFailedResponseState(final int newSessionModificationState) {
|
|
handler.removeCallbacksAndMessages(null); // Clear everything
|
|
// Wait for 4 seconds and then clean the session modification state. This allows the video UI
|
|
// to stay up so that the user can read the error message.
|
|
handler.postDelayed(
|
|
() -> {
|
|
if (videoTech.getSessionModificationState() == newSessionModificationState) {
|
|
LogUtil.i("ImsVideoCallCallback.onSessionModifyResponseReceived", "clearing state");
|
|
videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST);
|
|
} else {
|
|
LogUtil.i(
|
|
"ImsVideoCallCallback.onSessionModifyResponseReceived",
|
|
"session modification state has changed, not clearing state");
|
|
}
|
|
},
|
|
CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS);
|
|
}
|
|
|
|
@SessionModificationState
|
|
private int getSessionModificationStateFromTelecomStatus(int telecomStatus) {
|
|
switch (telecomStatus) {
|
|
case VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS:
|
|
return SessionModificationState.NO_REQUEST;
|
|
case VideoProvider.SESSION_MODIFY_REQUEST_FAIL:
|
|
case VideoProvider.SESSION_MODIFY_REQUEST_INVALID:
|
|
// Check if it's already video call, which means the request is not video upgrade request.
|
|
if (VideoProfile.isVideo(call.getDetails().getVideoState())) {
|
|
return SessionModificationState.REQUEST_FAILED;
|
|
} else {
|
|
return SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_FAILED;
|
|
}
|
|
case VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT:
|
|
return SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT;
|
|
case VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE:
|
|
return SessionModificationState.REQUEST_REJECTED;
|
|
default:
|
|
LogUtil.e(
|
|
"ImsVideoCallCallback.getSessionModificationStateFromTelecomStatus",
|
|
"unknown status: %d",
|
|
telecomStatus);
|
|
return SessionModificationState.REQUEST_FAILED;
|
|
}
|
|
}
|
|
|
|
// In the vendor code rx_pause and rx_resume get triggered when the video player starts or stops
|
|
// playing the incoming video stream. For the case where you're resuming a held call, its
|
|
// definitely a good signal to use to know that the video is resuming (though the video state
|
|
// should change to indicate its not paused in this case as well). However, keep in mind you'll
|
|
// get these signals as well on carriers that don't support the video pause signalling (like TMO)
|
|
// so you want to ensure you don't send sessionModifyRequests with pause/resume based on these
|
|
// signals. Also, its technically possible to have a pause/resume if the video signal degrades.
|
|
@Override
|
|
public void onCallSessionEvent(int event) {
|
|
switch (event) {
|
|
case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
|
|
LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "rx_pause");
|
|
break;
|
|
case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
|
|
LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "rx_resume");
|
|
break;
|
|
case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
|
|
LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "camera_failure");
|
|
break;
|
|
case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY:
|
|
LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "camera_ready");
|
|
break;
|
|
default:
|
|
LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "unknown event = : " + event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onPeerDimensionsChanged(int width, int height) {
|
|
listener.onPeerDimensionsChanged(width, height);
|
|
}
|
|
|
|
@Override
|
|
public void onVideoQualityChanged(int videoQuality) {
|
|
LogUtil.i("ImsVideoCallCallback.onVideoQualityChanged", "videoQuality: %d", videoQuality);
|
|
}
|
|
|
|
@Override
|
|
public void onCallDataUsageChanged(long dataUsage) {
|
|
LogUtil.i("ImsVideoCallCallback.onCallDataUsageChanged", "dataUsage: %d", dataUsage);
|
|
}
|
|
|
|
@Override
|
|
public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) {
|
|
if (cameraCapabilities != null) {
|
|
listener.onCameraDimensionsChanged(
|
|
cameraCapabilities.getWidth(), cameraCapabilities.getHeight());
|
|
}
|
|
}
|
|
|
|
int getRequestedVideoState() {
|
|
return requestedVideoState;
|
|
}
|
|
}
|