• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.server.telecom;
18 
19 import android.net.Uri;
20 import android.os.IBinder;
21 import android.os.Looper;
22 import android.os.RemoteException;
23 import android.telecom.Connection;
24 import android.telecom.InCallService;
25 import android.telecom.VideoProfile;
26 import android.view.Surface;
27 
28 import com.android.internal.telecom.IVideoCallback;
29 import com.android.internal.telecom.IVideoProvider;
30 
31 import java.util.Collections;
32 import java.util.Set;
33 import java.util.concurrent.ConcurrentHashMap;
34 
35 /**
36  * Proxies video provider messages from {@link InCallService.VideoCall}
37  * implementations to the underlying {@link Connection.VideoProvider} implementation.  Also proxies
38  * callbacks from the {@link Connection.VideoProvider} to {@link InCallService.VideoCall}
39  * implementations.
40  *
41  * Also provides a means for Telecom to send and receive these messages.
42  */
43 public class VideoProviderProxy extends Connection.VideoProvider {
44 
45     /**
46      * Listener for Telecom components interested in callbacks from the video provider.
47      */
48     interface Listener {
onSessionModifyRequestReceived(Call call, VideoProfile videoProfile)49         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
50     }
51 
52     /**
53      * Set of listeners on this VideoProviderProxy.
54      *
55      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
56      * load factor before resizing, 1 means we only expect a single thread to
57      * access the map so make only a single shard
58      */
59     private final Set<Listener> mListeners = Collections.newSetFromMap(
60             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
61 
62     /** The TelecomSystem SyncRoot used for synchronized operations. */
63     private final TelecomSystem.SyncRoot mLock;
64 
65     /**
66      * The {@link android.telecom.Connection.VideoProvider} implementation residing with the
67      * {@link android.telecom.ConnectionService} which is being wrapped by this
68      * {@link VideoProviderProxy}.
69      */
70     private final IVideoProvider mConectionServiceVideoProvider;
71 
72     /**
73      * Binder used to bind to the {@link android.telecom.ConnectionService}'s
74      * {@link com.android.internal.telecom.IVideoCallback}.
75      */
76     private final VideoCallListenerBinder mVideoCallListenerBinder;
77 
78     /**
79      * The Telecom {@link Call} this {@link VideoProviderProxy} is associated with.
80      */
81     private Call mCall;
82 
83     private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
84         @Override
85         public void binderDied() {
86             mConectionServiceVideoProvider.asBinder().unlinkToDeath(this, 0);
87         }
88     };
89 
90     /**
91      * Creates a new instance of the {@link VideoProviderProxy}, binding it to the passed in
92      * {@code videoProvider} residing with the {@link android.telecom.ConnectionService}.
93      *
94      *
95      * @param lock
96      * @param videoProvider The {@link android.telecom.ConnectionService}'s video provider.
97      * @param call The current call.
98      * @throws RemoteException Remote exception.
99      */
VideoProviderProxy(TelecomSystem.SyncRoot lock, IVideoProvider videoProvider, Call call)100     VideoProviderProxy(TelecomSystem.SyncRoot lock,
101             IVideoProvider videoProvider, Call call) throws RemoteException {
102 
103         super(Looper.getMainLooper());
104 
105         mLock = lock;
106 
107         mConectionServiceVideoProvider = videoProvider;
108         mConectionServiceVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
109 
110         mVideoCallListenerBinder = new VideoCallListenerBinder();
111         mConectionServiceVideoProvider.addVideoCallback(mVideoCallListenerBinder);
112         mCall = call;
113     }
114 
115     /**
116      * IVideoCallback stub implementation.  An instance of this class receives callbacks from the
117      * {@code ConnectionService}'s video provider.
118      */
119     private final class VideoCallListenerBinder extends IVideoCallback.Stub {
120         /**
121          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
122          * {@link InCallService} when a session modification request is received.
123          *
124          * @param videoProfile The requested video profile.
125          */
126         @Override
receiveSessionModifyRequest(VideoProfile videoProfile)127         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
128             try {
129                 Log.startSession("VPP.rSMR");
130                 synchronized (mLock) {
131                     logFromVideoProvider("receiveSessionModifyRequest: " + videoProfile);
132                     Log.event(mCall, Log.Events.RECEIVE_VIDEO_REQUEST,
133                             VideoProfile.videoStateToString(videoProfile.getVideoState()));
134 
135                     mCall.getAnalytics().addVideoEvent(
136                             Analytics.RECEIVE_REMOTE_SESSION_MODIFY_REQUEST,
137                             videoProfile.getVideoState());
138 
139                     if (!mCall.isVideoCallingSupported() &&
140                             VideoProfile.isVideo(videoProfile.getVideoState())) {
141                         // If video calling is not supported by the phone account, and we receive
142                         // a request to upgrade to video, automatically reject it without informing
143                         // the InCallService.
144 
145                         Log.event(mCall, Log.Events.SEND_VIDEO_RESPONSE, "video not supported");
146                         VideoProfile responseProfile = new VideoProfile(
147                                 VideoProfile.STATE_AUDIO_ONLY);
148                         try {
149                             mConectionServiceVideoProvider.sendSessionModifyResponse(
150                                     responseProfile);
151                         } catch (RemoteException e) {
152                         }
153 
154                         // Don't want to inform listeners of the request as we've just rejected it.
155                         return;
156                     }
157 
158                     // Inform other Telecom components of the session modification request.
159                     for (Listener listener : mListeners) {
160                         listener.onSessionModifyRequestReceived(mCall, videoProfile);
161                     }
162 
163                     VideoProviderProxy.this.receiveSessionModifyRequest(videoProfile);
164                 }
165             } finally {
166                 Log.endSession();
167             }
168         }
169 
170         /**
171          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
172          * {@link InCallService} when a session modification response is received.
173          *
174          * @param status The status of the response.
175          * @param requestProfile The requested video profile.
176          * @param responseProfile The response video profile.
177          */
178         @Override
receiveSessionModifyResponse(int status, VideoProfile requestProfile, VideoProfile responseProfile)179         public void receiveSessionModifyResponse(int status, VideoProfile requestProfile,
180                 VideoProfile responseProfile) {
181             logFromVideoProvider("receiveSessionModifyResponse: status=" + status +
182                     " requestProfile=" + requestProfile + " responseProfile=" + responseProfile);
183             String eventMessage = "Status Code : " + status + " Video State: " +
184                     (responseProfile != null ? responseProfile.getVideoState() : "null");
185             Log.event(mCall, Log.Events.RECEIVE_VIDEO_RESPONSE, eventMessage);
186             synchronized (mLock) {
187                 if (status == Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
188                     mCall.getAnalytics().addVideoEvent(
189                             Analytics.RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE,
190                             responseProfile == null ?
191                                     VideoProfile.STATE_AUDIO_ONLY :
192                                     responseProfile.getVideoState());
193                 }
194                 VideoProviderProxy.this.receiveSessionModifyResponse(status, requestProfile,
195                         responseProfile);
196             }
197         }
198 
199         /**
200          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
201          * {@link InCallService} when a call session event occurs.
202          *
203          * @param event The call session event.
204          */
205         @Override
handleCallSessionEvent(int event)206         public void handleCallSessionEvent(int event) {
207             synchronized (mLock) {
208                 logFromVideoProvider("handleCallSessionEvent: " +
209                         Connection.VideoProvider.sessionEventToString(event));
210                 VideoProviderProxy.this.handleCallSessionEvent(event);
211             }
212         }
213 
214         /**
215          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
216          * {@link InCallService} when the peer dimensions change.
217          *
218          * @param width The width of the peer's video.
219          * @param height The height of the peer's video.
220          */
221         @Override
changePeerDimensions(int width, int height)222         public void changePeerDimensions(int width, int height) {
223             synchronized (mLock) {
224                 logFromVideoProvider("changePeerDimensions: width=" + width + " height=" +
225                         height);
226                 VideoProviderProxy.this.changePeerDimensions(width, height);
227             }
228         }
229 
230         /**
231          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
232          * {@link InCallService} when the video quality changes.
233          *
234          * @param videoQuality The video quality.
235          */
236         @Override
changeVideoQuality(int videoQuality)237         public void changeVideoQuality(int videoQuality) {
238             synchronized (mLock) {
239                 logFromVideoProvider("changeVideoQuality: " + videoQuality);
240                 VideoProviderProxy.this.changeVideoQuality(videoQuality);
241             }
242         }
243 
244         /**
245          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
246          * {@link InCallService} when the call data usage changes.
247          *
248          * Also tracks the current call data usage on the {@link Call} for use when writing to the
249          * call log.
250          *
251          * @param dataUsage The data usage.
252          */
253         @Override
changeCallDataUsage(long dataUsage)254         public void changeCallDataUsage(long dataUsage) {
255             synchronized (mLock) {
256                 logFromVideoProvider("changeCallDataUsage: " + dataUsage);
257                 VideoProviderProxy.this.setCallDataUsage(dataUsage);
258                 mCall.setCallDataUsage(dataUsage);
259             }
260         }
261 
262         /**
263          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
264          * {@link InCallService} when the camera capabilities change.
265          *
266          * @param cameraCapabilities The camera capabilities.
267          */
268         @Override
changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities)269         public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
270             synchronized (mLock) {
271                 logFromVideoProvider("changeCameraCapabilities: " + cameraCapabilities);
272                 VideoProviderProxy.this.changeCameraCapabilities(cameraCapabilities);
273             }
274         }
275     }
276 
277     /**
278      * Proxies a request from the {@link InCallService} to the
279      * {@link #mConectionServiceVideoProvider} to change the camera.
280      *
281      * @param cameraId The id of the camera.
282      */
283     @Override
onSetCamera(String cameraId)284     public void onSetCamera(String cameraId) {
285         synchronized (mLock) {
286             logFromInCall("setCamera: " + cameraId);
287             try {
288                 mConectionServiceVideoProvider.setCamera(cameraId);
289             } catch (RemoteException e) {
290             }
291         }
292     }
293 
294     /**
295      * Proxies a request from the {@link InCallService} to the
296      * {@link #mConectionServiceVideoProvider} to set the preview surface.
297      *
298      * @param surface The surface.
299      */
300     @Override
onSetPreviewSurface(Surface surface)301     public void onSetPreviewSurface(Surface surface) {
302         synchronized (mLock) {
303             logFromInCall("setPreviewSurface");
304             try {
305                 mConectionServiceVideoProvider.setPreviewSurface(surface);
306             } catch (RemoteException e) {
307             }
308         }
309     }
310 
311     /**
312      * Proxies a request from the {@link InCallService} to the
313      * {@link #mConectionServiceVideoProvider} to change the display surface.
314      *
315      * @param surface The surface.
316      */
317     @Override
onSetDisplaySurface(Surface surface)318     public void onSetDisplaySurface(Surface surface) {
319         synchronized (mLock) {
320             logFromInCall("setDisplaySurface");
321             try {
322                 mConectionServiceVideoProvider.setDisplaySurface(surface);
323             } catch (RemoteException e) {
324             }
325         }
326     }
327 
328     /**
329      * Proxies a request from the {@link InCallService} to the
330      * {@link #mConectionServiceVideoProvider} to change the device orientation.
331      *
332      * @param rotation The device orientation, in degrees.
333      */
334     @Override
onSetDeviceOrientation(int rotation)335     public void onSetDeviceOrientation(int rotation) {
336         synchronized (mLock) {
337             logFromInCall("setDeviceOrientation: " + rotation);
338             try {
339                 mConectionServiceVideoProvider.setDeviceOrientation(rotation);
340             } catch (RemoteException e) {
341             }
342         }
343     }
344 
345     /**
346      * Proxies a request from the {@link InCallService} to the
347      * {@link #mConectionServiceVideoProvider} to change the camera zoom ratio.
348      *
349      * @param value The camera zoom ratio.
350      */
351     @Override
onSetZoom(float value)352     public void onSetZoom(float value) {
353         synchronized (mLock) {
354             logFromInCall("setZoom: " + value);
355             try {
356                 mConectionServiceVideoProvider.setZoom(value);
357             } catch (RemoteException e) {
358             }
359         }
360     }
361 
362     /**
363      * Proxies a request from the {@link InCallService} to the
364      * {@link #mConectionServiceVideoProvider} to provide a response to a session modification
365      * request.
366      *
367      * @param fromProfile The video properties prior to the request.
368      * @param toProfile The video properties with the requested changes made.
369      */
370     @Override
onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile)371     public void onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
372         synchronized (mLock) {
373             logFromInCall("sendSessionModifyRequest: from=" + fromProfile + " to=" + toProfile);
374             Log.event(mCall, Log.Events.SEND_VIDEO_REQUEST,
375                     VideoProfile.videoStateToString(toProfile.getVideoState()));
376             mCall.getAnalytics().addVideoEvent(
377                     Analytics.SEND_LOCAL_SESSION_MODIFY_REQUEST,
378                     toProfile.getVideoState());
379             try {
380                 mConectionServiceVideoProvider.sendSessionModifyRequest(fromProfile, toProfile);
381             } catch (RemoteException e) {
382             }
383         }
384     }
385 
386     /**
387      * Proxies a request from the {@link InCallService} to the
388      * {@link #mConectionServiceVideoProvider} to send a session modification request.
389      *
390      * @param responseProfile The response connection video properties.
391      */
392     @Override
onSendSessionModifyResponse(VideoProfile responseProfile)393     public void onSendSessionModifyResponse(VideoProfile responseProfile) {
394         synchronized (mLock) {
395             logFromInCall("sendSessionModifyResponse: " + responseProfile);
396             Log.event(mCall, Log.Events.SEND_VIDEO_RESPONSE,
397                     VideoProfile.videoStateToString(responseProfile.getVideoState()));
398             mCall.getAnalytics().addVideoEvent(
399                     Analytics.SEND_LOCAL_SESSION_MODIFY_RESPONSE,
400                     responseProfile.getVideoState());
401             try {
402                 mConectionServiceVideoProvider.sendSessionModifyResponse(responseProfile);
403             } catch (RemoteException e) {
404             }
405         }
406     }
407 
408     /**
409      * Proxies a request from the {@link InCallService} to the
410      * {@link #mConectionServiceVideoProvider} to request the camera capabilities.
411      */
412     @Override
onRequestCameraCapabilities()413     public void onRequestCameraCapabilities() {
414         synchronized (mLock) {
415             logFromInCall("requestCameraCapabilities");
416             try {
417                 mConectionServiceVideoProvider.requestCameraCapabilities();
418             } catch (RemoteException e) {
419             }
420         }
421     }
422 
423     /**
424      * Proxies a request from the {@link InCallService} to the
425      * {@link #mConectionServiceVideoProvider} to request the connection data usage.
426      */
427     @Override
onRequestConnectionDataUsage()428     public void onRequestConnectionDataUsage() {
429         synchronized (mLock) {
430             logFromInCall("requestCallDataUsage");
431             try {
432                 mConectionServiceVideoProvider.requestCallDataUsage();
433             } catch (RemoteException e) {
434             }
435         }
436     }
437 
438     /**
439      * Proxies a request from the {@link InCallService} to the
440      * {@link #mConectionServiceVideoProvider} to set the pause image.
441      *
442      * @param uri URI of image to display.
443      */
444     @Override
onSetPauseImage(Uri uri)445     public void onSetPauseImage(Uri uri) {
446         synchronized (mLock) {
447             logFromInCall("setPauseImage: " + uri);
448             try {
449                 mConectionServiceVideoProvider.setPauseImage(uri);
450             } catch (RemoteException e) {
451             }
452         }
453     }
454 
455     /**
456      * Add a listener to this {@link VideoProviderProxy}.
457      *
458      * @param listener The listener.
459      */
addListener(Listener listener)460     public void addListener(Listener listener) {
461         mListeners.add(listener);
462     }
463 
464     /**
465      * Remove a listener from this {@link VideoProviderProxy}.
466      *
467      * @param listener The listener.
468      */
removeListener(Listener listener)469     public void removeListener(Listener listener) {
470         if (listener != null) {
471             mListeners.remove(listener);
472         }
473     }
474 
475     /**
476      * Logs a message originating from the {@link InCallService}.
477      *
478      * @param toLog The message to log.
479      */
logFromInCall(String toLog)480     private void logFromInCall(String toLog) {
481         Log.i(this, "IC->VP (callId=" + (mCall == null ? "?" : mCall.getId()) + "): " + toLog);
482     }
483 
484     /**
485      * Logs a message originating from the {@link android.telecom.ConnectionService}'s
486      * {@link Connection.VideoProvider}.
487      *
488      * @param toLog The message to log.
489      */
logFromVideoProvider(String toLog)490     private void logFromVideoProvider(String toLog) {
491         Log.i(this, "VP->IC (callId=" + (mCall == null ? "?" : mCall.getId()) + "): " + toLog);
492     }
493 }
494