1 /**
2  * Copyright (C) 2022 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.telephony.imsmedia;
18 
19 import android.os.Handler;
20 import android.os.Looper;
21 import android.os.Message;
22 import android.os.RemoteException;
23 import android.support.annotation.VisibleForTesting;
24 import android.telephony.ims.RtpHeaderExtension;
25 import android.telephony.imsmedia.IImsVideoSession;
26 import android.telephony.imsmedia.IImsVideoSessionCallback;
27 import android.telephony.imsmedia.ImsMediaSession;
28 import android.telephony.imsmedia.MediaQualityThreshold;
29 import android.telephony.imsmedia.RtpConfig;
30 import android.telephony.imsmedia.VideoConfig;
31 import android.view.Surface;
32 
33 import androidx.annotation.NonNull;
34 import androidx.annotation.Nullable;
35 
36 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
37 import com.android.telephony.imsmedia.util.Log;
38 
39 import java.util.List;
40 
41 /**
42  * Video session binder implementation which handles all video session APIs
43  * from the VT applications.
44  */
45 public final class VideoSession extends IImsVideoSession.Stub implements IMediaSession {
46     private static final String TAG = "VideoSession";
47 
48     public static final int CMD_OPEN_SESSION = 101;
49     public static final int CMD_CLOSE_SESSION = 102;
50     public static final int CMD_MODIFY_SESSION = 103;
51     public static final int CMD_SET_PREVIEW_SURFACE = 104;
52     public static final int CMD_SET_DISPLAY_SURFACE = 105;
53     public static final int CMD_SEND_RTP_HDR_EXTN = 106;
54     public static final int CMD_SET_MEDIA_QUALITY_THRESHOLD = 107;
55     public static final int CMD_REQUEST_VIDEO_DATA_USAGE = 108;
56 
57     public static final int EVENT_OPEN_SESSION_SUCCESS = 201;
58     public static final int EVENT_OPEN_SESSION_FAILURE = 202;
59     public static final int EVENT_MODIFY_SESSION_RESPONSE = 203;
60     public static final int EVENT_FIRST_MEDIA_PACKET_IND = 204;
61     public static final int EVENT_PEER_DIMENSION_CHANGED = 205;
62     public static final int EVENT_RTP_HEADER_EXTENSION_IND = 206;
63     public static final int EVENT_MEDIA_INACTIVITY_IND = 207;
64     public static final int EVENT_NOTIFY_BITRATE_IND = 208;
65     public static final int EVENT_VIDEO_DATA_USAGE_IND = 209;
66     public static final int EVENT_SESSION_CLOSED = 210;
67 
68     private int mSessionId;
69     private IImsVideoSessionCallback mCallback;
70     private VideoSessionHandler mHandler;
71     private VideoService mVideoService;
72     private VideoListener mVideoListener;
73     private VideoLocalSession mLocalSession;
74 
VideoSession(final int sessionId, final IImsVideoSessionCallback callback)75     VideoSession(final int sessionId, final IImsVideoSessionCallback callback) {
76         mSessionId = sessionId;
77         mCallback = callback;
78         mHandler = new VideoSessionHandler(Looper.getMainLooper());
79         Log.d(TAG, "Initialize local video service");
80         mVideoService = new VideoService();
81         mVideoListener = new VideoListener(mHandler);
82         mVideoService.setListener(mVideoListener);
83         mVideoListener.setNativeObject(mVideoService.getNativeObject());
84     }
85 
86     @VisibleForTesting
VideoSession(final int sessionId, final @NonNull IImsVideoSessionCallback callback, final @Nullable VideoService videoService, final @Nullable VideoLocalSession localSession, Looper looper)87     VideoSession(final int sessionId,
88             final @NonNull IImsVideoSessionCallback callback,
89             final @Nullable VideoService videoService,
90             final @Nullable VideoLocalSession localSession, Looper looper) {
91         mSessionId = sessionId;
92         mCallback = callback;
93         mHandler = new VideoSessionHandler(looper);
94         mVideoService = videoService;
95         mLocalSession = localSession;
96         mVideoListener = new VideoListener(mHandler);
97     }
98 
99     @VisibleForTesting
getVideoSessionHandler()100     VideoSessionHandler getVideoSessionHandler() {
101         return mHandler;
102     }
103 
104     @VisibleForTesting
getVideoListener()105     VideoListener getVideoListener() {
106         return mVideoListener;
107     }
108 
109     @Override
openSession(OpenSessionParams sessionParams)110     public void openSession(OpenSessionParams sessionParams) {
111         Utils.sendMessage(mHandler, CMD_OPEN_SESSION, sessionParams);
112         RtpConfig rtpConfig = sessionParams.getRtpConfig();
113         if (rtpConfig != null) {
114             WakeLockManager.getInstance().manageWakeLockOnMediaDirectionUpdate(
115                     mSessionId, rtpConfig.getMediaDirection());
116         }
117     }
118 
119     @Override
closeSession()120     public void closeSession() {
121         Utils.sendMessage(mHandler, CMD_CLOSE_SESSION);
122         WakeLockManager.getInstance().manageWakeLockOnMediaDirectionUpdate(
123                 mSessionId, RtpConfig.MEDIA_DIRECTION_NO_FLOW);
124     }
125 
126     @Override
getSessionId()127     public int getSessionId() {
128         return mSessionId;
129     }
130 
131     @Override
modifySession(VideoConfig config)132     public void modifySession(VideoConfig config) {
133         Log.d(TAG, "modifySession: " + Log.hidePii(String.valueOf(config)));
134         Utils.sendMessage(mHandler, CMD_MODIFY_SESSION, config);
135         WakeLockManager.getInstance().manageWakeLockOnMediaDirectionUpdate(
136                 mSessionId, config.getMediaDirection());
137     }
138 
139     @Override
setPreviewSurface(Surface surface)140     public void setPreviewSurface(Surface surface) {
141         Log.dc(TAG, "setPreviewSurface: " + surface);
142         Utils.sendMessage(mHandler, CMD_SET_PREVIEW_SURFACE, surface);
143     }
144 
145     @Override
setDisplaySurface(Surface surface)146     public void setDisplaySurface(Surface surface) {
147         Log.dc(TAG, "setDisplaySurface: " + surface);
148         Utils.sendMessage(mHandler, CMD_SET_DISPLAY_SURFACE, surface);
149     }
150 
151     @Override
sendHeaderExtension(List<RtpHeaderExtension> extensions)152     public void sendHeaderExtension(List<RtpHeaderExtension> extensions) {
153         Log.d(TAG, "sendHeaderExtension" + Log.hidePii(String.valueOf(extensions)));
154         Utils.sendMessage(mHandler, CMD_SEND_RTP_HDR_EXTN, extensions);
155     }
156 
157     @Override
setMediaQualityThreshold(MediaQualityThreshold threshold)158     public void setMediaQualityThreshold(MediaQualityThreshold threshold) {
159         Log.d(TAG, "setMediaQualityThreshold: " + Log.hidePii(String.valueOf(threshold)));
160         Utils.sendMessage(mHandler, CMD_SET_MEDIA_QUALITY_THRESHOLD, threshold);
161     }
162 
163     @Override
requestVideoDataUsage()164     public void requestVideoDataUsage() {
165         Log.dc(TAG, "requestVideoDataUsage: ");
166         Utils.sendMessage(mHandler, CMD_REQUEST_VIDEO_DATA_USAGE);
167     }
168 
169     @Override
onOpenSessionSuccess(Object session)170     public void onOpenSessionSuccess(Object session) {
171         Utils.sendMessage(mHandler, EVENT_OPEN_SESSION_SUCCESS, session);
172     }
173 
174     @Override
onOpenSessionFailure(int error)175     public void onOpenSessionFailure(int error) {
176         Utils.sendMessage(mHandler, EVENT_OPEN_SESSION_FAILURE, error);
177     }
178 
179     @Override
onSessionClosed()180     public void onSessionClosed() {
181         Utils.sendMessage(mHandler, EVENT_SESSION_CLOSED);
182     }
183 
184     /**
185      * Video session message mHandler
186      */
187     class VideoSessionHandler extends Handler {
VideoSessionHandler(Looper looper)188         VideoSessionHandler(Looper looper) {
189             super(looper);
190         }
191 
192         @Override
handleMessage(Message msg)193         public void handleMessage(Message msg) {
194             Log.dc(TAG, "handleMessage() -" + VideoSessionHandler.this + ", " + msg.what);
195             switch(msg.what) {
196                 case CMD_OPEN_SESSION:
197                     handleOpenSession((OpenSessionParams) msg.obj);
198                     break;
199                 case CMD_CLOSE_SESSION:
200                     handleCloseSession();
201                     break;
202                 case CMD_MODIFY_SESSION:
203                     handleModifySession((VideoConfig) msg.obj);
204                     break;
205                 case CMD_SET_PREVIEW_SURFACE:
206                     handleSetPreviewSurface((Surface) msg.obj);
207                     break;
208                 case CMD_SET_DISPLAY_SURFACE:
209                     handleSetDisplaySurface((Surface) msg.obj);
210                     break;
211                 case CMD_SEND_RTP_HDR_EXTN:
212                     handleSendRtpHeaderExtension((List<RtpHeaderExtension>) msg.obj);
213                     break;
214                 case CMD_SET_MEDIA_QUALITY_THRESHOLD:
215                     handleSetMediaQualityThreshold((MediaQualityThreshold) msg.obj);
216                     break;
217                 case CMD_REQUEST_VIDEO_DATA_USAGE:
218                     handleRequestVideoDataUsage();
219                     break;
220                 case EVENT_OPEN_SESSION_SUCCESS:
221                     handleOpenSuccess(msg.obj);
222                     break;
223                 case EVENT_OPEN_SESSION_FAILURE:
224                     handleOpenFailure((int) msg.obj);
225                     break;
226                 case EVENT_SESSION_CLOSED:
227                     handleSessionClosed();
228                     break;
229                 case EVENT_MODIFY_SESSION_RESPONSE:
230                     handleModifySessionRespose((VideoConfig) msg.obj, msg.arg1);
231                     break;
232                 case EVENT_FIRST_MEDIA_PACKET_IND:
233                     handleFirstMediaPacketInd((VideoConfig) msg.obj);
234                     break;
235                 case EVENT_PEER_DIMENSION_CHANGED:
236                     handlePeerDimensionChanged(msg.arg1, msg.arg2);
237                     break;
238                 case EVENT_RTP_HEADER_EXTENSION_IND:
239                     handleRtpHeaderExtensionInd((List<RtpHeaderExtension>) msg.obj);
240                     break;
241                 case EVENT_MEDIA_INACTIVITY_IND:
242                     handleNotifyMediaInactivityInd(msg.arg1);
243                     break;
244                 case EVENT_NOTIFY_BITRATE_IND:
245                     handleNotifyBitrateInd(msg.arg1);
246                     break;
247                 case EVENT_VIDEO_DATA_USAGE_IND:
248                     handleNotifyVideoDataUsage((long) msg.obj);
249                     break;
250                 default:
251             }
252         }
253     }
254 
handleOpenSession(OpenSessionParams sessionParams)255     private void handleOpenSession(OpenSessionParams sessionParams) {
256         mVideoListener.setMediaCallback(sessionParams.getCallback());
257         int result = mVideoService.openSession(mSessionId, sessionParams);
258         if (result != ImsMediaSession.RESULT_SUCCESS) {
259             handleOpenFailure(result);
260         }
261     }
262 
handleCloseSession()263     private void handleCloseSession() {
264         mVideoService.closeSession(mSessionId);
265     }
266 
handleModifySession(VideoConfig config)267     private void handleModifySession(VideoConfig config) {
268         mLocalSession.modifySession(config);
269     }
270 
handleSetPreviewSurface(Surface surface)271     private void handleSetPreviewSurface(Surface surface) {
272         mLocalSession.setPreviewSurface(surface);
273     }
274 
handleSetDisplaySurface(Surface surface)275     private void handleSetDisplaySurface(Surface surface) {
276         mLocalSession.setDisplaySurface(surface);
277     }
278 
handleSendRtpHeaderExtension(List<RtpHeaderExtension> extensions)279     private void handleSendRtpHeaderExtension(List<RtpHeaderExtension> extensions) {
280         mLocalSession.sendHeaderExtension(extensions);
281     }
282 
handleSetMediaQualityThreshold(MediaQualityThreshold threshold)283     private void handleSetMediaQualityThreshold(MediaQualityThreshold threshold) {
284         mLocalSession.setMediaQualityThreshold(threshold);
285     }
286 
handleRequestVideoDataUsage()287     private void handleRequestVideoDataUsage() {
288         mLocalSession.requestVideoDataUsage();
289     }
290 
handleOpenSuccess(Object session)291     private void handleOpenSuccess(Object session) {
292         mLocalSession = (VideoLocalSession) session;
293         try {
294             mCallback.onOpenSessionSuccess(this);
295         } catch (RemoteException e) {
296             Log.e(TAG, "Failed to notify openSuccess: " + e);
297         }
298     }
299 
handleOpenFailure(int error)300     private void handleOpenFailure(int error) {
301         try {
302             mCallback.onOpenSessionFailure(error);
303         } catch (RemoteException e) {
304             Log.e(TAG, "Failed to notify openFailure: " + e);
305         } finally {
306             WakeLockManager.getInstance().manageWakeLockOnMediaDirectionUpdate(
307                     mSessionId, RtpConfig.MEDIA_DIRECTION_NO_FLOW);
308         }
309     }
310 
handleSessionClosed()311     private void handleSessionClosed() {
312         try {
313             mCallback.onSessionClosed();
314         }  catch (RemoteException e) {
315             Log.e(TAG, "Failed to notify SessionClosed: " + e);
316         }
317     }
318 
handleModifySessionRespose(VideoConfig config, int error)319     private void handleModifySessionRespose(VideoConfig config, int error) {
320         try {
321             mCallback.onModifySessionResponse(config, error);
322         } catch (RemoteException e) {
323             Log.e(TAG, "Failed to notify modifySessionResponse: " + e);
324         }
325     }
326 
handleFirstMediaPacketInd(VideoConfig config)327     private void handleFirstMediaPacketInd(VideoConfig config) {
328         try {
329             mCallback.onFirstMediaPacketReceived(config);
330         } catch (RemoteException e) {
331             Log.e(TAG, "Failed to notify first media packet received indication: " + e);
332         }
333     }
334 
handlePeerDimensionChanged(int width, int height)335     private void handlePeerDimensionChanged(int width, int height) {
336         try {
337             mCallback.onPeerDimensionChanged(width, height);
338         } catch (RemoteException e) {
339             Log.e(TAG, "Failed to notify dimension changed: " + e);
340         }
341     }
342 
handleRtpHeaderExtensionInd(List<RtpHeaderExtension> extensions)343     private void handleRtpHeaderExtensionInd(List<RtpHeaderExtension> extensions) {
344         try {
345             mCallback.onHeaderExtensionReceived(extensions);
346         } catch (RemoteException e) {
347             Log.e(TAG, "Failed to notify RTP header extension: " + e);
348         }
349     }
350 
handleNotifyMediaInactivityInd(int packetType)351     private void handleNotifyMediaInactivityInd(int packetType) {
352         try {
353             mCallback.notifyMediaInactivity(packetType);
354         } catch (RemoteException e) {
355             Log.e(TAG, "Failed to notify media timeout: " + e);
356         }
357     }
358 
handleNotifyBitrateInd(int percentage)359     private void handleNotifyBitrateInd(int percentage) {
360         try {
361             mCallback.notifyBitrate(percentage);
362         } catch (RemoteException e) {
363             Log.e(TAG, "Failed to notify packet loss: " + e);
364         }
365     }
366 
handleNotifyVideoDataUsage(long bytes)367     private void handleNotifyVideoDataUsage(long bytes) {
368         try {
369             mCallback.notifyVideoDataUsage(bytes);
370         } catch (RemoteException e) {
371             Log.e(TAG, "Failed to notify video data usage: " + e);
372         }
373     }
374 }
375