Home | History | Annotate | Download | only in telecom
      1 /*
      2  * Copyright (C) 2016 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.telecom.Connection;
     21 import android.telecom.ParcelableCall;
     22 import android.telecom.ParcelableRttCall;
     23 import android.telecom.TelecomManager;
     24 
     25 import java.util.ArrayList;
     26 import java.util.List;
     27 
     28 /**
     29  * Utilities dealing with {@link ParcelableCall}.
     30  */
     31 public class ParcelableCallUtils {
     32     private static final int CALL_STATE_OVERRIDE_NONE = -1;
     33 
     34     public static class Converter {
     35         public ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider,
     36                 PhoneAccountRegistrar phoneAccountRegistrar) {
     37             return ParcelableCallUtils.toParcelableCall(
     38                     call, includeVideoProvider, phoneAccountRegistrar, false, false);
     39         }
     40     }
     41 
     42     /**
     43      * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
     44      *
     45      * @param call The {@link Call} to parcel.
     46      * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
     47      *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
     48      *      method creates a {@link VideoCallImpl} instance on access it is important for the
     49      *      recipient of the {@link ParcelableCall} to know if the video provider changed.
     50      * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
     51      * @param supportsExternalCalls Indicates whether the call should be parcelled for an
     52      *      {@link InCallService} which supports external calls or not.
     53      */
     54     public static ParcelableCall toParcelableCall(
     55             Call call,
     56             boolean includeVideoProvider,
     57             PhoneAccountRegistrar phoneAccountRegistrar,
     58             boolean supportsExternalCalls,
     59             boolean includeRttCall) {
     60         return toParcelableCall(call, includeVideoProvider, phoneAccountRegistrar,
     61                 supportsExternalCalls, CALL_STATE_OVERRIDE_NONE /* overrideState */,
     62                 includeRttCall);
     63     }
     64 
     65     /**
     66      * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
     67      *
     68      * @param call The {@link Call} to parcel.
     69      * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
     70      *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
     71      *      method creates a {@link VideoCallImpl} instance on access it is important for the
     72      *      recipient of the {@link ParcelableCall} to know if the video provider changed.
     73      * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
     74      * @param supportsExternalCalls Indicates whether the call should be parcelled for an
     75      *      {@link InCallService} which supports external calls or not.
     76      * @param overrideState When not {@link #CALL_STATE_OVERRIDE_NONE}, use the provided state as an
     77      *      override to whatever is defined in the call.
     78      * @return The {@link ParcelableCall} containing all call information from the {@link Call}.
     79      */
     80     public static ParcelableCall toParcelableCall(
     81             Call call,
     82             boolean includeVideoProvider,
     83             PhoneAccountRegistrar phoneAccountRegistrar,
     84             boolean supportsExternalCalls,
     85             int overrideState,
     86             boolean includeRttCall) {
     87         int state;
     88         if (overrideState == CALL_STATE_OVERRIDE_NONE) {
     89             state = getParcelableState(call, supportsExternalCalls);
     90         } else {
     91             state = overrideState;
     92         }
     93         int capabilities = convertConnectionToCallCapabilities(call.getConnectionCapabilities());
     94         int properties = convertConnectionToCallProperties(call.getConnectionProperties());
     95         int supportedAudioRoutes = call.getSupportedAudioRoutes();
     96 
     97         if (call.isConference()) {
     98             properties |= android.telecom.Call.Details.PROPERTY_CONFERENCE;
     99         }
    100 
    101         if (call.isWorkCall()) {
    102             properties |= android.telecom.Call.Details.PROPERTY_ENTERPRISE_CALL;
    103         }
    104 
    105         // If this is a single-SIM device, the "default SIM" will always be the only SIM.
    106         boolean isDefaultSmsAccount = phoneAccountRegistrar != null &&
    107                 phoneAccountRegistrar.isUserSelectedSmsPhoneAccount(call.getTargetPhoneAccount());
    108         if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) {
    109             capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT;
    110         }
    111 
    112         if (call.isEmergencyCall()) {
    113             capabilities = removeCapability(
    114                     capabilities, android.telecom.Call.Details.CAPABILITY_MUTE);
    115         }
    116 
    117         if (state == android.telecom.Call.STATE_DIALING) {
    118             capabilities = removeCapability(capabilities,
    119                     android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
    120             capabilities = removeCapability(capabilities,
    121                     android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
    122         }
    123 
    124         String parentCallId = null;
    125         Call parentCall = call.getParentCall();
    126         if (parentCall != null) {
    127             parentCallId = parentCall.getId();
    128         }
    129 
    130         long connectTimeMillis = call.getConnectTimeMillis();
    131         List<Call> childCalls = call.getChildCalls();
    132         List<String> childCallIds = new ArrayList<>();
    133         if (!childCalls.isEmpty()) {
    134             long childConnectTimeMillis = Long.MAX_VALUE;
    135             for (Call child : childCalls) {
    136                 if (child.getConnectTimeMillis() > 0) {
    137                     childConnectTimeMillis = Math.min(child.getConnectTimeMillis(),
    138                             childConnectTimeMillis);
    139                 }
    140                 childCallIds.add(child.getId());
    141             }
    142 
    143             if (childConnectTimeMillis != Long.MAX_VALUE) {
    144                 connectTimeMillis = childConnectTimeMillis;
    145             }
    146         }
    147 
    148         Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
    149                 call.getHandle() : null;
    150         String callerDisplayName = call.getCallerDisplayNamePresentation() ==
    151                 TelecomManager.PRESENTATION_ALLOWED ?  call.getCallerDisplayName() : null;
    152 
    153         List<Call> conferenceableCalls = call.getConferenceableCalls();
    154         List<String> conferenceableCallIds = new ArrayList<String>(conferenceableCalls.size());
    155         for (Call otherCall : conferenceableCalls) {
    156             conferenceableCallIds.add(otherCall.getId());
    157         }
    158 
    159         ParcelableRttCall rttCall = includeRttCall ? getParcelableRttCall(call) : null;
    160 
    161         return new ParcelableCall(
    162                 call.getId(),
    163                 state,
    164                 call.getDisconnectCause(),
    165                 call.getCannedSmsResponses(),
    166                 capabilities,
    167                 properties,
    168                 supportedAudioRoutes,
    169                 connectTimeMillis,
    170                 handle,
    171                 call.getHandlePresentation(),
    172                 callerDisplayName,
    173                 call.getCallerDisplayNamePresentation(),
    174                 call.getGatewayInfo(),
    175                 call.getTargetPhoneAccount(),
    176                 includeVideoProvider,
    177                 includeVideoProvider ? call.getVideoProvider() : null,
    178                 includeRttCall,
    179                 rttCall,
    180                 parentCallId,
    181                 childCallIds,
    182                 call.getStatusHints(),
    183                 call.getVideoState(),
    184                 conferenceableCallIds,
    185                 call.getIntentExtras(),
    186                 call.getExtras(),
    187                 call.getCreationTimeMillis());
    188     }
    189 
    190     private static int getParcelableState(Call call, boolean supportsExternalCalls) {
    191         int state = CallState.NEW;
    192         switch (call.getState()) {
    193             case CallState.ABORTED:
    194             case CallState.DISCONNECTED:
    195                 state = android.telecom.Call.STATE_DISCONNECTED;
    196                 break;
    197             case CallState.ACTIVE:
    198                 state = android.telecom.Call.STATE_ACTIVE;
    199                 break;
    200             case CallState.CONNECTING:
    201                 state = android.telecom.Call.STATE_CONNECTING;
    202                 break;
    203             case CallState.DIALING:
    204                 state = android.telecom.Call.STATE_DIALING;
    205                 break;
    206             case CallState.PULLING:
    207                 if (supportsExternalCalls) {
    208                     // The InCallService supports external calls, so it must handle
    209                     // STATE_PULLING_CALL.
    210                     state = android.telecom.Call.STATE_PULLING_CALL;
    211                 } else {
    212                     // The InCallService does NOT support external calls, so remap
    213                     // STATE_PULLING_CALL to STATE_DIALING.  In essence, pulling a call can be seen
    214                     // as a form of dialing, so it is appropriate for InCallServices which do not
    215                     // handle external calls.
    216                     state = android.telecom.Call.STATE_DIALING;
    217                 }
    218                 break;
    219             case CallState.DISCONNECTING:
    220                 state = android.telecom.Call.STATE_DISCONNECTING;
    221                 break;
    222             case CallState.NEW:
    223                 state = android.telecom.Call.STATE_NEW;
    224                 break;
    225             case CallState.ON_HOLD:
    226                 state = android.telecom.Call.STATE_HOLDING;
    227                 break;
    228             case CallState.RINGING:
    229                 state = android.telecom.Call.STATE_RINGING;
    230                 break;
    231             case CallState.SELECT_PHONE_ACCOUNT:
    232                 state = android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT;
    233                 break;
    234         }
    235 
    236         // If we are marked as 'locally disconnecting' then mark ourselves as disconnecting instead.
    237         // Unless we're disconnect*ED*, in which case leave it at that.
    238         if (call.isLocallyDisconnecting() &&
    239                 (state != android.telecom.Call.STATE_DISCONNECTED)) {
    240             state = android.telecom.Call.STATE_DISCONNECTING;
    241         }
    242         return state;
    243     }
    244 
    245     private static final int[] CONNECTION_TO_CALL_CAPABILITY = new int[] {
    246         Connection.CAPABILITY_HOLD,
    247         android.telecom.Call.Details.CAPABILITY_HOLD,
    248 
    249         Connection.CAPABILITY_SUPPORT_HOLD,
    250         android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD,
    251 
    252         Connection.CAPABILITY_MERGE_CONFERENCE,
    253         android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE,
    254 
    255         Connection.CAPABILITY_SWAP_CONFERENCE,
    256         android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE,
    257 
    258         Connection.CAPABILITY_RESPOND_VIA_TEXT,
    259         android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT,
    260 
    261         Connection.CAPABILITY_MUTE,
    262         android.telecom.Call.Details.CAPABILITY_MUTE,
    263 
    264         Connection.CAPABILITY_MANAGE_CONFERENCE,
    265         android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE,
    266 
    267         Connection.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
    268         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
    269 
    270         Connection.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
    271         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
    272 
    273         Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
    274         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
    275 
    276         Connection.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
    277         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
    278 
    279         Connection.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
    280         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
    281 
    282         Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
    283         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
    284 
    285         Connection.CAPABILITY_SEPARATE_FROM_CONFERENCE,
    286         android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE,
    287 
    288         Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
    289         android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
    290 
    291         Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
    292         android.telecom.Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
    293 
    294         Connection.CAPABILITY_CAN_PAUSE_VIDEO,
    295         android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO,
    296 
    297         Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
    298         android.telecom.Call.Details.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
    299 
    300         Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
    301         android.telecom.Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
    302 
    303         Connection.CAPABILITY_CAN_PULL_CALL,
    304         android.telecom.Call.Details.CAPABILITY_CAN_PULL_CALL
    305     };
    306 
    307     private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
    308         int callCapabilities = 0;
    309         for (int i = 0; i < CONNECTION_TO_CALL_CAPABILITY.length; i += 2) {
    310             if ((CONNECTION_TO_CALL_CAPABILITY[i] & connectionCapabilities) ==
    311                     CONNECTION_TO_CALL_CAPABILITY[i]) {
    312 
    313                 callCapabilities |= CONNECTION_TO_CALL_CAPABILITY[i + 1];
    314             }
    315         }
    316         return callCapabilities;
    317     }
    318 
    319     private static final int[] CONNECTION_TO_CALL_PROPERTIES = new int[] {
    320         Connection.PROPERTY_HIGH_DEF_AUDIO,
    321         android.telecom.Call.Details.PROPERTY_HIGH_DEF_AUDIO,
    322 
    323         Connection.PROPERTY_WIFI,
    324         android.telecom.Call.Details.PROPERTY_WIFI,
    325 
    326         Connection.PROPERTY_GENERIC_CONFERENCE,
    327         android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE,
    328 
    329         Connection.PROPERTY_EMERGENCY_CALLBACK_MODE,
    330         android.telecom.Call.Details.PROPERTY_EMERGENCY_CALLBACK_MODE,
    331 
    332         Connection.PROPERTY_IS_EXTERNAL_CALL,
    333         android.telecom.Call.Details.PROPERTY_IS_EXTERNAL_CALL,
    334 
    335         Connection.PROPERTY_HAS_CDMA_VOICE_PRIVACY,
    336         android.telecom.Call.Details.PROPERTY_HAS_CDMA_VOICE_PRIVACY,
    337 
    338         Connection.PROPERTY_SELF_MANAGED,
    339         android.telecom.Call.Details.PROPERTY_SELF_MANAGED
    340     };
    341 
    342     private static int convertConnectionToCallProperties(int connectionProperties) {
    343         int callProperties = 0;
    344         for (int i = 0; i < CONNECTION_TO_CALL_PROPERTIES.length; i += 2) {
    345             if ((CONNECTION_TO_CALL_PROPERTIES[i] & connectionProperties) ==
    346                     CONNECTION_TO_CALL_PROPERTIES[i]) {
    347 
    348                 callProperties |= CONNECTION_TO_CALL_PROPERTIES[i + 1];
    349             }
    350         }
    351         return callProperties;
    352     }
    353 
    354     /**
    355      * Removes the specified capability from the set of capabilities bits and returns the new set.
    356      */
    357     private static int removeCapability(int capabilities, int capability) {
    358         return capabilities & ~capability;
    359     }
    360 
    361     private static ParcelableRttCall getParcelableRttCall(Call call) {
    362         if (!call.isRttCall()) {
    363             return null;
    364         }
    365         return new ParcelableRttCall(call.getRttMode(), call.getInCallToCsRttPipeForInCall(),
    366                 call.getCsToInCallRttPipeForInCall());
    367     }
    368 
    369     private ParcelableCallUtils() {}
    370 }
    371