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