1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.incallui.videotech.ims; 18 19 import android.os.Handler; 20 import android.telecom.Call; 21 import android.telecom.Connection; 22 import android.telecom.Connection.VideoProvider; 23 import android.telecom.InCallService.VideoCall; 24 import android.telecom.VideoProfile; 25 import android.telecom.VideoProfile.CameraCapabilities; 26 import com.android.dialer.common.LogUtil; 27 import com.android.dialer.logging.DialerImpression; 28 import com.android.dialer.logging.LoggingBindings; 29 import com.android.incallui.videotech.VideoTech.VideoTechListener; 30 import com.android.incallui.videotech.utils.SessionModificationState; 31 32 /** Receives IMS video call state updates. */ 33 public class ImsVideoCallCallback extends VideoCall.Callback { 34 private static final int CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS = 4000; 35 private final Handler handler = new Handler(); 36 private final LoggingBindings logger; 37 private final Call call; 38 private final ImsVideoTech videoTech; 39 private final VideoTechListener listener; 40 private int requestedVideoState = VideoProfile.STATE_AUDIO_ONLY; 41 ImsVideoCallCallback( final LoggingBindings logger, final Call call, ImsVideoTech videoTech, VideoTechListener listener)42 ImsVideoCallCallback( 43 final LoggingBindings logger, 44 final Call call, 45 ImsVideoTech videoTech, 46 VideoTechListener listener) { 47 this.logger = logger; 48 this.call = call; 49 this.videoTech = videoTech; 50 this.listener = listener; 51 } 52 53 @Override onSessionModifyRequestReceived(VideoProfile videoProfile)54 public void onSessionModifyRequestReceived(VideoProfile videoProfile) { 55 LogUtil.i( 56 "ImsVideoCallCallback.onSessionModifyRequestReceived", "videoProfile: " + videoProfile); 57 58 int previousVideoState = ImsVideoTech.getUnpausedVideoState(call.getDetails().getVideoState()); 59 int newVideoState = ImsVideoTech.getUnpausedVideoState(videoProfile.getVideoState()); 60 61 boolean wasVideoCall = VideoProfile.isVideo(previousVideoState); 62 boolean isVideoCall = VideoProfile.isVideo(newVideoState); 63 64 if (wasVideoCall && !isVideoCall) { 65 LogUtil.i( 66 "ImsVideoTech.onSessionModifyRequestReceived", "call downgraded to %d", newVideoState); 67 } else if (previousVideoState != newVideoState) { 68 requestedVideoState = newVideoState; 69 videoTech.setSessionModificationState( 70 SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST); 71 listener.onVideoUpgradeRequestReceived(); 72 logger.logImpression(DialerImpression.Type.IMS_VIDEO_REQUEST_RECEIVED); 73 } 74 } 75 76 /** 77 * @param status Status of the session modify request. Valid values are {@link 78 * Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS}, {@link 79 * Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL}, {@link 80 * Connection.VideoProvider#SESSION_MODIFY_REQUEST_INVALID} 81 * @param responseProfile The actual profile changes made by the peer device. 82 */ 83 @Override onSessionModifyResponseReceived( int status, VideoProfile requestedProfile, VideoProfile responseProfile)84 public void onSessionModifyResponseReceived( 85 int status, VideoProfile requestedProfile, VideoProfile responseProfile) { 86 LogUtil.i( 87 "ImsVideoCallCallback.onSessionModifyResponseReceived", 88 "status: %d, requestedProfile: %s, responseProfile: %s, session modification state: %d", 89 status, 90 requestedProfile, 91 responseProfile, 92 videoTech.getSessionModificationState()); 93 94 if (videoTech.getSessionModificationState() 95 == SessionModificationState.WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE) { 96 handler.removeCallbacksAndMessages(null); // Clear everything 97 98 final int newSessionModificationState = getSessionModificationStateFromTelecomStatus(status); 99 if (status == VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) { 100 // Telecom manages audio route for us 101 listener.onUpgradedToVideo(false /* switchToSpeaker */); 102 } else { 103 // This will update the video UI to display the error message. 104 videoTech.setSessionModificationState(newSessionModificationState); 105 } 106 107 // Wait for 4 seconds and then clean the session modification state. This allows the video UI 108 // to stay up so that the user can read the error message. 109 // 110 // If the other person accepted the upgrade request then this will keep the video UI up until 111 // the call's video state change. Without this we would switch to the voice call and then 112 // switch back to video UI. 113 handler.postDelayed( 114 () -> { 115 if (videoTech.getSessionModificationState() == newSessionModificationState) { 116 LogUtil.i("ImsVideoCallCallback.onSessionModifyResponseReceived", "clearing state"); 117 videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST); 118 } else { 119 LogUtil.i( 120 "ImsVideoCallCallback.onSessionModifyResponseReceived", 121 "session modification state has changed, not clearing state"); 122 } 123 }, 124 CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS); 125 } else if (videoTech.getSessionModificationState() 126 == SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { 127 videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST); 128 } else if (videoTech.getSessionModificationState() 129 == SessionModificationState.WAITING_FOR_RESPONSE) { 130 videoTech.setSessionModificationState(getSessionModificationStateFromTelecomStatus(status)); 131 } else { 132 LogUtil.i( 133 "ImsVideoCallCallback.onSessionModifyResponseReceived", 134 "call is not waiting for response, doing nothing"); 135 } 136 } 137 138 @SessionModificationState getSessionModificationStateFromTelecomStatus(int telecomStatus)139 private int getSessionModificationStateFromTelecomStatus(int telecomStatus) { 140 switch (telecomStatus) { 141 case VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS: 142 return SessionModificationState.NO_REQUEST; 143 case VideoProvider.SESSION_MODIFY_REQUEST_FAIL: 144 case VideoProvider.SESSION_MODIFY_REQUEST_INVALID: 145 // Check if it's already video call, which means the request is not video upgrade request. 146 if (VideoProfile.isVideo(call.getDetails().getVideoState())) { 147 return SessionModificationState.REQUEST_FAILED; 148 } else { 149 return SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_FAILED; 150 } 151 case VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT: 152 return SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT; 153 case VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE: 154 return SessionModificationState.REQUEST_REJECTED; 155 default: 156 LogUtil.e( 157 "ImsVideoCallCallback.getSessionModificationStateFromTelecomStatus", 158 "unknown status: %d", 159 telecomStatus); 160 return SessionModificationState.REQUEST_FAILED; 161 } 162 } 163 164 @Override onCallSessionEvent(int event)165 public void onCallSessionEvent(int event) { 166 switch (event) { 167 case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE: 168 LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "rx_pause"); 169 break; 170 case Connection.VideoProvider.SESSION_EVENT_RX_RESUME: 171 LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "rx_resume"); 172 break; 173 case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE: 174 LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "camera_failure"); 175 break; 176 case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY: 177 LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "camera_ready"); 178 break; 179 default: 180 LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "unknown event = : " + event); 181 break; 182 } 183 } 184 185 @Override onPeerDimensionsChanged(int width, int height)186 public void onPeerDimensionsChanged(int width, int height) { 187 listener.onPeerDimensionsChanged(width, height); 188 } 189 190 @Override onVideoQualityChanged(int videoQuality)191 public void onVideoQualityChanged(int videoQuality) { 192 LogUtil.i("ImsVideoCallCallback.onVideoQualityChanged", "videoQuality: %d", videoQuality); 193 } 194 195 @Override onCallDataUsageChanged(long dataUsage)196 public void onCallDataUsageChanged(long dataUsage) { 197 LogUtil.i("ImsVideoCallCallback.onCallDataUsageChanged", "dataUsage: %d", dataUsage); 198 } 199 200 @Override onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities)201 public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) { 202 if (cameraCapabilities != null) { 203 listener.onCameraDimensionsChanged( 204 cameraCapabilities.getWidth(), cameraCapabilities.getHeight()); 205 } 206 } 207 getRequestedVideoState()208 int getRequestedVideoState() { 209 return requestedVideoState; 210 } 211 } 212