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