/* * 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.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.incallui.videotech.VideoTech; import com.android.incallui.videotech.VideoTech.SessionModificationState; import com.android.incallui.videotech.VideoTech.VideoTechListener; /** 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 Call call; private final ImsVideoTech videoTech; private final VideoTechListener listener; private int requestedVideoState = VideoProfile.STATE_AUDIO_ONLY; ImsVideoCallCallback(final Call call, ImsVideoTech videoTech, VideoTechListener listener) { this.call = call; this.videoTech = videoTech; this.listener = listener; } @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; videoTech.setSessionModificationState( VideoTech.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST); listener.onVideoUpgradeRequestReceived(); } } /** * @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() == VideoTech.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE) { handler.removeCallbacksAndMessages(null); // Clear everything final int newSessionModificationState = getSessionModificationStateFromTelecomStatus(status); if (status != VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) { // This will update the video UI to display the error message. videoTech.setSessionModificationState(newSessionModificationState); } // 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. // // 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. handler.postDelayed( () -> { if (videoTech.getSessionModificationState() == newSessionModificationState) { LogUtil.i("ImsVideoCallCallback.onSessionModifyResponseReceived", "clearing state"); videoTech.setSessionModificationState( VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST); } else { LogUtil.i( "ImsVideoCallCallback.onSessionModifyResponseReceived", "session modification state has changed, not clearing state"); } }, CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS); } else if (videoTech.getSessionModificationState() == VideoTech.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { videoTech.setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST); } else if (videoTech.getSessionModificationState() == VideoTech.SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE) { videoTech.setSessionModificationState(getSessionModificationStateFromTelecomStatus(status)); } else { LogUtil.i( "ImsVideoCallCallback.onSessionModifyResponseReceived", "call is not waiting for response, doing nothing"); } } @SessionModificationState private int getSessionModificationStateFromTelecomStatus(int telecomStatus) { switch (telecomStatus) { case VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS: return VideoTech.SESSION_MODIFICATION_STATE_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 VideoTech.SESSION_MODIFICATION_STATE_REQUEST_FAILED; } else { return VideoTech.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED; } case VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT: return VideoTech.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT; case VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE: return VideoTech.SESSION_MODIFICATION_STATE_REQUEST_REJECTED; default: LogUtil.e( "ImsVideoCallCallback.getSessionModificationStateFromTelecomStatus", "unknown status: %d", telecomStatus); return VideoTech.SESSION_MODIFICATION_STATE_REQUEST_FAILED; } } @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; } }