Home | History | Annotate | Download | only in telecom
      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