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