• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.Context;
20 import android.os.Handler;
21 import android.telecom.Call;
22 import android.telecom.Connection;
23 import android.telecom.Connection.VideoProvider;
24 import android.telecom.InCallService.VideoCall;
25 import android.telecom.VideoProfile;
26 import android.telecom.VideoProfile.CameraCapabilities;
27 import com.android.dialer.common.LogUtil;
28 import com.android.dialer.logging.DialerImpression;
29 import com.android.dialer.logging.LoggingBindings;
30 import com.android.incallui.videotech.VideoTech.VideoTechListener;
31 import com.android.incallui.videotech.utils.SessionModificationState;
32 
33 /** Receives IMS video call state updates. */
34 public class ImsVideoCallCallback extends VideoCall.Callback {
35   private static final int CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS = 4000;
36   private final Handler handler = new Handler();
37   private final LoggingBindings logger;
38   private final Call call;
39   private final ImsVideoTech videoTech;
40   private final VideoTechListener listener;
41   private final Context context;
42   private int requestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
43 
ImsVideoCallCallback( final LoggingBindings logger, final Call call, ImsVideoTech videoTech, VideoTechListener listener, Context context)44   ImsVideoCallCallback(
45       final LoggingBindings logger,
46       final Call call,
47       ImsVideoTech videoTech,
48       VideoTechListener listener,
49       Context context) {
50     this.logger = logger;
51     this.call = call;
52     this.videoTech = videoTech;
53     this.listener = listener;
54     this.context = context;
55   }
56 
57   @Override
onSessionModifyRequestReceived(VideoProfile videoProfile)58   public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
59     LogUtil.i(
60         "ImsVideoCallCallback.onSessionModifyRequestReceived", "videoProfile: " + videoProfile);
61 
62     int previousVideoState = ImsVideoTech.getUnpausedVideoState(call.getDetails().getVideoState());
63     int newVideoState = ImsVideoTech.getUnpausedVideoState(videoProfile.getVideoState());
64 
65     boolean wasVideoCall = VideoProfile.isVideo(previousVideoState);
66     boolean isVideoCall = VideoProfile.isVideo(newVideoState);
67 
68     if (wasVideoCall && !isVideoCall) {
69       LogUtil.i(
70           "ImsVideoTech.onSessionModifyRequestReceived", "call downgraded to %d", newVideoState);
71     } else if (previousVideoState != newVideoState) {
72       requestedVideoState = newVideoState;
73       if (!wasVideoCall) {
74         videoTech.setSessionModificationState(
75             SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST);
76         listener.onVideoUpgradeRequestReceived();
77         logger.logImpression(DialerImpression.Type.IMS_VIDEO_REQUEST_RECEIVED);
78       } else {
79         LogUtil.i(
80             "ImsVideoTech.onSessionModifyRequestReceived", "call updated to %d", newVideoState);
81         videoTech.acceptVideoRequest(context);
82       }
83     }
84   }
85 
86   /**
87    * @param status Status of the session modify request. Valid values are {@link
88    *     Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS}, {@link
89    *     Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL}, {@link
90    *     Connection.VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
91    * @param responseProfile The actual profile changes made by the peer device.
92    */
93   @Override
onSessionModifyResponseReceived( int status, VideoProfile requestedProfile, VideoProfile responseProfile)94   public void onSessionModifyResponseReceived(
95       int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
96     LogUtil.i(
97         "ImsVideoCallCallback.onSessionModifyResponseReceived",
98         "status: %d, requestedProfile: %s, responseProfile: %s, session modification state: %d",
99         status,
100         requestedProfile,
101         responseProfile,
102         videoTech.getSessionModificationState());
103 
104     if (videoTech.getSessionModificationState()
105         == SessionModificationState.WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE) {
106       handler.removeCallbacksAndMessages(null); // Clear everything
107 
108       final int newSessionModificationState = getSessionModificationStateFromTelecomStatus(status);
109       if (status == VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
110         // Telecom manages audio route for us
111         listener.onUpgradedToVideo(false /* switchToSpeaker */);
112       } else {
113         // This will update the video UI to display the error message.
114         videoTech.setSessionModificationState(newSessionModificationState);
115       }
116 
117       // Wait for 4 seconds and then clean the session modification state. This allows the video UI
118       // to stay up so that the user can read the error message.
119       //
120       // If the other person accepted the upgrade request then this will keep the video UI up until
121       // the call's video state change. Without this we would switch to the voice call and then
122       // switch back to video UI.
123       handler.postDelayed(
124           () -> {
125             if (videoTech.getSessionModificationState() == newSessionModificationState) {
126               LogUtil.i("ImsVideoCallCallback.onSessionModifyResponseReceived", "clearing state");
127               videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST);
128             } else {
129               LogUtil.i(
130                   "ImsVideoCallCallback.onSessionModifyResponseReceived",
131                   "session modification state has changed, not clearing state");
132             }
133           },
134           CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS);
135     } else if (videoTech.getSessionModificationState()
136         == SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
137       requestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
138       videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST);
139     } else if (videoTech.getSessionModificationState()
140         == SessionModificationState.WAITING_FOR_RESPONSE) {
141       videoTech.setSessionModificationState(getSessionModificationStateFromTelecomStatus(status));
142     } else {
143       LogUtil.i(
144           "ImsVideoCallCallback.onSessionModifyResponseReceived",
145           "call is not waiting for response, doing nothing");
146     }
147   }
148 
149   @SessionModificationState
getSessionModificationStateFromTelecomStatus(int telecomStatus)150   private int getSessionModificationStateFromTelecomStatus(int telecomStatus) {
151     switch (telecomStatus) {
152       case VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS:
153         return SessionModificationState.NO_REQUEST;
154       case VideoProvider.SESSION_MODIFY_REQUEST_FAIL:
155       case VideoProvider.SESSION_MODIFY_REQUEST_INVALID:
156         // Check if it's already video call, which means the request is not video upgrade request.
157         if (VideoProfile.isVideo(call.getDetails().getVideoState())) {
158           return SessionModificationState.REQUEST_FAILED;
159         } else {
160           return SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_FAILED;
161         }
162       case VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT:
163         return SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT;
164       case VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE:
165         return SessionModificationState.REQUEST_REJECTED;
166       default:
167         LogUtil.e(
168             "ImsVideoCallCallback.getSessionModificationStateFromTelecomStatus",
169             "unknown status: %d",
170             telecomStatus);
171         return SessionModificationState.REQUEST_FAILED;
172     }
173   }
174 
175   // In the vendor code rx_pause and rx_resume get triggered when the video player starts or stops
176   // playing the incoming video stream.  For the case where you're resuming a held call, its
177   // definitely a good signal to use to know that the video is resuming (though the video state
178   // should change to indicate its not paused in this case as well).  However, keep in mind you'll
179   // get these signals as well on carriers that don't support the video pause signalling (like TMO)
180   // so you want to ensure you don't send sessionModifyRequests with pause/resume based on these
181   // signals. Also, its technically possible to have a pause/resume if the video signal degrades.
182   @Override
onCallSessionEvent(int event)183   public void onCallSessionEvent(int event) {
184     switch (event) {
185       case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
186         LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "rx_pause");
187         break;
188       case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
189         LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "rx_resume");
190         break;
191       case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
192         LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "camera_failure");
193         break;
194       case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY:
195         LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "camera_ready");
196         break;
197       default:
198         LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "unknown event = : " + event);
199         break;
200     }
201   }
202 
203   @Override
onPeerDimensionsChanged(int width, int height)204   public void onPeerDimensionsChanged(int width, int height) {
205     listener.onPeerDimensionsChanged(width, height);
206   }
207 
208   @Override
onVideoQualityChanged(int videoQuality)209   public void onVideoQualityChanged(int videoQuality) {
210     LogUtil.i("ImsVideoCallCallback.onVideoQualityChanged", "videoQuality: %d", videoQuality);
211   }
212 
213   @Override
onCallDataUsageChanged(long dataUsage)214   public void onCallDataUsageChanged(long dataUsage) {
215     LogUtil.i("ImsVideoCallCallback.onCallDataUsageChanged", "dataUsage: %d", dataUsage);
216   }
217 
218   @Override
onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities)219   public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) {
220     if (cameraCapabilities != null) {
221       listener.onCameraDimensionsChanged(
222           cameraCapabilities.getWidth(), cameraCapabilities.getHeight());
223     }
224   }
225 
getRequestedVideoState()226   int getRequestedVideoState() {
227     return requestedVideoState;
228   }
229 }
230