• 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                             requestProfile.getVideoState());
191                 }
192                 VideoProviderProxy.this.receiveSessionModifyResponse(status, requestProfile,
193                         responseProfile);
194             }
195         }
196 
197         /**
198          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
199          * {@link InCallService} when a call session event occurs.
200          *
201          * @param event The call session event.
202          */
203         @Override
handleCallSessionEvent(int event)204         public void handleCallSessionEvent(int event) {
205             synchronized (mLock) {
206                 logFromVideoProvider("handleCallSessionEvent: " + event);
207                 VideoProviderProxy.this.handleCallSessionEvent(event);
208             }
209         }
210 
211         /**
212          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
213          * {@link InCallService} when the peer dimensions change.
214          *
215          * @param width The width of the peer's video.
216          * @param height The height of the peer's video.
217          */
218         @Override
changePeerDimensions(int width, int height)219         public void changePeerDimensions(int width, int height) {
220             synchronized (mLock) {
221                 logFromVideoProvider("changePeerDimensions: width=" + width + " height=" +
222                         height);
223                 VideoProviderProxy.this.changePeerDimensions(width, height);
224             }
225         }
226 
227         /**
228          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
229          * {@link InCallService} when the video quality changes.
230          *
231          * @param videoQuality The video quality.
232          */
233         @Override
changeVideoQuality(int videoQuality)234         public void changeVideoQuality(int videoQuality) {
235             synchronized (mLock) {
236                 logFromVideoProvider("changeVideoQuality: " + videoQuality);
237                 VideoProviderProxy.this.changeVideoQuality(videoQuality);
238             }
239         }
240 
241         /**
242          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
243          * {@link InCallService} when the call data usage changes.
244          *
245          * Also tracks the current call data usage on the {@link Call} for use when writing to the
246          * call log.
247          *
248          * @param dataUsage The data usage.
249          */
250         @Override
changeCallDataUsage(long dataUsage)251         public void changeCallDataUsage(long dataUsage) {
252             synchronized (mLock) {
253                 logFromVideoProvider("changeCallDataUsage: " + dataUsage);
254                 VideoProviderProxy.this.setCallDataUsage(dataUsage);
255                 mCall.setCallDataUsage(dataUsage);
256             }
257         }
258 
259         /**
260          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
261          * {@link InCallService} when the camera capabilities change.
262          *
263          * @param cameraCapabilities The camera capabilities.
264          */
265         @Override
changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities)266         public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
267             synchronized (mLock) {
268                 logFromVideoProvider("changeCameraCapabilities: " + cameraCapabilities);
269                 VideoProviderProxy.this.changeCameraCapabilities(cameraCapabilities);
270             }
271         }
272     }
273 
274     /**
275      * Proxies a request from the {@link InCallService} to the
276      * {@link #mConectionServiceVideoProvider} to change the camera.
277      *
278      * @param cameraId The id of the camera.
279      */
280     @Override
onSetCamera(String cameraId)281     public void onSetCamera(String cameraId) {
282         synchronized (mLock) {
283             logFromInCall("setCamera: " + cameraId);
284             try {
285                 mConectionServiceVideoProvider.setCamera(cameraId);
286             } catch (RemoteException e) {
287             }
288         }
289     }
290 
291     /**
292      * Proxies a request from the {@link InCallService} to the
293      * {@link #mConectionServiceVideoProvider} to set the preview surface.
294      *
295      * @param surface The surface.
296      */
297     @Override
onSetPreviewSurface(Surface surface)298     public void onSetPreviewSurface(Surface surface) {
299         synchronized (mLock) {
300             logFromInCall("setPreviewSurface");
301             try {
302                 mConectionServiceVideoProvider.setPreviewSurface(surface);
303             } catch (RemoteException e) {
304             }
305         }
306     }
307 
308     /**
309      * Proxies a request from the {@link InCallService} to the
310      * {@link #mConectionServiceVideoProvider} to change the display surface.
311      *
312      * @param surface The surface.
313      */
314     @Override
onSetDisplaySurface(Surface surface)315     public void onSetDisplaySurface(Surface surface) {
316         synchronized (mLock) {
317             logFromInCall("setDisplaySurface");
318             try {
319                 mConectionServiceVideoProvider.setDisplaySurface(surface);
320             } catch (RemoteException e) {
321             }
322         }
323     }
324 
325     /**
326      * Proxies a request from the {@link InCallService} to the
327      * {@link #mConectionServiceVideoProvider} to change the device orientation.
328      *
329      * @param rotation The device orientation, in degrees.
330      */
331     @Override
onSetDeviceOrientation(int rotation)332     public void onSetDeviceOrientation(int rotation) {
333         synchronized (mLock) {
334             logFromInCall("setDeviceOrientation: " + rotation);
335             try {
336                 mConectionServiceVideoProvider.setDeviceOrientation(rotation);
337             } catch (RemoteException e) {
338             }
339         }
340     }
341 
342     /**
343      * Proxies a request from the {@link InCallService} to the
344      * {@link #mConectionServiceVideoProvider} to change the camera zoom ratio.
345      *
346      * @param value The camera zoom ratio.
347      */
348     @Override
onSetZoom(float value)349     public void onSetZoom(float value) {
350         synchronized (mLock) {
351             logFromInCall("setZoom: " + value);
352             try {
353                 mConectionServiceVideoProvider.setZoom(value);
354             } catch (RemoteException e) {
355             }
356         }
357     }
358 
359     /**
360      * Proxies a request from the {@link InCallService} to the
361      * {@link #mConectionServiceVideoProvider} to provide a response to a session modification
362      * request.
363      *
364      * @param fromProfile The video properties prior to the request.
365      * @param toProfile The video properties with the requested changes made.
366      */
367     @Override
onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile)368     public void onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
369         synchronized (mLock) {
370             logFromInCall("sendSessionModifyRequest: from=" + fromProfile + " to=" + toProfile);
371             Log.event(mCall, Log.Events.SEND_VIDEO_REQUEST,
372                     VideoProfile.videoStateToString(toProfile.getVideoState()));
373             mCall.getAnalytics().addVideoEvent(
374                     Analytics.SEND_LOCAL_SESSION_MODIFY_REQUEST,
375                     toProfile.getVideoState());
376             try {
377                 mConectionServiceVideoProvider.sendSessionModifyRequest(fromProfile, toProfile);
378             } catch (RemoteException e) {
379             }
380         }
381     }
382 
383     /**
384      * Proxies a request from the {@link InCallService} to the
385      * {@link #mConectionServiceVideoProvider} to send a session modification request.
386      *
387      * @param responseProfile The response connection video properties.
388      */
389     @Override
onSendSessionModifyResponse(VideoProfile responseProfile)390     public void onSendSessionModifyResponse(VideoProfile responseProfile) {
391         synchronized (mLock) {
392             logFromInCall("sendSessionModifyResponse: " + responseProfile);
393             Log.event(mCall, Log.Events.SEND_VIDEO_RESPONSE,
394                     VideoProfile.videoStateToString(responseProfile.getVideoState()));
395             mCall.getAnalytics().addVideoEvent(
396                     Analytics.SEND_LOCAL_SESSION_MODIFY_RESPONSE,
397                     responseProfile.getVideoState());
398             try {
399                 mConectionServiceVideoProvider.sendSessionModifyResponse(responseProfile);
400             } catch (RemoteException e) {
401             }
402         }
403     }
404 
405     /**
406      * Proxies a request from the {@link InCallService} to the
407      * {@link #mConectionServiceVideoProvider} to request the camera capabilities.
408      */
409     @Override
onRequestCameraCapabilities()410     public void onRequestCameraCapabilities() {
411         synchronized (mLock) {
412             logFromInCall("requestCameraCapabilities");
413             try {
414                 mConectionServiceVideoProvider.requestCameraCapabilities();
415             } catch (RemoteException e) {
416             }
417         }
418     }
419 
420     /**
421      * Proxies a request from the {@link InCallService} to the
422      * {@link #mConectionServiceVideoProvider} to request the connection data usage.
423      */
424     @Override
onRequestConnectionDataUsage()425     public void onRequestConnectionDataUsage() {
426         synchronized (mLock) {
427             logFromInCall("requestCallDataUsage");
428             try {
429                 mConectionServiceVideoProvider.requestCallDataUsage();
430             } catch (RemoteException e) {
431             }
432         }
433     }
434 
435     /**
436      * Proxies a request from the {@link InCallService} to the
437      * {@link #mConectionServiceVideoProvider} to set the pause image.
438      *
439      * @param uri URI of image to display.
440      */
441     @Override
onSetPauseImage(Uri uri)442     public void onSetPauseImage(Uri uri) {
443         synchronized (mLock) {
444             logFromInCall("setPauseImage: " + uri);
445             try {
446                 mConectionServiceVideoProvider.setPauseImage(uri);
447             } catch (RemoteException e) {
448             }
449         }
450     }
451 
452     /**
453      * Add a listener to this {@link VideoProviderProxy}.
454      *
455      * @param listener The listener.
456      */
addListener(Listener listener)457     public void addListener(Listener listener) {
458         mListeners.add(listener);
459     }
460 
461     /**
462      * Remove a listener from this {@link VideoProviderProxy}.
463      *
464      * @param listener The listener.
465      */
removeListener(Listener listener)466     public void removeListener(Listener listener) {
467         if (listener != null) {
468             mListeners.remove(listener);
469         }
470     }
471 
472     /**
473      * Logs a message originating from the {@link InCallService}.
474      *
475      * @param toLog The message to log.
476      */
logFromInCall(String toLog)477     private void logFromInCall(String toLog) {
478         Log.v(this, "IC->VP: " + toLog);
479     }
480 
481     /**
482      * Logs a message originating from the {@link android.telecom.ConnectionService}'s
483      * {@link Connection.VideoProvider}.
484      *
485      * @param toLog The message to log.
486      */
logFromVideoProvider(String toLog)487     private void logFromVideoProvider(String toLog) {
488         Log.v(this, "VP->IC: " + toLog);
489     }
490 }
491