1 /*
2  * Copyright (C) 2014 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 android.telecom;
18 
19 import android.net.Uri;
20 import android.os.Handler;
21 import android.os.IBinder;
22 import android.os.Looper;
23 import android.os.Message;
24 import android.os.RemoteException;
25 import android.telecom.InCallService.VideoCall;
26 import android.view.Surface;
27 
28 import com.android.internal.os.SomeArgs;
29 import com.android.internal.telecom.IVideoCallback;
30 import com.android.internal.telecom.IVideoProvider;
31 
32 /**
33  * Implementation of a Video Call, which allows InCallUi to communicate commands to the underlying
34  * {@link Connection.VideoProvider}, and direct callbacks from the
35  * {@link Connection.VideoProvider} to the appropriate {@link VideoCall.Listener}.
36  *
37  * {@hide}
38  */
39 public class VideoCallImpl extends VideoCall {
40 
41     private final IVideoProvider mVideoProvider;
42     private final VideoCallListenerBinder mBinder;
43     private VideoCall.Callback mCallback;
44     private int mVideoQuality = VideoProfile.QUALITY_UNKNOWN;
45     private Call mCall;
46 
47     private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
48         @Override
49         public void binderDied() {
50             mVideoProvider.asBinder().unlinkToDeath(this, 0);
51         }
52     };
53 
54     /**
55      * IVideoCallback stub implementation.
56      */
57     private final class VideoCallListenerBinder extends IVideoCallback.Stub {
58         @Override
receiveSessionModifyRequest(VideoProfile videoProfile)59         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
60             mHandler.obtainMessage(MessageHandler.MSG_RECEIVE_SESSION_MODIFY_REQUEST,
61                     videoProfile).sendToTarget();
62         }
63 
64         @Override
receiveSessionModifyResponse(int status, VideoProfile requestProfile, VideoProfile responseProfile)65         public void receiveSessionModifyResponse(int status, VideoProfile requestProfile,
66                 VideoProfile responseProfile) {
67             SomeArgs args = SomeArgs.obtain();
68             args.arg1 = status;
69             args.arg2 = requestProfile;
70             args.arg3 = responseProfile;
71             mHandler.obtainMessage(MessageHandler.MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args)
72                     .sendToTarget();
73         }
74 
75         @Override
handleCallSessionEvent(int event)76         public void handleCallSessionEvent(int event) {
77             mHandler.obtainMessage(MessageHandler.MSG_HANDLE_CALL_SESSION_EVENT, event)
78                     .sendToTarget();
79         }
80 
81         @Override
changePeerDimensions(int width, int height)82         public void changePeerDimensions(int width, int height) {
83             SomeArgs args = SomeArgs.obtain();
84             args.arg1 = width;
85             args.arg2 = height;
86             mHandler.obtainMessage(MessageHandler.MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
87         }
88 
89         @Override
changeVideoQuality(int videoQuality)90         public void changeVideoQuality(int videoQuality) {
91             mHandler.obtainMessage(MessageHandler.MSG_CHANGE_VIDEO_QUALITY, videoQuality, 0)
92                     .sendToTarget();
93         }
94 
95         @Override
changeCallDataUsage(long dataUsage)96         public void changeCallDataUsage(long dataUsage) {
97             mHandler.obtainMessage(MessageHandler.MSG_CHANGE_CALL_DATA_USAGE, dataUsage)
98                     .sendToTarget();
99         }
100 
101         @Override
changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities)102         public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
103             mHandler.obtainMessage(MessageHandler.MSG_CHANGE_CAMERA_CAPABILITIES,
104                     cameraCapabilities).sendToTarget();
105         }
106     }
107 
108     /** Default handler used to consolidate binder method calls onto a single thread. */
109     private final class MessageHandler extends Handler {
110         private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
111         private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
112         private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
113         private static final int MSG_CHANGE_PEER_DIMENSIONS = 4;
114         private static final int MSG_CHANGE_CALL_DATA_USAGE = 5;
115         private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 6;
116         private static final int MSG_CHANGE_VIDEO_QUALITY = 7;
117 
MessageHandler(Looper looper)118         public MessageHandler(Looper looper) {
119             super(looper);
120         }
121 
122         @Override
handleMessage(Message msg)123         public void handleMessage(Message msg) {
124             if (mCallback == null) {
125                 return;
126             }
127 
128             SomeArgs args;
129             switch (msg.what) {
130                 case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
131                     mCallback.onSessionModifyRequestReceived((VideoProfile) msg.obj);
132                     break;
133                 case MSG_RECEIVE_SESSION_MODIFY_RESPONSE:
134                     args = (SomeArgs) msg.obj;
135                     try {
136                         int status = (int) args.arg1;
137                         VideoProfile requestProfile = (VideoProfile) args.arg2;
138                         VideoProfile responseProfile = (VideoProfile) args.arg3;
139 
140                         mCallback.onSessionModifyResponseReceived(
141                                 status, requestProfile, responseProfile);
142                     } finally {
143                         args.recycle();
144                     }
145                     break;
146                 case MSG_HANDLE_CALL_SESSION_EVENT:
147                     mCallback.onCallSessionEvent((int) msg.obj);
148                     break;
149                 case MSG_CHANGE_PEER_DIMENSIONS:
150                     args = (SomeArgs) msg.obj;
151                     try {
152                         int width = (int) args.arg1;
153                         int height = (int) args.arg2;
154                         mCallback.onPeerDimensionsChanged(width, height);
155                     } finally {
156                         args.recycle();
157                     }
158                     break;
159                 case MSG_CHANGE_CALL_DATA_USAGE:
160                     mCallback.onCallDataUsageChanged((long) msg.obj);
161                     break;
162                 case MSG_CHANGE_CAMERA_CAPABILITIES:
163                     mCallback.onCameraCapabilitiesChanged(
164                             (VideoProfile.CameraCapabilities) msg.obj);
165                     break;
166                 case MSG_CHANGE_VIDEO_QUALITY:
167                     mVideoQuality = msg.arg1;
168                     mCallback.onVideoQualityChanged(msg.arg1);
169                     break;
170                 default:
171                     break;
172             }
173         }
174     };
175 
176     private Handler mHandler;
177 
VideoCallImpl(IVideoProvider videoProvider, Call call)178     VideoCallImpl(IVideoProvider videoProvider, Call call) throws RemoteException {
179         mVideoProvider = videoProvider;
180         mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
181 
182         mBinder = new VideoCallListenerBinder();
183         mVideoProvider.addVideoCallback(mBinder);
184         mCall = call;
185     }
186 
destroy()187     public void destroy() {
188         unregisterCallback(mCallback);
189     }
190 
191     /** {@inheritDoc} */
registerCallback(VideoCall.Callback callback)192     public void registerCallback(VideoCall.Callback callback) {
193         registerCallback(callback, null);
194     }
195 
196     /** {@inheritDoc} */
registerCallback(VideoCall.Callback callback, Handler handler)197     public void registerCallback(VideoCall.Callback callback, Handler handler) {
198         mCallback = callback;
199         if (handler == null) {
200             mHandler = new MessageHandler(Looper.getMainLooper());
201         } else {
202             mHandler = new MessageHandler(handler.getLooper());
203         }
204     }
205 
206     /** {@inheritDoc} */
unregisterCallback(VideoCall.Callback callback)207     public void unregisterCallback(VideoCall.Callback callback) {
208         if (callback != mCallback) {
209             return;
210         }
211 
212         mCallback = null;
213         try {
214             mVideoProvider.removeVideoCallback(mBinder);
215         } catch (RemoteException e) {
216         }
217     }
218 
219     /** {@inheritDoc} */
setCamera(String cameraId)220     public void setCamera(String cameraId) {
221         try {
222             mVideoProvider.setCamera(cameraId);
223         } catch (RemoteException e) {
224         }
225     }
226 
227     /** {@inheritDoc} */
setPreviewSurface(Surface surface)228     public void setPreviewSurface(Surface surface) {
229         try {
230             mVideoProvider.setPreviewSurface(surface);
231         } catch (RemoteException e) {
232         }
233     }
234 
235     /** {@inheritDoc} */
setDisplaySurface(Surface surface)236     public void setDisplaySurface(Surface surface) {
237         try {
238             mVideoProvider.setDisplaySurface(surface);
239         } catch (RemoteException e) {
240         }
241     }
242 
243     /** {@inheritDoc} */
setDeviceOrientation(int rotation)244     public void setDeviceOrientation(int rotation) {
245         try {
246             mVideoProvider.setDeviceOrientation(rotation);
247         } catch (RemoteException e) {
248         }
249     }
250 
251     /** {@inheritDoc} */
setZoom(float value)252     public void setZoom(float value) {
253         try {
254             mVideoProvider.setZoom(value);
255         } catch (RemoteException e) {
256         }
257     }
258 
259     /**
260      * Sends a session modification request to the video provider.
261      * <p>
262      * The {@link InCallService} will create the {@code requestProfile} based on the current
263      * video state (i.e. {@link Call.Details#getVideoState()}).  It is, however, possible that the
264      * video state maintained by the {@link InCallService} could get out of sync with what is known
265      * by the {@link android.telecom.Connection.VideoProvider}.  To remove ambiguity, the
266      * {@link VideoCallImpl} passes along the pre-modify video profile to the {@code VideoProvider}
267      * to ensure it has full context of the requested change.
268      *
269      * @param requestProfile The requested video profile.
270      */
sendSessionModifyRequest(VideoProfile requestProfile)271     public void sendSessionModifyRequest(VideoProfile requestProfile) {
272         try {
273             VideoProfile originalProfile = new VideoProfile(mCall.getDetails().getVideoState(),
274                     mVideoQuality);
275 
276             mVideoProvider.sendSessionModifyRequest(originalProfile, requestProfile);
277         } catch (RemoteException e) {
278         }
279     }
280 
281     /** {@inheritDoc} */
sendSessionModifyResponse(VideoProfile responseProfile)282     public void sendSessionModifyResponse(VideoProfile responseProfile) {
283         try {
284             mVideoProvider.sendSessionModifyResponse(responseProfile);
285         } catch (RemoteException e) {
286         }
287     }
288 
289     /** {@inheritDoc} */
requestCameraCapabilities()290     public void requestCameraCapabilities() {
291         try {
292             mVideoProvider.requestCameraCapabilities();
293         } catch (RemoteException e) {
294         }
295     }
296 
297     /** {@inheritDoc} */
requestCallDataUsage()298     public void requestCallDataUsage() {
299         try {
300             mVideoProvider.requestCallDataUsage();
301         } catch (RemoteException e) {
302         }
303     }
304 
305     /** {@inheritDoc} */
setPauseImage(Uri uri)306     public void setPauseImage(Uri uri) {
307         try {
308             mVideoProvider.setPauseImage(uri);
309         } catch (RemoteException e) {
310         }
311     }
312 }
313