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