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