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