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