Home | History | Annotate | Download | only in telecom
      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.Set;
     33 import java.util.concurrent.ConcurrentHashMap;
     34 
     35 /**
     36  * Proxies video provider messages from {@link InCallService.VideoCall}
     37  * implementations to the underlying {@link Connection.VideoProvider} implementation.  Also proxies
     38  * callbacks from the {@link Connection.VideoProvider} to {@link InCallService.VideoCall}
     39  * implementations.
     40  *
     41  * Also provides a means for Telecom to send and receive these messages.
     42  */
     43 public class VideoProviderProxy extends Connection.VideoProvider {
     44 
     45     /**
     46      * Listener for Telecom components interested in callbacks from the video provider.
     47      */
     48     interface Listener {
     49         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
     50     }
     51 
     52     /**
     53      * Set of listeners on this VideoProviderProxy.
     54      *
     55      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
     56      * load factor before resizing, 1 means we only expect a single thread to
     57      * access the map so make only a single shard
     58      */
     59     private final Set<Listener> mListeners = Collections.newSetFromMap(
     60             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
     61 
     62     /** The TelecomSystem SyncRoot used for synchronized operations. */
     63     private final TelecomSystem.SyncRoot mLock;
     64 
     65     /**
     66      * The {@link android.telecom.Connection.VideoProvider} implementation residing with the
     67      * {@link android.telecom.ConnectionService} which is being wrapped by this
     68      * {@link VideoProviderProxy}.
     69      */
     70     private final IVideoProvider mConectionServiceVideoProvider;
     71 
     72     /**
     73      * Binder used to bind to the {@link android.telecom.ConnectionService}'s
     74      * {@link com.android.internal.telecom.IVideoCallback}.
     75      */
     76     private final VideoCallListenerBinder mVideoCallListenerBinder;
     77 
     78     /**
     79      * The Telecom {@link Call} this {@link VideoProviderProxy} is associated with.
     80      */
     81     private Call mCall;
     82 
     83     private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
     84         @Override
     85         public void binderDied() {
     86             mConectionServiceVideoProvider.asBinder().unlinkToDeath(this, 0);
     87         }
     88     };
     89 
     90     /**
     91      * Creates a new instance of the {@link VideoProviderProxy}, binding it to the passed in
     92      * {@code videoProvider} residing with the {@link android.telecom.ConnectionService}.
     93      *
     94      *
     95      * @param lock
     96      * @param videoProvider The {@link android.telecom.ConnectionService}'s video provider.
     97      * @param call The current call.
     98      * @throws RemoteException Remote exception.
     99      */
    100     VideoProviderProxy(TelecomSystem.SyncRoot lock,
    101             IVideoProvider videoProvider, Call call) throws RemoteException {
    102 
    103         super(Looper.getMainLooper());
    104 
    105         mLock = lock;
    106 
    107         mConectionServiceVideoProvider = videoProvider;
    108         mConectionServiceVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
    109 
    110         mVideoCallListenerBinder = new VideoCallListenerBinder();
    111         mConectionServiceVideoProvider.addVideoCallback(mVideoCallListenerBinder);
    112         mCall = call;
    113     }
    114 
    115     /**
    116      * IVideoCallback stub implementation.  An instance of this class receives callbacks from the
    117      * {@code ConnectionService}'s video provider.
    118      */
    119     private final class VideoCallListenerBinder extends IVideoCallback.Stub {
    120         /**
    121          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
    122          * {@link InCallService} when a session modification request is received.
    123          *
    124          * @param videoProfile The requested video profile.
    125          */
    126         @Override
    127         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
    128             try {
    129                 Log.startSession("VPP.rSMR");
    130                 synchronized (mLock) {
    131                     logFromVideoProvider("receiveSessionModifyRequest: " + videoProfile);
    132                     Log.event(mCall, Log.Events.RECEIVE_VIDEO_REQUEST,
    133                             VideoProfile.videoStateToString(videoProfile.getVideoState()));
    134 
    135                     mCall.getAnalytics().addVideoEvent(
    136                             Analytics.RECEIVE_REMOTE_SESSION_MODIFY_REQUEST,
    137                             videoProfile.getVideoState());
    138 
    139                     if (!mCall.isVideoCallingSupported() &&
    140                             VideoProfile.isVideo(videoProfile.getVideoState())) {
    141                         // If video calling is not supported by the phone account, and we receive
    142                         // a request to upgrade to video, automatically reject it without informing
    143                         // the InCallService.
    144 
    145                         Log.event(mCall, Log.Events.SEND_VIDEO_RESPONSE, "video not supported");
    146                         VideoProfile responseProfile = new VideoProfile(
    147                                 VideoProfile.STATE_AUDIO_ONLY);
    148                         try {
    149                             mConectionServiceVideoProvider.sendSessionModifyResponse(
    150                                     responseProfile);
    151                         } catch (RemoteException e) {
    152                         }
    153 
    154                         // Don't want to inform listeners of the request as we've just rejected it.
    155                         return;
    156                     }
    157 
    158                     // Inform other Telecom components of the session modification request.
    159                     for (Listener listener : mListeners) {
    160                         listener.onSessionModifyRequestReceived(mCall, videoProfile);
    161                     }
    162 
    163                     VideoProviderProxy.this.receiveSessionModifyRequest(videoProfile);
    164                 }
    165             } finally {
    166                 Log.endSession();
    167             }
    168         }
    169 
    170         /**
    171          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
    172          * {@link InCallService} when a session modification response is received.
    173          *
    174          * @param status The status of the response.
    175          * @param requestProfile The requested video profile.
    176          * @param responseProfile The response video profile.
    177          */
    178         @Override
    179         public void receiveSessionModifyResponse(int status, VideoProfile requestProfile,
    180                 VideoProfile responseProfile) {
    181             logFromVideoProvider("receiveSessionModifyResponse: status=" + status +
    182                     " requestProfile=" + requestProfile + " responseProfile=" + responseProfile);
    183             String eventMessage = "Status Code : " + status + " Video State: " +
    184                     (responseProfile != null ? responseProfile.getVideoState() : "null");
    185             Log.event(mCall, Log.Events.RECEIVE_VIDEO_RESPONSE, eventMessage);
    186             synchronized (mLock) {
    187                 if (status == Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
    188                     mCall.getAnalytics().addVideoEvent(
    189                             Analytics.RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE,
    190                             requestProfile.getVideoState());
    191                 }
    192                 VideoProviderProxy.this.receiveSessionModifyResponse(status, requestProfile,
    193                         responseProfile);
    194             }
    195         }
    196 
    197         /**
    198          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
    199          * {@link InCallService} when a call session event occurs.
    200          *
    201          * @param event The call session event.
    202          */
    203         @Override
    204         public void handleCallSessionEvent(int event) {
    205             synchronized (mLock) {
    206                 logFromVideoProvider("handleCallSessionEvent: " + event);
    207                 VideoProviderProxy.this.handleCallSessionEvent(event);
    208             }
    209         }
    210 
    211         /**
    212          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
    213          * {@link InCallService} when the peer dimensions change.
    214          *
    215          * @param width The width of the peer's video.
    216          * @param height The height of the peer's video.
    217          */
    218         @Override
    219         public void changePeerDimensions(int width, int height) {
    220             synchronized (mLock) {
    221                 logFromVideoProvider("changePeerDimensions: width=" + width + " height=" +
    222                         height);
    223                 VideoProviderProxy.this.changePeerDimensions(width, height);
    224             }
    225         }
    226 
    227         /**
    228          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
    229          * {@link InCallService} when the video quality changes.
    230          *
    231          * @param videoQuality The video quality.
    232          */
    233         @Override
    234         public void changeVideoQuality(int videoQuality) {
    235             synchronized (mLock) {
    236                 logFromVideoProvider("changeVideoQuality: " + videoQuality);
    237                 VideoProviderProxy.this.changeVideoQuality(videoQuality);
    238             }
    239         }
    240 
    241         /**
    242          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
    243          * {@link InCallService} when the call data usage changes.
    244          *
    245          * Also tracks the current call data usage on the {@link Call} for use when writing to the
    246          * call log.
    247          *
    248          * @param dataUsage The data usage.
    249          */
    250         @Override
    251         public void changeCallDataUsage(long dataUsage) {
    252             synchronized (mLock) {
    253                 logFromVideoProvider("changeCallDataUsage: " + dataUsage);
    254                 VideoProviderProxy.this.setCallDataUsage(dataUsage);
    255                 mCall.setCallDataUsage(dataUsage);
    256             }
    257         }
    258 
    259         /**
    260          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
    261          * {@link InCallService} when the camera capabilities change.
    262          *
    263          * @param cameraCapabilities The camera capabilities.
    264          */
    265         @Override
    266         public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
    267             synchronized (mLock) {
    268                 logFromVideoProvider("changeCameraCapabilities: " + cameraCapabilities);
    269                 VideoProviderProxy.this.changeCameraCapabilities(cameraCapabilities);
    270             }
    271         }
    272     }
    273 
    274     /**
    275      * Proxies a request from the {@link InCallService} to the
    276      * {@link #mConectionServiceVideoProvider} to change the camera.
    277      *
    278      * @param cameraId The id of the camera.
    279      */
    280     @Override
    281     public void onSetCamera(String cameraId) {
    282         synchronized (mLock) {
    283             logFromInCall("setCamera: " + cameraId);
    284             try {
    285                 mConectionServiceVideoProvider.setCamera(cameraId);
    286             } catch (RemoteException e) {
    287             }
    288         }
    289     }
    290 
    291     /**
    292      * Proxies a request from the {@link InCallService} to the
    293      * {@link #mConectionServiceVideoProvider} to set the preview surface.
    294      *
    295      * @param surface The surface.
    296      */
    297     @Override
    298     public void onSetPreviewSurface(Surface surface) {
    299         synchronized (mLock) {
    300             logFromInCall("setPreviewSurface");
    301             try {
    302                 mConectionServiceVideoProvider.setPreviewSurface(surface);
    303             } catch (RemoteException e) {
    304             }
    305         }
    306     }
    307 
    308     /**
    309      * Proxies a request from the {@link InCallService} to the
    310      * {@link #mConectionServiceVideoProvider} to change the display surface.
    311      *
    312      * @param surface The surface.
    313      */
    314     @Override
    315     public void onSetDisplaySurface(Surface surface) {
    316         synchronized (mLock) {
    317             logFromInCall("setDisplaySurface");
    318             try {
    319                 mConectionServiceVideoProvider.setDisplaySurface(surface);
    320             } catch (RemoteException e) {
    321             }
    322         }
    323     }
    324 
    325     /**
    326      * Proxies a request from the {@link InCallService} to the
    327      * {@link #mConectionServiceVideoProvider} to change the device orientation.
    328      *
    329      * @param rotation The device orientation, in degrees.
    330      */
    331     @Override
    332     public void onSetDeviceOrientation(int rotation) {
    333         synchronized (mLock) {
    334             logFromInCall("setDeviceOrientation: " + rotation);
    335             try {
    336                 mConectionServiceVideoProvider.setDeviceOrientation(rotation);
    337             } catch (RemoteException e) {
    338             }
    339         }
    340     }
    341 
    342     /**
    343      * Proxies a request from the {@link InCallService} to the
    344      * {@link #mConectionServiceVideoProvider} to change the camera zoom ratio.
    345      *
    346      * @param value The camera zoom ratio.
    347      */
    348     @Override
    349     public void onSetZoom(float value) {
    350         synchronized (mLock) {
    351             logFromInCall("setZoom: " + value);
    352             try {
    353                 mConectionServiceVideoProvider.setZoom(value);
    354             } catch (RemoteException e) {
    355             }
    356         }
    357     }
    358 
    359     /**
    360      * Proxies a request from the {@link InCallService} to the
    361      * {@link #mConectionServiceVideoProvider} to provide a response to a session modification
    362      * request.
    363      *
    364      * @param fromProfile The video properties prior to the request.
    365      * @param toProfile The video properties with the requested changes made.
    366      */
    367     @Override
    368     public void onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
    369         synchronized (mLock) {
    370             logFromInCall("sendSessionModifyRequest: from=" + fromProfile + " to=" + toProfile);
    371             Log.event(mCall, Log.Events.SEND_VIDEO_REQUEST,
    372                     VideoProfile.videoStateToString(toProfile.getVideoState()));
    373             mCall.getAnalytics().addVideoEvent(
    374                     Analytics.SEND_LOCAL_SESSION_MODIFY_REQUEST,
    375                     toProfile.getVideoState());
    376             try {
    377                 mConectionServiceVideoProvider.sendSessionModifyRequest(fromProfile, toProfile);
    378             } catch (RemoteException e) {
    379             }
    380         }
    381     }
    382 
    383     /**
    384      * Proxies a request from the {@link InCallService} to the
    385      * {@link #mConectionServiceVideoProvider} to send a session modification request.
    386      *
    387      * @param responseProfile The response connection video properties.
    388      */
    389     @Override
    390     public void onSendSessionModifyResponse(VideoProfile responseProfile) {
    391         synchronized (mLock) {
    392             logFromInCall("sendSessionModifyResponse: " + responseProfile);
    393             Log.event(mCall, Log.Events.SEND_VIDEO_RESPONSE,
    394                     VideoProfile.videoStateToString(responseProfile.getVideoState()));
    395             mCall.getAnalytics().addVideoEvent(
    396                     Analytics.SEND_LOCAL_SESSION_MODIFY_RESPONSE,
    397                     responseProfile.getVideoState());
    398             try {
    399                 mConectionServiceVideoProvider.sendSessionModifyResponse(responseProfile);
    400             } catch (RemoteException e) {
    401             }
    402         }
    403     }
    404 
    405     /**
    406      * Proxies a request from the {@link InCallService} to the
    407      * {@link #mConectionServiceVideoProvider} to request the camera capabilities.
    408      */
    409     @Override
    410     public void onRequestCameraCapabilities() {
    411         synchronized (mLock) {
    412             logFromInCall("requestCameraCapabilities");
    413             try {
    414                 mConectionServiceVideoProvider.requestCameraCapabilities();
    415             } catch (RemoteException e) {
    416             }
    417         }
    418     }
    419 
    420     /**
    421      * Proxies a request from the {@link InCallService} to the
    422      * {@link #mConectionServiceVideoProvider} to request the connection data usage.
    423      */
    424     @Override
    425     public void onRequestConnectionDataUsage() {
    426         synchronized (mLock) {
    427             logFromInCall("requestCallDataUsage");
    428             try {
    429                 mConectionServiceVideoProvider.requestCallDataUsage();
    430             } catch (RemoteException e) {
    431             }
    432         }
    433     }
    434 
    435     /**
    436      * Proxies a request from the {@link InCallService} to the
    437      * {@link #mConectionServiceVideoProvider} to set the pause image.
    438      *
    439      * @param uri URI of image to display.
    440      */
    441     @Override
    442     public void onSetPauseImage(Uri uri) {
    443         synchronized (mLock) {
    444             logFromInCall("setPauseImage: " + uri);
    445             try {
    446                 mConectionServiceVideoProvider.setPauseImage(uri);
    447             } catch (RemoteException e) {
    448             }
    449         }
    450     }
    451 
    452     /**
    453      * Add a listener to this {@link VideoProviderProxy}.
    454      *
    455      * @param listener The listener.
    456      */
    457     public void addListener(Listener listener) {
    458         mListeners.add(listener);
    459     }
    460 
    461     /**
    462      * Remove a listener from this {@link VideoProviderProxy}.
    463      *
    464      * @param listener The listener.
    465      */
    466     public void removeListener(Listener listener) {
    467         if (listener != null) {
    468             mListeners.remove(listener);
    469         }
    470     }
    471 
    472     /**
    473      * Logs a message originating from the {@link InCallService}.
    474      *
    475      * @param toLog The message to log.
    476      */
    477     private void logFromInCall(String toLog) {
    478         Log.v(this, "IC->VP: " + toLog);
    479     }
    480 
    481     /**
    482      * Logs a message originating from the {@link android.telecom.ConnectionService}'s
    483      * {@link Connection.VideoProvider}.
    484      *
    485      * @param toLog The message to log.
    486      */
    487     private void logFromVideoProvider(String toLog) {
    488         Log.v(this, "VP->IC: " + toLog);
    489     }
    490 }
    491