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